Skip to content

Commit 3f1e1be

Browse files
committed
MODSIGN: Use PKCS#7 messages as module signatures
Move to using PKCS#7 messages as module signatures because: (1) We have to be able to support the use of X.509 certificates that don't have a subjKeyId set. We're currently relying on this to look up the X.509 certificate in the trusted keyring list. (2) PKCS#7 message signed information blocks have a field that supplies the data required to match with the X.509 certificate that signed it. (3) The PKCS#7 certificate carries fields that specify the digest algorithm used to generate the signature in a standardised way and the X.509 certificates specify the public key algorithm in a standardised way - so we don't need our own methods of specifying these. (4) We now have PKCS#7 message support in the kernel for signed kexec purposes and we can make use of this. To make this work, the old sign-file script has been replaced with a program that needs compiling in a previous patch. The rules to build it are added here. Signed-off-by: David Howells <dhowells@redhat.com> Tested-by: Vivek Goyal <vgoyal@redhat.com>
1 parent bc1c373 commit 3f1e1be

File tree

5 files changed

+48
-598
lines changed

5 files changed

+48
-598
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -873,7 +873,7 @@ ifdef CONFIG_MODULE_SIG_ALL
873873
MODSECKEY = ./signing_key.priv
874874
MODPUBKEY = ./signing_key.x509
875875
export MODPUBKEY
876-
mod_sign_cmd = perl $(srctree)/scripts/sign-file $(CONFIG_MODULE_SIG_HASH) $(MODSECKEY) $(MODPUBKEY)
876+
mod_sign_cmd = scripts/sign-file $(CONFIG_MODULE_SIG_HASH) $(MODSECKEY) $(MODPUBKEY)
877877
else
878878
mod_sign_cmd = true
879879
endif

init/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1869,6 +1869,7 @@ config MODULE_SIG
18691869
select ASN1
18701870
select OID_REGISTRY
18711871
select X509_CERTIFICATE_PARSER
1872+
select PKCS7_MESSAGE_PARSER
18721873
help
18731874
Check modules for valid signatures upon load: the signature
18741875
is simply appended to the module. For more information see

kernel/module_signing.c

Lines changed: 44 additions & 176 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@
1111

1212
#include <linux/kernel.h>
1313
#include <linux/err.h>
14-
#include <crypto/public_key.h>
15-
#include <crypto/hash.h>
16-
#include <keys/asymmetric-type.h>
1714
#include <keys/system_keyring.h>
15+
#include <crypto/public_key.h>
16+
#include <crypto/pkcs7.h>
1817
#include "module-internal.h"
1918

2019
/*
@@ -28,170 +27,62 @@
2827
* - Information block
2928
*/
3029
struct module_signature {
31-
u8 algo; /* Public-key crypto algorithm [enum pkey_algo] */
32-
u8 hash; /* Digest algorithm [enum hash_algo] */
33-
u8 id_type; /* Key identifier type [enum pkey_id_type] */
34-
u8 signer_len; /* Length of signer's name */
35-
u8 key_id_len; /* Length of key identifier */
30+
u8 algo; /* Public-key crypto algorithm [0] */
31+
u8 hash; /* Digest algorithm [0] */
32+
u8 id_type; /* Key identifier type [PKEY_ID_PKCS7] */
33+
u8 signer_len; /* Length of signer's name [0] */
34+
u8 key_id_len; /* Length of key identifier [0] */
3635
u8 __pad[3];
3736
__be32 sig_len; /* Length of signature data */
3837
};
3938

4039
/*
41-
* Digest the module contents.
40+
* Verify a PKCS#7-based signature on a module.
4241
*/
43-
static struct public_key_signature *mod_make_digest(enum hash_algo hash,
44-
const void *mod,
45-
unsigned long modlen)
42+
static int mod_verify_pkcs7(const void *mod, unsigned long modlen,
43+
const void *raw_pkcs7, size_t pkcs7_len)
4644
{
47-
struct public_key_signature *pks;
48-
struct crypto_shash *tfm;
49-
struct shash_desc *desc;
50-
size_t digest_size, desc_size;
45+
struct pkcs7_message *pkcs7;
46+
bool trusted;
5147
int ret;
5248

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);
49+
pkcs7 = pkcs7_parse_message(raw_pkcs7, pkcs7_len);
50+
if (IS_ERR(pkcs7))
51+
return PTR_ERR(pkcs7);
52+
53+
/* The data should be detached - so we need to supply it. */
54+
if (pkcs7_supply_detached_data(pkcs7, mod, modlen) < 0) {
55+
pr_err("PKCS#7 signature with non-detached data\n");
56+
ret = -EBADMSG;
57+
goto error;
58+
}
59+
60+
ret = pkcs7_verify(pkcs7);
8261
if (ret < 0)
8362
goto error;
8463

