Skip to content

Commit

Permalink
MODSIGN: Implement module signature checking
Browse files Browse the repository at this point in the history
Check the signature on the module against the keys compiled into the kernel or
available in a hardware key store.

Currently, only RSA keys are supported - though that's easy enough to change,
and the signature is expected to contain raw components (so not a PGP or
PKCS#7 formatted blob).

The signature blob is expected to consist of the following pieces in order:

 (1) The binary identifier for the key.  This is expected to match the
     SubjectKeyIdentifier from an X.509 certificate.  Only X.509 type
     identifiers are currently supported.

 (2) The signature data, consisting of a series of MPIs in which each is in
     the format of a 2-byte BE word sizes followed by the content data.

 (3) A 12 byte information block of the form:

	struct module_signature {
		enum pkey_algo		algo : 8;
		enum pkey_hash_algo	hash : 8;
		enum pkey_id_type	id_type : 8;
		u8			__pad;
		__be32			id_length;
		__be32			sig_length;
	};

     The three enums are defined in crypto/public_key.h.

     'algo' contains the public-key algorithm identifier (0->DSA, 1->RSA).

     'hash' contains the digest algorithm identifier (0->MD4, 1->MD5, 2->SHA1,
      etc.).

     'id_type' contains the public-key identifier type (0->PGP, 1->X.509).

     '__pad' should be 0.

     'id_length' should contain in the binary identifier length in BE form.

     'sig_length' should contain in the signature data length in BE form.

     The lengths are in BE order rather than CPU order to make dealing with
     cross-compilation easier.

Signed-off-by: David Howells <[email protected]>
Signed-off-by: Rusty Russell <[email protected]> (minor Kconfig fix)
  • Loading branch information
dhowells authored and rustyrussell committed Oct 10, 2012
1 parent 631cc66 commit 48ba246
Show file tree
Hide file tree
Showing 2 changed files with 229 additions and 1 deletion.
8 changes: 8 additions & 0 deletions init/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1588,6 +1588,14 @@ config MODULE_SRCVERSION_ALL
config MODULE_SIG
bool "Module signature verification"
depends on MODULES
select KEYS
select CRYPTO
select ASYMMETRIC_KEY_TYPE
select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
select PUBLIC_KEY_ALGO_RSA
select ASN1
select OID_REGISTRY
select X509_CERTIFICATE_PARSER
help
Check modules for valid signatures upon load: the signature
is simply appended to the module. For more information see
Expand Down
222 changes: 221 additions & 1 deletion kernel/module_signing.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,233 @@

#include <linux/kernel.h>
#include <linux/err.h>
#include <crypto/public_key.h>
#include <crypto/hash.h>
#include <keys/asymmetric-type.h>
#include "module-internal.h"

/*
* Module signature information block.
*
* The constituents of the signature section are, in order:
*
* - Signer's name
* - Key identifier
* - Signature data
* - Information block
*/
struct module_signature {
enum pkey_algo algo : 8; /* Public-key crypto algorithm */
enum pkey_hash_algo hash : 8; /* Digest algorithm */
enum pkey_id_type id_type : 8; /* Key identifier type */
u8 signer_len; /* Length of signer's name */
u8 key_id_len; /* Length of key identifier */
u8 __pad[3];
__be32 sig_len; /* Length of signature data */
};

/*
* Digest the module contents.
*/
static struct public_key_signature *mod_make_digest(enum pkey_hash_algo hash,
const void *mod,
unsigned long modlen)
{
struct public_key_signature *pks;
struct crypto_shash *tfm;
struct shash_desc *desc;
size_t digest_size, desc_size;
int ret;

pr_devel("==>%s()\n", __func__);

/* Allocate the hashing algorithm we're going to need and find out how
* big the hash operational data will be.
*/
tfm = crypto_alloc_shash(pkey_hash_algo[hash], 0, 0);
if (IS_ERR(tfm))
return (PTR_ERR(tfm) == -ENOENT) ? ERR_PTR(-ENOPKG) : ERR_CAST(tfm);

desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
digest_size = crypto_shash_digestsize(tfm);

/* We allocate the hash operational data storage on the end of our
* context data and the digest output buffer on the end of that.
*/
ret = -ENOMEM;
pks = kzalloc(digest_size + sizeof(*pks) + desc_size, GFP_KERNEL);
if (!pks)
goto error_no_pks;

pks->pkey_hash_algo = hash;
pks->digest = (u8 *)pks + sizeof(*pks) + desc_size;
pks->digest_size = digest_size;

desc = (void *)pks + sizeof(*pks);
desc->tfm = tfm;
desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;

ret = crypto_shash_init(desc);
if (ret < 0)
goto error;

ret = crypto_shash_finup(desc, mod, modlen, pks->digest);
if (ret < 0)
goto error;

crypto_free_shash(tfm);
pr_devel("<==%s() = ok\n", __func__);
return pks;

error:
kfree(pks);
error_no_pks:
crypto_free_shash(tfm);
pr_devel("<==%s() = %d\n", __func__, ret);
return ERR_PTR(ret);
}

/*
* Extract an MPI array from the signature data. This represents the actual
* signature. Each raw MPI is prefaced by a BE 2-byte value indicating the
* size of the MPI in bytes.
*
* RSA signatures only have one MPI, so currently we only read one.
*/
static int mod_extract_mpi_array(struct public_key_signature *pks,
const void *data, size_t len)
{
size_t nbytes;
MPI mpi;

if (len < 3)
return -EBADMSG;
nbytes = ((const u8 *)data)[0] << 8 | ((const u8 *)data)[1];
data += 2;
len -= 2;
if (len != nbytes)
return -EBADMSG;

mpi = mpi_read_raw_data(data, nbytes);
if (!mpi)
return -ENOMEM;
pks->mpi[0] = mpi;
pks->nr_mpi = 1;
return 0;
}

/*
* Request an asymmetric key.
*/
static struct key *request_asymmetric_key(const char *signer, size_t signer_len,
const u8 *key_id, size_t key_id_len)
{
key_ref_t key;
size_t i;
char *id, *q;

pr_devel("==>%s(,%zu,,%zu)\n", __func__, signer_len, key_id_len);

/* Construct an identifier. */
id = kmalloc(signer_len + 2 + key_id_len * 2 + 1, GFP_KERNEL);
if (!id)
return ERR_PTR(-ENOKEY);

memcpy(id, signer, signer_len);

q = id + signer_len;
*q++ = ':';
*q++ = ' ';
for (i = 0; i < key_id_len; i++) {
*q++ = hex_asc[*key_id >> 4];
*q++ = hex_asc[*key_id++ & 0x0f];
}

*q = 0;

pr_debug("Look up: \"%s\"\n", id);

key = keyring_search(make_key_ref(modsign_keyring, 1),
&key_type_asymmetric, id);
if (IS_ERR(key))
pr_warn("Request for unknown module key '%s' err %ld\n",
id, PTR_ERR(key));
kfree(id);

if (IS_ERR(key)) {
switch (PTR_ERR(key)) {
/* Hide some search errors */
case -EACCES:
case -ENOTDIR:
case -EAGAIN:
return ERR_PTR(-ENOKEY);
default:
return ERR_CAST(key);
}
}

pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key_ref_to_ptr(key)));
return key_ref_to_ptr(key);
}

