diff options
author | Eric Biggers <ebiggers@google.com> | 2019-07-22 12:26:23 -0400 |
---|---|---|
committer | Eric Biggers <ebiggers@google.com> | 2019-08-12 22:33:50 -0400 |
commit | 432434c9f8e18cb4cf0fe05bc3eeceada0e10dc6 (patch) | |
tree | cfcf57b2bb1fbdb1e9b5739f020b0311405363c0 /fs/verity/signature.c | |
parent | add890c9f9d2d1d79184ded72f23b37b164fc673 (diff) |
fs-verity: support builtin file signatures
To meet some users' needs, add optional support for having fs-verity
handle a portion of the authentication policy in the kernel. An
".fs-verity" keyring is created to which X.509 certificates can be
added; then a sysctl 'fs.verity.require_signatures' can be set to cause
the kernel to enforce that all fs-verity files contain a signature of
their file measurement by a key in this keyring.
See the "Built-in signature verification" section of
Documentation/filesystems/fsverity.rst for the full documentation.
Reviewed-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Eric Biggers <ebiggers@google.com>
Diffstat (limited to 'fs/verity/signature.c')
-rw-r--r-- | fs/verity/signature.c | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/fs/verity/signature.c b/fs/verity/signature.c new file mode 100644 index 000000000000..c8b255232de5 --- /dev/null +++ b/fs/verity/signature.c | |||
@@ -0,0 +1,157 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * fs/verity/signature.c: verification of builtin signatures | ||
4 | * | ||
5 | * Copyright 2019 Google LLC | ||
6 | */ | ||
7 | |||
8 | #include "fsverity_private.h" | ||
9 | |||
10 | #include <linux/cred.h> | ||
11 | #include <linux/key.h> | ||
12 | #include <linux/slab.h> | ||
13 | #include <linux/verification.h> | ||
14 | |||
15 | /* | ||
16 | * /proc/sys/fs/verity/require_signatures | ||
17 | * If 1, all verity files must have a valid builtin signature. | ||
18 | */ | ||
19 | static int fsverity_require_signatures; | ||
20 | |||
21 | /* | ||
22 | * Keyring that contains the trusted X.509 certificates. | ||
23 | * | ||
24 | * Only root (kuid=0) can modify this. Also, root may use | ||
25 | * keyctl_restrict_keyring() to prevent any more additions. | ||
26 | */ | ||
27 | static struct key *fsverity_keyring; | ||
28 | |||
29 | /** | ||
30 | * fsverity_verify_signature() - check a verity file's signature | ||
31 | * | ||
32 | * If the file's fs-verity descriptor includes a signature of the file | ||
33 | * measurement, verify it against the certificates in the fs-verity keyring. | ||
34 | * | ||
35 | * Return: 0 on success (signature valid or not required); -errno on failure | ||
36 | */ | ||
37 | int fsverity_verify_signature(const struct fsverity_info *vi, | ||
38 | const struct fsverity_descriptor *desc, | ||
39 | size_t desc_size) | ||
40 | { | ||
41 | const struct inode *inode = vi->inode; | ||
42 | const struct fsverity_hash_alg *hash_alg = vi->tree_params.hash_alg; | ||
43 | const u32 sig_size = le32_to_cpu(desc->sig_size); | ||
44 | struct fsverity_signed_digest *d; | ||
45 | int err; | ||
46 | |||
47 | if (sig_size == 0) { | ||
48 | if (fsverity_require_signatures) { | ||
49 | fsverity_err(inode, | ||
50 | "require_signatures=1, rejecting unsigned file!"); | ||
51 | return -EPERM; | ||
52 | } | ||
53 | return 0; | ||
54 | } | ||
55 | |||
56 | if (sig_size > desc_size - sizeof(*desc)) { | ||
57 | fsverity_err(inode, "Signature overflows verity descriptor"); | ||
58 | return -EBADMSG; | ||
59 | } | ||
60 | |||
61 | d = kzalloc(sizeof(*d) + hash_alg->digest_size, GFP_KERNEL); | ||
62 | if (!d) | ||
63 | return -ENOMEM; | ||
64 | memcpy(d->magic, "FSVerity", 8); | ||
65 | d->digest_algorithm = cpu_to_le16(hash_alg - fsverity_hash_algs); | ||
66 | d->digest_size = cpu_to_le16(hash_alg->digest_size); | ||
67 | memcpy(d->digest, vi->measurement, hash_alg->digest_size); | ||
68 | |||
69 | err = verify_pkcs7_signature(d, sizeof(*d) + hash_alg->digest_size, | ||
70 | desc->signature, sig_size, | ||
71 | fsverity_keyring, | ||
72 | VERIFYING_UNSPECIFIED_SIGNATURE, | ||
73 | NULL, NULL); | ||
74 | kfree(d); | ||
75 | |||
76 | if (err) { | ||
77 | if (err == -ENOKEY) | ||
78 | fsverity_err(inode, | ||
79 | "File's signing cert isn't in the fs-verity keyring"); | ||
80 | else if (err == -EKEYREJECTED) | ||
81 | fsverity_err(inode, "Incorrect file signature"); | ||
82 | else if (err == -EBADMSG) | ||
83 | fsverity_err(inode, "Malformed file signature"); | ||
84 | else | ||
85 | fsverity_err(inode, "Error %d verifying file signature", | ||
86 | err); | ||
87 | return err; | ||
88 | } | ||
89 | |||
90 | pr_debug("Valid signature for file measurement %s:%*phN\n", | ||
91 | hash_alg->name, hash_alg->digest_size, vi->measurement); | ||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | #ifdef CONFIG_SYSCTL | ||
96 | static struct ctl_table_header *fsverity_sysctl_header; | ||
97 | |||
98 | static const struct ctl_path fsverity_sysctl_path[] = { | ||
99 | { .procname = "fs", }, | ||
100 | { .procname = "verity", }, | ||
101 | { } | ||
102 | }; | ||
103 | |||
104 | static struct ctl_table fsverity_sysctl_table[] = { | ||
105 | { | ||
106 | .procname = "require_signatures", | ||
107 | .data = &fsverity_require_signatures, | ||
108 | .maxlen = sizeof(int), | ||
109 | .mode = 0644, | ||
110 | .proc_handler = proc_dointvec_minmax, | ||
111 | .extra1 = SYSCTL_ZERO, | ||
112 | .extra2 = SYSCTL_ONE, | ||
113 | }, | ||
114 | { } | ||
115 | }; | ||
116 | |||
117 | static int __init fsverity_sysctl_init(void) | ||
118 | { | ||
119 | fsverity_sysctl_header = register_sysctl_paths(fsverity_sysctl_path, | ||
120 | fsverity_sysctl_table); | ||
121 | if (!fsverity_sysctl_header) { | ||
122 | pr_err("sysctl registration failed!\n"); | ||
123 | return -ENOMEM; | ||
124 | } | ||
125 | return 0; | ||
126 | } | ||
127 | #else /* !CONFIG_SYSCTL */ | ||
128 | static inline int __init fsverity_sysctl_init(void) | ||
129 | { | ||
130 | return 0; | ||
131 | } | ||
132 | #endif /* !CONFIG_SYSCTL */ | ||
133 | |||
134 | int __init fsverity_init_signature(void) | ||
135 | { | ||
136 | struct key *ring; | ||
137 | int err; | ||
138 | |||
139 | ring = keyring_alloc(".fs-verity", KUIDT_INIT(0), KGIDT_INIT(0), | ||
140 | current_cred(), KEY_POS_SEARCH | | ||
141 | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE | | ||
142 | KEY_USR_SEARCH | KEY_USR_SETATTR, | ||
143 | KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); | ||
144 | if (IS_ERR(ring)) | ||
145 | return PTR_ERR(ring); | ||
146 | |||
147 | err = fsverity_sysctl_init(); | ||
148 | if (err) | ||
149 | goto err_put_ring; | ||
150 | |||
151 | fsverity_keyring = ring; | ||
152 | return 0; | ||
153 | |||
154 | err_put_ring: | ||
155 | key_put(ring); | ||
156 | return err; | ||
157 | } | ||