85-
ret = crypto_shash_finup(desc, mod, modlen, pks->digest);
64+
ret = pkcs7_validate_trust(pkcs7, system_trusted_keyring, &trusted);
8665
if (ret < 0)
8766
goto error;
8867

89-
crypto_free_shash(tfm);
90-
pr_devel("<==%s() = ok\n", __func__);
91-
return pks;
68+
if (!trusted) {
69+
pr_err("PKCS#7 signature not signed with a trusted key\n");
70+
ret = -ENOKEY;
71+
}
9272

9373
error:
94-
kfree(pks);
95-
error_no_pks:
96-
crypto_free_shash(tfm);
74+
pkcs7_free_message(pkcs7);
9775
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-
*/
108-
static 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-
*/
133-
static 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);
76+
return ret;
18277
}
18378

18479
/*
18580
* Verify the signature on a module.
18681
*/
18782
int mod_verify_sig(const void *mod, unsigned long *_modlen)
18883
{
189-
struct public_key_signature *pks;
19084
struct module_signature ms;
191-
struct key *key;
192-
const void *sig;
19385
size_t modlen = *_modlen, sig_len;
194-
int ret;
19586

19687
pr_devel("==>%s(,%zu)\n", __func__, modlen);
19788

@@ -205,46 +96,23 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen)
20596
if (sig_len >= modlen)
20697
return -EBADMSG;
20798
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-
21299
*_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;
219100

220-
if (ms.hash >= PKEY_HASH__LAST ||
221-
!hash_algo_name[ms.hash])
101+
if (ms.id_type != PKEY_ID_PKCS7) {
102+
pr_err("Module is not signed with expected PKCS#7 message\n");
222103
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;
233104
}
234105

235-
ret = mod_extract_mpi_array(pks, sig + ms.signer_len + ms.key_id_len,
236-
sig_len);
237-
if (ret < 0)
238-
goto error_free_pks;
239-
240-
ret = verify_signature(key, pks);
241-
pr_devel("verify_signature() = %d\n", ret);
106+
if (ms.algo != 0 ||
107+
ms.hash != 0 ||
108+
ms.signer_len != 0 ||
109+
ms.key_id_len != 0 ||
110+
ms.__pad[0] != 0 ||
111+
ms.__pad[1] != 0 ||
112+
ms.__pad[2] != 0) {
113+
pr_err("PKCS#7 signature info has unexpected non-zero params\n");
114+
return -EBADMSG;
115+
}
242116

243-
error_free_pks:
244-
mpi_free(pks->rsa.s);
245-
kfree(pks);
246-
error_put_key:
247-
key_put(key);
248-
pr_devel("<==%s() = %d\n", __func__, ret);
249-
return ret;
117+
return mod_verify_pkcs7(mod, modlen, mod + modlen, sig_len);
250118
}

scripts/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ hostprogs-$(CONFIG_VT) += conmakehash
1616
hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount
1717
hostprogs-$(CONFIG_BUILDTIME_EXTABLE_SORT) += sortextable
1818
hostprogs-$(CONFIG_ASN1) += asn1_compiler
19+
hostprogs-$(CONFIG_MODULE_SIG) += sign-file
1920

2021
HOSTCFLAGS_sortextable.o = -I$(srctree)/tools/include
2122
HOSTCFLAGS_asn1_compiler.o = -I$(srctree)/include
23+
HOSTLOADLIBES_sign-file = -lcrypto
2224

2325
always := $(hostprogs-y) $(hostprogs-m)
2426

0 commit comments

Comments
 (0)