aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/Makefile112
-rw-r--r--kernel/module_signing.c213
-rw-r--r--kernel/system_certificates.S3
-rw-r--r--kernel/system_keyring.c53
4 files changed, 154 insertions, 227 deletions
diff --git a/kernel/Makefile b/kernel/Makefile
index 43c4c920f30a..65ef3846fbe8 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -114,46 +114,74 @@ $(obj)/config_data.h: $(obj)/config_data.gz FORCE
114 114
115############################################################################### 115###############################################################################
116# 116#
117# Roll all the X.509 certificates that we can find together and pull them into 117# When a Kconfig string contains a filename, it is suitable for
118# the kernel so that they get loaded into the system trusted keyring during 118# passing to shell commands. It is surrounded by double-quotes, and
119# boot. 119# any double-quotes or backslashes within it are escaped by
120# backslashes.
120# 121#
121# We look in the source root and the build root for all files whose name ends 122# This is no use for dependencies or $(wildcard). We need to strip the
122# in ".x509". Unfortunately, this will generate duplicate filenames, so we 123# surrounding quotes and the escaping from quotes and backslashes, and
123# have make canonicalise the pathnames and then sort them to discard the 124# we *do* need to escape any spaces in the string. So, for example:
124# duplicates. 125#
126# Usage: $(eval $(call config_filename,FOO))
127#
128# Defines FOO_FILENAME based on the contents of the CONFIG_FOO option,
129# transformed as described above to be suitable for use within the
130# makefile.
131#
132# Also, if the filename is a relative filename and exists in the source
133# tree but not the build tree, define FOO_SRCPREFIX as $(srctree)/ to
134# be prefixed to *both* command invocation and dependencies.
135#
136# Note: We also print the filenames in the quiet_cmd_foo text, and
137# perhaps ought to have a version specially escaped for that purpose.
138# But it's only cosmetic, and $(patsubst "%",%,$(CONFIG_FOO)) is good
139# enough. It'll strip the quotes in the common case where there's no
140# space and it's a simple filename, and it'll retain the quotes when
141# there's a space. There are some esoteric cases in which it'll print
142# the wrong thing, but we don't really care. The actual dependencies
143# and commands *do* get it right, with various combinations of single
144# and double quotes, backslashes and spaces in the filenames.
125# 145#
126############################################################################### 146###############################################################################
127ifeq ($(CONFIG_SYSTEM_TRUSTED_KEYRING),y) 147#
128X509_CERTIFICATES-y := $(wildcard *.x509) $(wildcard $(srctree)/*.x509) 148quote := $(firstword " ")
129X509_CERTIFICATES-$(CONFIG_MODULE_SIG) += $(objtree)/signing_key.x509 149space :=
130X509_CERTIFICATES-raw := $(sort $(foreach CERT,$(X509_CERTIFICATES-y), \ 150space +=
131 $(or $(realpath $(CERT)),$(CERT)))) 151space_escape := %%%SPACE%%%
132X509_CERTIFICATES := $(subst $(realpath $(objtree))/,,$(X509_CERTIFICATES-raw)) 152#
133 153define config_filename
134ifeq ($(X509_CERTIFICATES),) 154ifneq ($$(CONFIG_$(1)),"")
135$(warning *** No X.509 certificates found ***) 155$(1)_FILENAME := $$(subst \\,\,$$(subst \$$(quote),$$(quote),$$(subst $$(space_escape),\$$(space),$$(patsubst "%",%,$$(subst $$(space),$$(space_escape),$$(CONFIG_$(1)))))))
156ifneq ($$(patsubst /%,%,$$(firstword $$($(1)_FILENAME))),$$(firstword $$($(1)_FILENAME)))
157else
158ifeq ($$(wildcard $$($(1)_FILENAME)),)
159ifneq ($$(wildcard $$(srctree)/$$($(1)_FILENAME)),)
160$(1)_SRCPREFIX := $(srctree)/
136endif 161endif
137
138ifneq ($(wildcard $(obj)/.x509.list),)
139ifneq ($(shell cat $(obj)/.x509.list),$(X509_CERTIFICATES))
140$(warning X.509 certificate list changed to "$(X509_CERTIFICATES)" from "$(shell cat $(obj)/.x509.list)")
141$(shell rm $(obj)/.x509.list)
142endif 162endif
143endif 163endif
164endif
165endef
166#
167###############################################################################
168
169ifeq ($(CONFIG_SYSTEM_TRUSTED_KEYRING),y)
144 170
145kernel/system_certificates.o: $(obj)/x509_certificate_list 171$(eval $(call config_filename,SYSTEM_TRUSTED_KEYS))
146 172
147quiet_cmd_x509certs = CERTS $@ 173# GCC doesn't include .incbin files in -MD generated dependencies (PR#66871)
148 cmd_x509certs = cat $(X509_CERTIFICATES) /dev/null >$@ $(foreach X509,$(X509_CERTIFICATES),; $(kecho) " - Including cert $(X509)") 174$(obj)/system_certificates.o: $(obj)/x509_certificate_list
149 175
150targets += $(obj)/x509_certificate_list 176# Cope with signing_key.x509 existing in $(srctree) not $(objtree)
151$(obj)/x509_certificate_list: $(X509_CERTIFICATES) $(obj)/.x509.list 177AFLAGS_system_certificates.o := -I$(srctree)
152 $(call if_changed,x509certs)
153 178
154targets += $(obj)/.x509.list 179quiet_cmd_extract_certs = EXTRACT_CERTS $(patsubst "%",%,$(2))
155$(obj)/.x509.list: 180 cmd_extract_certs = scripts/extract-cert $(2) $@ || ( rm $@; exit 1)
156 @echo $(X509_CERTIFICATES) >$@ 181
182targets += x509_certificate_list
183$(obj)/x509_certificate_list: scripts/extract-cert $(SYSTEM_TRUSTED_KEYS_SRCPREFIX)$(SYSTEM_TRUSTED_KEYS_FILENAME) FORCE
184 $(call if_changed,extract_certs,$(SYSTEM_TRUSTED_KEYS_SRCPREFIX)$(CONFIG_SYSTEM_TRUSTED_KEYS))
157endif 185endif
158 186
159clean-files := x509_certificate_list .x509.list 187clean-files := x509_certificate_list .x509.list
@@ -170,7 +198,11 @@ ifndef CONFIG_MODULE_SIG_HASH
170$(error Could not determine digest type to use from kernel config) 198$(error Could not determine digest type to use from kernel config)
171endif 199endif
172 200
173signing_key.priv signing_key.x509: x509.genkey 201# We do it this way rather than having a boolean option for enabling an
202# external private key, because 'make randconfig' might enable such a
203# boolean option and we unfortunately can't make it depend on !RANDCONFIG.
204ifeq ($(CONFIG_MODULE_SIG_KEY),"signing_key.pem")
205signing_key.pem: x509.genkey
174 @echo "###" 206 @echo "###"
175 @echo "### Now generating an X.509 key pair to be used for signing modules." 207 @echo "### Now generating an X.509 key pair to be used for signing modules."
176 @echo "###" 208 @echo "###"
@@ -181,8 +213,8 @@ signing_key.priv signing_key.x509: x509.genkey
181 @echo "###" 213 @echo "###"
182 openssl req -new -nodes -utf8 -$(CONFIG_MODULE_SIG_HASH) -days 36500 \ 214 openssl req -new -nodes -utf8 -$(CONFIG_MODULE_SIG_HASH) -days 36500 \
183 -batch -x509 -config x509.genkey \ 215 -batch -x509 -config x509.genkey \
184 -outform DER -out signing_key.x509 \ 216 -outform PEM -out signing_key.pem \
185 -keyout signing_key.priv 2>&1 217 -keyout signing_key.pem 2>&1
186 @echo "###" 218 @echo "###"
187 @echo "### Key pair generated." 219 @echo "### Key pair generated."
188 @echo "###" 220 @echo "###"
@@ -207,3 +239,17 @@ x509.genkey:
207 @echo >>x509.genkey "subjectKeyIdentifier=hash" 239 @echo >>x509.genkey "subjectKeyIdentifier=hash"
208 @echo >>x509.genkey "authorityKeyIdentifier=keyid" 240 @echo >>x509.genkey "authorityKeyIdentifier=keyid"
209endif 241endif
242
243$(eval $(call config_filename,MODULE_SIG_KEY))
244
245# If CONFIG_MODULE_SIG_KEY isn't a PKCS#11 URI, depend on it
246ifeq ($(patsubst pkcs11:%,%,$(firstword $(MODULE_SIG_KEY_FILENAME))),$(firstword $(MODULE_SIG_KEY_FILENAME)))
247X509_DEP := $(MODULE_SIG_KEY_SRCPREFIX)$(MODULE_SIG_KEY_FILENAME)
248endif
249
250# GCC PR#66871 again.
251$(obj)/system_certificates.o: signing_key.x509
252
253signing_key.x509: scripts/extract-cert include/config/module/sig/key.h $(X509_DEP)
254 $(call cmd,extract_certs,$(MODULE_SIG_KEY_SRCPREFIX)$(CONFIG_MODULE_SIG_KEY))
255endif
diff --git a/kernel/module_signing.c b/kernel/module_signing.c
index be5b8fac4bd0..bd62f5cda746 100644
--- a/kernel/module_signing.c
+++ b/kernel/module_signing.c
@@ -10,11 +10,8 @@
10 */ 10 */
11 11
12#include <linux/kernel.h> 12#include <linux/kernel.h>
13#include <linux/err.h>
14#include <crypto/public_key.h>
15#include <crypto/hash.h>
16#include <keys/asymmetric-type.h>
17#include <keys/system_keyring.h> 13#include <keys/system_keyring.h>
14#include <crypto/public_key.h>
18#include "module-internal.h" 15#include "module-internal.h"
19 16
20/* 17/*
@@ -28,170 +25,22 @@
28 * - Information block 25 * - Information block
29 */ 26 */
30struct module_signature { 27struct module_signature {
31 u8 algo; /* Public-key crypto algorithm [enum pkey_algo] */ 28 u8 algo; /* Public-key crypto algorithm [0] */
32 u8 hash; /* Digest algorithm [enum hash_algo] */ 29 u8 hash; /* Digest algorithm [0] */
33 u8 id_type; /* Key identifier type [enum pkey_id_type] */ 30 u8 id_type; /* Key identifier type [PKEY_ID_PKCS7] */
34 u8 signer_len; /* Length of signer's name */ 31 u8 signer_len; /* Length of signer's name [0] */
35 u8 key_id_len; /* Length of key identifier */ 32 u8 key_id_len; /* Length of key identifier [0] */
36 u8 __pad[3]; 33 u8 __pad[3];
37 __be32 sig_len; /* Length of signature data */ 34 __be32 sig_len; /* Length of signature data */
38}; 35};
39 36
40/* 37/*
41 * Digest the module contents.
42 */
43static struct public_key_signature *mod_make_digest(enum hash_algo hash,
44 const void *mod,
45 unsigned long modlen)
46{
47 struct public_key_signature *pks;
48 struct crypto_shash *tfm;
49 struct shash_desc *desc;
50 size_t digest_size, desc_size;
51 int ret;
52
53 pr_devel("==>%s()\n", __func__);
54
55 /* Allocate the hashing algorithm we're going to need and find out how
56 * big the hash operational data will be.
57 */
58 tfm = crypto_alloc_shash(hash_algo_name[hash], 0, 0);
59 if (IS_ERR(tfm))
60 return (PTR_ERR(tfm) == -ENOENT) ? ERR_PTR(-ENOPKG) : ERR_CAST(tfm);
61
62 desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
63 digest_size = crypto_shash_digestsize(tfm);
64
65 /* We allocate the hash operational data storage on the end of our
66 * context data and the digest output buffer on the end of that.
67 */
68 ret = -ENOMEM;
69 pks = kzalloc(digest_size + sizeof(*pks) + desc_size, GFP_KERNEL);
70 if (!pks)
71 goto error_no_pks;
72
73 pks->pkey_hash_algo = hash;
74 pks->digest = (u8 *)pks + sizeof(*pks) + desc_size;
75 pks->digest_size = digest_size;
76
77 desc = (void *)pks + sizeof(*pks);
78 desc->tfm = tfm;
79 desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
80
81 ret = crypto_shash_init(desc);
82 if (ret < 0)
83 goto error;
84
85 ret = crypto_shash_finup(desc, mod, modlen, pks->digest);
86 if (ret < 0)
87 goto error;
88
89 crypto_free_shash(tfm);
90 pr_devel("<==%s() = ok\n", __func__);
91 return pks;
92
93error:
94 kfree(pks);
95error_no_pks:
96 crypto_free_shash(tfm);
97 pr_devel("<==%s() = %d\n", __func__, ret);
98 return ERR_PTR(ret);
99}
100
101/*
102 * Extract an MPI array from the signature data. This represents the actual
103 * signature. Each raw MPI is prefaced by a BE 2-byte value indicating the
104 * size of the MPI in bytes.
105 *
106 * RSA signatures only have one MPI, so currently we only read one.
107 */
108static int mod_extract_mpi_array(struct public_key_signature *pks,
109 const void *data, size_t len)
110{
111 size_t nbytes;
112 MPI mpi;
113
114 if (len < 3)
115 return -EBADMSG;
116 nbytes = ((const u8 *)data)[0] << 8 | ((const u8 *)data)[1];
117 data += 2;
118 len -= 2;
119 if (len != nbytes)
120 return -EBADMSG;
121
122 mpi = mpi_read_raw_data(data, nbytes);
123 if (!mpi)
124 return -ENOMEM;
125 pks->mpi[0] = mpi;
126 pks->nr_mpi = 1;
127 return 0;
128}
129
130/*
131 * Request an asymmetric key.
132 */
133static struct key *request_asymmetric_key(const char *signer, size_t signer_len,
134 const u8 *key_id, size_t key_id_len)
135{
136 key_ref_t key;
137 size_t i;
138 char *id, *q;
139
140 pr_devel("==>%s(,%zu,,%zu)\n", __func__, signer_len, key_id_len);
141
142 /* Construct an identifier. */
143 id = kmalloc(signer_len + 2 + key_id_len * 2 + 1, GFP_KERNEL);
144 if (!id)
145 return ERR_PTR(-ENOKEY);
146
147 memcpy(id, signer, signer_len);
148
149 q = id + signer_len;
150 *q++ = ':';
151 *q++ = ' ';
152 for (i = 0; i < key_id_len; i++) {
153 *q++ = hex_asc[*key_id >> 4];
154 *q++ = hex_asc[*key_id++ & 0x0f];
155 }
156
157 *q = 0;
158
159 pr_debug("Look up: \"%s\"\n", id);
160
161 key = keyring_search(make_key_ref(system_trusted_keyring, 1),
162 &key_type_asymmetric, id);
163 if (IS_ERR(key))
164 pr_warn("Request for unknown module key '%s' err %ld\n",
165 id, PTR_ERR(key));
166 kfree(id);
167
168 if (IS_ERR(key)) {
169 switch (PTR_ERR(key)) {
170 /* Hide some search errors */
171 case -EACCES:
172 case -ENOTDIR:
173 case -EAGAIN:
174 return ERR_PTR(-ENOKEY);
175 default:
176 return ERR_CAST(key);
177 }
178 }
179
180 pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key_ref_to_ptr(key)));
181 return key_ref_to_ptr(key);
182}
183
184/*
185 * Verify the signature on a module. 38 * Verify the signature on a module.
186 */ 39 */
187int mod_verify_sig(const void *mod, unsigned long *_modlen) 40int mod_verify_sig(const void *mod, unsigned long *_modlen)
188{ 41{
189 struct public_key_signature *pks;
190 struct module_signature ms; 42 struct module_signature ms;
191 struct key *key;
192 const void *sig;
193 size_t modlen = *_modlen, sig_len; 43 size_t modlen = *_modlen, sig_len;
194 int ret;
195 44
196 pr_devel("==>%s(,%zu)\n", __func__, modlen); 45 pr_devel("==>%s(,%zu)\n", __func__, modlen);
197 46
@@ -205,46 +54,24 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen)
205 if (sig_len >= modlen) 54 if (sig_len >= modlen)
206 return -EBADMSG; 55 return -EBADMSG;
207 modlen -= sig_len; 56 modlen -= sig_len;
208 if ((size_t)ms.signer_len + ms.key_id_len >= modlen)
209 return -EBADMSG;
210 modlen -= (size_t)ms.signer_len + ms.key_id_len;
211
212 *_modlen = modlen; 57 *_modlen = modlen;
213 sig = mod + modlen;
214
215 /* For the moment, only support RSA and X.509 identifiers */
216 if (ms.algo != PKEY_ALGO_RSA ||
217 ms.id_type != PKEY_ID_X509)
218 return -ENOPKG;
219 58
220 if (ms.hash >= PKEY_HASH__LAST || 59 if (ms.id_type != PKEY_ID_PKCS7) {
221 !hash_algo_name[ms.hash]) 60 pr_err("Module is not signed with expected PKCS#7 message\n");
222 return -ENOPKG; 61 return -ENOPKG;
223
224 key = request_asymmetric_key(sig, ms.signer_len,
225 sig + ms.signer_len, ms.key_id_len);
226 if (IS_ERR(key))
227 return PTR_ERR(key);
228
229 pks = mod_make_digest(ms.hash, mod, modlen);
230 if (IS_ERR(pks)) {
231 ret = PTR_ERR(pks);
232 goto error_put_key;
233 } 62 }
234 63
235 ret = mod_extract_mpi_array(pks, sig + ms.signer_len + ms.key_id_len, 64 if (ms.algo != 0 ||
236 sig_len); 65 ms.hash != 0 ||
237 if (ret < 0) 66 ms.signer_len != 0 ||
238 goto error_free_pks; 67 ms.key_id_len != 0 ||
239 68 ms.__pad[0] != 0 ||
240 ret = verify_signature(key, pks); 69 ms.__pad[1] != 0 ||
241 pr_devel("verify_signature() = %d\n", ret); 70 ms.__pad[2] != 0) {
71 pr_err("PKCS#7 signature info has unexpected non-zero params\n");
72 return -EBADMSG;
73 }
242 74
243error_free_pks: 75 return system_verify_data(mod, modlen, mod + modlen, sig_len,
244 mpi_free(pks->rsa.s); 76 VERIFYING_MODULE_SIGNATURE);
245 kfree(pks);
246error_put_key:
247 key_put(key);
248 pr_devel("<==%s() = %d\n", __func__, ret);
249 return ret;
250} 77}
diff --git a/kernel/system_certificates.S b/kernel/system_certificates.S
index 3e9868d47535..6ba2f75e7ba5 100644
--- a/kernel/system_certificates.S
+++ b/kernel/system_certificates.S
@@ -7,6 +7,9 @@
7 .globl VMLINUX_SYMBOL(system_certificate_list) 7 .globl VMLINUX_SYMBOL(system_certificate_list)
8VMLINUX_SYMBOL(system_certificate_list): 8VMLINUX_SYMBOL(system_certificate_list):
9__cert_list_start: 9__cert_list_start:
10#ifdef CONFIG_MODULE_SIG
11 .incbin "signing_key.x509"
12#endif
10 .incbin "kernel/x509_certificate_list" 13 .incbin "kernel/x509_certificate_list"
11__cert_list_end: 14__cert_list_end:
12 15
diff --git a/kernel/system_keyring.c b/kernel/system_keyring.c
index 875f64e8935b..2570598b784d 100644
--- a/kernel/system_keyring.c
+++ b/kernel/system_keyring.c
@@ -16,7 +16,7 @@
16#include <linux/err.h> 16#include <linux/err.h>
17#include <keys/asymmetric-type.h> 17#include <keys/asymmetric-type.h>
18#include <keys/system_keyring.h> 18#include <keys/system_keyring.h>
19#include "module-internal.h" 19#include <crypto/pkcs7.h>
20 20
21struct key *system_trusted_keyring; 21struct key *system_trusted_keyring;
22EXPORT_SYMBOL_GPL(system_trusted_keyring); 22EXPORT_SYMBOL_GPL(system_trusted_keyring);
@@ -104,3 +104,54 @@ dodgy_cert:
104 return 0; 104 return 0;
105} 105}
106late_initcall(load_system_certificate_list); 106late_initcall(load_system_certificate_list);
107
108#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
109
110/**
111 * Verify a PKCS#7-based signature on system data.
112 * @data: The data to be verified.
113 * @len: Size of @data.
114 * @raw_pkcs7: The PKCS#7 message that is the signature.
115 * @pkcs7_len: The size of @raw_pkcs7.
116 * @usage: The use to which the key is being put.
117 */
118int system_verify_data(const void *data, unsigned long len,
119 const void *raw_pkcs7, size_t pkcs7_len,
120 enum key_being_used_for usage)
121{
122 struct pkcs7_message *pkcs7;
123 bool trusted;
124 int ret;
125
126 pkcs7 = pkcs7_parse_message(raw_pkcs7, pkcs7_len);
127 if (IS_ERR(pkcs7))
128 return PTR_ERR(pkcs7);
129
130 /* The data should be detached - so we need to supply it. */
131 if (pkcs7_supply_detached_data(pkcs7, data, len) < 0) {
132 pr_err("PKCS#7 signature with non-detached data\n");
133 ret = -EBADMSG;
134 goto error;
135 }
136
137 ret = pkcs7_verify(pkcs7, usage);
138 if (ret < 0)
139 goto error;
140
141 ret = pkcs7_validate_trust(pkcs7, system_trusted_keyring, &trusted);
142 if (ret < 0)
143 goto error;
144
145 if (!trusted) {
146 pr_err("PKCS#7 signature not signed with a trusted key\n");
147 ret = -ENOKEY;
148 }
149
150error:
151 pkcs7_free_message(pkcs7);
152 pr_devel("<==%s() = %d\n", __func__, ret);
153 return ret;
154}
155EXPORT_SYMBOL_GPL(system_verify_data);
156
157#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */