xref: /kernel/linux/linux-5.10/fs/verity/signature.c (revision 8c2ecf20)
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#include <linux/hck/lite_hck_code_sign.h>
15
16/*
17 * /proc/sys/fs/verity/require_signatures
18 * If 1, all verity files must have a valid builtin signature.
19 */
20static int fsverity_require_signatures;
21
22/*
23 * Keyring that contains the trusted X.509 certificates.
24 *
25 * Only root (kuid=0) can modify this.  Also, root may use
26 * keyctl_restrict_keyring() to prevent any more additions.
27 */
28static struct key *fsverity_keyring;
29
30#ifdef CONFIG_SECURITY_CODE_SIGN
31
32void fsverity_set_cert_type(struct fsverity_info *vi,
33	int cert_type)
34{
35	vi->cert_type = cert_type;
36}
37
38int fsverity_get_cert_type(const struct inode *inode)
39{
40	return fsverity_get_info(inode)->cert_type;
41}
42
43#else /* !CONFIG_SECURITY_CODE_SIGN */
44
45static void inline fsverity_set_cert_type(struct fsverity_info *verity_info,
46	int cert_type)
47{
48}
49
50#endif
51
52static inline int fsverity_verify_certchain(struct fsverity_info *vi,
53	const void *raw_pkcs7, size_t pkcs7_len)
54{
55	int ret = 0;
56
57	CALL_HCK_LITE_HOOK(code_sign_verify_certchain_lhck,
58		raw_pkcs7, pkcs7_len, &vi->fcs_info, &ret);
59	if (ret > 0) {
60		fsverity_set_cert_type(vi, ret);
61		ret = 0;
62	}
63
64	return ret;
65}
66
67/**
68 * fsverity_verify_signature() - check a verity file's signature
69 * @vi: the file's fsverity_info
70 * @desc: the file's fsverity_descriptor
71 * @desc_size: size of @desc
72 *
73 * If the file's fs-verity descriptor includes a signature of the file
74 * measurement, verify it against the certificates in the fs-verity keyring.
75 *
76 * Return: 0 on success (signature valid or not required); -errno on failure
77 */
78int fsverity_verify_signature(struct fsverity_info *vi,
79			      const struct fsverity_descriptor *desc,
80			      size_t desc_size)
81{
82	const struct inode *inode = vi->inode;
83	const struct fsverity_hash_alg *hash_alg = vi->tree_params.hash_alg;
84	const u32 sig_size = le32_to_cpu(desc->sig_size);
85	struct fsverity_signed_digest *d;
86	int err;
87
88	if (sig_size == 0) {
89		if (fsverity_require_signatures) {
90			fsverity_err(inode,
91				     "require_signatures=1, rejecting unsigned file!");
92			return -EPERM;
93		}
94		return 0;
95	}
96
97	if (sig_size > desc_size - sizeof(*desc)) {
98		fsverity_err(inode, "Signature overflows verity descriptor");
99		return -EBADMSG;
100	}
101
102	if (fsverity_keyring->keys.nr_leaves_on_tree == 0) {
103		/*
104		 * The ".fs-verity" keyring is empty, due to builtin signatures
105		 * being supported by the kernel but not actually being used.
106		 * In this case, verify_pkcs7_signature() would always return an
107		 * error, usually ENOKEY.  It could also be EBADMSG if the
108		 * PKCS#7 is malformed, but that isn't very important to
109		 * distinguish.  So, just skip to ENOKEY to avoid the attack
110		 * surface of the PKCS#7 parser, which would otherwise be
111		 * reachable by any task able to execute FS_IOC_ENABLE_VERITY.
112		 */
113		fsverity_err(inode,
114			     "fs-verity keyring is empty, rejecting signed file!");
115		return -ENOKEY;
116	}
117
118	d = kzalloc(sizeof(*d) + hash_alg->digest_size, GFP_KERNEL);
119	if (!d)
120		return -ENOMEM;
121	memcpy(d->magic, "FSVerity", 8);
122	d->digest_algorithm = cpu_to_le16(hash_alg - fsverity_hash_algs);
123	d->digest_size = cpu_to_le16(hash_alg->digest_size);
124	memcpy(d->digest, vi->measurement, hash_alg->digest_size);
125
126	err = fsverity_verify_certchain(vi, desc->signature, sig_size);
127	if (err) {
128		fsverity_err(inode, "verify cert chain failed, err = %d", err);
129		return err;
130	}
131	pr_debug("verify cert chain success\n");
132
133	err = verify_pkcs7_signature(d, sizeof(*d) + hash_alg->digest_size,
134				     desc->signature, sig_size,
135				     fsverity_keyring,
136				     VERIFYING_UNSPECIFIED_SIGNATURE,
137				     NULL, NULL);
138	kfree(d);
139
140	if (err) {
141		if (err == -ENOKEY)
142			fsverity_err(inode,
143				     "File's signing cert isn't in the fs-verity keyring");
144		else if (err == -EKEYREJECTED)
145			fsverity_err(inode, "Incorrect file signature");
146		else if (err == -EBADMSG)
147			fsverity_err(inode, "Malformed file signature");
148		else
149			fsverity_err(inode, "Error %d verifying file signature",
150				     err);
151		return err;
152	}
153
154	pr_debug("Valid signature for file measurement %s:%*phN\n",
155		 hash_alg->name, hash_alg->digest_size, vi->measurement);
156	return 0;
157}
158
159#ifdef CONFIG_SYSCTL
160static struct ctl_table_header *fsverity_sysctl_header;
161
162static const struct ctl_path fsverity_sysctl_path[] = {
163	{ .procname = "fs", },
164	{ .procname = "verity", },
165	{ }
166};
167
168static struct ctl_table fsverity_sysctl_table[] = {
169	{
170		.procname       = "require_signatures",
171		.data           = &fsverity_require_signatures,
172		.maxlen         = sizeof(int),
173		.mode           = 0644,
174		.proc_handler   = proc_dointvec_minmax,
175		.extra1         = SYSCTL_ZERO,
176		.extra2         = SYSCTL_ONE,
177	},
178	{ }
179};
180
181static int __init fsverity_sysctl_init(void)
182{
183	fsverity_sysctl_header = register_sysctl_paths(fsverity_sysctl_path,
184						       fsverity_sysctl_table);
185	if (!fsverity_sysctl_header) {
186		pr_err("sysctl registration failed!\n");
187		return -ENOMEM;
188	}
189	return 0;
190}
191#else /* !CONFIG_SYSCTL */
192static inline int __init fsverity_sysctl_init(void)
193{
194	return 0;
195}
196#endif /* !CONFIG_SYSCTL */
197
198int __init fsverity_init_signature(void)
199{
200	struct key *ring;
201	int err;
202
203	ring = keyring_alloc(".fs-verity", KUIDT_INIT(0), KGIDT_INIT(0),
204			     current_cred(), KEY_POS_SEARCH |
205				KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE |
206				KEY_USR_SEARCH | KEY_USR_SETATTR,
207			     KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
208	if (IS_ERR(ring))
209		return PTR_ERR(ring);
210
211	err = fsverity_sysctl_init();
212	if (err)
213		goto err_put_ring;
214
215	fsverity_keyring = ring;
216	return 0;
217
218err_put_ring:
219	key_put(ring);
220	return err;
221}
222