/*
* Verify the signature on a module.
*/
int mod_verify_sig(const void *mod, unsigned long modlen,
const void *sig, unsigned long siglen)
{
return -ENOKEY;
struct public_key_signature *pks;
struct module_signature ms;
struct key *key;
size_t sig_len;
int ret;

pr_devel("==>%s(,%lu,,%lu,)\n", __func__, modlen, siglen);

if (siglen <= sizeof(ms))
return -EBADMSG;

memcpy(&ms, sig + (siglen - sizeof(ms)), sizeof(ms));
siglen -= sizeof(ms);

sig_len = be32_to_cpu(ms.sig_len);
if (sig_len >= siglen ||
siglen - sig_len != (size_t)ms.signer_len + ms.key_id_len)
return -EBADMSG;

/* For the moment, only support RSA and X.509 identifiers */
if (ms.algo != PKEY_ALGO_RSA ||
ms.id_type != PKEY_ID_X509)
return -ENOPKG;

if (ms.hash >= PKEY_HASH__LAST ||
!pkey_hash_algo[ms.hash])
return -ENOPKG;

key = request_asymmetric_key(sig, ms.signer_len,
sig + ms.signer_len, ms.key_id_len);
if (IS_ERR(key))
return PTR_ERR(key);

pks = mod_make_digest(ms.hash, mod, modlen);
if (IS_ERR(pks)) {
ret = PTR_ERR(pks);
goto error_put_key;
}

ret = mod_extract_mpi_array(pks, sig + ms.signer_len + ms.key_id_len,
sig_len);
if (ret < 0)
goto error_free_pks;

ret = verify_signature(key, pks);
pr_devel("verify_signature() = %d\n", ret);

error_free_pks:
mpi_free(pks->rsa.s);
kfree(pks);
error_put_key:
key_put(key);
pr_devel("<==%s() = %d\n", __func__, ret);
return ret;
}

0 comments on commit 48ba246

Please sign in to comment.