From d093caa39460495390c94556f0680218223168c2 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 4 May 2012 15:55:49 +0100 Subject: [PATCH 01/36] MPILIB: Export some more symbols Export some more symbols for use by the DSA key subtype. Signed-off-by: David Howells --- lib/mpi/mpi-cmp.c | 2 ++ lib/mpi/mpi-div.c | 1 + lib/mpi/mpi-inv.c | 1 + lib/mpi/mpi-mpow.c | 1 + lib/mpi/mpi-mul.c | 1 + 5 files changed, 6 insertions(+) diff --git a/lib/mpi/mpi-cmp.c b/lib/mpi/mpi-cmp.c index 914bc42..1871e7b 100644 --- a/lib/mpi/mpi-cmp.c +++ b/lib/mpi/mpi-cmp.c @@ -39,6 +39,7 @@ int mpi_cmp_ui(MPI u, unsigned long v) else return -1; } +EXPORT_SYMBOL_GPL(mpi_cmp_ui); int mpi_cmp(MPI u, MPI v) { @@ -66,3 +67,4 @@ int mpi_cmp(MPI u, MPI v) return 1; return -1; } +EXPORT_SYMBOL_GPL(mpi_cmp); diff --git a/lib/mpi/mpi-div.c b/lib/mpi/mpi-div.c index f68cbbb..fed3405 100644 --- a/lib/mpi/mpi-div.c +++ b/lib/mpi/mpi-div.c @@ -59,6 +59,7 @@ nomem: mpi_free(temp_divisor); return rc; } +EXPORT_SYMBOL_GPL(mpi_fdiv_r); /**************** * Division rounding the quotient towards -infinity. diff --git a/lib/mpi/mpi-inv.c b/lib/mpi/mpi-inv.c index 0951f98..bfc5ca1 100644 --- a/lib/mpi/mpi-inv.c +++ b/lib/mpi/mpi-inv.c @@ -185,3 +185,4 @@ cleanup: mpi_free(v); return rc; } +EXPORT_SYMBOL_GPL(mpi_invm); diff --git a/lib/mpi/mpi-mpow.c b/lib/mpi/mpi-mpow.c index 7328d0d..b8b22e5 100644 --- a/lib/mpi/mpi-mpow.c +++ b/lib/mpi/mpi-mpow.c @@ -132,3 +132,4 @@ nomem: err_out: return rc; } +EXPORT_SYMBOL_GPL(mpi_mulpowm); diff --git a/lib/mpi/mpi-mul.c b/lib/mpi/mpi-mul.c index 1f3219e..3d514b9 100644 --- a/lib/mpi/mpi-mul.c +++ b/lib/mpi/mpi-mul.c @@ -192,3 +192,4 @@ int mpi_mulm(MPI w, MPI u, MPI v, MPI m) return -ENOMEM; return mpi_fdiv_r(w, w, m); } +EXPORT_SYMBOL_GPL(mpi_mulm); -- 1.7.10.2 From 8c5366bc5c1c9ecaa1104d769f60c7b83ed342a9 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 4 May 2012 16:15:09 +0100 Subject: [PATCH 05/36] KEYS: Create a key type that can be used for general cryptographic operations Create a key type that can be used for general cryptographic operations, such as encryption, decryption, signature generation and signature verification. The key type is "crypto" and can provide access to a variety of cryptographic algorithms. Signed-off-by: David Howells --- Documentation/security/keys-crypto.txt | 181 +++++++++++++++++++++++++ include/keys/crypto-subtype.h | 56 ++++++++ include/keys/crypto-type.h | 25 ++++ security/keys/Kconfig | 2 + security/keys/Makefile | 1 + security/keys/crypto/Kconfig | 7 + security/keys/crypto/Makefile | 7 + security/keys/crypto/crypto_keys.h | 28 ++++ security/keys/crypto/crypto_type.c | 228 ++++++++++++++++++++++++++++++++ 9 files changed, 535 insertions(+) create mode 100644 Documentation/security/keys-crypto.txt create mode 100644 include/keys/crypto-subtype.h create mode 100644 include/keys/crypto-type.h create mode 100644 security/keys/crypto/Kconfig create mode 100644 security/keys/crypto/Makefile create mode 100644 security/keys/crypto/crypto_keys.h create mode 100644 security/keys/crypto/crypto_type.c diff --git a/Documentation/security/keys-crypto.txt b/Documentation/security/keys-crypto.txt new file mode 100644 index 0000000..97dee80 --- /dev/null +++ b/Documentation/security/keys-crypto.txt @@ -0,0 +1,181 @@ + ====================== + CRYPTOGRAPHIC KEY TYPE + ====================== + +Contents: + + - Overview. + - Key identification. + - Accessing crypto keys. + - Implementing crypto parsers. + - Implementing crypto subtypes. + + +======== +OVERVIEW +======== + +The "crypto" key type is designed to be a container for cryptographic keys, +without imposing any particular restrictions on the form of the cryptography or +the key. + +The crypto key is given a subtype that defines what sort of data is associated +with the key and provides operations to describe and destroy it. However, no +requirement is made that the key data actually be loaded into the key. + +The crypto key also has a number of data parsers registered with it. The data +parsers are responsible for extracing information the blobs of data passed to +the instantiator function. The first data parser that recognises the blob gets +to set the subtype of the key and define the operations that can be done on +that key. + +Completely in-kernel key retention and operation subtypes and parsers can be +defined, but it would also be possible to provide access to cryptographic +hardware (such as a TPM) that might be used to both retain the relevant key and +perform operations using that key. In such a case, the crypto key would then +merely be an interface to the TPM driver. + + +================== +KEY IDENTIFICATION +================== + +Because the identity of a key is not necessarily known and may not be easily +calculated when a crypto key is allocated, it may not be a simple matter to set +a key description to something that's useful for determining whether this is +the key you're looking for. Furthermore, it may be necessary to perform a +partial match upon the key identity. + +To help with this, when a key is loaded, the parser calculates the key +fingerprint and stores a copy in the key structure. + +The crypto key type's key matching function then performs more checks than just +the straightforward comparison of the description with the criterion string: + + (1) If the criterion string is of the form "id:" then the match + function will examine a key's fingerprint to see if the hex digits given + after the "id:" match the tail. For instance: + + keyctl search @s crypto id:5acc2142 + + will match a key with fingerprint: + + 1A00 2040 7601 7889 DE11 882C 3823 04AD 5ACC 2142 + + (2) If the criterion string is of the form ":" then the + match will match the ID as in (1), but with the added restriction that + only keys of the specified subtype (e.g. dsa or rsa) will be matched. For + instance: + + keyctl search @s crypto dsa:5acc2142 + +Looking in /proc/keys, the last 8 hex digits of the key fingerprint are +displayed, along with the subtype: + + 1a39e171 I----- 1 perm 3f010000 0 0 crypto modsign.0: DSA 5acc2142 [] + + +===================== +ACCESSING CRYPTO KEYS +===================== + +To access crypto keys from within the kernel, the following inclusion is +required: + + #include + +This gives access to the key type: + + struct key_type key_type_crypto; + + +=========================== +IMPLEMENTING CRYPTO PARSERS +=========================== + +The crypto key type keeps a list of registered data parsers. An example of +such a parser is one that parses OpenPGP packet formatted data [RFC 4880]. + +During key instantiation each parser in the list is tried until one doesn't +return -EBADMSG. + +The parser definition structure looks like the following: + + struct crypto_key_parser { + struct module *owner; + const char *name; + + int (*instantiate)(struct key *key, + const void *data, size_t datalen); + }; + +The owner and name fields should be set to the owning module and the name of +the parser. + +There are a number of operations defined by the parser. They are all optional, +but it is expected that at least one will be defined. + + (1) instantiate(). + + The arguments are the same as for the instantiate function in the key + type. 'key' is the crypto key being instantiated; data and datalen are + the instantiation data, presumably containing cryptographic key data, and + the length of that data. + + If the data format is not recognised, -EBADMSG should be returned. If it + is recognised, but the key cannot for some reason be set up, some other + negative error code should be returned. + + If the key can be successfully set up, then key->payload should be set to + point to the retained data, key->type_data.p[0] should be set to point to + the subtype chosen and key->type_data.p[1] should be set to point to a + copy of the key's identity string and 0 should be returned. + + The key's identity string may be partially matched upon. For a public-key + algorithm such as RSA and DSA this will likely be a printable hex version + of the key's fingerprint. + +Functions are provided to register and unregister parsers: + + int register_crypto_key_parser(struct crypto_key_parser *parser); + void unregister_crypto_key_parser(struct crypto_key_parser *subtype); + +Parsers may not have the same name. The names are only used for displaying in +debugging messages. + + +============================ +IMPLEMENTING CRYPTO SUBTYPES +============================ + +The parser selects the appropriate subtype directly and sets it on the key; the +crypto key then retains a reference on the subtype module (which means the +parser can be removed thereafter). + +The subtype definition structure looks like the following: + + struct crypto_key_subtype { + struct module *owner; + const char *name; + + void (*describe)(const struct key *key, struct seq_file *m); + void (*destroy)(void *payload); + }; + +The owner and name fields should be set to the owning module and the name of +the subtype. + +There are a number of operations defined by the subtype: + + (1) describe(). + + Mandatory. This allows the subtype to display something in /proc/keys + against the key. For instance the name of the public key algorithm type + could be displayed. The key type will display the tail of the key + identity string after this. + + (2) destroy(). + + Mandatory. This should free the memory associated with the key. The + crypto key will look after freeing the fingerprint and releasing the + reference on the subtype module. diff --git a/include/keys/crypto-subtype.h b/include/keys/crypto-subtype.h new file mode 100644 index 0000000..fa87555 --- /dev/null +++ b/include/keys/crypto-subtype.h @@ -0,0 +1,56 @@ +/* Cryptographic key subtype + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + * + * See Documentation/security/keys-crypto.txt + */ + +#ifndef _KEYS_CRYPTO_SUBTYPE_H +#define _KEYS_CRYPTO_SUBTYPE_H + +#include +#include + +extern struct key_type key_type_crypto; + +/* + * Keys of this type declare a subtype that indicates the handlers and + * capabilities. + */ +struct crypto_key_subtype { + struct module *owner; + const char *name; + unsigned short name_len; /* length of name */ + + void (*describe)(const struct key *key, struct seq_file *m); + + void (*destroy)(void *payload); +}; + +/* + * Data parser. Called during instantiation and signature verification + * initiation. + */ +struct crypto_key_parser { + struct list_head link; + struct module *owner; + const char *name; + + /* Attempt to instantiate a key from the data blob passed to add_key() + * or keyctl_instantiate(). + * + * Return EBADMSG if not recognised. + */ + int (*instantiate)(struct key *key, const void *data, size_t datalen); +}; + +extern int register_crypto_key_parser(struct crypto_key_parser *); +extern void unregister_crypto_key_parser(struct crypto_key_parser *); + +#endif /* _KEYS_CRYPTO_SUBTYPE_H */ diff --git a/include/keys/crypto-type.h b/include/keys/crypto-type.h new file mode 100644 index 0000000..47c00c7 --- /dev/null +++ b/include/keys/crypto-type.h @@ -0,0 +1,25 @@ +/* Cryptographic key type interface + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + * + * See Documentation/security/keys-crypto.txt + */ + +#ifndef _KEYS_CRYPTO_TYPE_H +#define _KEYS_CRYPTO_TYPE_H + +#include + +extern struct key_type key_type_crypto; + +/* + * The payload is at the discretion of the subtype. + */ + +#endif /* _KEYS_CRYPTO_TYPE_H */ diff --git a/security/keys/Kconfig b/security/keys/Kconfig index a90d6d3..992fe52 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -69,3 +69,5 @@ config KEYS_DEBUG_PROC_KEYS the resulting table. If you are unsure as to whether this is required, answer N. + +source security/keys/crypto/Kconfig diff --git a/security/keys/Makefile b/security/keys/Makefile index 504aaa0..67dae73 100644 --- a/security/keys/Makefile +++ b/security/keys/Makefile @@ -24,3 +24,4 @@ obj-$(CONFIG_SYSCTL) += sysctl.o # obj-$(CONFIG_TRUSTED_KEYS) += trusted.o obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/ +obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto/ diff --git a/security/keys/crypto/Kconfig b/security/keys/crypto/Kconfig new file mode 100644 index 0000000..3d15710 --- /dev/null +++ b/security/keys/crypto/Kconfig @@ -0,0 +1,7 @@ +config CRYPTO_KEY_TYPE + tristate "Cryptographic key type" + depends on KEYS + help + This option provides support for a type of key that holds the keys + required for cryptographic operations such as encryption, decryption, + signature generation and signature verification. diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile new file mode 100644 index 0000000..36db1d5 --- /dev/null +++ b/security/keys/crypto/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for cryptographic keys +# + +obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o + +crypto_keys-y := crypto_type.o diff --git a/security/keys/crypto/crypto_keys.h b/security/keys/crypto/crypto_keys.h new file mode 100644 index 0000000..a339ce0 --- /dev/null +++ b/security/keys/crypto/crypto_keys.h @@ -0,0 +1,28 @@ +/* Internal crypto type stuff + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +static inline +struct crypto_key_subtype *crypto_key_subtype(const struct key *key) +{ + return key->type_data.p[0]; +} + +static inline char *crypto_key_id(const struct key *key) +{ + return key->type_data.p[1]; +} + + +/* + * crypto_type.c + */ +extern struct list_head crypto_key_parsers; +extern struct rw_semaphore crypto_key_parsers_sem; diff --git a/security/keys/crypto/crypto_type.c b/security/keys/crypto/crypto_type.c new file mode 100644 index 0000000..33d279b --- /dev/null +++ b/security/keys/crypto/crypto_type.c @@ -0,0 +1,228 @@ +/* Cryptographic key type + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + * + * See Documentation/security/keys-crypto.txt + */ +#include +#include +#include +#include +#include "crypto_keys.h" + +MODULE_LICENSE("GPL"); + +LIST_HEAD(crypto_key_parsers); +DECLARE_RWSEM(crypto_key_parsers_sem); + +/* + * Match crypto_keys on (part of) their name + * We have some shorthand methods for matching keys. We allow: + * + * "" - request a key by description + * "id:" - request a key matching the ID + * ":" - request a key of a subtype + */ +static int crypto_key_match(const struct key *key, const void *description) +{ + const struct crypto_key_subtype *subtype = crypto_key_subtype(key); + const char *spec = description; + const char *id, *kid; + ptrdiff_t speclen; + size_t idlen, kidlen; + + if (!subtype || !spec || !*spec) + return 0; + + /* See if the full key description matches as is */ + if (key->description && strcmp(key->description, description) == 0) + return 1; + + /* All tests from here on break the criterion description into a + * specifier, a colon and then an identifier. + */ + id = strchr(spec, ':'); + if (!id) + return 0; + + speclen = id - spec; + id++; + + /* Anything after here requires a partial match on the ID string */ + kid = crypto_key_id(key); + if (!kid) + return 0; + + idlen = strlen(id); + kidlen = strlen(kid); + if (idlen > kidlen) + return 0; + + kid += kidlen - idlen; + if (strcasecmp(id, kid) != 0) + return 0; + + if (speclen == 2 && + memcmp(spec, "id", 2) == 0) + return 1; + + if (speclen == subtype->name_len && + memcmp(spec, subtype->name, speclen) == 0) + return 1; + + return 0; +} + +/* + * Describe the crypto key + */ +static void crypto_key_describe(const struct key *key, struct seq_file *m) +{ + const struct crypto_key_subtype *subtype = crypto_key_subtype(key); + const char *kid = crypto_key_id(key); + size_t n; + + seq_puts(m, key->description); + + if (subtype) { + seq_puts(m, ": "); + subtype->describe(key, m); + + if (kid) { + seq_putc(m, ' '); + n = strlen(kid); + if (n <= 8) + seq_puts(m, kid); + else + seq_puts(m, kid + n - 8); + } + + seq_puts(m, " ["); + /* put something here to indicate the key's capabilities */ + seq_putc(m, ']'); + } +} + +/* + * Instantiate a crypto_key defined key + */ +static int crypto_key_instantiate(struct key *key, + const void *data, size_t datalen) +{ + struct crypto_key_parser *parser; + int ret; + + pr_devel("==>%s()\n", __func__); + + if (datalen == 0) + return -EINVAL; + + down_read(&crypto_key_parsers_sem); + + ret = -EBADMSG; + list_for_each_entry(parser, &crypto_key_parsers, link) { + pr_debug("Trying parser '%s'\n", parser->name); + + ret = parser->instantiate(key, data, datalen); + if (ret != -EBADMSG) { + pr_debug("Parser recognised the format (ret %d)\n", + ret); + break; + } + } + + up_read(&crypto_key_parsers_sem); + pr_devel("<==%s() = %d\n", __func__, ret); + return ret; +} + +/* + * dispose of the data dangling from the corpse of a crypto key + */ +static void crypto_key_destroy(struct key *key) +{ + struct crypto_key_subtype *subtype = crypto_key_subtype(key); + if (subtype) { + subtype->destroy(key->payload.data); + module_put(subtype->owner); + key->type_data.p[0] = NULL; + } + kfree(key->type_data.p[1]); + key->type_data.p[1] = NULL; +} + +struct key_type key_type_crypto = { + .name = "crypto", + .instantiate = crypto_key_instantiate, + .match = crypto_key_match, + .destroy = crypto_key_destroy, + .describe = crypto_key_describe, +}; +EXPORT_SYMBOL_GPL(key_type_crypto); + +/** + * register_crypto_key_parser - Register a crypto key blob parser + * @parser: The parser to register + */ +int register_crypto_key_parser(struct crypto_key_parser *parser) +{ + struct crypto_key_parser *cursor; + int ret; + + down_write(&crypto_key_parsers_sem); + + list_for_each_entry(cursor, &crypto_key_parsers, link) { + if (strcmp(cursor->name, parser->name) == 0) { + pr_err("Crypto key parser '%s' already registered\n", + parser->name); + ret = -EEXIST; + goto out; + } + } + + list_add_tail(&parser->link, &crypto_key_parsers); + + pr_notice("Crypto key parser '%s' registered\n", parser->name); + ret = 0; + +out: + up_write(&crypto_key_parsers_sem); + return ret; +} +EXPORT_SYMBOL_GPL(register_crypto_key_parser); + +/** + * unregister_crypto_key_parser - Unregister a crypto key blob parser + * @parser: The parser to unregister + */ +void unregister_crypto_key_parser(struct crypto_key_parser *parser) +{ + down_write(&crypto_key_parsers_sem); + list_del(&parser->link); + up_write(&crypto_key_parsers_sem); + + pr_notice("Crypto key parser '%s' unregistered\n", parser->name); +} +EXPORT_SYMBOL_GPL(unregister_crypto_key_parser); + +/* + * Module stuff + */ +static int __init crypto_key_init(void) +{ + return register_key_type(&key_type_crypto); +} + +static void __exit crypto_key_cleanup(void) +{ + unregister_key_type(&key_type_crypto); +} + +module_init(crypto_key_init); +module_exit(crypto_key_cleanup); -- 1.7.10.2 From e8d4b12988cd23815f1a5f4654d819f5a0a2194b Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 4 May 2012 16:15:57 +0100 Subject: [PATCH 06/36] KEYS: Add signature verification facility Add a facility whereby a key subtype may be asked to verify a signature against the data it is purported to have signed. This adds four routines: (1) struct crypto_key_verify_context * verify_sig_begin(struct key *keyring, const void *sig, size_t siglen); This sets up a verification context for the given signature using information in that signature to select a key from the specified keyring and to request a hash algorithm from the crypto layer. (2) int verify_sig_add_data(struct crypto_key_verify_context *ctx, const void *data, size_t datalen); Incrementally supply data to be signed. May be called multiple times. (3) int verify_sig_end(struct crypto_key_verify_context *ctx, const void *sig, size_t siglen); Complete the verification process and return the result. -EKEYREJECTED will indicate that the verification failed and 0 will indicate success. Other errors are also possible. (4) void verify_sig_cancel(struct crypto_key_verify_context *ctx); Cancel the verification process. Signed-off-by: David Howells --- Documentation/security/keys-crypto.txt | 101 +++++++++++++++++++++++++++++ include/keys/crypto-subtype.h | 21 ++++++ include/keys/crypto-type.h | 9 +++ security/keys/crypto/Makefile | 2 +- security/keys/crypto/crypto_verify.c | 111 ++++++++++++++++++++++++++++++++ 5 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 security/keys/crypto/crypto_verify.c diff --git a/Documentation/security/keys-crypto.txt b/Documentation/security/keys-crypto.txt index 97dee80..a964717 100644 --- a/Documentation/security/keys-crypto.txt +++ b/Documentation/security/keys-crypto.txt @@ -7,6 +7,7 @@ Contents: - Overview. - Key identification. - Accessing crypto keys. + - Signature verification. - Implementing crypto parsers. - Implementing crypto subtypes. @@ -89,6 +90,65 @@ This gives access to the key type: struct key_type key_type_crypto; +SIGNATURE VERIFICATION +---------------------- + +The four operations that can perform cryptographic signature verification, +using one of a set of keys to provide the public key: + + (1) Begin verification procedure. + + struct crypto_key_verify_context * + verify_sig_begin(struct key *keyring, const void *sig, size_t siglen); + + This function sets up a verification context from the information in the + signature and looks for a suitable key in the keyring. The signature blob + must be presented again at the end of the procedure. The keys will be + checked against parameters in the signature, and if the matching one is + not found then -ENOKEY will be returned. + + The hashing algorithm, if such a thing applies, will be determined from + information in the signature and the appropriate crypto module will be + used. -ENOPKG will be returned if the hash algorithm is unavailable. + + The return value is an opaque pointer to be passed to the other functions, + or a negative error code. + + (2) Indicate data to be verified. + + int verify_sig_add_data(struct crypto_key_verify_context *ctx, + const void *data, size_t datalen); + + This function is used to shovel data to the verification procedure so that + it can load it into the hash, pass it to hardware or whatever is + appropriate for the algorithm being employed. + + The data is not canonicalised for the document type specified in the + signature. The caller must do that. + + It will return 0 if successful and a negative error code if not. + + (3) Complete the verification process. + + int verify_sig_end(struct crypto_key_verify_context *ctx, + const void *sig, size_t siglen); + + This function performs the actual signature verification step and cleans + up the resources allocated at the beginning. The signature must be + presented again as some of the data therein may need to be added to the + internal hash. + + It will return -EKEYREJECTED if the signature didn't match, 0 if + successful and may return other errors as appropriate. + + (4) Cancel the verification process. + + void verify_sig_cancel(struct crypto_key_verify_context *ctx); + + This function cleans up the resources allocated at the beginning. This is + not necessary if verify_sig_end() was called. + + =========================== IMPLEMENTING CRYPTO PARSERS =========================== @@ -96,6 +156,7 @@ IMPLEMENTING CRYPTO PARSERS The crypto key type keeps a list of registered data parsers. An example of such a parser is one that parses OpenPGP packet formatted data [RFC 4880]. + During key instantiation each parser in the list is tried until one doesn't return -EBADMSG. @@ -107,6 +168,8 @@ The parser definition structure looks like the following: int (*instantiate)(struct key *key, const void *data, size_t datalen); + struct crypto_key_verify_context *(*verify_sig_begin)( + struct key *keyring, const u8 *sig, size_t siglen); }; The owner and name fields should be set to the owning module and the name of @@ -135,6 +198,44 @@ but it is expected that at least one will be defined. algorithm such as RSA and DSA this will likely be a printable hex version of the key's fingerprint. + (2) verify_sig_begin(). + + This is similar in concept to the instantiate() function, except that it + is given a signature blob to parse rather than a key data blob. + + If the data format is not recognised, -EBADMSG should be returned. If it + is recognised, but the signature verification process cannot for some + reason be set up, some other negative error code should be returned. + -ENOKEY should be used to indicate that no matching key is available and + -ENOPKG should be returned if the hash algorithm or the verification + algorithm are unavailable. + + If successful, the parser should allocate a verification context and embed + the following struct in it: + + struct crypto_key_verify_context { + struct key *key; + int (*add_data)(struct crypto_key_verify_context *ctx, + const void *data, size_t datalen); + int (*end)(struct crypto_key_verify_context *ctx, + const u8 *sig, size_t siglen); + void (*cancel)(struct crypto_key_verify_context *ctx); + }; + + and return a pointer to this to the caller, who will then pass it to the + verification operation wrappers described in the "Signature Verification" + section. The three operation pointers here correspond exactly to those + wrappers and are all mandatory. container_of() should be used to retrieve + the actual context. + + Note that the crypto key type retains a reference on the parser module for + the lifetime of this context, though the operation pointers need not point + into this module. + + The parser should also record a pointer to the key selected and take a + reference on that key with key_get(). + + Functions are provided to register and unregister parsers: int register_crypto_key_parser(struct crypto_key_parser *parser); diff --git a/include/keys/crypto-subtype.h b/include/keys/crypto-subtype.h index fa87555..f2b927a 100644 --- a/include/keys/crypto-subtype.h +++ b/include/keys/crypto-subtype.h @@ -20,6 +20,20 @@ extern struct key_type key_type_crypto; /* + * Context base for signature verification methods. Allocated by the subtype + * and presumably embedded in something appropriate. + */ +struct crypto_key_verify_context { + struct key *key; + struct crypto_key_parser *parser; + int (*add_data)(struct crypto_key_verify_context *ctx, + const void *data, size_t datalen); + int (*end)(struct crypto_key_verify_context *ctx, + const u8 *sig, size_t siglen); + void (*cancel)(struct crypto_key_verify_context *ctx); +}; + +/* * Keys of this type declare a subtype that indicates the handlers and * capabilities. */ @@ -48,6 +62,13 @@ struct crypto_key_parser { * Return EBADMSG if not recognised. */ int (*instantiate)(struct key *key, const void *data, size_t datalen); + + /* Attempt to recognise a signature blob and find a matching key. + * + * Return EBADMSG if not recognised. + */ + struct crypto_key_verify_context *(*verify_sig_begin)( + struct key *keyring, const u8 *sig, size_t siglen); }; extern int register_crypto_key_parser(struct crypto_key_parser *); diff --git a/include/keys/crypto-type.h b/include/keys/crypto-type.h index 47c00c7..6b93366 100644 --- a/include/keys/crypto-type.h +++ b/include/keys/crypto-type.h @@ -18,6 +18,15 @@ extern struct key_type key_type_crypto; +struct crypto_key_verify_context; +extern struct crypto_key_verify_context *verify_sig_begin( + struct key *key, const void *sig, size_t siglen); +extern int verify_sig_add_data(struct crypto_key_verify_context *ctx, + const void *data, size_t datalen); +extern int verify_sig_end(struct crypto_key_verify_context *ctx, + const void *sig, size_t siglen); +extern void verify_sig_cancel(struct crypto_key_verify_context *ctx); + /* * The payload is at the discretion of the subtype. */ diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile index 36db1d5..67001bc 100644 --- a/security/keys/crypto/Makefile +++ b/security/keys/crypto/Makefile @@ -4,4 +4,4 @@ obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o -crypto_keys-y := crypto_type.o +crypto_keys-y := crypto_type.o crypto_verify.o diff --git a/security/keys/crypto/crypto_verify.c b/security/keys/crypto/crypto_verify.c new file mode 100644 index 0000000..65f734c --- /dev/null +++ b/security/keys/crypto/crypto_verify.c @@ -0,0 +1,111 @@ +/* Signature verification with a crypto key + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + * + * See Documentation/security/keys-crypto.txt + */ + +#include +#include +#include "crypto_keys.h" + +/** + * verify_sig_begin - Initiate the use of a crypto key to verify a signature + * @keyring: The public keys to verify against + * @sig: The signature data + * @siglen: The signature length + * + * Returns a context or an error. + */ +struct crypto_key_verify_context *verify_sig_begin( + struct key *keyring, const void *sig, size_t siglen) +{ + struct crypto_key_verify_context *ret; + struct crypto_key_parser *parser; + + pr_devel("==>%s()\n", __func__); + + if (siglen == 0 || !sig) + return ERR_PTR(-EINVAL); + + down_read(&crypto_key_parsers_sem); + + ret = ERR_PTR(-EBADMSG); + list_for_each_entry(parser, &crypto_key_parsers, link) { + if (parser->verify_sig_begin) { + if (!try_module_get(parser->owner)) + continue; + + pr_debug("Trying parser '%s'\n", parser->name); + + ret = parser->verify_sig_begin(keyring, sig, siglen); + if (IS_ERR(ret)) + module_put(parser->owner); + else + ret->parser = parser; + if (ret != ERR_PTR(-EBADMSG)) { + pr_debug("Parser recognised the format" + " (ret %ld)\n", + PTR_ERR(ret)); + break; + } + } + } + + up_read(&crypto_key_parsers_sem); + pr_devel("<==%s() = %p\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL_GPL(verify_sig_begin); + +/** + * verify_sig_add_data - Incrementally provide data to be verified + * @ctx: The context from verify_sig_begin() + * @data: Data + * @datalen: The amount of @data + * + * This may be called multiple times. + */ +int verify_sig_add_data(struct crypto_key_verify_context *ctx, + const void *data, size_t datalen) +{ + return ctx->add_data(ctx, data, datalen); +} +EXPORT_SYMBOL_GPL(verify_sig_add_data); + +/** + * verify_sig_end - Finalise signature verification and return result + * @ctx: The context from verify_sig_begin() + * @sig: The signature data + * @siglen: The signature length + */ +int verify_sig_end(struct crypto_key_verify_context *ctx, + const void *sig, size_t siglen) +{ + struct crypto_key_parser *parser = ctx->parser; + int ret; + + ret = ctx->end(ctx, sig, siglen); + module_put(parser->owner); + return ret; +} +EXPORT_SYMBOL_GPL(verify_sig_end); + +/** + * verify_sig_end - Cancel signature verification + * @ctx: The context from verify_sig_begin() + */ +void verify_sig_cancel(struct crypto_key_verify_context *ctx) +{ + struct crypto_key_parser *parser = ctx->parser; + + ctx->cancel(ctx); + module_put(parser->owner); +} +EXPORT_SYMBOL_GPL(verify_sig_cancel); -- 1.7.10.2 From db170ec4f4df7d8f188134c9c9a3e2736ec0dbff Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 4 May 2012 16:16:46 +0100 Subject: [PATCH 07/36] KEYS: Asymmetric public-key algorithm crypto key subtype Add a subtype for supporting asymmetric public-key encryption algorithms such as DSA (FIPS-186) and RSA (PKCS#1 / RFC1337). Signed-off-by: David Howells --- security/keys/crypto/Kconfig | 10 ++++ security/keys/crypto/Makefile | 3 +- security/keys/crypto/public_key.c | 55 +++++++++++++++++++ security/keys/crypto/public_key.h | 106 +++++++++++++++++++++++++++++++++++++ 4 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 security/keys/crypto/public_key.c create mode 100644 security/keys/crypto/public_key.h diff --git a/security/keys/crypto/Kconfig b/security/keys/crypto/Kconfig index 3d15710..5f2b8ac 100644 --- a/security/keys/crypto/Kconfig +++ b/security/keys/crypto/Kconfig @@ -5,3 +5,13 @@ config CRYPTO_KEY_TYPE This option provides support for a type of key that holds the keys required for cryptographic operations such as encryption, decryption, signature generation and signature verification. + +config CRYPTO_KEY_PUBLIC_KEY_SUBTYPE + tristate "Asymmetric public-key crypto algorithm subtype" + depends on CRYPTO_KEY_TYPE + select MPILIB + help + This option provides support for asymmetric public key type handling. + If signature generation and/or verification are to be used, + appropriate hash algorithms (such as SHA-1) must be available. + ENOPKG will be reported if the requisite algorithm is unavailable. diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile index 67001bc..6384306 100644 --- a/security/keys/crypto/Makefile +++ b/security/keys/crypto/Makefile @@ -3,5 +3,6 @@ # obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o - crypto_keys-y := crypto_type.o crypto_verify.o + +obj-$(CONFIG_CRYPTO_KEY_PUBLIC_KEY_SUBTYPE) += public_key.o diff --git a/security/keys/crypto/public_key.c b/security/keys/crypto/public_key.c new file mode 100644 index 0000000..c00ddac --- /dev/null +++ b/security/keys/crypto/public_key.c @@ -0,0 +1,55 @@ +/* Asymmetric public key crypto subtype + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "PKEY: "fmt +#include +#include +#include "public_key.h" + +MODULE_LICENSE("GPL"); + +/* + * Provide a part of a description of the key for /proc/keys. + */ +static void public_key_describe(const struct key *crypto_key, + struct seq_file *m) +{ + struct public_key *key = crypto_key->payload.data; + + if (key) + seq_puts(m, key->algo->name); +} + +/* + * Destroy a public key algorithm key + */ +static void public_key_destroy(void *payload) +{ + struct public_key *key = payload; + int i; + + if (key) { + for (i = 0; i < ARRAY_SIZE(key->mpi); i++) + mpi_free(key->mpi[i]); + kfree(key); + } +} + +/* + * Public key algorithm crypto key subtype + */ +struct crypto_key_subtype public_key_crypto_key_subtype = { + .owner = THIS_MODULE, + .name = "public_key", + .describe = public_key_describe, + .destroy = public_key_destroy, +}; +EXPORT_SYMBOL_GPL(public_key_crypto_key_subtype); diff --git a/security/keys/crypto/public_key.h b/security/keys/crypto/public_key.h new file mode 100644 index 0000000..81ed603 --- /dev/null +++ b/security/keys/crypto/public_key.h @@ -0,0 +1,106 @@ +/* Asymmetric public-key algorithm definitions + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _LINUX_PUBLIC_KEY_H +#define _LINUX_PUBLIC_KEY_H + +#include +#include +#include + +struct public_key; +struct public_key_signature; + +enum pkey_hash_algo { + PKEY_HASH_MD5, + PKEY_HASH_SHA1, + PKEY_HASH_RIPE_MD_160, + PKEY_HASH_SHA256, + PKEY_HASH_SHA384, + PKEY_HASH_SHA512, + PKEY_HASH_SHA224, + PKEY_HASH__LAST +}; + +/* + * Public key type definition + */ +struct public_key_algorithm { + const char *name; + u8 n_pub_mpi; /* Number of MPIs in public key */ + u8 n_sec_mpi; /* Number of MPIs in secret key */ + u8 n_sig_mpi; /* Number of MPIs in a signature */ + int (*verify)(const struct public_key *key, + const struct public_key_signature *sig); +}; + +/* + * Asymmetric public key data + */ +struct public_key { + const struct public_key_algorithm *algo; + u8 capabilities; +#define PKEY_CAN_ENCRYPT 0x01 +#define PKEY_CAN_DECRYPT 0x02 +#define PKEY_CAN_ENCDEC (PKEY_CAN_ENCRYPT | PKEY_CAN_DECRYPT) +#define PKEY_CAN_SIGN 0x04 +#define PKEY_CAN_VERIFY 0x08 +#define PKEY_CAN_SIGVER (PKEY_CAN_SIGN | PKEY_CAN_VERIFY) + union { + MPI mpi[5]; + struct { + MPI p; /* DSA prime */ + MPI q; /* DSA group order */ + MPI g; /* DSA group generator */ + MPI y; /* DSA public-key value = g^x mod p */ + MPI x; /* DSA secret exponent (if present) */ + } dsa; + struct { + MPI n; /* RSA public modulus */ + MPI e; /* RSA public encryption exponent */ + MPI d; /* RSA secret encryption exponent (if present) */ + MPI p; /* RSA secret prime (if present) */ + MPI q; /* RSA secret prime (if present) */ + } rsa; + }; + + u8 key_id[8]; /* ID of this key pair */ + u8 key_id_size; /* Number of bytes in key_id */ +}; + +/* + * Asymmetric public key algorithm signature data + */ +struct public_key_signature { + struct crypto_key_verify_context base; + u8 *digest; + enum pkey_hash_algo pkey_hash_algo : 8; + u8 signed_hash_msw[2]; + u8 digest_size; /* Number of bytes in digest */ + union { + MPI mpi[2]; + struct { + MPI s; /* m^d mod n */ + } rsa; + struct { + MPI r; + MPI s; + } dsa; + }; + struct shash_desc hash; /* This must go last! */ +}; + +extern struct crypto_key_verify_context *pgp_pkey_verify_sig_begin( + struct key *crypto_key, const u8 *sigdata, size_t siglen); + +extern struct crypto_key_subtype public_key_crypto_key_subtype; + +#endif /* _LINUX_PUBLIC_KEY_H */ -- 1.7.10.2 From 07f1001d10c926d28b92f0a32dbb57131a0f0942 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 4 May 2012 16:30:18 +0100 Subject: [PATCH 08/36] KEYS: RSA signature verification algorithm Implement the RSA algorithm (PKCS#1 / RFC3447). At this time, only signature verification is supported. This uses the asymmetric public key subtype to hold its key data. Signed-off-by: David Howells --- security/keys/crypto/Kconfig | 7 + security/keys/crypto/Makefile | 1 + security/keys/crypto/crypto_rsa.c | 282 +++++++++++++++++++++++++++++++++++++ security/keys/crypto/public_key.h | 2 + 4 files changed, 292 insertions(+) create mode 100644 security/keys/crypto/crypto_rsa.c diff --git a/security/keys/crypto/Kconfig b/security/keys/crypto/Kconfig index 5f2b8ac..4e3777e 100644 --- a/security/keys/crypto/Kconfig +++ b/security/keys/crypto/Kconfig @@ -15,3 +15,10 @@ config CRYPTO_KEY_PUBLIC_KEY_SUBTYPE If signature generation and/or verification are to be used, appropriate hash algorithms (such as SHA-1) must be available. ENOPKG will be reported if the requisite algorithm is unavailable. + +config CRYPTO_KEY_PKEY_ALGO_RSA + tristate "RSA public-key algorithm" + depends on CRYPTO_KEY_PUBLIC_KEY_SUBTYPE + select MPILIB_EXTRA + help + This option enables support for the RSA algorithm (PKCS#1, RFC3447). diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile index 6384306..b6b1a5a 100644 --- a/security/keys/crypto/Makefile +++ b/security/keys/crypto/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o crypto_keys-y := crypto_type.o crypto_verify.o obj-$(CONFIG_CRYPTO_KEY_PUBLIC_KEY_SUBTYPE) += public_key.o +obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) += crypto_rsa.o diff --git a/security/keys/crypto/crypto_rsa.c b/security/keys/crypto/crypto_rsa.c new file mode 100644 index 0000000..beb5181 --- /dev/null +++ b/security/keys/crypto/crypto_rsa.c @@ -0,0 +1,282 @@ +/* RSA asymmetric public-key algorithm [RFC3447] + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "RSA: "fmt +#include +#include +#include "public_key.h" + +MODULE_LICENSE("GPL"); + +#define kenter(FMT, ...) \ + pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__) +#define kleave(FMT, ...) \ + pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__) + +/* + * Hash algorithm OIDs plus ASN.1 DER wrappings [RFC4880 sec 5.2.2]. + */ +static const u8 RSA_digest_info_MD5[] = { + 0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, /* OID */ + 0x05, 0x00, 0x04, 0x10 +}; + +static const u8 RSA_digest_info_SHA1[] = { + 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, + 0x2B, 0x0E, 0x03, 0x02, 0x1A, + 0x05, 0x00, 0x04, 0x14 +}; + +static const u8 RSA_digest_info_RIPE_MD_160[] = { + 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, + 0x2B, 0x24, 0x03, 0x02, 0x01, + 0x05, 0x00, 0x04, 0x14 +}; + +static const u8 RSA_digest_info_SHA224[] = { + 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, + 0x05, 0x00, 0x04, 0x1C +}; + +static const u8 RSA_digest_info_SHA256[] = { + 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, + 0x05, 0x00, 0x04, 0x20 +}; + +static const u8 RSA_digest_info_SHA384[] = { + 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, + 0x05, 0x00, 0x04, 0x30 +}; + +static const u8 RSA_digest_info_SHA512[] = { + 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, + 0x05, 0x00, 0x04, 0x40 +}; + +static const struct { + const u8 const *data; + size_t size; +} RSA_ASN1_templates[PKEY_HASH__LAST] = { +#define _(X) { RSA_digest_info_##X, sizeof(RSA_digest_info_##X) } + [PKEY_HASH_MD5] = _(MD5), + [PKEY_HASH_SHA1] = _(SHA1), + [PKEY_HASH_RIPE_MD_160] = _(RIPE_MD_160), + [PKEY_HASH_SHA256] = _(SHA256), + [PKEY_HASH_SHA384] = _(SHA384), + [PKEY_HASH_SHA512] = _(SHA512), + [PKEY_HASH_SHA224] = _(SHA224), +#undef _ +}; + +/* + * RSAVP1() function [RFC3447 sec 5.2.2] + */ +static int RSAVP1(const struct public_key *key, MPI s, MPI *_m) +{ + MPI m; + int ret; + + /* (1) Validate 0 <= s < n */ + if (mpi_cmp_ui(s, 0) < 0) { + kleave(" = -EBADMSG [s < 0]"); + return -EBADMSG; + } + if (mpi_cmp(s, key->rsa.n) >= 0) { + kleave(" = -EBADMSG [s >= n]"); + return -EBADMSG; + } + + m = mpi_alloc(0); + if (!m) + return -ENOMEM; + + /* (2) m = s^e mod n */ + ret = mpi_powm(m, s, key->rsa.e, key->rsa.n); + if (ret < 0) { + mpi_free(m); + return ret; + } + + *_m = m; + return 0; +} + +/* + * Integer to Octet String conversion [RFC3447 sec 4.1] + */ +static int RSA_I2OSP(MPI x, size_t xLen, u8 **_X) +{ + unsigned X_size, x_size; + int X_sign; + u8 *X; + + /* Make sure the string is the right length. The number should begin + * with { 0x00, 0x01, ... } so we have to account for 15 leading zero + * bits not being reported by MPI. + */ + x_size = mpi_get_nbits(x); + pr_devel("size(x)=%u xLen*8=%zu\n", x_size, xLen * 8); + if (x_size != xLen * 8 - 15) + return -ERANGE; + + X = mpi_get_buffer(x, &X_size, &X_sign); + if (!X) + return -ENOMEM; + if (X_sign < 0) { + kfree(X); + return -EBADMSG; + } + if (X_size != xLen - 1) { + kfree(X); + return -EBADMSG; + } + + *_X = X; + return 0; +} + +/* + * Perform the RSA signature verification. + * @H: Value of hash of data and metadata + * @EM: The computed signature value + * @k: The size of EM (EM[0] is an invalid location but should hold 0x00) + * @hash_size: The size of H + * @asn1_template: The DigestInfo ASN.1 template + * @asn1_size: Size of asm1_template[] + */ +static int RSA_verify(const u8 *H, const u8 *EM, size_t k, size_t hash_size, + const u8 *asn1_template, size_t asn1_size) +{ + unsigned PS_end, T_offset, i; + + kenter(",,%zu,%zu,%zu", k, hash_size, asn1_size); + + if (k < 2 + 1 + asn1_size + hash_size) + return -EBADMSG; + + /* Decode the EMSA-PKCS1-v1_5 */ + if (EM[1] != 0x01) { + kleave(" = -EBADMSG [EM[1] == %02u]", EM[1]); + return -EBADMSG; + } + + T_offset = k - (asn1_size + hash_size); + PS_end = T_offset - 1; + if (EM[PS_end] != 0x00) { + kleave(" = -EBADMSG [EM[T-1] == %02u]", EM[PS_end]); + return -EBADMSG; + } + + for (i = 2; i < PS_end; i++) { + if (EM[i] != 0xff) { + kleave(" = -EBADMSG [EM[PS%x] == %02u]", i - 2, EM[i]); + return -EBADMSG; + } + } + + if (memcmp(asn1_template, EM + T_offset, asn1_size) != 0) { + kleave(" = -EBADMSG [EM[T] ASN.1 mismatch]"); + return -EBADMSG; + } + + if (memcmp(H, EM + T_offset + asn1_size, hash_size) != 0) { + kleave(" = -EKEYREJECTED [EM[T] hash mismatch]"); + return -EKEYREJECTED; + } + + kleave(" = 0"); + return 0; +} + +/* + * Perform the verification step [RFC3447 sec 8.2.2]. + */ +static int RSA_verify_signature(const struct public_key *key, + const struct public_key_signature *sig) +{ + size_t tsize; + int ret; + + /* Variables as per RFC3447 sec 8.2.2 */ + const u8 *H = sig->digest; + u8 *EM = NULL; + MPI m = NULL; + size_t k; + + kenter(""); + + /* (1) Check the signature size against the public key modulus size */ + k = (mpi_get_nbits(key->rsa.n) + 7) / 8; + + tsize = (mpi_get_nbits(sig->rsa.s) + 7) / 8; + pr_devel("step 1: k=%zu size(S)=%zu\n", k, tsize); + if (tsize != k) { + ret = -EBADMSG; + goto error; + } + + /* (2b) Apply the RSAVP1 verification primitive to the public key */ + ret = RSAVP1(key, sig->rsa.s, &m); + if (ret < 0) + goto error; + + /* (2c) Convert the message representative (m) to an encoded message + * (EM) of length k octets. + * + * NOTE! The leading zero byte is suppressed by MPI, so we pass a + * pointer to the _preceding_ byte to RSA_verify()! + */ + ret = RSA_I2OSP(m, k, &EM); + if (ret < 0) + goto error; + +#if 0 + { + int i; + printk("H: "); + for (i = 0; i < sig->digest_size; i++) + printk("%02x", H[i]); + printk("\n"); + } + + { + int i; + printk("EM: 00"); + for (i = 0; i < k - 1; i++) + printk("%02x", EM[i]); + printk("\n"); + } +#endif + + ret = RSA_verify(H, EM - 1, k, sig->digest_size, + RSA_ASN1_templates[sig->pkey_hash_algo].data, + RSA_ASN1_templates[sig->pkey_hash_algo].size); + +error: + kfree(EM); + mpi_free(m); + kleave(" = %d", ret); + return ret; +} + +const struct public_key_algorithm RSA_public_key_algorithm = { + .name = "RSA", + .n_pub_mpi = 2, + .n_sec_mpi = 3, + .n_sig_mpi = 1, + .verify = RSA_verify_signature, +}; +EXPORT_SYMBOL_GPL(RSA_public_key_algorithm); diff --git a/security/keys/crypto/public_key.h b/security/keys/crypto/public_key.h index 81ed603..7913615 100644 --- a/security/keys/crypto/public_key.h +++ b/security/keys/crypto/public_key.h @@ -42,6 +42,8 @@ struct public_key_algorithm { const struct public_key_signature *sig); }; +extern const struct public_key_algorithm RSA_public_key_algorithm; + /* * Asymmetric public key data */ -- 1.7.10.2 From a1ac402f327ab537a629c4d365b3f5461eaecb82 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 4 May 2012 16:30:20 +0100 Subject: [PATCH 09/36] Fix signature verification for shorter signatures gpg can produce a signature file where length of signature is less than the modulus size because the amount of space an MPI takes up is kept as low as possible by discarding leading zeros. This regularly happens for several modules during the build. Fix it by relaxing check in RSA verification code. Thanks to Tomas Mraz and Miloslav Trmac for help. Signed-off-by: Milan Broz Signed-off-by: David Howells --- security/keys/crypto/crypto_rsa.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/security/keys/crypto/crypto_rsa.c b/security/keys/crypto/crypto_rsa.c index beb5181..cc5cd95 100644 --- a/security/keys/crypto/crypto_rsa.c +++ b/security/keys/crypto/crypto_rsa.c @@ -219,15 +219,23 @@ static int RSA_verify_signature(const struct public_key *key, kenter(""); /* (1) Check the signature size against the public key modulus size */ - k = (mpi_get_nbits(key->rsa.n) + 7) / 8; + k = mpi_get_nbits(key->rsa.n); + tsize = mpi_get_nbits(sig->rsa.s); - tsize = (mpi_get_nbits(sig->rsa.s) + 7) / 8; + /* According to RFC 4880 sec 3.2, length of MPI is computed starting + * from most significant bit. So the RFC 3447 sec 8.2.2 size check + * must be relaxed to conform with shorter signatures - so we fail here + * only if signature length is longer than modulus size. + */ pr_devel("step 1: k=%zu size(S)=%zu\n", k, tsize); - if (tsize != k) { + if (k < tsize) { ret = -EBADMSG; goto error; } + /* Round up and convert to octets */ + k = (k + 7) / 8; + /* (2b) Apply the RSAVP1 verification primitive to the public key */ ret = RSAVP1(key, sig->rsa.s, &m); if (ret < 0) -- 1.7.10.2 From d682ea629a2494aab705468d1479058a03fd7879 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 4 May 2012 16:30:23 +0100 Subject: [PATCH 10/36] PGPLIB: PGP definitions (RFC 4880) Provide some useful PGP definitions from RFC 4880. These describe details of public key crypto as used by crypto keys for things like signature verification. Signed-off-by: David Howells --- include/linux/pgp.h | 206 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 include/linux/pgp.h diff --git a/include/linux/pgp.h b/include/linux/pgp.h new file mode 100644 index 0000000..1359f64 --- /dev/null +++ b/include/linux/pgp.h @@ -0,0 +1,206 @@ +/* PGP definitions (RFC 4880) + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _LINUX_PGP_H +#define _LINUX_PGP_H + +#include + +struct pgp_key_ID { + u8 id[8]; +}; + +struct pgp_time { + u8 time[4]; +}; + +/* + * PGP public-key algorithm identifiers [RFC4880: 9.1] + */ +enum pgp_pubkey_algo { + PGP_PUBKEY_RSA_ENC_OR_SIG = 1, + PGP_PUBKEY_RSA_ENC_ONLY = 2, + PGP_PUBKEY_RSA_SIG_ONLY = 3, + PGP_PUBKEY_ELGAMAL = 16, + PGP_PUBKEY_DSA = 17, + PGP_PUBKEY__LAST +}; + +/* + * PGP symmetric-key algorithm identifiers [RFC4880: 9.2] + */ +enum pgp_symkey_algo { + PGP_SYMKEY_PLAINTEXT = 0, + PGP_SYMKEY_IDEA = 1, + PGP_SYMKEY_3DES = 2, + PGP_SYMKEY_CAST5 = 3, + PGP_SYMKEY_BLOWFISH = 4, + PGP_SYMKEY_AES_128KEY = 7, + PGP_SYMKEY_AES_192KEY = 8, + PGP_SYMKEY_AES_256KEY = 9, + PGP_SYMKEY_TWOFISH_256KEY = 10, +}; + +/* + * PGP compression algorithm identifiers [RFC4880: 9.3] + */ +enum pgp_compr_algo { + PGP_COMPR_UNCOMPRESSED = 0, + PGP_COMPR_ZIP = 1, + PGP_COMPR_ZLIB = 2, + PGP_COMPR_BZIP2 = 3, +}; + +/* + * PGP hash algorithm identifiers [RFC4880: 9.4] + */ +enum pgp_hash_algo { + PGP_HASH_MD5 = 1, + PGP_HASH_SHA1 = 2, + PGP_HASH_RIPE_MD_160 = 3, + PGP_HASH_SHA256 = 8, + PGP_HASH_SHA384 = 9, + PGP_HASH_SHA512 = 10, + PGP_HASH_SHA224 = 11, + PGP_HASH__LAST +}; + +extern const char *const pgp_hash_algorithms[PGP_HASH__LAST]; + +/* + * PGP packet type tags [RFC4880: 4.3]. + */ +enum pgp_packet_tag { + PGP_PKT_RESERVED = 0, + PGP_PKT_PUBKEY_ENC_SESSION_KEY = 1, + PGP_PKT_SIGNATURE = 2, + PGP_PKT_SYMKEY_ENC_SESSION_KEY = 3, + PGP_PKT_ONEPASS_SIGNATURE = 4, + PGP_PKT_SECRET_KEY = 5, + PGP_PKT_PUBLIC_KEY = 6, + PGP_PKT_SECRET_SUBKEY = 7, + PGP_PKT_COMPRESSED_DATA = 8, + PGP_PKT_SYM_ENC_DATA = 9, + PGP_PKT_MARKER = 10, + PGP_PKT_LITERAL_DATA = 11, + PGP_PKT_TRUST = 12, + PGP_PKT_USER_ID = 13, + PGP_PKT_PUBLIC_SUBKEY = 14, + PGP_PKT_USER_ATTRIBUTE = 17, + PGP_PKT_SYM_ENC_AND_INTEG_DATA = 18, + PGP_PKT_MODIFY_DETECT_CODE = 19, + PGP_PKT_PRIVATE_0 = 60, + PGP_PKT_PRIVATE_3 = 63, + PGP_PKT__HIGHEST = 63 +}; + +/* + * Signature (tag 2) packet [RFC4880: 5.2]. + */ +enum pgp_signature_version { + PGP_SIG_VERSION_3 = 3, + PGP_SIG_VERSION_4 = 4, +}; + +enum pgp_signature_type { + PGP_SIG_BINARY_DOCUMENT_SIG = 0x00, + PGP_SIG_CANONICAL_TEXT_DOCUMENT_SIG = 0x01, + PGP_SIG_STANDALONE_SIG = 0x02, + PGP_SIG_GENERAL_CERT_OF_UID_PUBKEY = 0x10, + PGP_SIG_PERSONAL_CERT_OF_UID_PUBKEY = 0x11, + PGP_SIG_CASUAL_CERT_OF_UID_PUBKEY = 0x12, + PGP_SIG_POSTITIVE_CERT_OF_UID_PUBKEY = 0x13, + PGP_SIG_SUBKEY_BINDING_SIG = 0x18, + PGP_SIG_PRIMARY_KEY_BINDING_SIG = 0x19, + PGP_SIG_DIRECTLY_ON_KEY = 0x1F, + PGP_SIG_KEY_REVOCATION_SIG = 0x20, + PGP_SIG_SUBKEY_REVOCATION_SIG = 0x28, + PGP_SIG_CERT_REVOCATION_SIG = 0x30, + PGP_SIG_TIMESTAMP_SIG = 0x40, + PGP_SIG_THIRD_PARTY_CONFIRM_SIG = 0x50, +}; + +struct pgp_signature_v3_packet { + enum pgp_signature_version version : 8; /* == PGP_SIG_VERSION_3 */ + u8 length_of_hashed; /* == 5 */ + struct { + enum pgp_signature_type signature_type : 8; + struct pgp_time creation_time; + } hashed; + struct pgp_key_ID issuer; + enum pgp_pubkey_algo pubkey_algo : 8; + enum pgp_hash_algo hash_algo : 8; +} __packed; + +struct pgp_signature_v4_packet { + enum pgp_signature_version version : 8; /* == PGP_SIG_VERSION_4 */ + enum pgp_signature_type signature_type : 8; + enum pgp_pubkey_algo pubkey_algo : 8; + enum pgp_hash_algo hash_algo : 8; +} __packed; + +/* + * V4 signature subpacket types [RFC4880: 5.2.3.1]. + */ +enum pgp_sig_subpkt_type { + PGP_SIG_CREATION_TIME = 2, + PGP_SIG_EXPIRATION_TIME = 3, + PGP_SIG_EXPORTABLE_CERT = 4, + PGP_SIG_TRUST_SIG = 5, + PGP_SIG_REGEXP = 6, + PGP_SIG_REVOCABLE = 7, + PGP_SIG_KEY_EXPIRATION_TIME = 9, + PGP_SIG_PREF_SYM_ALGO = 11, + PGP_SIG_REVOCATION_KEY = 12, + PGP_SIG_ISSUER = 16, + PGP_SIG_NOTATION_DATA = 20, + PGP_SIG_PREF_HASH_ALGO = 21, + PGP_SIG_PREF_COMPR_ALGO = 22, + PGP_SIG_KEY_SERVER_PREFS = 23, + PGP_SIG_PREF_KEY_SERVER = 24, + PGP_SIG_PRIMARY_USER_ID = 25, + PGP_SIG_POLICY_URI = 26, + PGP_SIG_KEY_FLAGS = 27, + PGP_SIG_SIGNERS_USER_ID = 28, + PGP_SIG_REASON_FOR_REVOCATION = 29, + PGP_SIG_FEATURES = 30, + PGP_SIG_TARGET = 31, + PGP_SIG_EMBEDDED_SIG = 32, + PGP_SIG__LAST +}; + +#define PGP_SIG_SUBPKT_TYPE_CRITICAL_MASK 0x80 + +/* + * Key (tag 5, 6, 7 and 14) packet + */ +enum pgp_key_version { + PGP_KEY_VERSION_2 = 2, + PGP_KEY_VERSION_3 = 3, + PGP_KEY_VERSION_4 = 4, +}; + +struct pgp_key_v3_packet { + enum pgp_key_version version : 8; + struct pgp_time creation_time; + u8 expiry[2]; /* 0 or time in days till expiry */ + enum pgp_pubkey_algo pubkey_algo : 8; + u8 key_material[0]; +} __packed; + +struct pgp_key_v4_packet { + enum pgp_key_version version : 8; + struct pgp_time creation_time; + enum pgp_pubkey_algo pubkey_algo : 8; + u8 key_material[0]; +} __packed; + +#endif /* _LINUX_PGP_H */ -- 1.7.10.2 From 651d6e549374b13b2d0473db17df90ab66abf855 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 4 May 2012 16:33:28 +0100 Subject: [PATCH 11/36] PGPLIB: Basic packet parser Provide a simple parser that extracts the packets from a PGP packet blob and passes the desirous ones to the given processor function: struct pgp_parse_context { u64 types_of_interest; int (*process_packet)(struct pgp_parse_context *context, enum pgp_packet_tag type, u8 headerlen, const u8 *data, size_t datalen); }; int pgp_parse_packets(const u8 *data, size_t datalen, struct pgp_parse_context *ctx); This is configured on with CONFIG_PGP_LIBRARY. Signed-off-by: David Howells --- include/linux/pgp.h | 25 ++++ security/keys/crypto/Kconfig | 6 + security/keys/crypto/Makefile | 1 + security/keys/crypto/pgp_library.c | 254 ++++++++++++++++++++++++++++++++++++ 4 files changed, 286 insertions(+) create mode 100644 security/keys/crypto/pgp_library.c diff --git a/include/linux/pgp.h b/include/linux/pgp.h index 1359f64..235270a 100644 --- a/include/linux/pgp.h +++ b/include/linux/pgp.h @@ -203,4 +203,29 @@ struct pgp_key_v4_packet { u8 key_material[0]; } __packed; +/* + * PGP library packet parser + */ +struct pgp_parse_context { + u64 types_of_interest; + int (*process_packet)(struct pgp_parse_context *context, + enum pgp_packet_tag type, + u8 headerlen, + const u8 *data, + size_t datalen); +}; + +extern int pgp_parse_packets(const u8 *data, size_t datalen, + struct pgp_parse_context *ctx); + +struct pgp_parse_pubkey { + enum pgp_key_version version : 8; + enum pgp_pubkey_algo pubkey_algo : 8; + time_t creation_time; + time_t expires_at; +}; + +extern int pgp_parse_public_key(const u8 **_data, size_t *_datalen, + struct pgp_parse_pubkey *pk); + #endif /* _LINUX_PGP_H */ diff --git a/security/keys/crypto/Kconfig b/security/keys/crypto/Kconfig index 4e3777e..88ce0e2 100644 --- a/security/keys/crypto/Kconfig +++ b/security/keys/crypto/Kconfig @@ -22,3 +22,9 @@ config CRYPTO_KEY_PKEY_ALGO_RSA select MPILIB_EXTRA help This option enables support for the RSA algorithm (PKCS#1, RFC3447). + +config PGP_LIBRARY + tristate "PGP parsing library" + help + This option enables a library that provides a number of simple + utility functions for parsing PGP (RFC 4880) packet-based messages. diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile index b6b1a5a..5fbe54e 100644 --- a/security/keys/crypto/Makefile +++ b/security/keys/crypto/Makefile @@ -7,3 +7,4 @@ crypto_keys-y := crypto_type.o crypto_verify.o obj-$(CONFIG_CRYPTO_KEY_PUBLIC_KEY_SUBTYPE) += public_key.o obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) += crypto_rsa.o +obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o diff --git a/security/keys/crypto/pgp_library.c b/security/keys/crypto/pgp_library.c new file mode 100644 index 0000000..685660f --- /dev/null +++ b/security/keys/crypto/pgp_library.c @@ -0,0 +1,254 @@ +/* PGP packet parser (RFC 4880) + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#define pr_fmt(fmt) "PGP: "fmt +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + +const char *const pgp_hash_algorithms[PGP_HASH__LAST] = { + [PGP_HASH_MD5] = "md5", + [PGP_HASH_SHA1] = "sha1", + [PGP_HASH_RIPE_MD_160] = "rmd160", + [PGP_HASH_SHA256] = "sha256", + [PGP_HASH_SHA384] = "sha384", + [PGP_HASH_SHA512] = "sha512", + [PGP_HASH_SHA224] = "sha224", +}; +EXPORT_SYMBOL_GPL(pgp_hash_algorithms); + +/** + * pgp_parse_packet_header - Parse a PGP packet header + * @_data: Start of the PGP packet (updated to PGP packet data) + * @_datalen: Amount of data remaining in buffer (decreased) + * @_type: Where the packet type will be returned + * @_headerlen: Where the header length will be returned + * + * Parse a set of PGP packet header [RFC 4880: 4.2]. + * + * Returns packet data size on success; non-zero on error. If successful, + * *_data and *_datalen will have been updated and *_headerlen will be set to + * hold the length of the packet header. + */ +ssize_t pgp_parse_packet_header(const u8 **_data, size_t *_datalen, + enum pgp_packet_tag *_type, + u8 *_headerlen) +{ + enum pgp_packet_tag type; + const u8 *data = *_data; + size_t size, datalen = *_datalen; + + pr_devel("-->pgp_parse_packet_header(,%zu,,)", datalen); + + if (datalen < 2) + goto short_packet; + + pr_devel("pkthdr %02x, %02x\n", data[0], data[1]); + + type = *data++; + datalen--; + if (!(type & 0x80)) { + pr_debug("Packet type does not have MSB set\n"); + return -EBADMSG; + } + type &= ~0x80; + + if (type & 0x40) { + /* New packet length format */ + type &= ~0x40; + pr_devel("new format: t=%u\n", type); + switch (data[0]) { + case 0x00 ... 0xbf: + /* One-byte length */ + size = data[0]; + data++; + datalen--; + *_headerlen = 2; + break; + case 0xc0 ... 0xdf: + /* Two-byte length */ + if (datalen < 2) + goto short_packet; + size = (data[0] - 192) * 256; + size += data[1] + 192; + data += 2; + datalen -= 2; + *_headerlen = 3; + break; + case 0xff: + pr_debug("Five-byte packet length not supported\n"); + return -EBADMSG; + default: + pr_debug("Error parsing packet length\n"); + return -EBADMSG; + } + } else { + /* Old packet length format */ + u8 length_type = type & 0x03; + type >>= 2; + pr_devel("old format: t=%u lt=%u\n", type, length_type); + + switch (length_type) { + case 0: + /* One-byte length */ + size = data[0]; + data++; + datalen--; + *_headerlen = 2; + break; + case 1: + /* Two-byte length */ + if (datalen < 2) + goto short_packet; + size = data[0] << 8; + size |= data[1]; + data += 2; + datalen -= 2; + *_headerlen = 3; + break; + case 2: + /* Four-byte length */ + if (datalen < 4) + goto short_packet; + size = data[0] << 24; + size |= data[1] << 16; + size |= data[2] << 8; + size |= data[3]; + data += 4; + datalen -= 4; + *_headerlen = 5; + break; + default: + pr_debug("Indefinite length packet not supported\n"); + return -EBADMSG; + } + } + + pr_devel("datalen=%zu size=%zu", datalen, size); + if (datalen < size) + goto short_packet; + + *_data = data; + *_datalen = datalen; + *_type = type; + pr_devel("Found packet type=%u size=%zd\n", type, size); + return size; + +short_packet: + pr_debug("Attempt to parse short packet\n"); + return -EBADMSG; +} + +/** + * pgp_parse_packets - Parse a set of PGP packets + * @_data: Data to be parsed (updated) + * @_datalen: Amount of data (updated) + * @ctx: Parsing context + * + * Parse a set of PGP packets [RFC 4880: 4]. + */ +int pgp_parse_packets(const u8 *data, size_t datalen, + struct pgp_parse_context *ctx) +{ + enum pgp_packet_tag type; + ssize_t pktlen; + u8 headerlen; + int ret; + + while (datalen > 2) { + pktlen = pgp_parse_packet_header(&data, &datalen, &type, + &headerlen); + if (pktlen < 0) + return pktlen; + + if ((ctx->types_of_interest >> type) & 1) { + ret = ctx->process_packet(ctx, type, headerlen, + data, pktlen); + if (ret < 0) + return ret; + } + data += pktlen; + datalen -= pktlen; + } + + if (datalen != 0) { + pr_debug("Excess octets in packet stream\n"); + return -EBADMSG; + } + + return 0; +} +EXPORT_SYMBOL_GPL(pgp_parse_packets); + +/** + * pgp_parse_public_key - Parse the common part of a PGP pubkey packet + * @_data: Content of packet (updated) + * @_datalen: Length of packet remaining (updated) + * @pk: Public key data + * + * Parse the common data struct for a PGP pubkey packet [RFC 4880: 5.5.2]. + */ +int pgp_parse_public_key(const u8 **_data, size_t *_datalen, + struct pgp_parse_pubkey *pk) +{ + const u8 *data = *_data; + size_t datalen = *_datalen; + __be32 tmp; + + if (datalen < 12) { + pr_debug("Public key packet too short\n"); + return -EBADMSG; + } + + pk->version = *data++; + switch (pk->version) { + case PGP_KEY_VERSION_2: + case PGP_KEY_VERSION_3: + case PGP_KEY_VERSION_4: + break; + default: + pr_debug("Public key packet with unhandled version %d\n", + pk->version); + return -EBADMSG; + } + + tmp = *data++ << 24; + tmp |= *data++ << 16; + tmp |= *data++ << 8; + tmp |= *data++; + pk->creation_time = tmp; + if (pk->version == PGP_KEY_VERSION_4) { + pk->expires_at = 0; /* Have to get it from the selfsignature */ + } else { + unsigned short ndays; + ndays = *data++ << 8; + ndays |= *data++; + if (ndays) + pk->expires_at = pk->creation_time + ndays * 86400UL; + else + pk->expires_at = 0; + datalen -= 2; + } + + pk->pubkey_algo = *data++; + datalen -= 6; + + pr_devel("%x,%x,%lx,%lx", + pk->version, pk->pubkey_algo, pk->creation_time, + pk->expires_at); + + *_data = data; + *_datalen = datalen; + return 0; +} +EXPORT_SYMBOL_GPL(pgp_parse_public_key); -- 1.7.10.2 From 1123a360effa696546dbce1273c680b45c5cf946 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 4 May 2012 16:33:30 +0100 Subject: [PATCH 12/36] PGPLIB: Signature parser Provide some PGP signature parsing helpers: (1) A function to parse V4 signature subpackets and pass the desired ones to a processor function: int pgp_parse_sig_subpkts(const u8 *data, size_t datalen, struct pgp_parse_sig_context *ctx); (2) A function to parse out basic signature parameters from any PGP signature such that the algorithms and public key can be selected: int pgp_parse_sig_params(const u8 **_data, size_t *_datalen, struct pgp_sig_parameters *p); Signed-off-by: David Howells --- include/linux/pgp.h | 24 ++++ security/keys/crypto/pgp_library.c | 277 ++++++++++++++++++++++++++++++++++++ 2 files changed, 301 insertions(+) diff --git a/include/linux/pgp.h b/include/linux/pgp.h index 235270a..ab1a8fa 100644 --- a/include/linux/pgp.h +++ b/include/linux/pgp.h @@ -228,4 +228,28 @@ struct pgp_parse_pubkey { extern int pgp_parse_public_key(const u8 **_data, size_t *_datalen, struct pgp_parse_pubkey *pk); +struct pgp_parse_sig_context { + unsigned long types_of_interest[128 / BITS_PER_LONG]; + int (*process_packet)(struct pgp_parse_sig_context *context, + enum pgp_sig_subpkt_type type, + const u8 *data, + size_t datalen); +}; + +extern int pgp_parse_sig_packets(const u8 *data, size_t datalen, + struct pgp_parse_sig_context *ctx); + +struct pgp_sig_parameters { + enum pgp_signature_type signature_type : 8; + union { + struct pgp_key_ID issuer; + __be32 issuer32[2]; + }; + enum pgp_pubkey_algo pubkey_algo : 8; + enum pgp_hash_algo hash_algo : 8; +}; + +extern int pgp_parse_sig_params(const u8 **_data, size_t *_datalen, + struct pgp_sig_parameters *p); + #endif /* _LINUX_PGP_H */ diff --git a/security/keys/crypto/pgp_library.c b/security/keys/crypto/pgp_library.c index 685660f..f6b831f 100644 --- a/security/keys/crypto/pgp_library.c +++ b/security/keys/crypto/pgp_library.c @@ -252,3 +252,280 @@ int pgp_parse_public_key(const u8 **_data, size_t *_datalen, return 0; } EXPORT_SYMBOL_GPL(pgp_parse_public_key); + +/** + * pgp_parse_sig_subpkt_header - Parse a PGP V4 signature subpacket header + * @_data: Start of the subpacket (updated to subpacket data) + * @_datalen: Amount of data remaining in buffer (decreased) + * @_type: Where the subpacket type will be returned + * + * Parse a PGP V4 signature subpacket header [RFC 4880: 5.2.3.1]. + * + * Returns packet data size on success; non-zero on error. If successful, + * *_data and *_datalen will have been updated and *_headerlen will be set to + * hold the length of the packet header. + */ +ssize_t pgp_parse_sig_subpkt_header(const u8 **_data, size_t *_datalen, + enum pgp_sig_subpkt_type *_type) +{ + enum pgp_sig_subpkt_type type; + const u8 *data = *_data; + size_t size, datalen = *_datalen; + + pr_devel("-->pgp_parse_sig_subpkt_header(,%zu,,)", datalen); + + if (datalen < 2) + goto short_subpacket; + + pr_devel("subpkt hdr %02x, %02x\n", data[0], data[1]); + + switch (data[0]) { + case 0x00 ... 0xbf: + /* One-byte length */ + size = data[0]; + data++; + datalen--; + break; + case 0xc0 ... 0xfe: + /* Two-byte length */ + if (datalen < 3) + goto short_subpacket; + size = (data[0] - 192) * 256; + size += data[1] + 192; + data += 2; + datalen -= 2; + break; + case 0xff: + if (datalen < 6) + goto short_subpacket; + size = data[1] << 24; + size |= data[2] << 16; + size |= data[3] << 8; + size |= data[4]; + data += 5; + datalen -= 5; + break; + } + + /* The type octet is included in the size */ + if (size == 0) { + pr_debug("Signature subpacket size can't be zero\n"); + return -EBADMSG; + } + + type = *data++ & ~PGP_SIG_SUBPKT_TYPE_CRITICAL_MASK; + datalen--; + size--; + + pr_devel("datalen=%zu size=%zu", datalen, size); + if (datalen < size) + goto short_subpacket; + + *_data = data; + *_datalen = datalen; + *_type = type; + pr_devel("Found subpkt type=%u size=%zd\n", type, size); + return size; + +short_subpacket: + pr_debug("Attempt to parse short signature subpacket\n"); + return -EBADMSG; +} + +/** + * pgp_parse_sig_subpkts - Parse a set of PGP V4 signatute subpackets + * @_data: Data to be parsed (updated) + * @_datalen: Amount of data (updated) + * @ctx: Parsing context + * + * Parse a set of PGP signature subpackets [RFC 4880: 5.2.3]. + */ +int pgp_parse_sig_subpkts(const u8 *data, size_t datalen, + struct pgp_parse_sig_context *ctx) +{ + enum pgp_sig_subpkt_type type; + ssize_t pktlen; + int ret; + + pr_devel("-->pgp_parse_sig_subpkts(,%zu,,)", datalen); + + while (datalen > 2) { + pktlen = pgp_parse_sig_subpkt_header(&data, &datalen, &type); + if (pktlen < 0) + return pktlen; + if (test_bit(type, ctx->types_of_interest)) { + ret = ctx->process_packet(ctx, type, data, pktlen); + if (ret < 0) + return ret; + } + data += pktlen; + datalen -= pktlen; + } + + if (datalen != 0) { + pr_debug("Excess octets in signature subpacket stream\n"); + return -EBADMSG; + } + + return 0; +} +EXPORT_SYMBOL_GPL(pgp_parse_sig_subpkts); + +struct pgp_parse_sig_params_ctx { + struct pgp_parse_sig_context base; + struct pgp_sig_parameters *params; + bool got_the_issuer; +}; + +/* + * Process a V4 signature subpacket. + */ +static int pgp_process_sig_params_subpkt(struct pgp_parse_sig_context *context, + enum pgp_sig_subpkt_type type, + const u8 *data, + size_t datalen) +{ + struct pgp_parse_sig_params_ctx *ctx = + container_of(context, struct pgp_parse_sig_params_ctx, base); + + if (ctx->got_the_issuer) { + pr_debug("V4 signature packet has multiple issuers\n"); + return -EBADMSG; + } + + if (datalen != 8) { + pr_debug("V4 signature issuer subpkt not 8 long (%zu)\n", + datalen); + return -EBADMSG; + } + + memcpy(&ctx->params->issuer, data, 8); + ctx->got_the_issuer = true; + return 0; +} + +/** + * pgp_parse_sig_params - Parse basic parameters from a PGP signature packet + * @_data: Content of packet (updated) + * @_datalen: Length of packet remaining (updated) + * @p: The basic parameters + * + * Parse the basic parameters from a PGP signature packet [RFC 4880: 5.2] that + * are needed to start off a signature verification operation. The only ones + * actually necessary are the signature type (which affects how the data is + * transformed) and the has algorithm. + * + * We also extract the public key algorithm and the issuer's key ID as we'll + * need those to determine if we actually have the public key available. If + * not, then we can't verify the signature anyway. + * + * Returns 0 if successful or a negative error code. *_data and *_datalen are + * updated to point to the 16-bit subset of the hash value and the set of MPIs. + */ +int pgp_parse_sig_params(const u8 **_data, size_t *_datalen, + struct pgp_sig_parameters *p) +{ + enum pgp_signature_version version; + const u8 *data = *_data; + size_t datalen = *_datalen; + int ret; + + pr_devel("-->pgp_parse_sig_params(,%zu,,)", datalen); + + if (datalen < 1) + return -EBADMSG; + version = *data; + + if (version == PGP_SIG_VERSION_3) { + const struct pgp_signature_v3_packet *v3 = (const void *)data; + + if (datalen < sizeof(*v3)) { + pr_debug("Short V3 signature packet\n"); + return -EBADMSG; + } + datalen -= sizeof(*v3); + data += sizeof(*v3); + + /* V3 has everything we need in the header */ + p->signature_type = v3->hashed.signature_type; + p->issuer = v3->issuer; + p->pubkey_algo = v3->pubkey_algo; + p->hash_algo = v3->hash_algo; + + } else if (version == PGP_SIG_VERSION_4) { + const struct pgp_signature_v4_packet *v4 = (const void *)data; + struct pgp_parse_sig_params_ctx ctx = { + .base.process_packet = pgp_process_sig_params_subpkt, + .params = p, + .got_the_issuer = false, + }; + size_t subdatalen; + + if (datalen < sizeof(*v4) + 2 + 2 + 2) { + pr_debug("Short V4 signature packet\n"); + return -EBADMSG; + } + datalen -= sizeof(*v4); + data += sizeof(*v4); + + /* V4 has most things in the header... */ + p->signature_type = v4->signature_type; + p->pubkey_algo = v4->pubkey_algo; + p->hash_algo = v4->hash_algo; + + /* ... but we have to get the key ID from the subpackets, of + * which there are two sets. */ + __set_bit(PGP_SIG_ISSUER, ctx.base.types_of_interest); + + subdatalen = *data++ << 8; + subdatalen |= *data++; + datalen -= 2; + if (subdatalen) { + /* Hashed subpackets */ + pr_devel("hashed data: %zu (after %zu)\n", + subdatalen, sizeof(*v4)); + if (subdatalen > datalen + 2 + 2) { + pr_debug("Short V4 signature packet [hdata]\n"); + return -EBADMSG; + } + ret = pgp_parse_sig_subpkts(data, subdatalen, + &ctx.base); + if (ret < 0) + return ret; + data += subdatalen; + datalen += subdatalen; + } + + subdatalen = *data++ << 8; + subdatalen |= *data++; + datalen -= 2; + if (subdatalen) { + /* Unhashed subpackets */ + pr_devel("unhashed data: %zu\n", subdatalen); + if (subdatalen > datalen + 2) { + pr_debug("Short V4 signature packet [udata]\n"); + return -EBADMSG; + } + ret = pgp_parse_sig_subpkts(data, subdatalen, + &ctx.base); + if (ret < 0) + return ret; + data += subdatalen; + datalen += subdatalen; + } + + if (!ctx.got_the_issuer) { + pr_debug("V4 signature packet lacks issuer\n"); + return -EBADMSG; + } + } else { + pr_debug("Signature packet with unhandled version %d\n", + version); + return -EBADMSG; + } + + *_data = data; + *_datalen = datalen; + return 0; +} +EXPORT_SYMBOL_GPL(pgp_parse_sig_params); -- 1.7.10.2 From 53f01a04d44b822af15cd31bb451ca13695bdf1c Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 4 May 2012 16:36:35 +0100 Subject: [PATCH 13/36] KEYS: PGP data parser Implement a PGP data parser for the crypto key type to use when instantiating a key. This parser attempts to parse the instantiation data as a PGP packet sequence (RFC 4880) and if it parses okay, attempts to extract a public-key algorithm key or subkey from it. If it finds such a key, it will set up a public_key subtype payload with appropriate handler routines (DSA or RSA) and attach it to the key. Signed-off-by: David Howells --- security/keys/crypto/Kconfig | 12 ++ security/keys/crypto/Makefile | 4 + security/keys/crypto/pgp_key_parser.c | 343 +++++++++++++++++++++++++++++++++ security/keys/crypto/pgp_parser.h | 23 +++ 4 files changed, 382 insertions(+) create mode 100644 security/keys/crypto/pgp_key_parser.c create mode 100644 security/keys/crypto/pgp_parser.h diff --git a/security/keys/crypto/Kconfig b/security/keys/crypto/Kconfig index 88ce0e2..1c2ae55 100644 --- a/security/keys/crypto/Kconfig +++ b/security/keys/crypto/Kconfig @@ -28,3 +28,15 @@ config PGP_LIBRARY help This option enables a library that provides a number of simple utility functions for parsing PGP (RFC 4880) packet-based messages. + +config CRYPTO_KEY_PGP_PARSER + tristate "PGP key blob parser" + depends on CRYPTO_KEY_TYPE + select CRYPTO_KEY_PUBLIC_KEY_SUBTYPE + select PGP_LIBRARY + select MD5 # V3 fingerprint generation + select SHA1 # V4 fingerprint generation + help + This option provides support for parsing PGP (RFC 4880) format blobs + for key data and provides the ability to instantiate a crypto key + from a public key packet found inside the blob. diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile index 5fbe54e..3bb2e61 100644 --- a/security/keys/crypto/Makefile +++ b/security/keys/crypto/Makefile @@ -8,3 +8,7 @@ crypto_keys-y := crypto_type.o crypto_verify.o obj-$(CONFIG_CRYPTO_KEY_PUBLIC_KEY_SUBTYPE) += public_key.o obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) += crypto_rsa.o obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o + +obj-$(CONFIG_CRYPTO_KEY_PGP_PARSER) += pgp_parser.o +pgp_parser-y := \ + pgp_key_parser.o diff --git a/security/keys/crypto/pgp_key_parser.c b/security/keys/crypto/pgp_key_parser.c new file mode 100644 index 0000000..4efc4de --- /dev/null +++ b/security/keys/crypto/pgp_key_parser.c @@ -0,0 +1,343 @@ +/* Parser for PGP format key data [RFC 4880] + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "PGP: "fmt +#include +#include +#include +#include +#include +#include +#include +#include "public_key.h" +#include "pgp_parser.h" + +MODULE_LICENSE("GPL"); + +const +struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST] = { +#if defined(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) || \ + defined(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA_MODULE) + [PGP_PUBKEY_RSA_ENC_OR_SIG] = &RSA_public_key_algorithm, + [PGP_PUBKEY_RSA_ENC_ONLY] = &RSA_public_key_algorithm, + [PGP_PUBKEY_RSA_SIG_ONLY] = &RSA_public_key_algorithm, +#endif + [PGP_PUBKEY_ELGAMAL] = NULL, + [PGP_PUBKEY_DSA] = NULL, +}; + +static const u8 pgp_public_key_capabilities[PGP_PUBKEY__LAST] = { + [PGP_PUBKEY_RSA_ENC_OR_SIG] = PKEY_CAN_ENCDEC | PKEY_CAN_SIGVER, + [PGP_PUBKEY_RSA_ENC_ONLY] = PKEY_CAN_ENCDEC, + [PGP_PUBKEY_RSA_SIG_ONLY] = PKEY_CAN_SIGVER, + [PGP_PUBKEY_ELGAMAL] = 0, + [PGP_PUBKEY_DSA] = 0, +}; + +static inline void digest_putc(struct shash_desc *digest, uint8_t ch) +{ + crypto_shash_update(digest, &ch, 1); +} + +struct pgp_key_data_parse_context { + struct pgp_parse_context pgp; + struct crypto_key_subtype *subtype; + char *fingerprint; + void *payload; +}; + +/* + * Calculate the public key ID (RFC4880 12.2) + */ +static int pgp_calc_pkey_keyid(struct shash_desc *digest, + struct pgp_parse_pubkey *pgp, + struct public_key *key) +{ + unsigned nb[ARRAY_SIZE(key->mpi)]; + unsigned nn[ARRAY_SIZE(key->mpi)]; + unsigned n; + u8 *pp[ARRAY_SIZE(key->mpi)]; + u32 a32; + int npkey = key->algo->n_pub_mpi; + int i, ret = -ENOMEM; + + kenter(""); + + n = (pgp->version < PGP_KEY_VERSION_4) ? 8 : 6; + for (i = 0; i < npkey; i++) { + nb[i] = mpi_get_nbits(key->mpi[i]); + pp[i] = mpi_get_buffer(key->mpi[i], nn + i, NULL); + if (!pp[i]) + goto error; + n += 2 + nn[i]; + } + + digest_putc(digest, 0x99); /* ctb */ + digest_putc(digest, n >> 8); /* 16-bit header length */ + digest_putc(digest, n); + digest_putc(digest, pgp->version); + + a32 = pgp->creation_time; + digest_putc(digest, a32 >> 24); + digest_putc(digest, a32 >> 16); + digest_putc(digest, a32 >> 8); + digest_putc(digest, a32 >> 0); + + if (pgp->version < PGP_KEY_VERSION_4) { + u16 a16; + + if( pgp->expires_at) + a16 = (pgp->expires_at - pgp->creation_time) / 86400UL; + else + a16 = 0; + digest_putc(digest, a16 >> 8); + digest_putc(digest, a16 >> 0); + } + + digest_putc(digest, pgp->pubkey_algo); + + for (i = 0; i < npkey; i++) { + digest_putc(digest, nb[i] >> 8); + digest_putc(digest, nb[i]); + crypto_shash_update(digest, pp[i], nn[i]); + } + ret = 0; + +error: + for (i = 0; i < npkey; i++) + kfree(pp[i]); + kleave(" = %d", ret); + return ret; +} + +/* + * Calculate the public key ID fingerprint + */ +static int pgp_generate_fingerprint(struct pgp_key_data_parse_context *ctx, + struct pgp_parse_pubkey *pgp, + struct public_key *key) +{ + struct crypto_shash *tfm; + struct shash_desc *digest; + char *fingerprint; + u8 *raw_fingerprint; + int digest_size, offset; + int ret, i; + + ret = -ENOMEM; + tfm = crypto_alloc_shash(pgp->version < PGP_KEY_VERSION_4 ? + "md5" : "sha1", 0, 0); + if (!tfm) + goto cleanup; + + digest = kmalloc(sizeof(*digest) + crypto_shash_descsize(tfm), + GFP_KERNEL); + if (!digest) + goto cleanup_tfm; + + digest->tfm = tfm; + digest->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + ret = crypto_shash_init(digest); + if (ret < 0) + goto cleanup_hash; + + ret = pgp_calc_pkey_keyid(digest, pgp, key); + if (ret < 0) + goto cleanup_hash; + + digest_size = crypto_shash_digestsize(tfm); + + raw_fingerprint = kmalloc(digest_size, GFP_KERNEL); + if (!raw_fingerprint) + goto cleanup_hash; + + ret = crypto_shash_final(digest, raw_fingerprint); + if (ret < 0) + goto cleanup_raw_fingerprint; + + fingerprint = kmalloc(digest_size * 2 + 1, GFP_KERNEL); + if (!fingerprint) + goto cleanup_raw_fingerprint; + + offset = digest_size - 8; + pr_debug("offset %u/%u\n", offset, digest_size); + + for (i = 0; i < digest_size; i++) + sprintf(fingerprint + i * 2, "%02x", raw_fingerprint[i]); + pr_debug("fingerprint %s\n", fingerprint); + + memcpy(&key->key_id, raw_fingerprint + offset, 8); + key->key_id_size = 8; + + ctx->fingerprint = fingerprint; + ret = 0; +cleanup_raw_fingerprint: + kfree(raw_fingerprint); +cleanup_hash: + kfree(digest); +cleanup_tfm: + crypto_free_shash(tfm); +cleanup: + kleave(" = %d", ret); + return ret; +} + +/* + * Extract a public key or public subkey from the PGP stream. + */ +static int pgp_process_public_key(struct pgp_parse_context *context, + enum pgp_packet_tag type, + u8 headerlen, + const u8 *data, + size_t datalen) +{ + const struct public_key_algorithm *algo; + struct pgp_key_data_parse_context *ctx = + container_of(context, struct pgp_key_data_parse_context, pgp); + struct pgp_parse_pubkey pgp; + struct public_key *key; + int i, ret; + + kenter(",%u,%u,,%zu", type, headerlen, datalen); + + if (ctx->subtype) { + kleave(" = -ENOKEY [already]"); + return -EBADMSG; + } + + key = kzalloc(sizeof(struct public_key), GFP_KERNEL); + if (!key) + return -ENOMEM; + + ret = pgp_parse_public_key(&data, &datalen, &pgp); + if (ret < 0) + goto cleanup; + + if (pgp.pubkey_algo >= PGP_PUBKEY__LAST || + !pgp_public_key_algorithms[pgp.pubkey_algo]) { + pr_debug("Unsupported public key algorithm %u\n", + pgp.pubkey_algo); + ret = -ENOPKG; + goto cleanup; + } + + algo = key->algo = pgp_public_key_algorithms[pgp.pubkey_algo]; + + /* It's a public key, so that only gives us encrypt and verify + * capabilities. + */ + key->capabilities = pgp_public_key_capabilities[pgp.pubkey_algo] & + (PKEY_CAN_ENCRYPT | PKEY_CAN_VERIFY); + + ret = -ENOMEM; + for (i = 0; i < algo->n_pub_mpi; i++) { + unsigned int remaining = datalen; + ret = -EBADMSG; + if (remaining == 0) { + pr_debug("short %zu mpi %d\n", datalen, i); + goto cleanup; + } + key->mpi[i] = mpi_read_from_buffer(data, &remaining); + if (!key->mpi[i]) + goto cleanup; + data += remaining; + datalen -= remaining; + } + + ret = -EBADMSG; + if (datalen != 0) { + pr_debug("excess %zu\n", datalen); + goto cleanup; + } + + ret = pgp_generate_fingerprint(ctx, &pgp, key); + if (ret < 0) + goto cleanup; + + /* We're pinning the module by being linked against it */ + __module_get(public_key_crypto_key_subtype.owner); + ctx->subtype = &public_key_crypto_key_subtype; + ctx->payload = key; + kleave(" = 0 [use]"); + return 0; + +cleanup: + pr_devel("cleanup"); + if (key) { + for (i = 0; i < ARRAY_SIZE(key->mpi); i++) + mpi_free(key->mpi[i]); + kfree(key); + } + kleave(" = %d", ret); + return ret; +} + +/* + * Attempt to parse the instantiation data blob for a key as a PGP packet + * message holding a key. + */ +static int pgp_key_instantiate(struct key *key, + const void *data, size_t datalen) +{ + struct pgp_key_data_parse_context ctx; + int ret; + + kenter(""); + + ret = key_payload_reserve(key, datalen); + if (ret < 0) + return ret; + + ctx.pgp.types_of_interest = + (1 << PGP_PKT_PUBLIC_KEY) | (1 << PGP_PKT_PUBLIC_SUBKEY); + ctx.pgp.process_packet = pgp_process_public_key; + ctx.subtype = NULL; + ctx.fingerprint = NULL; + ctx.payload = NULL; + + ret = pgp_parse_packets(data, datalen, &ctx.pgp); + if (ret < 0) { + if (ctx.payload) + ctx.subtype->destroy(ctx.payload); + if (ctx.subtype) + module_put(ctx.subtype->owner); + kfree(ctx.fingerprint); + key_payload_reserve(key, 0); + return ret; + } + + key->type_data.p[0] = ctx.subtype; + key->type_data.p[1] = ctx.fingerprint; + key->payload.data = ctx.payload; + return 0; +} + +static struct crypto_key_parser pgp_key_parser = { + .owner = THIS_MODULE, + .name = "pgp", + .instantiate = pgp_key_instantiate, +}; + +/* + * Module stuff + */ +static int __init pgp_key_init(void) +{ + return register_crypto_key_parser(&pgp_key_parser); +} + +static void __exit pgp_key_exit(void) +{ + unregister_crypto_key_parser(&pgp_key_parser); +} + +module_init(pgp_key_init); +module_exit(pgp_key_exit); diff --git a/security/keys/crypto/pgp_parser.h b/security/keys/crypto/pgp_parser.h new file mode 100644 index 0000000..1cda231 --- /dev/null +++ b/security/keys/crypto/pgp_parser.h @@ -0,0 +1,23 @@ +/* PGP crypto data parser internal definitions + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include + +#define kenter(FMT, ...) \ + pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__) +#define kleave(FMT, ...) \ + pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__) + +/* + * pgp_key_parser.c + */ +extern const +struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST]; -- 1.7.10.2 From 3e401cdc0ae1768ecbc301a631a2a34f56834313 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 4 May 2012 16:40:17 +0100 Subject: [PATCH 14/36] KEYS: PGP-based public key signature verification Provide handlers for PGP-based public-key algorithm signature verification. This does most of the work involved in signature verification as most of it is public-key algorithm agnostic. The public-key verification algorithm itself is just the last little bit and is supplied the complete hash data to process. This requires glue logic putting on top to make use of it - something the next patch provides. Signed-off-by: David Howells --- security/keys/crypto/Makefile | 3 +- security/keys/crypto/pgp_parser.h | 6 + security/keys/crypto/pgp_pubkey_sig.c | 323 +++++++++++++++++++++++++++++++++ 3 files changed, 331 insertions(+), 1 deletion(-) create mode 100644 security/keys/crypto/pgp_pubkey_sig.c diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile index 3bb2e61..d4ac8ac 100644 --- a/security/keys/crypto/Makefile +++ b/security/keys/crypto/Makefile @@ -11,4 +11,5 @@ obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o obj-$(CONFIG_CRYPTO_KEY_PGP_PARSER) += pgp_parser.o pgp_parser-y := \ - pgp_key_parser.o + pgp_key_parser.o \ + pgp_pubkey_sig.o diff --git a/security/keys/crypto/pgp_parser.h b/security/keys/crypto/pgp_parser.h index 1cda231..a6192ce 100644 --- a/security/keys/crypto/pgp_parser.h +++ b/security/keys/crypto/pgp_parser.h @@ -21,3 +21,9 @@ */ extern const struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST]; + +/* + * pgp_pubkey_sig.c + */ +extern struct crypto_key_verify_context *pgp_pkey_verify_sig_begin( + struct key *crypto_key, const u8 *sigdata, size_t siglen); diff --git a/security/keys/crypto/pgp_pubkey_sig.c b/security/keys/crypto/pgp_pubkey_sig.c new file mode 100644 index 0000000..b4b7cb0 --- /dev/null +++ b/security/keys/crypto/pgp_pubkey_sig.c @@ -0,0 +1,323 @@ +/* Handling for PGP public key signature data [RFC 4880] + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "PGPSIG: "fmt +#include +#include +#include +#include +#include "public_key.h" +#include "pgp_parser.h" + +const struct { + enum pkey_hash_algo algo : 8; +} pgp_pubkey_hash[PGP_HASH__LAST] = { + [PGP_HASH_MD5].algo = PKEY_HASH_MD5, + [PGP_HASH_SHA1].algo = PKEY_HASH_SHA1, + [PGP_HASH_RIPE_MD_160].algo = PKEY_HASH_RIPE_MD_160, + [PGP_HASH_SHA256].algo = PKEY_HASH_SHA256, + [PGP_HASH_SHA384].algo = PKEY_HASH_SHA384, + [PGP_HASH_SHA512].algo = PKEY_HASH_SHA512, + [PGP_HASH_SHA224].algo = PKEY_HASH_SHA224, +}; + +static int pgp_pkey_verify_sig_add_data(struct crypto_key_verify_context *ctx, + const void *data, size_t datalen); +static int pgp_pkey_verify_sig_end(struct crypto_key_verify_context *ctx, + const u8 *sig, size_t siglen); +static void pgp_pkey_verify_sig_cancel(struct crypto_key_verify_context *ctx); + +struct pgp_pkey_sig_parse_context { + struct pgp_parse_context pgp; + struct pgp_sig_parameters params; +}; + +static int pgp_pkey_parse_signature(struct pgp_parse_context *context, + enum pgp_packet_tag type, + u8 headerlen, + const u8 *data, + size_t datalen) +{ + struct pgp_pkey_sig_parse_context *ctx = + container_of(context, struct pgp_pkey_sig_parse_context, pgp); + + return pgp_parse_sig_params(&data, &datalen, &ctx->params); +} + +/* + * Begin the process of verifying a DSA signature. + * + * This involves allocating the hash into which first the data and then the + * metadata will be put, and parsing the signature to check that it matches the + * key. + */ +struct crypto_key_verify_context *pgp_pkey_verify_sig_begin( + struct key *crypto_key, const u8 *sigdata, size_t siglen) +{ + struct pgp_pkey_sig_parse_context p; + struct public_key_signature *sig; + struct crypto_shash *tfm; + const struct public_key *key = crypto_key->payload.data; + size_t digest_size, desc_size; + int ret; + + kenter("{%d},,%zu", key_serial(crypto_key), siglen); + + if (!key) { + kleave(" = -ENOKEY [no public key]"); + return ERR_PTR(-ENOKEY); + } + + p.pgp.types_of_interest = (1 << PGP_PKT_SIGNATURE); + p.pgp.process_packet = pgp_pkey_parse_signature; + ret = pgp_parse_packets(sigdata, siglen, &p.pgp); + if (ret < 0) + return ERR_PTR(ret); + + if (p.params.pubkey_algo >= PGP_PUBKEY__LAST || + !pgp_public_key_algorithms[p.params.pubkey_algo]) { + pr_debug("Unsupported public key algorithm %u\n", + p.params.pubkey_algo); + return ERR_PTR(-ENOKEY); + } + + if (pgp_public_key_algorithms[p.params.pubkey_algo] != key->algo) { + kleave(" = -ENOKEY [wrong pk algo]"); + return ERR_PTR(-ENOKEY); + } + + if (!(key->capabilities & PKEY_CAN_VERIFY)) { + kleave(" = -EKEYREJECTED [key can't verify]"); + return ERR_PTR(-EKEYREJECTED); + } + + if (p.params.hash_algo >= PGP_HASH__LAST || + !pgp_hash_algorithms[p.params.hash_algo]) { + kleave(" = -ENOPKG [hash]"); + return ERR_PTR(-ENOPKG); + } + + pr_debug("Signature generated with %s hash\n", + pgp_hash_algorithms[p.params.hash_algo]); + + if (memcmp(&p.params.issuer, key->key_id, 8) != 0) { + kleave(" = -ENOKEY [wrong key ID]"); + return ERR_PTR(-ENOKEY); + } + + if (p.params.signature_type != PGP_SIG_BINARY_DOCUMENT_SIG && + p.params.signature_type != PGP_SIG_STANDALONE_SIG) { + /* We don't want to canonicalise */ + kleave(" = -EOPNOTSUPP [canon]"); + return ERR_PTR(-EOPNOTSUPP); + } + + /* Allocate the hashing algorithm we're going to need and find out how + * big the hash operational data will be. + */ + tfm = crypto_alloc_shash(pgp_hash_algorithms[p.params.hash_algo], 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm) == -ENOENT ? + ERR_PTR(-ENOPKG) : ERR_CAST(tfm); + + desc_size = crypto_shash_descsize(tfm); + digest_size = crypto_shash_digestsize(tfm); + + /* We allocate the hash operational data storage on the end of our + * context data. + */ + sig = kzalloc(sizeof(*sig) + desc_size + digest_size, GFP_KERNEL); + if (!sig) { + crypto_free_shash(tfm); + return ERR_PTR(-ENOMEM); + } + + sig->base.key = crypto_key; + sig->base.add_data = pgp_pkey_verify_sig_add_data; + sig->base.end = pgp_pkey_verify_sig_end; + sig->base.cancel = pgp_pkey_verify_sig_cancel; + sig->pkey_hash_algo = pgp_pubkey_hash[p.params.hash_algo].algo; + sig->digest = (u8 *)sig + sizeof(*sig) + desc_size; + sig->digest_size = digest_size; + sig->hash.tfm = tfm; + sig->hash.flags = CRYPTO_TFM_REQ_MAY_SLEEP; + + ret = crypto_shash_init(&sig->hash); + if (ret < 0) { + crypto_free_shash(sig->hash.tfm); + kfree(sig); + return ERR_PTR(ret); + } + + key_get(sig->base.key); + kleave(" = %p", sig); + return &sig->base; +} + +/* + * Load data into the hash + */ +static int pgp_pkey_verify_sig_add_data(struct crypto_key_verify_context *ctx, + const void *data, size_t datalen) +{ + struct public_key_signature *sig = + container_of(ctx, struct public_key_signature, base); + + return crypto_shash_update(&sig->hash, data, datalen); +} + +struct pgp_pkey_sig_digest_context { + struct pgp_parse_context pgp; + const struct public_key *key; + struct public_key_signature *sig; +}; + +/* + * Extract required metadata from the signature packet and add what we need to + * to the hash. + */ +static int pgp_pkey_digest_signature(struct pgp_parse_context *context, + enum pgp_packet_tag type, + u8 headerlen, + const u8 *data, + size_t datalen) +{ + struct pgp_pkey_sig_digest_context *ctx = + container_of(context, struct pgp_pkey_sig_digest_context, pgp); + enum pgp_signature_version version; + int i; + + kenter(",%u,%u,,%zu", type, headerlen, datalen); + + version = *data; + if (version == PGP_SIG_VERSION_3) { + /* We just include an excerpt of the metadata from a V3 + * signature. + */ + crypto_shash_update(&ctx->sig->hash, data + 1, 5); + data += sizeof(struct pgp_signature_v3_packet); + datalen -= sizeof(struct pgp_signature_v3_packet); + } else if (version == PGP_SIG_VERSION_4) { + /* We add the whole metadata header and some of the hashed data + * for a V4 signature, plus a trailer. + */ + size_t hashedsz, unhashedsz; + u8 trailer[6]; + + hashedsz = 4 + 2 + (data[4] << 8) + data[5]; + crypto_shash_update(&ctx->sig->hash, data, hashedsz); + + trailer[0] = version; + trailer[1] = 0xffU; + trailer[2] = hashedsz >> 24; + trailer[3] = hashedsz >> 16; + trailer[4] = hashedsz >> 8; + trailer[5] = hashedsz; + + crypto_shash_update(&ctx->sig->hash, trailer, 6); + data += hashedsz; + datalen -= hashedsz; + + unhashedsz = 2 + (data[0] << 8) + data[1]; + data += unhashedsz; + datalen -= unhashedsz; + } + + if (datalen <= 2) { + kleave(" = -EBADMSG"); + return -EBADMSG; + } + + /* There's a quick check on the hash available. */ + ctx->sig->signed_hash_msw[0] = *data++; + ctx->sig->signed_hash_msw[1] = *data++; + datalen -= 2; + + /* And then the cryptographic data, which we'll need for the + * algorithm. + */ + for (i = 0; i < ctx->key->algo->n_sig_mpi; i++) { + unsigned int remaining = datalen; + if (remaining == 0) { + pr_debug("short %zu mpi %d\n", datalen, i); + return -EBADMSG; + } + ctx->sig->mpi[i] = mpi_read_from_buffer(data, &remaining); + if (!ctx->sig->mpi[i]) + return -ENOMEM; + data += remaining; + datalen -= remaining; + } + + if (datalen != 0) { + kleave(" = -EBADMSG [trailer %zu]", datalen); + return -EBADMSG; + } + + kleave(" = 0"); + return 0; +} + +/* + * The data is now all loaded into the hash; load the metadata, finalise the + * hash and perform the verification step. + */ +static int pgp_pkey_verify_sig_end(struct crypto_key_verify_context *ctx, + const u8 *sigdata, size_t siglen) +{ + struct public_key_signature *sig = + container_of(ctx, struct public_key_signature, base); + const struct public_key *key = sig->base.key->payload.data; + struct pgp_pkey_sig_digest_context p; + int ret; + + kenter(""); + + /* Firstly we add metadata, starting with some of the data from the + * signature packet */ + p.pgp.types_of_interest = (1 << PGP_PKT_SIGNATURE); + p.pgp.process_packet = pgp_pkey_digest_signature; + p.key = key; + p.sig = sig; + ret = pgp_parse_packets(sigdata, siglen, &p.pgp); + if (ret < 0) + goto error_free_ctx; + + crypto_shash_final(&sig->hash, sig->digest); + + ret = key->algo->verify(key, sig); + +error_free_ctx: + pgp_pkey_verify_sig_cancel(ctx); + kleave(" = %d", ret); + return ret; +} + +/* + * Cancel an in-progress data loading + */ +static void pgp_pkey_verify_sig_cancel(struct crypto_key_verify_context *ctx) +{ + struct public_key_signature *sig = + container_of(ctx, struct public_key_signature, base); + int i; + + kenter(""); + + /* !!! Do we need to tell the crypto layer to cancel too? */ + crypto_free_shash(sig->hash.tfm); + key_put(sig->base.key); + for (i = 0; i < ARRAY_SIZE(sig->mpi); i++) + mpi_free(sig->mpi[i]); + kfree(sig); + + kleave(""); +} -- 1.7.10.2 From 55e67eca050ff43915912e50dda49ecafdc816aa Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 4 May 2012 16:40:39 +0100 Subject: [PATCH 15/36] KEYS: PGP format signature parser Implement a signature parser that will attempt to parse a signature blob as a PGP packet format message. If it can, it will find an appropriate crypto key and set the public-key algorithm according to the data in the signature. Signed-off-by: David Howells --- security/keys/crypto/Makefile | 1 + security/keys/crypto/pgp_key_parser.c | 1 + security/keys/crypto/pgp_parser.h | 6 ++ security/keys/crypto/pgp_sig_parser.c | 104 +++++++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+) create mode 100644 security/keys/crypto/pgp_sig_parser.c diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile index d4ac8ac..fa7746d 100644 --- a/security/keys/crypto/Makefile +++ b/security/keys/crypto/Makefile @@ -12,4 +12,5 @@ obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o obj-$(CONFIG_CRYPTO_KEY_PGP_PARSER) += pgp_parser.o pgp_parser-y := \ pgp_key_parser.o \ + pgp_sig_parser.o \ pgp_pubkey_sig.o diff --git a/security/keys/crypto/pgp_key_parser.c b/security/keys/crypto/pgp_key_parser.c index 4efc4de..1407e2e 100644 --- a/security/keys/crypto/pgp_key_parser.c +++ b/security/keys/crypto/pgp_key_parser.c @@ -324,6 +324,7 @@ static struct crypto_key_parser pgp_key_parser = { .owner = THIS_MODULE, .name = "pgp", .instantiate = pgp_key_instantiate, + .verify_sig_begin = pgp_verify_sig_begin, }; /* diff --git a/security/keys/crypto/pgp_parser.h b/security/keys/crypto/pgp_parser.h index a6192ce..73c900e 100644 --- a/security/keys/crypto/pgp_parser.h +++ b/security/keys/crypto/pgp_parser.h @@ -23,6 +23,12 @@ extern const struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST]; /* + * pgp_sig_parser.c + */ +extern struct crypto_key_verify_context *pgp_verify_sig_begin( + struct key *keyring, const u8 *sig, size_t siglen); + +/* * pgp_pubkey_sig.c */ extern struct crypto_key_verify_context *pgp_pkey_verify_sig_begin( diff --git a/security/keys/crypto/pgp_sig_parser.c b/security/keys/crypto/pgp_sig_parser.c new file mode 100644 index 0000000..b72c505 --- /dev/null +++ b/security/keys/crypto/pgp_sig_parser.c @@ -0,0 +1,104 @@ +/* Handling for PGP public key signature data [RFC 4880] + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "PGPSIG: "fmt +#include +#include +#include +#include "public_key.h" +#include "pgp_parser.h" + +struct PGP_sig_parse_context { + struct pgp_parse_context pgp; + struct pgp_sig_parameters params; + bool found_sig; +}; + +/* + * Look inside signature sections for a key ID + */ +static int pgp_process_signature(struct pgp_parse_context *context, + enum pgp_packet_tag type, + u8 headerlen, + const u8 *data, + size_t datalen) +{ + struct PGP_sig_parse_context *ctx = + container_of(context, struct PGP_sig_parse_context, pgp); + + ctx->found_sig = true; + return pgp_parse_sig_params(&data, &datalen, &ctx->params); +} + +/* + * Attempt to find a key to use for PGP signature verification, starting off by + * looking in the supplied keyring. + * + * The function may also look for other key sources such as a TPM. If an + * alternative key is found it can be added to the keyring for future + * reference. + */ +static struct key *find_key_for_pgp_sig(struct key *keyring, + const u8 *sig, size_t siglen) +{ + struct PGP_sig_parse_context p; + key_ref_t key; + char criterion[3 + 8 * 2 + 1]; + int ret; + + if (!keyring) + return ERR_PTR(-ENOKEY); + + /* Need to find the key ID */ + p.pgp.types_of_interest = (1 << PGP_PKT_SIGNATURE); + p.pgp.process_packet = pgp_process_signature; + p.found_sig = false; + ret = pgp_parse_packets(sig, siglen, &p.pgp); + if (ret < 0) + return ERR_PTR(ret); + + if (!p.found_sig) + return ERR_PTR(-EINVAL); + + sprintf(criterion, "id:%08x%08x", + be32_to_cpu(p.params.issuer32[0]), + be32_to_cpu(p.params.issuer32[1])); + + pr_debug("Look up: %s\n", criterion); + + key = keyring_search(make_key_ref(keyring, 1), + &key_type_crypto, criterion); + if (IS_ERR(key)) + return ERR_CAST(key); + + pr_debug("Found key %x\n", key_serial(key_ref_to_ptr(key))); + return key_ref_to_ptr(key); +} + +/* + * Attempt to parse a signature as a PGP packet format blob and find a + * matching key. + */ +struct crypto_key_verify_context *pgp_verify_sig_begin( + struct key *keyring, const u8 *sig, size_t siglen) +{ + struct crypto_key_verify_context *ctx; + struct key *key; + + key = find_key_for_pgp_sig(keyring, sig, siglen); + if (IS_ERR(key)) + return ERR_CAST(key); + + /* We only handle in-kernel public key signatures for the moment */ + ctx = pgp_pkey_verify_sig_begin(key, sig, siglen); + key_put(key); + return ctx; +} -- 1.7.10.2 From 0760cf6519e184298944df0d9f6fe5a097fb16ff Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 4 May 2012 16:41:12 +0100 Subject: [PATCH 16/36] KEYS: Provide a function to load keys from a PGP keyring blob Provide a function to load keys from a PGP keyring blob for use in initialising the module signing key keyring: int load_PGP_keys(const u8 *pgpdata, size_t pgpdatalen, struct key *keyring, const char *descprefix); The keys are labelled with descprefix plus a number to uniquify them. The keys will actually be identified by the ID calculated from the PGP data rather than by the description, so this shouldn't be a problem. The keys are attached to the keyring supplied. Looking as root in /proc/keys after the module signing keyring has been loaded: 24460d1c I----- 1 perm 3f010000 0 0 crypto modsign.0: dsa 5acc2142 [] 3ca85723 I----- 1 perm 1f010000 0 0 keyring .module_sign: 1/4 Signed-off-by: David Howells --- Documentation/security/keys-crypto.txt | 20 +++++++ include/keys/crypto-type.h | 3 ++ security/keys/crypto/Kconfig | 9 ++++ security/keys/crypto/Makefile | 1 + security/keys/crypto/pgp_preload.c | 90 ++++++++++++++++++++++++++++++++ 5 files changed, 123 insertions(+) create mode 100644 security/keys/crypto/pgp_preload.c diff --git a/Documentation/security/keys-crypto.txt b/Documentation/security/keys-crypto.txt index a964717..ba2ab55 100644 --- a/Documentation/security/keys-crypto.txt +++ b/Documentation/security/keys-crypto.txt @@ -10,6 +10,7 @@ Contents: - Signature verification. - Implementing crypto parsers. - Implementing crypto subtypes. + - Initial PGP key preloading. ======== @@ -280,3 +281,22 @@ There are a number of operations defined by the subtype: Mandatory. This should free the memory associated with the key. The crypto key will look after freeing the fingerprint and releasing the reference on the subtype module. + + +======================= +INITIAL PGP KEY LOADING +======================= + +A function is provided to perform an initial load of a set of public keys bound +into a PGP packet format blob: + + int preload_pgp_keys(const u8 *pgpdata, size_t pgpdatalen, + struct key *keyring, const char *descprefix); + +This takes the blob of data defined by pgpdata and pgpdatalen, extracts keys +from them and adds them to the specified keyring. The keys are labelled with +descprefix plus a simple uniquifier - it is not expected that the description +will be used to identify the key. The description is required to prevent all +but the last key being discarded when the keys are linked into the keyring. + +This function is only available during initial kernel set up. diff --git a/include/keys/crypto-type.h b/include/keys/crypto-type.h index 6b93366..710e77f 100644 --- a/include/keys/crypto-type.h +++ b/include/keys/crypto-type.h @@ -31,4 +31,7 @@ extern void verify_sig_cancel(struct crypto_key_verify_context *ctx); * The payload is at the discretion of the subtype. */ +extern __init int preload_pgp_keys(const u8 *pgpdata, size_t pgpdatalen, + struct key *keyring, const char *descprefix); + #endif /* _KEYS_CRYPTO_TYPE_H */ diff --git a/security/keys/crypto/Kconfig b/security/keys/crypto/Kconfig index 1c2ae55..8af0155 100644 --- a/security/keys/crypto/Kconfig +++ b/security/keys/crypto/Kconfig @@ -40,3 +40,12 @@ config CRYPTO_KEY_PGP_PARSER This option provides support for parsing PGP (RFC 4880) format blobs for key data and provides the ability to instantiate a crypto key from a public key packet found inside the blob. + +config PGP_PRELOAD + bool "PGP public key preloading facility" + select PGP_LIBRARY + select CRYPTO_KEY_PGP_PARSER + help + This option provides a facility for the kernel to preload PGP-wrapped + bundles of keys during boot. It is used by module signing to load + the module signing keys for example. diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile index fa7746d..4162ecb 100644 --- a/security/keys/crypto/Makefile +++ b/security/keys/crypto/Makefile @@ -8,6 +8,7 @@ crypto_keys-y := crypto_type.o crypto_verify.o obj-$(CONFIG_CRYPTO_KEY_PUBLIC_KEY_SUBTYPE) += public_key.o obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) += crypto_rsa.o obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o +obj-$(CONFIG_PGP_PRELOAD) += pgp_preload.o obj-$(CONFIG_CRYPTO_KEY_PGP_PARSER) += pgp_parser.o pgp_parser-y := \ diff --git a/security/keys/crypto/pgp_preload.c b/security/keys/crypto/pgp_preload.c new file mode 100644 index 0000000..25154e3 --- /dev/null +++ b/security/keys/crypto/pgp_preload.c @@ -0,0 +1,90 @@ +/* Cryptographic key request handling + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + * + * See Documentation/security/keys-crypto.txt + */ + +#include +#include +#include +#include "crypto_keys.h" + +struct preload_pgp_keys_context { + struct pgp_parse_context pgp; + key_ref_t keyring; + char descbuf[20]; + u8 key_n; + u8 dsize; +}; + +/* + * Extract a public key or subkey from the PGP stream. + */ +static int __init found_pgp_key(struct pgp_parse_context *context, + enum pgp_packet_tag type, u8 headerlen, + const u8 *data, size_t datalen) +{ + struct preload_pgp_keys_context *ctx = + container_of(context, struct preload_pgp_keys_context, pgp); + key_ref_t key; + + sprintf(ctx->descbuf + ctx->dsize, "%d", ctx->key_n++); + + key = key_create_or_update(ctx->keyring, "crypto", ctx->descbuf, + data - headerlen, datalen + headerlen, + KEY_POS_ALL | KEY_USR_VIEW, + KEY_ALLOC_NOT_IN_QUOTA); + + if (IS_ERR(key)) + return PTR_ERR(key); + + pr_notice("Loaded %s key: %s\n", + key_ref_to_ptr(key)->description, + crypto_key_id(key_ref_to_ptr(key))); + + key_ref_put(key); + return 0; +} + +/** + * preload_pgp_keys - Load keys from a PGP keyring blob + * @pgpdata: The PGP keyring blob containing the keys. + * @pgpdatalen: The size of the @pgpdata blob. + * @keyring: The keyring to add the new keys to. + * @descprefix: The key description prefix. + * + * Preload a pack of keys from a PGP keyring blob. + * + * The keys are given description of @descprefix + the number of the key in the + * list. Since keys can be matched on their key IDs independently of the key + * description, the description is mostly irrelevant apart from the fact that + * keys of the same description displace one another from a keyring. + * + * The caller should override the current creds if they want the keys to be + * owned by someone other than the current process's owner. Keys will not be + * accounted towards the owner's quota. + * + * This function may only be called whilst the kernel is booting. + */ +int __init preload_pgp_keys(const u8 *pgpdata, size_t pgpdatalen, + struct key *keyring, const char *descprefix) +{ + struct preload_pgp_keys_context ctx; + + ctx.pgp.types_of_interest = + (1 << PGP_PKT_PUBLIC_KEY) | (1 << PGP_PKT_PUBLIC_SUBKEY); + ctx.pgp.process_packet = found_pgp_key; + ctx.keyring = make_key_ref(keyring, 1); + ctx.key_n = 0; + ctx.dsize = strlen(descprefix); + strcpy(ctx.descbuf, descprefix); + + return pgp_parse_packets(pgpdata, pgpdatalen, &ctx.pgp); +} -- 1.7.10.2 From 8f7d6b082b6b7357e44f9345c8c040f53aa60a7f Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 10 May 2012 23:46:56 +0100 Subject: [PATCH 17/36] Provide macros for forming the name of an ELF note and its section Provide macros for stringifying the name of an ELF note and its section appropriately so that the macro can be used in both C and assembly. Signed-off-by: David Howells --- include/linux/elfnote.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/linux/elfnote.h b/include/linux/elfnote.h index 278e3ef..949d494 100644 --- a/include/linux/elfnote.h +++ b/include/linux/elfnote.h @@ -58,6 +58,7 @@ ELFNOTE_END #else /* !__ASSEMBLER__ */ +#include #include /* * Use an anonymous structure which matches the shape of @@ -93,6 +94,9 @@ #define ELFNOTE32(name, type, desc) ELFNOTE(32, name, type, desc) #define ELFNOTE64(name, type, desc) ELFNOTE(64, name, type, desc) + +#define ELFNOTE_NAME(name) __stringify(name) +#define ELFNOTE_SECTION(name) ".note."ELFNOTE_NAME(name) #endif /* __ASSEMBLER__ */ #endif /* _LINUX_ELFNOTE_H */ -- 1.7.10.2 From a7f2ec2a77490ed84bf8020cd5b41d7c7ea3f3cb Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 10 May 2012 23:49:52 +0100 Subject: [PATCH 19/36] MODSIGN: Add indications of module ELF types Add per-arch indications of module ELF types and relocation table entry types. Signed-Off-By: David Howells --- arch/alpha/include/asm/module.h | 3 +++ arch/arm/include/asm/module.h | 5 +++++ arch/cris/include/asm/module.h | 5 +++++ arch/h8300/include/asm/module.h | 5 +++++ arch/ia64/include/asm/module.h | 5 +++++ arch/m32r/include/asm/module.h | 5 +++++ arch/m68k/include/asm/module.h | 5 +++++ arch/mips/include/asm/module.h | 12 ++++++++++-- arch/parisc/include/asm/module.h | 8 ++++++++ arch/powerpc/include/asm/module.h | 10 ++++++++++ arch/s390/include/asm/module.h | 3 +++ arch/x86/include/asm/module.h | 6 ++++++ include/asm-generic/module.h | 10 ++++++++++ 13 files changed, 80 insertions(+), 2 deletions(-) diff --git a/arch/alpha/include/asm/module.h b/arch/alpha/include/asm/module.h index 7b63743..3d5a3ea 100644 --- a/arch/alpha/include/asm/module.h +++ b/arch/alpha/include/asm/module.h @@ -6,6 +6,7 @@ struct mod_arch_specific unsigned int gotsecindex; }; +#define MODULES_ARE_ELF64 #define Elf_Sym Elf64_Sym #define Elf_Shdr Elf64_Shdr #define Elf_Ehdr Elf64_Ehdr @@ -13,6 +14,8 @@ struct mod_arch_specific #define Elf_Dyn Elf64_Dyn #define Elf_Rel Elf64_Rel #define Elf_Rela Elf64_Rela +#define ELF_R_TYPE(X) ELF64_R_TYPE(X) +#define ELF_R_SYM(X) ELF64_R_SYM(X) #define ARCH_SHF_SMALL SHF_ALPHA_GPREL diff --git a/arch/arm/include/asm/module.h b/arch/arm/include/asm/module.h index 6c6809f..f47d9cd 100644 --- a/arch/arm/include/asm/module.h +++ b/arch/arm/include/asm/module.h @@ -1,9 +1,14 @@ #ifndef _ASM_ARM_MODULE_H #define _ASM_ARM_MODULE_H +#define MODULES_ARE_ELF32 #define Elf_Shdr Elf32_Shdr #define Elf_Sym Elf32_Sym #define Elf_Ehdr Elf32_Ehdr +#define Elf_Rel Elf32_Rel +#define Elf_Rela Elf32_Rela +#define ELF_R_TYPE(X) ELF32_R_TYPE(X) +#define ELF_R_SYM(X) ELF32_R_SYM(X) struct unwind_table; diff --git a/arch/cris/include/asm/module.h b/arch/cris/include/asm/module.h index 7ee7231..03f7b2e 100644 --- a/arch/cris/include/asm/module.h +++ b/arch/cris/include/asm/module.h @@ -3,7 +3,12 @@ /* cris is simple */ struct mod_arch_specific { }; +#define MODULES_ARE_ELF32 #define Elf_Shdr Elf32_Shdr #define Elf_Sym Elf32_Sym #define Elf_Ehdr Elf32_Ehdr +#define Elf_Rel Elf32_Rel +#define Elf_Rela Elf32_Rela +#define ELF_R_TYPE(X) ELF32_R_TYPE(X) +#define ELF_R_SYM(X) ELF32_R_SYM(X) #endif /* _ASM_CRIS_MODULE_H */ diff --git a/arch/h8300/include/asm/module.h b/arch/h8300/include/asm/module.h index 8e46724..5140128 100644 --- a/arch/h8300/include/asm/module.h +++ b/arch/h8300/include/asm/module.h @@ -4,8 +4,13 @@ * This file contains the H8/300 architecture specific module code. */ struct mod_arch_specific { }; +#define MODULES_ARE_ELF32 #define Elf_Shdr Elf32_Shdr #define Elf_Sym Elf32_Sym #define Elf_Ehdr Elf32_Ehdr +#define Elf_Rel Elf32_Rel +#define Elf_Rela Elf32_Rela +#define ELF_R_TYPE(X) ELF32_R_TYPE(X) +#define ELF_R_SYM(X) ELF32_R_SYM(X) #endif /* _ASM_H8/300_MODULE_H */ diff --git a/arch/ia64/include/asm/module.h b/arch/ia64/include/asm/module.h index 908eaef..3c4cd94 100644 --- a/arch/ia64/include/asm/module.h +++ b/arch/ia64/include/asm/module.h @@ -29,9 +29,14 @@ struct mod_arch_specific { unsigned int next_got_entry; /* index of next available got entry */ }; +#define MODULES_ARE_ELF64 #define Elf_Shdr Elf64_Shdr #define Elf_Sym Elf64_Sym #define Elf_Ehdr Elf64_Ehdr +#define Elf_Rel Elf64_Rel +#define Elf_Rela Elf64_Rela +#define ELF_R_TYPE(X) ELF64_R_TYPE(X) +#define ELF_R_SYM(X) ELF64_R_SYM(X) #define MODULE_PROC_FAMILY "ia64" #define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY \ diff --git a/arch/m32r/include/asm/module.h b/arch/m32r/include/asm/module.h index eb73ee0..7146455 100644 --- a/arch/m32r/include/asm/module.h +++ b/arch/m32r/include/asm/module.h @@ -3,8 +3,13 @@ struct mod_arch_specific { }; +#define MODULES_ARE_ELF32 #define Elf_Shdr Elf32_Shdr #define Elf_Sym Elf32_Sym #define Elf_Ehdr Elf32_Ehdr +#define Elf_Rel Elf32_Rel +#define Elf_Rela Elf32_Rela +#define ELF_R_TYPE(X) ELF32_R_TYPE(X) +#define ELF_R_SYM(X) ELF32_R_SYM(X) #endif /* _ASM_M32R_MODULE_H */ diff --git a/arch/m68k/include/asm/module.h b/arch/m68k/include/asm/module.h index edffe66..9e2cd74 100644 --- a/arch/m68k/include/asm/module.h +++ b/arch/m68k/include/asm/module.h @@ -36,8 +36,13 @@ struct module; extern void module_fixup(struct module *mod, struct m68k_fixup_info *start, struct m68k_fixup_info *end); +#define MODULES_ARE_ELF32 #define Elf_Shdr Elf32_Shdr #define Elf_Sym Elf32_Sym #define Elf_Ehdr Elf32_Ehdr +#define Elf_Rel Elf32_Rel +#define Elf_Rela Elf32_Rela +#define ELF_R_TYPE(X) ELF32_R_TYPE(X) +#define ELF_R_SYM(X) ELF32_R_SYM(X) #endif /* _ASM_M68K_MODULE_H */ diff --git a/arch/mips/include/asm/module.h b/arch/mips/include/asm/module.h index 7467d1d..4404cca 100644 --- a/arch/mips/include/asm/module.h +++ b/arch/mips/include/asm/module.h @@ -33,11 +33,15 @@ typedef struct { } Elf64_Mips_Rela; #ifdef CONFIG_32BIT - +#define MODULES_ARE_ELF32 #define Elf_Shdr Elf32_Shdr #define Elf_Sym Elf32_Sym #define Elf_Ehdr Elf32_Ehdr #define Elf_Addr Elf32_Addr +#define Elf_Rel Elf32_Rel +#define Elf_Rela Elf32_Rela +#define ELF_R_TYPE(X) ELF32_R_TYPE(X) +#define ELF_R_SYM(X) ELF32_R_SYM(X) #define Elf_Mips_Rel Elf32_Rel #define Elf_Mips_Rela Elf32_Rela @@ -48,11 +52,15 @@ typedef struct { #endif #ifdef CONFIG_64BIT - +#define MODULES_ARE_ELF64 #define Elf_Shdr Elf64_Shdr #define Elf_Sym Elf64_Sym #define Elf_Ehdr Elf64_Ehdr #define Elf_Addr Elf64_Addr +#define Elf_Rel Elf64_Rel +#define Elf_Rela Elf64_Rela +#define ELF_R_TYPE(X) ELF64_R_TYPE(X) +#define ELF_R_SYM(X) ELF64_R_SYM(X) #define Elf_Mips_Rel Elf64_Mips_Rel #define Elf_Mips_Rela Elf64_Mips_Rela diff --git a/arch/parisc/include/asm/module.h b/arch/parisc/include/asm/module.h index 1f41234..3e13f69 100644 --- a/arch/parisc/include/asm/module.h +++ b/arch/parisc/include/asm/module.h @@ -4,17 +4,25 @@ * This file contains the parisc architecture specific module code. */ #ifdef CONFIG_64BIT +#define MODULES_ARE_ELF64 #define Elf_Shdr Elf64_Shdr #define Elf_Sym Elf64_Sym #define Elf_Ehdr Elf64_Ehdr #define Elf_Addr Elf64_Addr +#define Elf_Rel Elf64_Rel #define Elf_Rela Elf64_Rela +#define ELF_R_TYPE(X) ELF64_R_TYPE(X) +#define ELF_R_SYM(X) ELF64_R_SYM(X) #else +#define MODULES_ARE_ELF32 #define Elf_Shdr Elf32_Shdr #define Elf_Sym Elf32_Sym #define Elf_Ehdr Elf32_Ehdr #define Elf_Addr Elf32_Addr +#define Elf_Rel Elf32_Rel #define Elf_Rela Elf32_Rela +#define ELF_R_TYPE(X) ELF32_R_TYPE(X) +#define ELF_R_SYM(X) ELF32_R_SYM(X) #endif struct unwind_table; diff --git a/arch/powerpc/include/asm/module.h b/arch/powerpc/include/asm/module.h index 0192a4e..e949704 100644 --- a/arch/powerpc/include/asm/module.h +++ b/arch/powerpc/include/asm/module.h @@ -60,16 +60,26 @@ struct mod_arch_specific { */ #ifdef __powerpc64__ +# define MODULES_ARE_ELF64 # define Elf_Shdr Elf64_Shdr # define Elf_Sym Elf64_Sym # define Elf_Ehdr Elf64_Ehdr +# define Elf_Rel Elf64_Rel +# define Elf_Rela Elf64_Rela +# define ELF_R_TYPE(X) ELF64_R_TYPE(X) +# define ELF_R_SYM(X) ELF64_R_SYM(X) # ifdef MODULE asm(".section .stubs,\"ax\",@nobits; .align 3; .previous"); # endif #else +# define MODULES_ARE_ELF32 # define Elf_Shdr Elf32_Shdr # define Elf_Sym Elf32_Sym # define Elf_Ehdr Elf32_Ehdr +# define Elf_Rel Elf32_Rel +# define Elf_Rela Elf32_Rela +# define ELF_R_TYPE(X) ELF32_R_TYPE(X) +# define ELF_R_SYM(X) ELF32_R_SYM(X) # ifdef MODULE asm(".section .plt,\"ax\",@nobits; .align 3; .previous"); asm(".section .init.plt,\"ax\",@nobits; .align 3; .previous"); diff --git a/arch/s390/include/asm/module.h b/arch/s390/include/asm/module.h index 1cc1c5a..b64dab0 100644 --- a/arch/s390/include/asm/module.h +++ b/arch/s390/include/asm/module.h @@ -29,14 +29,17 @@ struct mod_arch_specific }; #ifdef CONFIG_64BIT +#define MODULES_ARE_ELF64 #define ElfW(x) Elf64_ ## x #define ELFW(x) ELF64_ ## x #else +#define MODULES_ARE_ELF32 #define ElfW(x) Elf32_ ## x #define ELFW(x) ELF32_ ## x #endif #define Elf_Addr ElfW(Addr) +#define Elf_Rel ElfW(Rel) #define Elf_Rela ElfW(Rela) #define Elf_Shdr ElfW(Shdr) #define Elf_Sym ElfW(Sym) diff --git a/arch/x86/include/asm/module.h b/arch/x86/include/asm/module.h index 9eae775..724f173 100644 --- a/arch/x86/include/asm/module.h +++ b/arch/x86/include/asm/module.h @@ -63,4 +63,10 @@ # define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY #endif +#ifdef CONFIG_X86_32 +#define MODULE_HAS_ELF_REL_ONLY +#else +#define MODULE_HAS_ELF_RELA_ONLY +#endif + #endif /* _ASM_X86_MODULE_H */ diff --git a/include/asm-generic/module.h b/include/asm-generic/module.h index ed5b44d..e053617b 100644 --- a/include/asm-generic/module.h +++ b/include/asm-generic/module.h @@ -10,13 +10,23 @@ struct mod_arch_specific }; #ifdef CONFIG_64BIT +#define MODULES_ARE_ELF64 #define Elf_Shdr Elf64_Shdr #define Elf_Sym Elf64_Sym #define Elf_Ehdr Elf64_Ehdr +#define Elf_Rel Elf64_Rel +#define Elf_Rela Elf64_Rela +#define ELF_R_TYPE(X) ELF64_R_TYPE(X) +#define ELF_R_SYM(X) ELF64_R_SYM(X) #else +#define MODULES_ARE_ELF32 #define Elf_Shdr Elf32_Shdr #define Elf_Sym Elf32_Sym #define Elf_Ehdr Elf32_Ehdr +#define Elf_Rel Elf32_Rel +#define Elf_Rela Elf32_Rela +#define ELF_R_TYPE(X) ELF32_R_TYPE(X) +#define ELF_R_SYM(X) ELF32_R_SYM(X) #endif #endif /* __ASM_GENERIC_MODULE_H */ -- 1.7.10.2 From b31d1ea8afb0b63e872a1bed3a3c88d7696bf2e7 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 10 May 2012 23:49:53 +0100 Subject: [PATCH 20/36] MODSIGN: Provide gitignore and make clean rules for extra files Provide gitignore and make clean rules for extra files to hide and clean up the extra files produced by module signing stuff once it is added. Also add a clean up rule for the module content extractor program used to extract the data to be signed. Signed-off-by: David Howells --- .gitignore | 12 ++++++++++++ Makefile | 1 + scripts/mod/.gitignore | 1 + 3 files changed, 14 insertions(+) diff --git a/.gitignore b/.gitignore index 57af07c..7948eeb 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,9 @@ *.o.* *.a *.s +*.ko.unsigned +*.ko.digest +*.ko.digest.sig *.ko *.so *.so.dbg @@ -84,3 +87,12 @@ GTAGS *.orig *~ \#*# + +# +# GPG leavings from module signing +# +genkey +modsign.pub +modsign.sec +random_seed +trustdb.gpg diff --git a/Makefile b/Makefile index a06ee9f..1df8b14 100644 --- a/Makefile +++ b/Makefile @@ -1407,6 +1407,7 @@ clean: $(clean-dirs) $(call cmd,rmfiles) @find $(if $(KBUILD_EXTMOD), $(KBUILD_EXTMOD), .) $(RCS_FIND_IGNORE) \ \( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \ + -o -name '*.ko.*' \ -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \ -o -name '*.symtypes' -o -name 'modules.order' \ -o -name modules.builtin -o -name '.tmp_*.o.*' \ diff --git a/scripts/mod/.gitignore b/scripts/mod/.gitignore index e9b7abe..223dfd6 100644 --- a/scripts/mod/.gitignore +++ b/scripts/mod/.gitignore @@ -1,4 +1,5 @@ elfconfig.h mk_elfconfig modpost +mod-extract -- 1.7.10.2 From dce3a6eca8dda09a28cb2f45b87e70e1c2d52139 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 10 May 2012 23:49:53 +0100 Subject: [PATCH 21/36] MODSIGN: Provide Documentation and Kconfig options Provide documentation and kernel configuration options for module signing. The documentation can be found in: Documentation/module-signing.txt The following configuration options are added: (1) CONFIG_MODULE_SIG Enable module signing. This will both cause the build process to sign modules and the kernel to check modules when they're loaded. (2) CONFIG_MODULE_SIG_SHA1 CONFIG_MODULE_SIG_SHA224 CONFIG_MODULE_SIG_SHA256 CONFIG_MODULE_SIG_SHA384 CONFIG_MODULE_SIG_SHA512 Select the cryptographic hash used to digest the data prior to signing. Additionally, the crypto module selected will be built into the kernel as it won't be possible to load it as a module without incurring a circular dependency when the kernel tries to check its signature. (3) CONFIG_MODULE_SIG_FORCE Require that any module loaded must be signed with a key compiled into the kernel. All other modules are rejected with EKEYREJECTED. Signed-off-by: David Howells --- Documentation/module-signing.txt | 194 ++++++++++++++++++++++++++++++++++++++ include/linux/modsign.h | 27 ++++++ init/Kconfig | 53 +++++++++++ 3 files changed, 274 insertions(+) create mode 100644 Documentation/module-signing.txt create mode 100644 include/linux/modsign.h diff --git a/Documentation/module-signing.txt b/Documentation/module-signing.txt new file mode 100644 index 0000000..d75d473 --- /dev/null +++ b/Documentation/module-signing.txt @@ -0,0 +1,194 @@ + ============================== + KERNEL MODULE SIGNING FACILITY + ============================== + +The module signing facility applies cryptographic signature checking to modules +on module load, checking the signature against a ring of public keys compiled +into the kernel. GPG is used to do the cryptographic work and determines the +format of the signature and key data. The facility uses GPG's MPI library to +handle the huge numbers involved. + +This facility is enabled through CONFIG_MODULE_SIG. Turning on signature +checking will also force the module's ELF metadata to be verified before the +signature is checked. + +The signature checker in the kernel is capable of handling multiple keys of +either DSA or RSA type, and can support any of MD5, RIPE-MD-160, SHA-1, +SHA-224, SHA-256, SHA-384 and SHA-512 hashes - PROVIDED(!) the requisite +algorithms are compiled into the kernel. + +(!) NOTE: Modules may only be verified initially with algorithms compiled into +the kernel. Further algorithm modules may be loaded and used - but these must +first pass a verification step using already loaded/compiled-in algorithms. + + +===================== +SUPPLYING PUBLIC KEYS +===================== + +A set of public keys must be supplied at kernel image build time. This is done +by taking a GPG public key file and placing it in the base of the kernel +directory in a file called modsign.pub. + +For example, a throwaway key could be generated automatically by something like +the following: + + cat >genkey < + + This indicates the whereabouts of the GPG keyring that is the source of + the public key to be used. The default is "./modsign.pub". + + (*) MODKEYNAME= + + The name of the key pair to be used from the aforementioned keyrings. + This defaults to being unset, thus leaving the choice of default key to + gpg. + + (*) KEYFLAGS="gpg-options" + + Override the complete gpg command line, including the preceding three + options. The default options supplied to gpg are: + + --no-default-keyring + --secret-keyring $(MODSECKEY) + --keyring $(MODPUBKEY) + --no-default-keyring + --homedir . + --no-options + --no-auto-check-trustdb + --no-permission-warning + --digest-algo= + + with: + + --default-key $(MODKEYNAME) + + being added if requested. + +The resulting module.ko file will be the signed module. + + +======================== +STRIPPING SIGNED MODULES +======================== + +Signed modules may be safely stripped with any of the following: + + strip -x + strip -g + eu-strip + +as the signature only covers those parts of the module the kernel actually uses +and any ELF metadata required to deal with them. Any necessary ELF metadata +that is affected by stripping is canonicalised by the sig generator and the sig +checker to hide strip effects. + +This permits the debuginfo to be detached from the module and placed in another +spot so that gdb can find it when referring to that module without the need for +multiple signed versions of the module. Such is done by rpmbuild when +producing RPMs. + +It also permits the module to be stripped as far as possible for when modules +are being reduced prior to being included in an initial ramdisk composition. + +Note that "strip" and "strip -s" may not be used on a module, signed or +otherwise, as they remove the symbol table and render the relocation tables +unusable. + + +====================== +LOADING SIGNED MODULES +====================== + +Modules are loaded with insmod, exactly as for unsigned modules. The signature +is inserted into the module object file during the build process as an ELF note +called "module.sig" in an ELF section called ".note.module.sig". The signature +checker will detect it and apply signature checking. + + +========================================= +NON-VALID SIGNATURES AND UNSIGNED MODULES +========================================= + +If CONFIG_MODULE_SIG_FORCE is enabled or "enforcemodulesig=1" is supplied on +the kernel command line, the kernel will _only_ load validly signed modules +for which it has a public key. Otherwise, it will also load modules that are +unsigned. Any module for which the kernel has a key, but which proves to have +a signature mismatch will not be permitted to load (returning EKEYREJECTED). + +This table indicates the behaviours of the various situations: + + MODULE STATE PERMISSIVE MODE ENFORCING MODE + ======================================= =============== =============== + Unsigned Ok EKEYREJECTED + Signed, no public key ENOKEY ENOKEY + Validly signed, public key Ok Ok + Invalidly signed, public key EKEYREJECTED EKEYREJECTED + Validly signed, expired key EKEYEXPIRED EKEYEXPIRED + Signed, hash algorithm unavailable ENOPKG ENOPKG + Corrupt signature EBADMSG EBADMSG + Corrupt ELF ELIBBAD ELIBBAD diff --git a/include/linux/modsign.h b/include/linux/modsign.h new file mode 100644 index 0000000..c5ac87a --- /dev/null +++ b/include/linux/modsign.h @@ -0,0 +1,27 @@ +/* Module signing definitions + * + * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _LINUX_MODSIGN_H +#define _LINUX_MODSIGN_H + +#ifdef CONFIG_MODULE_SIG + +#include + +/* + * The parameters of the ELF note used to carry the signature + */ +#define MODSIGN_NOTE_NAME module.sig +#define MODSIGN_NOTE_TYPE 100 + +#endif + +#endif /* _LINUX_MODSIGN_H */ diff --git a/init/Kconfig b/init/Kconfig index 6cfd71d..7cda3e6 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1409,6 +1409,59 @@ config MODULE_SRCVERSION_ALL the version). With this option, such a "srcversion" field will be created for all modules. If unsure, say N. +config MODULE_SIG + bool "Module signature verification" + depends on MODULES + select CRYPTO_KEY_TYPE + select CRYPTO_KEY_PKEY_ALGO_DSA + select CRYPTO_KEY_PKEY_ALGO_RSA + select PGP_PARSER + select PGP_PRELOAD + help + Check modules for valid signatures upon load. For more information + see: + + Documentation/module-signing.txt + +choice + prompt "Which hash algorithm should modules be signed with?" + depends on MODULE_SIG + help + This determines which sort of hashing algorithm will be used during + signature generation. This algorithm _must_ be built into the kernel + directly so that signature verification can take place. It is not + possible to load a signed module containing the algorithm to check + the signature on that module. + +config MODULE_SIG_SHA1 + bool "Sign modules with SHA-1" + select CRYPTO_SHA1 + +config MODULE_SIG_SHA224 + bool "Sign modules with SHA-224" + select CRYPTO_SHA224 + +config MODULE_SIG_SHA256 + bool "Sign modules with SHA-256" + select CRYPTO_SHA256 + +config MODULE_SIG_SHA384 + bool "Sign modules with SHA-384" + select CRYPTO_SHA384 + +config MODULE_SIG_SHA512 + bool "Sign modules with SHA-512" + select CRYPTO_SHA512 + +endchoice + +config MODULE_SIG_FORCE + bool "Required modules to be validly signed (EXPERIMENTAL)" + depends on MODULE_SIG + help + Reject unsigned modules or signed modules for which we don't have a + key. + endif # MODULES config INIT_ALL_POSSIBLE -- 1.7.10.2 From f9980a27c5d2d02af2feb578957145a206088fff Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 10 May 2012 23:49:54 +0100 Subject: [PATCH 22/36] MODSIGN: Sign modules during the build process If CONFIG_MODULE_SIG is set, then this patch will cause the module to get a signature installed. The following steps will occur: (1) The module will be linked to foo.ko.unsigned instead of foo.ko (2) The module's signable content will be extracted to foo.ko.digest by the mod-extract program. (3) The signature will be generated on foo.ko.digest by gpg and placed in foo.ko.digest.sig (4) The signature will be encapsulated into an ELF note and placed into a file called foo.ko.note.o using the output from modsign-note.sh piped into the assembler. (5) The unsigned module from (1) and the signature ELF note from (4) will be linked together to produce foo.ko Step (3) requires private and public keys to be available. By default these are expected to be found in PGP keyring files called modsign.sec (the secret key) and modsign.pub (the public key) in the build root. If the secret key is not found then signing will be skipped and the unsigned module from (1) will just be copied to foo.ko. If signing occurs, lines like the following will be seen: LD [M] fs/foo/foo.ko.unsigned SIGN [M] fs/foo/foo.ko will appear in the build log. If it is skipped, the following will be seen: LD [M] fs/foo/foo.ko.unsigned NO SIGN [M] fs/foo/foo.ko Signed-off-by: David Howells --- scripts/Makefile.modpost | 87 ++++- scripts/mod/Makefile | 2 +- scripts/mod/mod-extract.c | 913 +++++++++++++++++++++++++++++++++++++++++++ scripts/mod/modsign-note.sh | 16 + 4 files changed, 1016 insertions(+), 2 deletions(-) create mode 100644 scripts/mod/mod-extract.c create mode 100644 scripts/mod/modsign-note.sh diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost index 08dce14..17465d8 100644 --- a/scripts/Makefile.modpost +++ b/scripts/Makefile.modpost @@ -14,7 +14,8 @@ # 3) create one .mod.c file pr. module # 4) create one Module.symvers file with CRC for all exported symbols # 5) compile all .mod.c files -# 6) final link of the module to a file +# 6) final link of the module to a (or ) file +# 7) signs the modules to a file # Step 3 is used to place certain information in the module's ELF # section, including information such as: @@ -32,6 +33,8 @@ # Step 4 is solely used to allow module versioning in external modules, # where the CRC of each module is retrieved from the Module.symvers file. +# Step 7 is dependent on CONFIG_MODULE_SIG being enabled. + # KBUILD_MODPOST_WARN can be set to avoid error out in case of undefined # symbols in the final module linking stage # KBUILD_MODPOST_NOFINAL can be set to skip the final link of modules. @@ -116,6 +119,7 @@ $(modules:.ko=.mod.o): %.mod.o: %.mod.c FORCE targets += $(modules:.ko=.mod.o) # Step 6), final link of the modules +ifneq ($(CONFIG_MODULE_SIG),y) quiet_cmd_ld_ko_o = LD [M] $@ cmd_ld_ko_o = $(LD) -r $(LDFLAGS) \ $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \ @@ -125,7 +129,88 @@ $(modules): %.ko :%.o %.mod.o FORCE $(call if_changed,ld_ko_o) targets += $(modules) +else +quiet_cmd_ld_ko_unsigned_o = LD [M] $@ + cmd_ld_ko_unsigned_o = \ + $(LD) -r $(LDFLAGS) \ + $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \ + -o $@ $(filter-out FORCE,$^) \ + $(if $(AFTER_LINK),; $(AFTER_LINK)) + +$(modules:.ko=.ko.unsigned): %.ko.unsigned :%.o %.mod.o FORCE + $(call if_changed,ld_ko_unsigned_o) + +targets += $(modules:.ko=.ko.unsigned) + +# Step 7), sign the modules +MODSECKEY = ./modsign.sec +MODPUBKEY = ./modsign.pub +KEYFLAGS = --no-default-keyring --secret-keyring $(MODSECKEY) --keyring $(MODPUBKEY) --no-default-keyring --homedir . --no-options --no-auto-check-trustdb --no-permission-warning + +ifdef CONFIG_MODULE_SIG_SHA1 +KEYFLAGS += --digest-algo=SHA1 +else +ifdef CONFIG_MODULE_SIG_SHA224 +KEYFLAGS += --digest-algo=SHA224 +else +ifdef CONFIG_MODULE_SIG_SHA256 +KEYFLAGS += --digest-algo=SHA256 +else +ifdef CONFIG_MODULE_SIG_SHA384 +KEYFLAGS += --digest-algo=SHA384 +else +ifdef CONFIG_MODULE_SIG_SHA512 +KEYFLAGS += --digest-algo=SHA512 +else +endif +endif +endif +endif +endif + +ifdef MODKEYNAME +KEYFLAGS += --default-key $(MODKEYNAME) +endif +ifeq ($(wildcard $(MODSECKEY))+$(wildcard $(MODPUBKEY)),$(MODSECKEY)+$(MODPUBKEY)) +ifeq ($(KBUILD_SRC),) + # no O= is being used + SCRIPTS_DIR := scripts +else + SCRIPTS_DIR := $(KBUILD_SRC)/scripts +endif +SIGN_MODULES := 1 +else +SIGN_MODULES := 0 +endif + +# only sign if it's an in-tree module +ifneq ($(KBUILD_EXTMOD),) +SIGN_MODULES := 0 +endif + +ifeq ($(SIGN_MODULES),1) +KEYRING_DEP := modsign.sec modsign.pub +quiet_cmd_sign_ko_ko_unsigned = SIGN [M] $@ + cmd_sign_ko_ko_unsigned = \ + scripts/mod/mod-extract $< $@.digest && \ + rm -f $@.digest.sig && \ + gpg --batch --no-greeting $(KEYFLAGS) -b $@.digest && \ + sh $(SCRIPTS_DIR)/mod/modsign-note.sh $@.digest.sig | \ + $(CC) -x assembler-with-cpp $(c_flags) $(CFLAGS_MODULE) -c -o $@.note.o - && \ + $(LD) -r $(LDFLAGS) -o $@ $< $@.note.o +else +KEYRING_DEP := +quiet_cmd_sign_ko_ko_unsigned = NO SIGN [M] $@ + cmd_sign_ko_ko_unsigned = \ + cp $< $@ +endif + +$(modules): %.ko :%.ko.unsigned $(KEYRING_DEP) FORCE + $(call if_changed,sign_ko_ko_unsigned) + +targets += $(modules) +endif # Add FORCE to the prequisites of a target to force it to be always rebuilt. # --------------------------------------------------------------------------- diff --git a/scripts/mod/Makefile b/scripts/mod/Makefile index ff954f8..4654e3b 100644 --- a/scripts/mod/Makefile +++ b/scripts/mod/Makefile @@ -1,4 +1,4 @@ -hostprogs-y := modpost mk_elfconfig +hostprogs-y := modpost mk_elfconfig mod-extract always := $(hostprogs-y) empty.o modpost-objs := modpost.o file2alias.o sumversion.o diff --git a/scripts/mod/mod-extract.c b/scripts/mod/mod-extract.c new file mode 100644 index 0000000..0c0e3e3 --- /dev/null +++ b/scripts/mod/mod-extract.c @@ -0,0 +1,913 @@ +/* mod-extract.c: module extractor for signing + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void extract_elf64(void *buffer, size_t size, Elf64_Ehdr *hdr); +static void extract_elf32(void *buffer, size_t size, Elf32_Ehdr *hdr); + +struct byteorder { + uint16_t (*get16)(const uint16_t *); + uint32_t (*get32)(const uint32_t *); + uint64_t (*get64)(const uint64_t *); + void (*set16)(uint16_t *, uint16_t); + void (*set32)(uint32_t *, uint32_t); + void (*set64)(uint64_t *, uint64_t); +}; + +static uint16_t get16_le(const uint16_t *p) { return __le16_to_cpu(*p); } +static uint32_t get32_le(const uint32_t *p) { return __le32_to_cpu(*p); } +static uint64_t get64_le(const uint64_t *p) { return __le64_to_cpu(*p); } +static uint16_t get16_be(const uint16_t *p) { return __be16_to_cpu(*p); } +static uint32_t get32_be(const uint32_t *p) { return __be32_to_cpu(*p); } +static uint64_t get64_be(const uint64_t *p) { return __be64_to_cpu(*p); } + +static void set16_le(uint16_t *p, uint16_t n) { *p = __cpu_to_le16(n); } +static void set32_le(uint32_t *p, uint32_t n) { *p = __cpu_to_le32(n); } +static void set64_le(uint64_t *p, uint64_t n) { *p = __cpu_to_le64(n); } +static void set16_be(uint16_t *p, uint16_t n) { *p = __cpu_to_be16(n); } +static void set32_be(uint32_t *p, uint32_t n) { *p = __cpu_to_be32(n); } +static void set64_be(uint64_t *p, uint64_t n) { *p = __cpu_to_be64(n); } + +static const struct byteorder byteorder_le = { + get16_le, get32_le, get64_le, + set16_le, set32_le, set64_le +}; +static const struct byteorder byteorder_be = { + get16_be, get32_be, get64_be, + set16_be, set32_be, set64_be +}; +static const struct byteorder *order; + +static inline uint16_t get16(const uint16_t *p) { return order->get16(p); } +static inline uint32_t get32(const uint32_t *p) { return order->get32(p); } +static inline uint64_t get64(const uint64_t *p) { return order->get64(p); } +static inline void set16(uint16_t *p, uint16_t n) { order->set16(p, n); } +static inline void set32(uint32_t *p, uint32_t n) { order->set32(p, n); } +static inline void set64(uint64_t *p, uint64_t n) { order->set64(p, n); } + +static FILE *outfd; +static uint8_t csum, xcsum; + +static void write_out(const void *data, size_t size) +{ + const uint8_t *p = data; + size_t loop; + + for (loop = 0; loop < size; loop++) { + csum += p[loop]; + xcsum += p[loop]; + } + + if (fwrite(data, 1, size, outfd) != size) { + perror("write"); + exit(1); + } +} + +#define write_out_val(VAL) write_out(&(VAL), sizeof(VAL)) + +static int is_verbose; + +static __attribute__((format(printf, 1, 2))) +void verbose(const char *fmt, ...) +{ + va_list va; + + if (is_verbose) { + va_start(va, fmt); + vprintf(fmt, va); + va_end(va); + } +} + +static __attribute__((noreturn)) +void usage(void) +{ + fprintf(stderr, "Usage: mod-extract [-v] \n"); + exit(2); +} + +/* + * + */ +int main(int argc, char **argv) +{ + struct stat st; + Elf32_Ehdr *hdr32; + Elf64_Ehdr *hdr64; + size_t len; + void *buffer; + int fd, be, b64; + + while (argc > 1 && strcmp("-v", argv[1]) == 0) { + argv++; + argc--; + is_verbose++; + } + + if (argc != 3) + usage(); + + /* map the module into memory */ + fd = open(argv[1], O_RDONLY); + if (fd < 0) { + perror("open input"); + exit(1); + } + + if (fstat(fd, &st) < 0) { + perror("fstat"); + exit(1); + } + + len = st.st_size; + + buffer = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); + if (buffer == MAP_FAILED) { + perror("mmap"); + exit(1); + } + + if (close(fd) < 0) { + perror("close input"); + exit(1); + } + + /* check it's an ELF object */ + hdr32 = buffer; + hdr64 = buffer; + + if (hdr32->e_ident[EI_MAG0] != ELFMAG0 || + hdr32->e_ident[EI_MAG1] != ELFMAG1 || + hdr32->e_ident[EI_MAG2] != ELFMAG2 || + hdr32->e_ident[EI_MAG3] != ELFMAG3 + ) { + fprintf(stderr, "Module does not appear to be ELF\n"); + exit(3); + } + + /* determine endianness and word size */ + b64 = (hdr32->e_ident[EI_CLASS] == ELFCLASS64); + be = (hdr32->e_ident[EI_DATA] == ELFDATA2MSB); + order = be ? &byteorder_be : &byteorder_le; + + verbose("Module is %s-bit %s-endian\n", + b64 ? "64" : "32", + be ? "big" : "little"); + + /* open the output file */ + outfd = fopen(argv[2], "w"); + if (!outfd) { + perror("open output"); + exit(1); + } + + /* perform the extraction */ + if (b64) + extract_elf64(buffer, len, hdr64); + else + extract_elf32(buffer, len, hdr32); + + /* done */ + if (fclose(outfd) == EOF) { + perror("close output"); + exit(1); + } + + return 0; +} + +/* + * extract a RELA table + * - need to canonicalise the entries in case section addition/removal has + * rearranged the symbol table and the section table + */ +static void extract_elf64_rela(const void *buffer, int secix, int targetix, + const Elf64_Rela *relatab, size_t nrels, + const Elf64_Sym *symbols, size_t nsyms, + const Elf64_Shdr *sections, size_t nsects, int *canonmap, + const char *strings, size_t nstrings, + const char *sh_name) +{ + struct { + uint64_t r_offset; + uint64_t r_addend; + uint64_t st_value; + uint64_t st_size; + uint32_t r_type; + uint16_t st_shndx; + uint8_t st_info; + uint8_t st_other; + + } __attribute__((packed)) relocation; + + const Elf64_Sym *symbol; + size_t loop; + + /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */ + for (loop = 0; loop < nrels; loop++) { + Elf64_Section st_shndx; + Elf64_Xword r_info; + + /* decode the relocation */ + r_info = get64(&relatab[loop].r_info); + relocation.r_offset = relatab[loop].r_offset; + relocation.r_addend = relatab[loop].r_addend; + set32(&relocation.r_type, ELF64_R_TYPE(r_info)); + + if (ELF64_R_SYM(r_info) >= nsyms) { + fprintf(stderr, "Invalid symbol ID %zx in relocation %zu\n", + (size_t)ELF64_R_SYM(r_info), loop); + exit(1); + } + + /* decode the symbol referenced by the relocation */ + symbol = &symbols[ELF64_R_SYM(r_info)]; + relocation.st_info = symbol->st_info; + relocation.st_other = symbol->st_other; + relocation.st_value = symbol->st_value; + relocation.st_size = symbol->st_size; + relocation.st_shndx = symbol->st_shndx; + st_shndx = get16(&symbol->st_shndx); + + /* canonicalise the section used by the symbol */ + if (st_shndx > SHN_UNDEF && st_shndx < nsects) + set16(&relocation.st_shndx, canonmap[st_shndx]); + + write_out_val(relocation); + + /* undefined symbols must be named if referenced */ + if (st_shndx == SHN_UNDEF) { + const char *name = strings + get32(&symbol->st_name); + write_out(name, strlen(name) + 1); + } + } + + verbose("%02x %4d %s [canon]\n", csum, secix, sh_name); +} + +/* + * extract a REL table + * - need to canonicalise the entries in case section addition/removal has + * rearranged the symbol table and the section table + */ +static void extract_elf64_rel(const void *buffer, int secix, int targetix, + const Elf64_Rel *relatab, size_t nrels, + const Elf64_Sym *symbols, size_t nsyms, + const Elf64_Shdr *sections, size_t nsects, int *canonmap, + const char *strings, size_t nstrings, + const char *sh_name) +{ + struct { + uint64_t r_offset; + uint64_t st_value; + uint64_t st_size; + uint32_t r_type; + uint16_t st_shndx; + uint8_t st_info; + uint8_t st_other; + + } __attribute__((packed)) relocation; + + const Elf64_Sym *symbol; + size_t loop; + + /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */ + for (loop = 0; loop < nrels; loop++) { + Elf64_Section st_shndx; + Elf64_Xword r_info; + + /* decode the relocation */ + r_info = get64(&relatab[loop].r_info); + relocation.r_offset = relatab[loop].r_offset; + set32(&relocation.r_type, ELF64_R_TYPE(r_info)); + + if (ELF64_R_SYM(r_info) >= nsyms) { + fprintf(stderr, "Invalid symbol ID %zx in relocation %zu\n", + (size_t)ELF64_R_SYM(r_info), loop); + exit(1); + } + + /* decode the symbol referenced by the relocation */ + symbol = &symbols[ELF64_R_SYM(r_info)]; + relocation.st_info = symbol->st_info; + relocation.st_other = symbol->st_other; + relocation.st_value = symbol->st_value; + relocation.st_size = symbol->st_size; + relocation.st_shndx = symbol->st_shndx; + st_shndx = get16(&symbol->st_shndx); + + /* canonicalise the section used by the symbol */ + if (st_shndx > SHN_UNDEF && st_shndx < nsects) + set16(&relocation.st_shndx, canonmap[st_shndx]); + + write_out_val(relocation); + + /* undefined symbols must be named if referenced */ + if (st_shndx == SHN_UNDEF) { + const char *name = strings + get32(&symbol->st_name); + write_out(name, strlen(name) + 1); + } + } + + verbose("%02x %4d %s [canon]\n", csum, secix, sh_name); +} + +/* + * extract the data from a 64-bit module + */ +static void extract_elf64(void *buffer, size_t len, Elf64_Ehdr *hdr) +{ + const Elf64_Sym *symbols; + Elf64_Shdr *sections; + const char *secstrings, *strings; + size_t nsyms, nstrings; + int loop, shnum, *canonlist, *canonmap, canon, changed, tmp; + + sections = buffer + get64(&hdr->e_shoff); + secstrings = buffer + get64(§ions[get16(&hdr->e_shstrndx)].sh_offset); + shnum = get16(&hdr->e_shnum); + + /* find the symbol table and the string table and produce a list of + * index numbers of sections that contribute to the kernel's module + * image + */ + canonlist = calloc(sizeof(int), shnum * 2); + if (!canonlist) { + perror("calloc"); + exit(1); + } + canonmap = canonlist + shnum; + canon = 0; + + symbols = NULL; + strings = NULL; + nstrings = 0; + nsyms = 0; + + for (loop = 1; loop < shnum; loop++) { + const char *sh_name = secstrings + get32(§ions[loop].sh_name); + Elf64_Word sh_type = get32(§ions[loop].sh_type); + Elf64_Xword sh_size = get64(§ions[loop].sh_size); + Elf64_Xword sh_flags = get64(§ions[loop].sh_flags); + Elf64_Word sh_info = get32(§ions[loop].sh_info); + Elf64_Off sh_offset = get64(§ions[loop].sh_offset); + void *data = buffer + sh_offset; + + /* quick sanity check */ + if (sh_type != SHT_NOBITS && len < sh_offset + sh_size) { + fprintf(stderr, "Section goes beyond EOF\n"); + exit(3); + } + + /* we only need to canonicalise allocatable sections */ + if (sh_flags & SHF_ALLOC) + canonlist[canon++] = loop; + else if ((sh_type == SHT_REL || sh_type == SHT_RELA) && + get64(§ions[sh_info].sh_flags) & SHF_ALLOC) + canonlist[canon++] = loop; + + /* keep track of certain special sections */ + switch (sh_type) { + case SHT_SYMTAB: + if (strcmp(sh_name, ".symtab") == 0) { + symbols = data; + nsyms = sh_size / sizeof(Elf64_Sym); + } + break; + + case SHT_STRTAB: + if (strcmp(sh_name, ".strtab") == 0) { + strings = data; + nstrings = sh_size; + } + break; + + default: + break; + } + } + + if (!symbols) { + fprintf(stderr, "Couldn't locate symbol table\n"); + exit(3); + } + + if (!strings) { + fprintf(stderr, "Couldn't locate strings table\n"); + exit(3); + } + + /* canonicalise the index numbers of the contributing section */ + do { + changed = 0; + + for (loop = 0; loop < canon - 1; loop++) { + const char *x = secstrings + get32(§ions[canonlist[loop + 0]].sh_name); + const char *y = secstrings + get32(§ions[canonlist[loop + 1]].sh_name); + if (strcmp(x, y) > 0) { + tmp = canonlist[loop + 0]; + canonlist[loop + 0] = canonlist[loop + 1]; + canonlist[loop + 1] = tmp; + changed = 1; + } + } + + } while (changed); + + for (loop = 0; loop < canon; loop++) + canonmap[canonlist[loop]] = loop + 1; + + if (is_verbose > 1) { + printf("\nSection canonicalisation map:\n"); + for (loop = 1; loop < shnum; loop++) { + const char *x = secstrings + get32(§ions[loop].sh_name); + printf("%4d %s\n", canonmap[loop], x); + } + + printf("\nAllocated section list in canonical order:\n"); + for (loop = 0; loop < canon; loop++) { + const char *x = secstrings + get32(§ions[canonlist[loop]].sh_name); + printf("%4d %s\n", canonlist[loop], x); + } + } + + /* iterate through the section table looking for sections we want to + * contribute to the signature */ + verbose("\n"); + verbose("CAN FILE POS CS SECT NAME\n"); + verbose("=== ======== == ==== ==============================\n"); + + for (loop = 0; loop < canon; loop++) { + int sect = canonlist[loop]; + const char *sh_name = secstrings + get32(§ions[sect].sh_name); + Elf64_Word sh_type = get32(§ions[sect].sh_type); + Elf64_Xword sh_size = get64(§ions[sect].sh_size); + Elf64_Xword sh_flags = get64(§ions[sect].sh_flags); + Elf64_Word sh_info = get32(§ions[sect].sh_info); + Elf64_Off sh_offset = get64(§ions[sect].sh_offset); + void *data = buffer + sh_offset; + + csum = 0; + + /* include canonicalised relocation sections */ + if (sh_type == SHT_REL || sh_type == SHT_RELA) { + Elf32_Word canon_sh_info; + + if (sh_info <= 0 && sh_info >= hdr->e_shnum) { + fprintf(stderr, + "Invalid ELF - REL/RELA sh_info does" + " not refer to a valid section\n"); + exit(3); + } + + verbose("%3u %08lx ", loop, ftell(outfd)); + + set32(&canon_sh_info, canonmap[sh_info]); + + /* write out selected portions of the section header */ + write_out(sh_name, strlen(sh_name)); + write_out_val(sections[sect].sh_type); + write_out_val(sections[sect].sh_flags); + write_out_val(sections[sect].sh_size); + write_out_val(sections[sect].sh_addralign); + write_out_val(canon_sh_info); + + if (sh_type == SHT_RELA) + extract_elf64_rela(buffer, sect, sh_info, + data, sh_size / sizeof(Elf64_Rela), + symbols, nsyms, + sections, shnum, canonmap, + strings, nstrings, + sh_name); + else + extract_elf64_rel(buffer, sect, sh_info, + data, sh_size / sizeof(Elf64_Rel), + symbols, nsyms, + sections, shnum, canonmap, + strings, nstrings, + sh_name); + continue; + } + + /* include the headers of BSS sections */ + if (sh_type == SHT_NOBITS && sh_flags & SHF_ALLOC) { + verbose("%3u %08lx ", loop, ftell(outfd)); + + /* write out selected portions of the section header */ + write_out(sh_name, strlen(sh_name)); + write_out_val(sections[sect].sh_type); + write_out_val(sections[sect].sh_flags); + write_out_val(sections[sect].sh_size); + write_out_val(sections[sect].sh_addralign); + + verbose("%02x %4d %s\n", csum, sect, sh_name); + } + + /* include allocatable loadable sections */ + if (sh_type != SHT_NOBITS && sh_flags & SHF_ALLOC) + goto include_section; + + /* not this section */ + continue; + + include_section: + verbose("%3u %08lx ", loop, ftell(outfd)); + + /* write out selected portions of the section header */ + write_out(sh_name, strlen(sh_name)); + write_out_val(sections[sect].sh_type); + write_out_val(sections[sect].sh_flags); + write_out_val(sections[sect].sh_size); + write_out_val(sections[sect].sh_addralign); + + /* write out the section data */ + write_out(data, sh_size); + + verbose("%02x %4d %s\n", csum, sect, sh_name); + } + + verbose("%08lx (%lu bytes csum 0x%02x)\n", + ftell(outfd), ftell(outfd), xcsum); +} + +/* + * extract a RELA table + * - need to canonicalise the entries in case section addition/removal has + * rearranged the symbol table and the section table + */ +static void extract_elf32_rela(const void *buffer, int secix, int targetix, + const Elf32_Rela *relatab, size_t nrels, + const Elf32_Sym *symbols, size_t nsyms, + const Elf32_Shdr *sections, size_t nsects, + int *canonmap, + const char *strings, size_t nstrings, + const char *sh_name) +{ + struct { + uint32_t r_offset; + uint32_t r_addend; + uint32_t st_value; + uint32_t st_size; + uint16_t st_shndx; + uint8_t r_type; + uint8_t st_info; + uint8_t st_other; + + } __attribute__((packed)) relocation; + + const Elf32_Sym *symbol; + size_t loop; + + /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */ + for (loop = 0; loop < nrels; loop++) { + Elf32_Section st_shndx; + Elf32_Word r_info; + + /* decode the relocation */ + r_info = get32(&relatab[loop].r_info); + relocation.r_offset = relatab[loop].r_offset; + relocation.r_addend = relatab[loop].r_addend; + relocation.r_type = ELF32_R_TYPE(r_info); + + if (ELF32_R_SYM(r_info) >= nsyms) { + fprintf(stderr, "Invalid symbol ID %x in relocation %zu\n", + ELF32_R_SYM(r_info), loop); + exit(1); + } + + /* decode the symbol referenced by the relocation */ + symbol = &symbols[ELF32_R_SYM(r_info)]; + relocation.st_info = symbol->st_info; + relocation.st_other = symbol->st_other; + relocation.st_value = symbol->st_value; + relocation.st_size = symbol->st_size; + relocation.st_shndx = symbol->st_shndx; + st_shndx = get16(&symbol->st_shndx); + + /* canonicalise the section used by the symbol */ + if (st_shndx > SHN_UNDEF && st_shndx < nsects) + set16(&relocation.st_shndx, canonmap[st_shndx]); + + write_out_val(relocation); + + /* undefined symbols must be named if referenced */ + if (st_shndx == SHN_UNDEF) { + const char *name = strings + get32(&symbol->st_name); + write_out(name, strlen(name) + 1); + } + } + + verbose("%02x %4d %s [canon]\n", csum, secix, sh_name); +} + +/* + * extract a REL table + * - need to canonicalise the entries in case section addition/removal has + * rearranged the symbol table and the section table + */ +static void extract_elf32_rel(const void *buffer, int secix, int targetix, + const Elf32_Rel *relatab, size_t nrels, + const Elf32_Sym *symbols, size_t nsyms, + const Elf32_Shdr *sections, size_t nsects, + int *canonmap, + const char *strings, size_t nstrings, + const char *sh_name) +{ + struct { + uint32_t r_offset; + uint32_t st_value; + uint32_t st_size; + uint16_t st_shndx; + uint8_t r_type; + uint8_t st_info; + uint8_t st_other; + + } __attribute__((packed)) relocation; + + const Elf32_Sym *symbol; + size_t loop; + + /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */ + for (loop = 0; loop < nrels; loop++) { + Elf32_Section st_shndx; + Elf32_Word r_info; + + /* decode the relocation */ + r_info = get32(&relatab[loop].r_info); + relocation.r_offset = relatab[loop].r_offset; + relocation.r_type = ELF32_R_TYPE(r_info); + + if (ELF32_R_SYM(r_info) >= nsyms) { + fprintf(stderr, "Invalid symbol ID %x in relocation %zu\n", + ELF32_R_SYM(r_info), loop); + exit(1); + } + + /* decode the symbol referenced by the relocation */ + symbol = &symbols[ELF32_R_SYM(r_info)]; + relocation.st_info = symbol->st_info; + relocation.st_other = symbol->st_other; + relocation.st_value = symbol->st_value; + relocation.st_size = symbol->st_size; + relocation.st_shndx = symbol->st_shndx; + st_shndx = get16(&symbol->st_shndx); + + /* canonicalise the section used by the symbol */ + if (st_shndx > SHN_UNDEF && st_shndx < nsects) + set16(&relocation.st_shndx, canonmap[st_shndx]); + + write_out_val(relocation); + + /* undefined symbols must be named if referenced */ + if (st_shndx == SHN_UNDEF) { + const char *name = strings + get32(&symbol->st_name); + write_out(name, strlen(name) + 1); + } + } + + verbose("%02x %4d %s [canon]\n", csum, secix, sh_name); +} + +/* + * extract the data from a 32-bit module + */ +static void extract_elf32(void *buffer, size_t len, Elf32_Ehdr *hdr) +{ + const Elf32_Sym *symbols; + Elf32_Shdr *sections; + const char *secstrings, *strings; + size_t nsyms, nstrings; + int loop, shnum, *canonlist, *canonmap, canon, changed, tmp; + + sections = buffer + get32(&hdr->e_shoff); + secstrings = buffer + get32(§ions[get16(&hdr->e_shstrndx)].sh_offset); + shnum = get16(&hdr->e_shnum); + + /* find the symbol table and the string table and produce a list of + * index numbers of sections that contribute to the kernel's module + * image + */ + canonlist = calloc(sizeof(int), shnum * 2); + if (!canonlist) { + perror("calloc"); + exit(1); + } + canonmap = canonlist + shnum; + canon = 0; + + symbols = NULL; + strings = NULL; + nstrings = 0; + nsyms = 0; + + for (loop = 1; loop < shnum; loop++) { + const char *sh_name = secstrings + get32(§ions[loop].sh_name); + Elf32_Word sh_type = get32(§ions[loop].sh_type); + Elf32_Xword sh_size = get32(§ions[loop].sh_size); + Elf32_Xword sh_flags = get32(§ions[loop].sh_flags); + Elf64_Word sh_info = get32(§ions[loop].sh_info); + Elf32_Off sh_offset = get32(§ions[loop].sh_offset); + void *data = buffer + sh_offset; + + /* quick sanity check */ + if (sh_type != SHT_NOBITS && len < sh_offset + sh_size) { + fprintf(stderr, "Section goes beyond EOF\n"); + exit(3); + } + + /* we only need to canonicalise allocatable sections */ + if (sh_flags & SHF_ALLOC) + canonlist[canon++] = loop; + else if ((sh_type == SHT_REL || sh_type == SHT_RELA) && + get32(§ions[sh_info].sh_flags) & SHF_ALLOC) + canonlist[canon++] = loop; + + /* keep track of certain special sections */ + switch (sh_type) { + case SHT_SYMTAB: + if (strcmp(sh_name, ".symtab") == 0) { + symbols = data; + nsyms = sh_size / sizeof(Elf32_Sym); + } + break; + + case SHT_STRTAB: + if (strcmp(sh_name, ".strtab") == 0) { + strings = data; + nstrings = sh_size; + } + break; + + default: + break; + } + } + + if (!symbols) { + fprintf(stderr, "Couldn't locate symbol table\n"); + exit(3); + } + + if (!strings) { + fprintf(stderr, "Couldn't locate strings table\n"); + exit(3); + } + + /* canonicalise the index numbers of the contributing section */ + do { + changed = 0; + + for (loop = 0; loop < canon - 1; loop++) { + const char *x = secstrings + get32(§ions[canonlist[loop + 0]].sh_name); + const char *y = secstrings + get32(§ions[canonlist[loop + 1]].sh_name); + if (strcmp(x, y) > 0) { + tmp = canonlist[loop + 0]; + canonlist[loop + 0] = canonlist[loop + 1]; + canonlist[loop + 1] = tmp; + changed = 1; + } + } + + } while (changed); + + for (loop = 0; loop < canon; loop++) + canonmap[canonlist[loop]] = loop + 1; + + if (is_verbose > 1) { + printf("\nSection canonicalisation map:\n"); + for (loop = 1; loop < shnum; loop++) { + const char *x = secstrings + get32(§ions[loop].sh_name); + printf("%4d %s\n", canonmap[loop], x); + } + + printf("\nAllocated section list in canonical order:\n"); + for (loop = 0; loop < canon; loop++) { + const char *x = secstrings + get32(§ions[canonlist[loop]].sh_name); + printf("%4d %s\n", canonlist[loop], x); + } + } + + /* iterate through the section table looking for sections we want to + * contribute to the signature */ + verbose("\n"); + verbose("CAN FILE POS CS SECT NAME\n"); + verbose("=== ======== == ==== ==============================\n"); + + for (loop = 0; loop < canon; loop++) { + int sect = canonlist[loop]; + const char *sh_name = secstrings + get32(§ions[sect].sh_name); + Elf32_Word sh_type = get32(§ions[sect].sh_type); + Elf32_Xword sh_size = get32(§ions[sect].sh_size); + Elf32_Xword sh_flags = get32(§ions[sect].sh_flags); + Elf32_Word sh_info = get32(§ions[sect].sh_info); + Elf32_Off sh_offset = get32(§ions[sect].sh_offset); + void *data = buffer + sh_offset; + + csum = 0; + + /* quick sanity check */ + if (sh_type != SHT_NOBITS && len < sh_offset + sh_size) { + fprintf(stderr, "section goes beyond EOF\n"); + exit(3); + } + + /* include canonicalised relocation sections */ + if (sh_type == SHT_REL || sh_type == SHT_RELA) { + Elf32_Word canon_sh_info; + + if (sh_info <= 0 && sh_info >= hdr->e_shnum) { + fprintf(stderr, + "Invalid ELF - REL/RELA sh_info does" + " not refer to a valid section\n"); + exit(3); + } + + verbose("%3u %08lx ", loop, ftell(outfd)); + + set32(&canon_sh_info, canonmap[sh_info]); + + /* write out selected portions of the section header */ + write_out(sh_name, strlen(sh_name)); + write_out_val(sections[sect].sh_type); + write_out_val(sections[sect].sh_flags); + write_out_val(sections[sect].sh_size); + write_out_val(sections[sect].sh_addralign); + write_out_val(canon_sh_info); + + if (sh_type == SHT_RELA) + extract_elf32_rela(buffer, sect, sh_info, + data, sh_size / sizeof(Elf32_Rela), + symbols, nsyms, + sections, shnum, canonmap, + strings, nstrings, + sh_name); + else + extract_elf32_rel(buffer, sect, sh_info, + data, sh_size / sizeof(Elf32_Rel), + symbols, nsyms, + sections, shnum, canonmap, + strings, nstrings, + sh_name); + continue; + } + + /* include the headers of BSS sections */ + if (sh_type == SHT_NOBITS && sh_flags & SHF_ALLOC) { + verbose("%3u %08lx ", loop, ftell(outfd)); + + /* write out selected portions of the section header */ + write_out(sh_name, strlen(sh_name)); + write_out_val(sections[sect].sh_type); + write_out_val(sections[sect].sh_flags); + write_out_val(sections[sect].sh_size); + write_out_val(sections[sect].sh_addralign); + + verbose("%02x %4d %s\n", csum, sect, sh_name); + } + + /* include allocatable loadable sections */ + if (sh_type != SHT_NOBITS && sh_flags & SHF_ALLOC) + goto include_section; + + /* not this section */ + continue; + + include_section: + verbose("%3u %08lx ", loop, ftell(outfd)); + + /* write out selected portions of the section header */ + write_out(sh_name, strlen(sh_name)); + write_out_val(sections[sect].sh_type); + write_out_val(sections[sect].sh_flags); + write_out_val(sections[sect].sh_size); + write_out_val(sections[sect].sh_addralign); + + /* write out the section data */ + write_out(data, sh_size); + + verbose("%02x %4d %s\n", csum, sect, sh_name); + } + + verbose("%08lx (%lu bytes csum 0x%02x)\n", + ftell(outfd), ftell(outfd), xcsum); +} diff --git a/scripts/mod/modsign-note.sh b/scripts/mod/modsign-note.sh new file mode 100644 index 0000000..bca67c0 --- /dev/null +++ b/scripts/mod/modsign-note.sh @@ -0,0 +1,16 @@ +#!/bin/sh +# +# Generate a module signature note source file +# +# mod-sign.sh > +# + +SIG=$1 + +cat < + +ELFNOTE(MODSIGN_NOTE_NAME, MODSIGN_NOTE_TYPE, .incbin "$SIG") +EOF + +exit 0 -- 1.7.10.2 From 38ddca03060502e8134320b732758fd6b6a98247 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 10 May 2012 23:49:55 +0100 Subject: [PATCH 23/36] MODSIGN: Module signature verification stub Create a stub for the module signature verifier and link it into module.c so that it gets called. A field is added to struct module to record whether or not a valid module signature was detected. The stub also implements the policy for handling unsigned modules and the printing of error messages to indicate various problems with the module. If CONFIG_MODULE_SIG_FORCE is enabled or "enforcemodulesig=1" is supplied on the kernel command line, the kernel will _only_ load validly signed modules for which it has a public key. Otherwise, it will also load modules that are unsigned. Any module for which the kernel has a key, but which proves to have a signature mismatch will not be permitted to load. This table indicates the behaviours in the various situations: MODULE STATE PERMISSIVE MODE ENFORCING MODE ======================================= =============== =============== Unsigned Ok EKEYREJECTED Signed, no public key ENOKEY ENOKEY Validly signed, public key Ok Ok Invalidly signed, public key EKEYREJECTED EKEYREJECTED Validly signed, expired key EKEYEXPIRED EKEYEXPIRED Signed, hash algorithm unavailable ENOPKG ENOPKG Corrupt signature EBADMSG EBADMSG Corrupt ELF ELIBBAD ELIBBAD Signed-off-by: David Howells --- include/linux/module.h | 3 ++ kernel/Makefile | 1 + kernel/module-verify-defs.h | 77 ++++++++++++++++++++++++++++++ kernel/module-verify.c | 109 +++++++++++++++++++++++++++++++++++++++++++ kernel/module-verify.h | 19 ++++++++ kernel/module.c | 26 +++++++++-- 6 files changed, 230 insertions(+), 5 deletions(-) create mode 100644 kernel/module-verify-defs.h create mode 100644 kernel/module-verify.c create mode 100644 kernel/module-verify.h diff --git a/include/linux/module.h b/include/linux/module.h index fbcafe2..7391833 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -227,6 +227,9 @@ struct module /* Unique handle for this module */ char name[MODULE_NAME_LEN]; + /* Is this module GPG signed */ + bool gpgsig_ok; + /* Sysfs stuff. */ struct module_kobject mkobj; struct module_attribute *modinfo_attrs; diff --git a/kernel/Makefile b/kernel/Makefile index cb41b95..7608053 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o obj-$(CONFIG_PROVE_LOCKING) += spinlock.o obj-$(CONFIG_UID16) += uid16.o obj-$(CONFIG_MODULES) += module.o +obj-$(CONFIG_MODULE_SIG) += module-verify.o obj-$(CONFIG_KALLSYMS) += kallsyms.o obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o obj-$(CONFIG_KEXEC) += kexec.o diff --git a/kernel/module-verify-defs.h b/kernel/module-verify-defs.h new file mode 100644 index 0000000..292d2ba --- /dev/null +++ b/kernel/module-verify-defs.h @@ -0,0 +1,77 @@ +/* Module verification internal definitions + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifdef CONFIG_MODULE_SIG + +/* + * Internal state + */ +struct module_verify_data { + struct crypto_key_verify_context *mod_sig; /* Module signing context */ + union { + const void *buffer; /* module buffer */ + const Elf_Ehdr *hdr; /* ELF header */ + }; + const Elf_Shdr *sections; /* ELF section table */ + const char *secstrings; /* ELF section string table */ + const void *sig; /* Signature note content */ + size_t size; /* module object size */ + size_t nsects; /* number of sections */ + size_t sig_size; /* Size of signature */ + size_t signed_size; /* count of bytes contributed to digest */ + unsigned *canonlist; /* list of canonicalised sections */ + unsigned *canonmap; /* section canonicalisation map */ + unsigned ncanon; /* number of canonicalised sections */ + unsigned sig_index; /* module signature section index */ + uint8_t xcsum; /* checksum of bytes contributed to digest */ + uint8_t csum; /* checksum of bytes representing a section */ +}; + +/* + * Whether or not we support various types of ELF relocation record + */ +#if defined(MODULE_HAS_ELF_REL_ONLY) +#define is_elf_rel(sh_type) ((sh_type) == SHT_REL) +#define is_elf_rela(sh_type) (0) +#elif defined(MODULE_HAS_ELF_RELA_ONLY) +#define is_elf_rel(sh_type) (0) +#define is_elf_rela(sh_type) ((sh_type) == SHT_RELA) +#else +#define is_elf_rel(sh_type) ((sh_type) == SHT_REL) +#define is_elf_rela(sh_type) ((sh_type) == SHT_RELA) +#endif + +/* + * Debugging. Define DEBUG to enable. + */ +#define _debug(FMT, ...) \ + do { \ + if (unlikely(modsign_debug)) \ + pr_debug(FMT, ##__VA_ARGS__); \ + } while(0) + +#ifdef DEBUG +#define count_and_csum(C, __p, __n) \ +do { \ + int __loop; \ + for (__loop = 0; __loop < __n; __loop++) { \ + (C)->csum += __p[__loop]; \ + (C)->xcsum += __p[__loop]; \ + } \ + (C)->signed_size += __n; \ +} while (0) +#else +#define count_and_csum(C, __p, __n) \ +do { \ +} while (0) +#endif + +#endif /* CONFIG_MODULE_SIG */ diff --git a/kernel/module-verify.c b/kernel/module-verify.c new file mode 100644 index 0000000..0a3eb4b --- /dev/null +++ b/kernel/module-verify.c @@ -0,0 +1,109 @@ +/* Module signature verification + * + * The code in this file examines a signed kernel module and attempts to + * determine if the PGP signature inside the module matches a digest of the + * allocatable sections and the canonicalised relocation tables for those + * allocatable sections. + * + * The module signature is included in an ELF note within the ELF structure of + * the module blob. This, combined with the minimal canonicalisation performed + * here, permits the module to pass through "strip -x", "strip -g" and + * "eu-strip" without becoming corrupt. "strip" and "strip -s" will render a + * module unusable by removing the symbol table. + * + * Copyright (C) 2004, 2011, 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from GregKH's RSA module signer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#undef DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "module-verify.h" +#include "module-verify-defs.h" + +#ifdef DEBUG +static int modsign_debug; +core_param(modsign_debug, modsign_debug, int, 0644); +#else +#define modsign_debug false +#endif + +#ifdef CONFIG_MODULE_SIG_FORCE +#define modsign_signedonly true +#else +static bool modsign_signedonly; +#endif + +static const char modsign_note_name[] = ELFNOTE_NAME(MODSIGN_NOTE_NAME); +static const char modsign_note_section[] = ELFNOTE_SECTION(MODSIGN_NOTE_NAME); + +/* + * Verify a module's integrity + */ +int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok) +{ + struct module_verify_data mvdata; + int ret; + + memset(&mvdata, 0, sizeof(mvdata)); + mvdata.buffer = hdr; + mvdata.size = size; + + if (mvdata.sig_index <= 0) { + /* Deal with an unsigned module */ + if (modsign_signedonly) { + pr_err("An attempt to load unsigned module was rejected\n"); + return -EKEYREJECTED; + } else { + return 0; + } + goto out; + } + + ret = 0; + +out: + switch (ret) { + case 0: /* Good signature */ + *_gpgsig_ok = true; + break; + case -ELIBBAD: + pr_err("Module format error encountered\n"); + break; + case -EBADMSG: + pr_err("Module signature error encountered\n"); + break; + case -EKEYREJECTED: /* Signature mismatch or number format error */ + pr_err("Module signature verification failed\n"); + break; + case -ENOKEY: /* Signed, but we don't have the public key */ + pr_err("Module signed with unknown public key\n"); + break; + default: /* Other error (probably ENOMEM) */ + break; + } + return ret; +} + +static int __init sign_setup(char *str) +{ +#ifndef CONFIG_MODULE_SIG_FORCE + modsign_signedonly = true; +#endif + return 0; +} +__setup("enforcemodulesig", sign_setup); diff --git a/kernel/module-verify.h b/kernel/module-verify.h new file mode 100644 index 0000000..6bb6b56 --- /dev/null +++ b/kernel/module-verify.h @@ -0,0 +1,19 @@ +/* Module verification definitions + * + * Copyright (C) 2004, 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifdef CONFIG_MODULE_SIG +extern int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok); +#else +static inline int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok) +{ + return 0; +} +#endif diff --git a/kernel/module.c b/kernel/module.c index 377cb06..c3797f7 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -58,6 +58,7 @@ #include #include #include +#include "module-verify.h" #define CREATE_TRACE_POINTS #include @@ -2402,7 +2403,8 @@ static inline void kmemleak_load_module(const struct module *mod, /* Sets info->hdr and info->len. */ static int copy_and_check(struct load_info *info, const void __user *umod, unsigned long len, - const char __user *uargs) + const char __user *uargs, + bool *_gpgsig_ok) { int err; Elf_Ehdr *hdr; @@ -2435,6 +2437,12 @@ static int copy_and_check(struct load_info *info, goto free_hdr; } + /* Verify the module's contents */ + *_gpgsig_ok = false; + err = module_verify(hdr, len, _gpgsig_ok); + if (err < 0) + goto free_hdr; + info->hdr = hdr; info->len = len; return 0; @@ -2777,7 +2785,8 @@ int __weak module_frob_arch_sections(Elf_Ehdr *hdr, return 0; } -static struct module *layout_and_allocate(struct load_info *info) +static struct module *layout_and_allocate(struct load_info *info, + bool gpgsig_ok) { /* Module within temporary copy. */ struct module *mod; @@ -2787,6 +2796,7 @@ static struct module *layout_and_allocate(struct load_info *info) mod = setup_load_info(info); if (IS_ERR(mod)) return mod; + mod->gpgsig_ok = gpgsig_ok; err = check_modinfo(mod, info); if (err) @@ -2870,17 +2880,18 @@ static struct module *load_module(void __user *umod, struct load_info info = { NULL, }; struct module *mod; long err; + bool gpgsig_ok; pr_debug("load_module: umod=%p, len=%lu, uargs=%p\n", umod, len, uargs); /* Copy in the blobs from userspace, check they are vaguely sane. */ - err = copy_and_check(&info, umod, len, uargs); + err = copy_and_check(&info, umod, len, uargs, &gpgsig_ok); if (err) return ERR_PTR(err); /* Figure out module layout, and allocate all the memory. */ - mod = layout_and_allocate(&info); + mod = layout_and_allocate(&info, gpgsig_ok); if (IS_ERR(mod)) { err = PTR_ERR(mod); goto free_copy; @@ -3517,8 +3528,13 @@ void print_modules(void) printk(KERN_DEFAULT "Modules linked in:"); /* Most callers should already have preempt disabled, but make sure */ preempt_disable(); - list_for_each_entry_rcu(mod, &modules, list) + list_for_each_entry_rcu(mod, &modules, list) { printk(" %s%s", mod->name, module_flags(mod, buf)); +#ifdef CONFIG_MODULE_SIG + if (!mod->gpgsig_ok) + printk("(U)"); +#endif + } preempt_enable(); if (last_unloaded_module[0]) printk(" [last unloaded: %s]", last_unloaded_module); -- 1.7.10.2 From 6f5323e52cdc37969939ef82036783da67afe69f Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 10 May 2012 23:49:55 +0100 Subject: [PATCH 24/36] MODSIGN: Provide module signing public keys to the kernel Include a PGP keyring containing the public keys required to perform module verification in the kernel image during build and create a special keyring during boot which is then populated with keys of crypto type holding the public keys found in the PGP keyring. These can be seen by root: [root@andromeda ~]# cat /proc/keys 07ad4ee0 I----- 1 perm 3f010000 0 0 crypto modsign.0: RSA 87b9b3bd [] 15c7f8c3 I----- 1 perm 1f030000 0 0 keyring .module_sign: 1/4 ... It is probably worth permitting root to invalidate these keys, resulting in their removal and preventing further modules from being loaded with that key. Signed-off-by: David Howells --- kernel/Makefile | 3 +- kernel/modsign-pubkey.c | 74 +++++++++++++++++++++++++++++++++++++++++++ kernel/module-verify-defs.h | 4 +++ kernel/module-verify.c | 2 -- 4 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 kernel/modsign-pubkey.c diff --git a/kernel/Makefile b/kernel/Makefile index 7608053..986ed7f 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -51,7 +51,8 @@ obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o obj-$(CONFIG_PROVE_LOCKING) += spinlock.o obj-$(CONFIG_UID16) += uid16.o obj-$(CONFIG_MODULES) += module.o -obj-$(CONFIG_MODULE_SIG) += module-verify.o +obj-$(CONFIG_MODULE_SIG) += module-verify.o modsign-pubkey.o +kernel/modsign-pubkey.o: modsign.pub obj-$(CONFIG_KALLSYMS) += kallsyms.o obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o obj-$(CONFIG_KEXEC) += kexec.o diff --git a/kernel/modsign-pubkey.c b/kernel/modsign-pubkey.c new file mode 100644 index 0000000..2ada460 --- /dev/null +++ b/kernel/modsign-pubkey.c @@ -0,0 +1,74 @@ +/* Public keys for module signature verification + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include "module-verify-defs.h" + +struct key *modsign_keyring; + +extern __initdata const u8 modsign_public_keys[]; +extern __initdata const u8 modsign_public_keys_end[]; +asm(".section .init.data,\"aw\"\n" + "modsign_public_keys:\n" + ".incbin \"modsign.pub\"\n" + "modsign_public_keys_end:" + ); + +/* + * We need to make sure ccache doesn't cache the .o file as it doesn't notice + * if modsign.pub changes. + */ +static __initdata const char annoy_ccache[] = __TIME__ "foo"; + +/* + * Load the compiled-in keys + */ +static __init int module_verify_init(void) +{ + pr_notice("Initialise module verification\n"); + + modsign_keyring = key_alloc(&key_type_keyring, ".module_sign", + 0, 0, current_cred(), + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ, + KEY_ALLOC_NOT_IN_QUOTA); + if (IS_ERR(modsign_keyring)) + panic("Can't allocate module signing keyring\n"); + + if (key_instantiate_and_link(modsign_keyring, NULL, 0, NULL, NULL) < 0) + panic("Can't instantiate module signing keyring\n"); + + return 0; +} + +/* + * Must be initialised before we try and load the keys into the keyring. + */ +device_initcall(module_verify_init); + +/* + * Load the compiled-in keys + */ +static __init int modsign_pubkey_init(void) +{ + pr_notice("Load module verification keys\n"); + + if (preload_pgp_keys(modsign_public_keys, + modsign_public_keys_end - modsign_public_keys, + modsign_keyring, "modsign.") < 0) + panic("Can't load module signing keys\n"); + + return 0; +} +late_initcall(modsign_pubkey_init); diff --git a/kernel/module-verify-defs.h b/kernel/module-verify-defs.h index 292d2ba..45bea45 100644 --- a/kernel/module-verify-defs.h +++ b/kernel/module-verify-defs.h @@ -11,6 +11,10 @@ #ifdef CONFIG_MODULE_SIG +#include + +extern struct key *modsign_keyring; + /* * Internal state */ diff --git a/kernel/module-verify.c b/kernel/module-verify.c index 0a3eb4b..b1c1d4c 100644 --- a/kernel/module-verify.c +++ b/kernel/module-verify.c @@ -27,8 +27,6 @@ #include #include #include -#include -#include #include #include #include -- 1.7.10.2 From 5727333d123ef9d7fa2666069306bf3ec2d7f110 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 10 May 2012 23:49:56 +0100 Subject: [PATCH 25/36] MODSIGN: Check the ELF container Check the ELF container of the kernel module to prevent the kernel from crashing or getting corrupted whilst trying to use it and locate the module signature note if present. We try to check as little as possible. We check the metadata that the signature checker actually has to use, and leave anything that it doesn't actually need to the signature to catch. The stuff we need to check is: (1) The locations and offsets in the ELF header of important parts like the section table. (2) The section table. Note that we only check sh_info for section types that we're actually interested in (string, symbol and relocation tables). We also check that alignments are what we expect for those tables. (3) That non-empty string tables have the required NUL at the end so that we can be sure that all strings therein are NUL-terminated. We don't bother checking for the required NUL at the beginning as it shouldn't cause a problem to us. (4) The name offset and section index in each symbol. We could defer this to when we deal with the relocation tables so that we only check symbols that are used by relocations - but we would then end up checking some symbols multiple times. (5) The module signature note section and the first note in it if present. (6) That relocations applied to an allocatable section only refer to symbols in allocatable sections and absolute symbols (done in the module signing code rather than here). Note that these checks survive "strip -x", "strip -g" and "eu-strip" being applied to a module and detect if the module was given to "strip" or "strip -s" and report an error. We can skip some direct checks that turn out unnecessary or redundant: (1) That sh_link has a greater than 0 value for symbol tables and relocation tables. These require the index of a string table and a symbol table respectively - and since we have already checked section 0 is of SHT_NULL type, checking the symbol type renders the sh_link > 0 check redundant. (2) That a non-empty string table begins with a NUL. Since we check the string table ends with a NUL, any string in there will be NUL-terminated and shouldn't cause us to transgress beyond the bounds of the string table when using strlen(). (3) That strings in a string table actually make sense. We don't care, so long as it is NUL terminated. Any string that refers to an undefined symbol is added to the crypto digest and will be checked that way. Strings that we directly look for (such as ".modinfo") will be validated by that. (4) That sections don't overlap. We don't actually care if sections overlap in the file, provided we don't see bad metadata. If the sections holding the allocatable content overlap, then the signature check is likely to fail. (5) That symbol values and relocation offsets and addends make sense. We just add this data to the digest if it pertains to an allocatable section. (6) That allocatable note sections, other than the signature note, make sense. The contents of these get added to the digest in their entirety, so we don't need to check them manually. If bad ELF is detected, ELIBBAD is indicated. Note! The "noinline" attribute on the module_verify_elf() function results in somewhat smaller code. Similarly, having separate loops to check basic section parameters and to check type-specific features of sections results in smaller code, presumably because some local variables can be discarded. Signed-off-by: David Howells --- kernel/module-verify.c | 226 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) diff --git a/kernel/module-verify.c b/kernel/module-verify.c index b1c1d4c..5711aeb 100644 --- a/kernel/module-verify.c +++ b/kernel/module-verify.c @@ -50,6 +50,224 @@ static const char modsign_note_name[] = ELFNOTE_NAME(MODSIGN_NOTE_NAME); static const char modsign_note_section[] = ELFNOTE_SECTION(MODSIGN_NOTE_NAME); /* + * Verify the minimum amount of ELF structure of a module needed to check the + * module's signature without bad ELF crashing the kernel. + */ +static noinline int module_verify_elf(struct module_verify_data *mvdata) +{ + const struct elf_note *note; + const Elf_Ehdr *hdr = mvdata->hdr; + const Elf_Shdr *section, *secstop; + const Elf_Sym *symbols, *symbol, *symstop; + const char *strtab; + size_t size, secsize, secstrsize, strsize, notesize, notemetasize; + unsigned line; + + size = mvdata->size; + +#define elfcheck(X) \ +do { if (unlikely(!(X))) { line = __LINE__; goto elfcheck_error; } } while(0) + +#define seccheck(X) \ +do { if (unlikely(!(X))) { line = __LINE__; goto seccheck_error; } } while(0) + +#define symcheck(X) \ +do { if (unlikely(!(X))) { line = __LINE__; goto symcheck_error; } } while(0) + + /* Validate the ELF header */ + elfcheck(size > sizeof(Elf_Ehdr)); + elfcheck(hdr->e_ehsize < size); + + elfcheck(hdr->e_shnum < SHN_LORESERVE); + elfcheck(hdr->e_shstrndx < hdr->e_shnum); + elfcheck(hdr->e_shentsize == sizeof(Elf_Shdr)); + elfcheck(hdr->e_shoff < size); + elfcheck(hdr->e_shoff >= hdr->e_ehsize); + elfcheck(hdr->e_shoff % sizeof(long) == 0); + elfcheck(hdr->e_shnum * sizeof(Elf_Shdr) <= size - hdr->e_shoff); + + /* Validate the section table contents */ + mvdata->nsects = hdr->e_shnum; + mvdata->sections = mvdata->buffer + hdr->e_shoff; + secstop = mvdata->sections + mvdata->nsects; + + /* Section 0 is special, usually indicating an undefined symbol */ + seccheck(mvdata->sections[SHN_UNDEF].sh_type == SHT_NULL); + + /* We also want access to the section name table */ + seccheck(mvdata->sections[hdr->e_shstrndx].sh_type == SHT_STRTAB); + secstrsize = mvdata->sections[hdr->e_shstrndx].sh_size; + + for (section = mvdata->sections + 1; section < secstop; section++) { + seccheck(section->sh_name < secstrsize); + seccheck(section->sh_link < hdr->e_shnum); + + /* Section file offsets must reside within the file, though + * they don't have to actually consume file space (.bss for + * example). + */ + seccheck(section->sh_offset >= hdr->e_ehsize); + seccheck((section->sh_offset & (section->sh_addralign - 1)) == 0); + seccheck(section->sh_offset <= size); + if (section->sh_type != SHT_NOBITS) + seccheck(section->sh_size <= size - section->sh_offset); + + /* Some types of section should contain arrays of fixed-length + * records of a predetermined size and mustn't contain partial + * records. Also, records we're going to access directly must + * have appropriate alignment that we don't get a misalignment + * exception. + */ + if (section->sh_entsize > 1) + seccheck(section->sh_size % section->sh_entsize == 0); + + switch (section->sh_type) { + case SHT_SYMTAB: + seccheck(section->sh_entsize == sizeof(Elf_Sym)); + seccheck(section->sh_addralign % sizeof(long) == 0); + break; + case SHT_REL: +#ifndef MODULE_HAS_ELF_RELA_ONLY + seccheck(section->sh_entsize == sizeof(Elf_Rel)); + seccheck(section->sh_addralign % sizeof(long) == 0); + break; +#else + seccheck(false); + break; +#endif + case SHT_RELA: +#ifndef MODULE_HAS_ELF_REL_ONLY + seccheck(section->sh_entsize == sizeof(Elf_Rela)); + seccheck(section->sh_addralign % sizeof(long) == 0); + break; +#else + seccheck(false); + break; +#endif + case SHT_NOTE: + seccheck(section->sh_addralign % 4 == 0); + break; + case SHT_STRTAB: + /* We require all string tables to be non-empty. If + * not empty, a string table must end in a NUL (it + * should also begin with a NUL, but it's not a problem + * for us if it doesn't). + */ + seccheck(section->sh_size >= 2); + strtab = mvdata->buffer + section->sh_offset; + seccheck(strtab[section->sh_size - 1] == '\0'); + break; + } + } + + /* Check features specific to the type of each section. + * + * Note that having a separate loop here allows the compiler to discard + * some local variables used in the above loop thus making the code + * smaller. + */ + for (section = mvdata->sections + 1; section < secstop; section++) { + switch (section->sh_type) { + case SHT_SYMTAB: + /* Symbol tables nominate a string table. */ + seccheck(mvdata->sections[section->sh_link].sh_type == + SHT_STRTAB); + + /* Validate the symbols in the table. The first symbol + * (STN_UNDEF) is special. + */ + symbol = symbols = mvdata->buffer + section->sh_offset; + symstop = mvdata->buffer + + (section->sh_offset + section->sh_size); + + symcheck(ELF_ST_TYPE(symbols[0].st_info) == STT_NOTYPE); + symcheck(symbol[0].st_shndx == SHN_UNDEF); + + strsize = mvdata->sections[section->sh_link].sh_size; + for (symbol++; symbol < symstop; symbol++) { + symcheck(symbol->st_name < strsize); + symcheck(symbol->st_shndx < hdr->e_shnum || + symbol->st_shndx >= SHN_LORESERVE); + } + break; + +#ifndef MODULE_HAS_ELF_RELA_ONLY + case SHT_REL: +#endif +#ifndef MODULE_HAS_ELF_REL_ONLY + case SHT_RELA: +#endif + /* Relocation tables nominate a symbol table and a + * target section to which the relocations will be + * applied. + */ + seccheck(mvdata->sections[section->sh_link].sh_type == + SHT_SYMTAB); + seccheck(section->sh_info > 0); + seccheck(section->sh_info < hdr->e_shnum); + break; + } + } + + /* We can now use section name string table section as we checked its + * bounds in the loop above. + * + * Each name is NUL-terminated, and the table as a whole should have a + * NUL at either end as there to be at least one named section for the + * module information. + */ + section = &mvdata->sections[hdr->e_shstrndx]; + mvdata->secstrings = mvdata->buffer + section->sh_offset; + + for (section = mvdata->sections + 1; section < secstop; section++) { + const char *name = mvdata->secstrings + section->sh_name; + + switch (section->sh_type) { + case SHT_NOTE: + if (strcmp(name, modsign_note_section) != 0) + continue; + + /* We've found a note purporting to contain a signature + * so we should check the structure of that. + */ + notemetasize = sizeof(struct elf_note) + + roundup(sizeof(modsign_note_name), 4); + + seccheck(mvdata->sig_index == 0); + seccheck(section->sh_size > notemetasize); + note = mvdata->buffer + section->sh_offset; + seccheck(note->n_type == MODSIGN_NOTE_TYPE); + seccheck(note->n_namesz == sizeof(modsign_note_name)); + + notesize = section->sh_size - notemetasize; + seccheck(note->n_descsz <= notesize); + + seccheck(memcmp(note + 1, modsign_note_name, + note->n_namesz) == 0); + + mvdata->sig_size = note->n_descsz; + mvdata->sig = (void *)note + notemetasize; + mvdata->sig_index = section - mvdata->sections; + break; + } + } + + return 0; + +elfcheck_error: + _debug("Verify ELF error (check %u)\n", line); + return -ELIBBAD; +seccheck_error: + _debug("Verify ELF error [sec %ld] (check %u)\n", + (long)(section - mvdata->sections), line); + return -ELIBBAD; +symcheck_error: + _debug("Verify ELF error [sym %ld] (check %u)\n", + (long)(symbol - symbols), line); + return -ELIBBAD; +} + +/* * Verify a module's integrity */ int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok) @@ -61,6 +279,14 @@ int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok) mvdata.buffer = hdr; mvdata.size = size; + /* Minimally check the ELF to make sure building the signature digest + * won't crash the kernel. + */ + ret = module_verify_elf(&mvdata); + if (ret < 0) + goto out; + + /* The ELF checker found the sig for us if it exists */ if (mvdata.sig_index <= 0) { /* Deal with an unsigned module */ if (modsign_signedonly) { -- 1.7.10.2 From 21a28e681f53685960f2780b7884e8b391122259 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 10 May 2012 23:49:56 +0100 Subject: [PATCH 26/36] MODSIGN: Produce a filtered and canonicalised section list Build a list of the sections in which we're interested and canonicalise the section indices to avoid the problems of the section table being altered by ld when the signature is linked into the binary and by strip. The only sections in which we're actually interested are those that are marked allocatable (which will be kept in memory) and relocation tables that are applicable to those sections. Canonicalisation is done by sorting the filtered list in order of section name. Signed-off-by: David Howells --- kernel/module-verify.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/kernel/module-verify.c b/kernel/module-verify.c index 5711aeb..13c60c2 100644 --- a/kernel/module-verify.c +++ b/kernel/module-verify.c @@ -268,6 +268,80 @@ symcheck_error: } /* + * Canonicalise the section table index numbers. + * + * We build a list of the sections we want to add to the digest and sort it by + * name. We're only interested in adding two types of section: + * + * (1) Allocatable sections. These should have no references to other + * sections. + * + * (2) Relocation tables for allocatable sections. The section table entry + * has a reference to the target section to which the relocations will be + * applied. The relocation entries have references to symbols in + * non-allocatable sections. Symbols can be replaced by their contents, + * but do include a further reference to a section - which must be + * canonicalised. + * + * We also build a map of raw section index to canonical section index. + */ +static int module_verify_canonicalise(struct module_verify_data *mvdata) +{ + const Elf_Shdr *sechdrs = mvdata->sections; + unsigned *canonlist, canon, loop, tmp; + bool changed; + + canonlist = kmalloc(sizeof(unsigned) * mvdata->nsects * 2, GFP_KERNEL); + if (!canonlist) + return -ENOMEM; + + mvdata->canonlist = canonlist; + mvdata->canonmap = canonlist + mvdata->nsects; + canon = 0; + + for (loop = 1; loop < mvdata->nsects; loop++) { + const Elf_Shdr *section = mvdata->sections + loop; + + if (loop == mvdata->sig_index) + continue; + + /* We only want allocatable sections and relocation tables */ + if (section->sh_flags & SHF_ALLOC) + canonlist[canon++] = loop; + else if ((is_elf_rel(section->sh_type) || + is_elf_rela(section->sh_type)) && + mvdata->sections[section->sh_info].sh_flags & SHF_ALLOC) + canonlist[canon++] = loop; + } + + /* Sort the canonicalisation list */ + do { + changed = false; + + for (loop = 0; loop < canon - 1; loop++) { + const char *x, *y; + + x = mvdata->secstrings + sechdrs[canonlist[loop + 0]].sh_name; + y = mvdata->secstrings + sechdrs[canonlist[loop + 1]].sh_name; + + if (strcmp(x, y) > 0) { + tmp = canonlist[loop + 0]; + canonlist[loop + 0] = canonlist[loop + 1]; + canonlist[loop + 1] = tmp; + changed = true; + } + } + } while (changed); + + /* What we really want is a raw-to-canon lookup table */ + memset(mvdata->canonmap, 0xff, mvdata->nsects * sizeof(unsigned)); + for (loop = 0; loop < canon; loop++) + mvdata->canonmap[mvdata->canonlist[loop]] = loop + 1; + mvdata->ncanon = canon; + return 0; +} + +/* * Verify a module's integrity */ int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok) @@ -298,7 +372,13 @@ int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok) goto out; } + /* Produce a canonicalisation map for the sections */ + ret = module_verify_canonicalise(&mvdata); + if (ret < 0) + goto out; + ret = 0; + kfree(mvdata.canonlist); out: switch (ret) { -- 1.7.10.2 From 92fb97859c50a0dd63886baf057477a7a336b2a1 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 10 May 2012 23:49:56 +0100 Subject: [PATCH 27/36] MODSIGN: Create digest of module content and check signature Apply signature checking to modules on module load, checking the signature against the ring of public keys compiled into the kernel (if enabled by CONFIG_MODULE_SIG). Turning on signature checking will also force the module's ELF metadata to be verified first. There are several reasons why these patches are useful, amongst which are: (1) to prevent accidentally corrupted modules from causing damage; (2) to prevent maliciously modified modules from causing damage; (3) to allow a sysadmin (or more likely an IT department) to enforce a policy that only known and approved modules shall be loaded onto machines which they're expected to support; (4) to allow other support providers to do likewise, or at least to _detect_ the fact that unsupported modules are loaded; (5) to allow the detection of modules replaced by a second-order distro or a preloaded Linux purveyor. These patches have two main appeals: (a) preventing malicious modules from being loaded, and (b) reducing support workload by pointing out modules on a crashing box that aren't what they're expected to be. Note that this is not a complete solution by any means: the core kernel is not protected, and nor are /dev/mem or /dev/kmem, but it denies (or at least controls) one relatively simple attack vector. To protect the kernel image would be the responsibility of the boot loader or the system BIOS. This facility is optional: the builder of a kernel is by no means under any requirement to actually enable it, let alone force the set of loadable modules to be restricted to just those that the builder provides (there are degrees of restriction available). Note! The "noinline" attribute on module_verify_signature() results in somewhat smaller code. Signed-off-by: David Howells --- kernel/module-verify.c | 321 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 320 insertions(+), 1 deletion(-) diff --git a/kernel/module-verify.c b/kernel/module-verify.c index 13c60c2..a31b39c 100644 --- a/kernel/module-verify.c +++ b/kernel/module-verify.c @@ -49,6 +49,22 @@ static bool modsign_signedonly; static const char modsign_note_name[] = ELFNOTE_NAME(MODSIGN_NOTE_NAME); static const char modsign_note_section[] = ELFNOTE_SECTION(MODSIGN_NOTE_NAME); +#define crypto_digest_update_data(C, PTR, N) \ +do { \ + uint8_t *__p = (uint8_t *)(PTR); \ + size_t __n = (N); \ + count_and_csum((C), __p, __n); \ + verify_sig_add_data((C)->mod_sig, __p, __n); \ +} while (0) + +#define crypto_digest_update_val(C, VAL) \ +do { \ + uint8_t *__p = (uint8_t *)&(VAL); \ + size_t __n = sizeof(VAL); \ + count_and_csum((C), __p, __n); \ + verify_sig_add_data((C)->mod_sig, __p, __n); \ +} while (0) + /* * Verify the minimum amount of ELF structure of a module needed to check the * module's signature without bad ELF crashing the kernel. @@ -342,6 +358,309 @@ static int module_verify_canonicalise(struct module_verify_data *mvdata) } /* + * Extract an ELF REL table + * + * We need to canonicalise the entries in case section/symbol addition/removal + * has rearranged the symbol table and the section table. + */ +static int extract_elf_rel(struct module_verify_data *mvdata, + unsigned secix, + const Elf_Rel *reltab, size_t nrels, + const char *sh_name) +{ + struct { +#if defined(MODULES_ARE_ELF32) + uint32_t r_offset; + uint32_t st_value; + uint32_t st_size; + uint16_t st_shndx; + uint8_t r_type; + uint8_t st_info; + uint8_t st_other; +#elif defined(MODULES_ARE_ELF64) + uint64_t r_offset; + uint64_t st_value; + uint64_t st_size; + uint32_t r_type; + uint16_t st_shndx; + uint8_t st_info; + uint8_t st_other; +#else +#error unsupported module type +#endif + } __attribute__((packed)) relocation; + + const Elf_Rel *reloc; + const Elf_Sym *symbols, *symbol; + const char *strings; + unsigned long r_sym; + size_t nsyms, loop; + + nsyms = mvdata->sections[secix].sh_size / sizeof(Elf_Sym); + symbols = mvdata->buffer + mvdata->sections[secix].sh_offset; + strings = mvdata->buffer + + mvdata->sections[mvdata->sections[secix].sh_link].sh_offset; + + /* Contribute the relevant bits from a join of { REL, SYMBOL, SECTION } */ + for (loop = 0; loop < nrels; loop++) { + unsigned st_shndx; + + reloc = &reltab[loop]; + + /* Decode the relocation */ + relocation.r_offset = reloc->r_offset; + relocation.r_type = ELF_R_TYPE(reloc->r_info); + + /* Decode the symbol referenced by the relocation */ + r_sym = ELF_R_SYM(reloc->r_info); + if (r_sym >= nsyms) + return -ELIBBAD; + symbol = &symbols[r_sym]; + relocation.st_info = symbol->st_info; + relocation.st_other = symbol->st_other; + relocation.st_value = symbol->st_value; + relocation.st_size = symbol->st_size; + relocation.st_shndx = symbol->st_shndx; + st_shndx = symbol->st_shndx; + + /* Canonicalise the section used by the symbol */ + if (st_shndx > SHN_UNDEF && st_shndx < mvdata->nsects) { + if (!(mvdata->sections[st_shndx].sh_flags & SHF_ALLOC)) + return -ELIBBAD; + relocation.st_shndx = mvdata->canonmap[st_shndx]; + } + + crypto_digest_update_val(mvdata, relocation); + + /* Undefined symbols must be named if referenced */ + if (st_shndx == SHN_UNDEF) { + const char *name = strings + symbol->st_name; + crypto_digest_update_data(mvdata, + name, strlen(name) + 1); + } + } + + _debug("%08zx %02x digested the %s section, nrels %zu\n", + mvdata->signed_size, mvdata->csum, sh_name, nrels); + + return 0; +} + +/* + * Extract an ELF RELA table + * + * We need to canonicalise the entries in case section/symbol addition/removal + * has rearranged the symbol table and the section table. + */ +static int extract_elf_rela(struct module_verify_data *mvdata, + unsigned secix, + const Elf_Rela *relatab, size_t nrels, + const char *sh_name) +{ + struct { +#if defined(MODULES_ARE_ELF32) + uint32_t r_offset; + uint32_t r_addend; + uint32_t st_value; + uint32_t st_size; + uint16_t st_shndx; + uint8_t r_type; + uint8_t st_info; + uint8_t st_other; +#elif defined(MODULES_ARE_ELF64) + uint64_t r_offset; + uint64_t r_addend; + uint64_t st_value; + uint64_t st_size; + uint32_t r_type; + uint16_t st_shndx; + uint8_t st_info; + uint8_t st_other; +#else +#error unsupported module type +#endif + } __attribute__((packed)) relocation; + + const Elf_Shdr *relsec, *symsec, *strsec; + const Elf_Rela *reloc; + const Elf_Sym *symbols, *symbol; + unsigned long r_sym; + const char *strings; + size_t nsyms, loop; + + relsec = &mvdata->sections[secix]; + symsec = &mvdata->sections[relsec->sh_link]; + strsec = &mvdata->sections[symsec->sh_link]; + nsyms = symsec->sh_size / sizeof(Elf_Sym); + symbols = mvdata->buffer + symsec->sh_offset; + strings = mvdata->buffer + strsec->sh_offset; + + /* Contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */ + for (loop = 0; loop < nrels; loop++) { + unsigned st_shndx; + + reloc = &relatab[loop]; + + /* Decode the relocation */ + relocation.r_offset = reloc->r_offset; + relocation.r_addend = reloc->r_addend; + relocation.r_type = ELF_R_TYPE(reloc->r_info); + + /* Decode the symbol referenced by the relocation */ + r_sym = ELF_R_SYM(reloc->r_info); + if (r_sym >= nsyms) + return -ELIBBAD; + symbol = &symbols[r_sym]; + relocation.st_info = symbol->st_info; + relocation.st_other = symbol->st_other; + relocation.st_value = symbol->st_value; + relocation.st_size = symbol->st_size; + relocation.st_shndx = 0; + st_shndx = symbol->st_shndx; + + /* Canonicalise the section used by the symbol */ + if (st_shndx > SHN_UNDEF && st_shndx < mvdata->nsects) { + if (!(mvdata->sections[st_shndx].sh_flags & SHF_ALLOC)) + return -ELIBBAD; + relocation.st_shndx = mvdata->canonmap[st_shndx]; + } + + crypto_digest_update_val(mvdata, relocation); + + /* Undefined symbols must be named if referenced */ + if (st_shndx == SHN_UNDEF) { + const char *name = strings + symbol->st_name; + crypto_digest_update_data(mvdata, + name, strlen(name) + 1); + } + } + + _debug("%08zx %02x digested the %s section, nrels %zu\n", + mvdata->signed_size, mvdata->csum, sh_name, nrels); + + return 0; +} + +/* + * Verify a module's signature + */ +static noinline int module_verify_signature(struct module_verify_data *mvdata) +{ + struct crypto_key_verify_context *mod_sig; + const Elf_Shdr *sechdrs = mvdata->sections; + const char *secstrings = mvdata->secstrings; + const u8 *sig = mvdata->sig; + size_t sig_size = mvdata->sig_size; + int loop, ret; + + _debug("sig in section %u (size %zu)\n", + mvdata->sig_index, mvdata->sig_size); + _debug("%02x%02x%02x%02x%02x%02x%02x%02x\n", + sig[0], sig[1], sig[2], sig[3], + sig[4], sig[5], sig[6], sig[7]); + + /* Find the crypto key for the module signature + * - !!! if this tries to load the required hash algorithm module, + * we will deadlock!!! + */ + mod_sig = verify_sig_begin(modsign_keyring, sig, sig_size); + if (IS_ERR(mod_sig)) { + pr_err("Couldn't initiate module signature verification: %ld\n", + PTR_ERR(mod_sig)); + return PTR_ERR(mod_sig); + } + + mvdata->mod_sig = mod_sig; +#ifdef DEBUG + mvdata->xcsum = 0; +#endif + + /* Load data from each relevant section into the digest. Note that + * canonlist[] is a filtered list and only contains the sections we + * actually want. + */ + for (loop = 0; loop < mvdata->ncanon; loop++) { + int sect = mvdata->canonlist[loop]; + unsigned long sh_type = sechdrs[sect].sh_type; + unsigned long sh_info = sechdrs[sect].sh_info; + unsigned long sh_size = sechdrs[sect].sh_size; + const char *sh_name = secstrings + sechdrs[sect].sh_name; + const void *data = mvdata->buffer + sechdrs[sect].sh_offset; + +#ifdef DEBUG + mvdata->csum = 0; +#endif + + /* Digest the headers of any section we include. */ + crypto_digest_update_data(mvdata, sh_name, strlen(sh_name)); + crypto_digest_update_val(mvdata, sechdrs[sect].sh_type); + crypto_digest_update_val(mvdata, sechdrs[sect].sh_flags); + crypto_digest_update_val(mvdata, sechdrs[sect].sh_size); + crypto_digest_update_val(mvdata, sechdrs[sect].sh_addralign); + + /* Relocation record sections refer to the section to be + * relocated, but this needs to be canonicalised to survive + * stripping. + */ + if (is_elf_rel(sh_type) || is_elf_rela(sh_type)) + crypto_digest_update_val(mvdata, + mvdata->canonmap[sh_info]); + + /* Since relocation records give details of how we have to + * alter the allocatable sections, we need to digest these too. + * + * These, however, refer to metadata (symbols and sections) + * that may have been altered by the process of adding the + * signature section or the process of being stripped. + * + * To deal with this, we substitute the referenced metadata for + * the references to that metadata. So, for instance, the + * symbol ref from the relocation record is replaced with the + * contents of the symbol to which it refers, and the symbol's + * section ref is replaced with a canonicalised section number. + */ + if (is_elf_rel(sh_type)) { + ret = extract_elf_rel(mvdata, sect, + data, + sh_size / sizeof(Elf_Rel), + sh_name); + if (ret < 0) + goto format_error; + continue; + } + + if (is_elf_rela(sh_type)) { + ret = extract_elf_rela(mvdata, sect, + data, + sh_size / sizeof(Elf_Rela), + sh_name); + if (ret < 0) + goto format_error; + continue; + } + + /* Include allocatable loadable sections */ + if (sh_type != SHT_NOBITS) + crypto_digest_update_data(mvdata, data, sh_size); + + _debug("%08zx %02x digested the %s section, size %ld\n", + mvdata->signed_size, mvdata->csum, sh_name, sh_size); + } + + _debug("Contributed %zu bytes to the digest (csum 0x%02x)\n", + mvdata->signed_size, mvdata->xcsum); + + /* Do the actual signature verification */ + ret = verify_sig_end(mvdata->mod_sig, sig, sig_size); + _debug("verify-sig : %d\n", ret); + return ret; + +format_error: + verify_sig_cancel(mvdata->mod_sig); + return -ELIBBAD; +} + +/* * Verify a module's integrity */ int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok) @@ -377,7 +696,7 @@ int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok) if (ret < 0) goto out; - ret = 0; + ret = module_verify_signature(&mvdata); kfree(mvdata.canonlist); out: -- 1.7.10.2 From 2e9f557c1235027c0c7223a8a072333758905066 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 10 May 2012 23:49:57 +0100 Subject: [PATCH 28/36] MODSIGN: Automatically generate module signing keys if missing Automatically generate keys for module signing if they're absent so that allyesconfig doesn't break. The builder should consider generating their own keyrings, however, so that the keys are appropriately named and any extra keys required get imported. Also change the names of the keyring files to modsign.pub and modsign.sec so that they are then a more obvious what they're about and add a dependency for the signing rules on the keyring files so that the signatures get regenerated if the keyrings change. Signed-off-by: David Howells --- kernel/Makefile | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/kernel/Makefile b/kernel/Makefile index 986ed7f..d8139bb 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -52,7 +52,6 @@ obj-$(CONFIG_PROVE_LOCKING) += spinlock.o obj-$(CONFIG_UID16) += uid16.o obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_MODULE_SIG) += module-verify.o modsign-pubkey.o -kernel/modsign-pubkey.o: modsign.pub obj-$(CONFIG_KALLSYMS) += kallsyms.o obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o obj-$(CONFIG_KEXEC) += kexec.o @@ -129,3 +128,44 @@ quiet_cmd_timeconst = TIMEC $@ targets += timeconst.h $(obj)/timeconst.h: $(src)/timeconst.pl FORCE $(call if_changed,timeconst) + +############################################################################### +# +# If module signing is requested, say by allyesconfig, but a key has not been +# supplied, then one will need to be generated to make sure the build does not +# fail and that the kernel may be used afterwards. +# +############################################################################### +ifeq ($(CONFIG_MODULE_SIG),y) +kernel/modsign-pubkey.o: modsign.pub + +modsign.pub modsign.sec: genkey + @echo "###" + @echo "### Now generating a PGP key pair to be used for signing modules." + @echo "###" + @echo "### If this takes a long time, you might wish to run rngd in the" + @echo "### background to keep the supply of entropy topped up. It" + @echo "### needs to be run as root, and should use a hardware random" + @echo "### number generator if one is available, eg:" + @echo "###" + @echo "### rngd -r /dev/hwrandom" + @echo "###" + gpg --homedir . --batch --gen-key genkey + @echo "###" + @echo "### Key pair generated." + @echo "###" + rm -f pubring.gpg secring.gpg trustdb.gpg + +genkey: + echo "%pubring modsign.pub" >genkey + echo "%secring modsign.sec" >>genkey + echo "%no-protection: yes" >> genkey + echo "%transient-key: yes" >>genkey + echo "Key-Type: RSA" >>genkey + echo "Key-Length: 4096" >>genkey + echo "Name-Real: Sample kernel key" >>genkey + echo "Name-Comment: Sample kernel module signing key" >>genkey + echo "%commit" >>genkey + +endif +CLEAN_FILES += modsign.pub modsign.sec genkey random_seed -- 1.7.10.2 From 958049a9def253735019a5acf19b4c2aeec9f01c Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 10 May 2012 23:55:35 +0100 Subject: [PATCH 29/36] MODSIGN: Suppress some redundant ELF checks Suppress some redundant ELF checks in module_verify_elf() that are also done by copy_and_check() in the core module loader code prior to calling module_verify(). Signed-off-by: David Howells --- kernel/module-verify.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/module-verify.c b/kernel/module-verify.c index a31b39c..e1bfd28 100644 --- a/kernel/module-verify.c +++ b/kernel/module-verify.c @@ -76,7 +76,7 @@ static noinline int module_verify_elf(struct module_verify_data *mvdata) const Elf_Shdr *section, *secstop; const Elf_Sym *symbols, *symbol, *symstop; const char *strtab; - size_t size, secsize, secstrsize, strsize, notesize, notemetasize; + size_t size, secstrsize, strsize, notesize, notemetasize; unsigned line; size = mvdata->size; @@ -96,11 +96,11 @@ do { if (unlikely(!(X))) { line = __LINE__; goto symcheck_error; } } while(0) elfcheck(hdr->e_shnum < SHN_LORESERVE); elfcheck(hdr->e_shstrndx < hdr->e_shnum); - elfcheck(hdr->e_shentsize == sizeof(Elf_Shdr)); - elfcheck(hdr->e_shoff < size); + /* elfcheck(hdr->e_shentsize == sizeof(Elf_Shdr)); */ + /* elfcheck(hdr->e_shoff < size); */ elfcheck(hdr->e_shoff >= hdr->e_ehsize); elfcheck(hdr->e_shoff % sizeof(long) == 0); - elfcheck(hdr->e_shnum * sizeof(Elf_Shdr) <= size - hdr->e_shoff); + /* elfcheck(hdr->e_shnum * sizeof(Elf_Shdr) <= size - hdr->e_shoff); */ /* Validate the section table contents */ mvdata->nsects = hdr->e_shnum; -- 1.7.10.2 From b5df4e7900852395a1ccb70190827cccc0c0de2d Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 11 May 2012 16:56:05 +0100 Subject: [PATCH 30/36] MODSIGN: Fix some checkpatch noise Fix some warnings and errors produced by checkpatch. Reported-by: Tetsuo Handa Signed-off-by: David Howells --- kernel/module-verify-defs.h | 24 ++++++++++++------------ kernel/module-verify.c | 18 +++++++++++------- kernel/module-verify.h | 3 ++- security/keys/crypto/pgp_key_parser.c | 2 +- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/kernel/module-verify-defs.h b/kernel/module-verify-defs.h index 45bea45..2fe31e1 100644 --- a/kernel/module-verify-defs.h +++ b/kernel/module-verify-defs.h @@ -60,22 +60,22 @@ struct module_verify_data { do { \ if (unlikely(modsign_debug)) \ pr_debug(FMT, ##__VA_ARGS__); \ - } while(0) + } while (0) #ifdef DEBUG -#define count_and_csum(C, __p, __n) \ -do { \ - int __loop; \ - for (__loop = 0; __loop < __n; __loop++) { \ - (C)->csum += __p[__loop]; \ - (C)->xcsum += __p[__loop]; \ - } \ - (C)->signed_size += __n; \ -} while (0) +#define count_and_csum(C, __p, __n) \ + do { \ + int __loop; \ + for (__loop = 0; __loop < __n; __loop++) { \ + (C)->csum += __p[__loop]; \ + (C)->xcsum += __p[__loop]; \ + } \ + (C)->signed_size += __n; \ + } while (0) #else #define count_and_csum(C, __p, __n) \ -do { \ -} while (0) + do { \ + } while (0) #endif #endif /* CONFIG_MODULE_SIG */ diff --git a/kernel/module-verify.c b/kernel/module-verify.c index e1bfd28..161cf3e 100644 --- a/kernel/module-verify.c +++ b/kernel/module-verify.c @@ -82,13 +82,13 @@ static noinline int module_verify_elf(struct module_verify_data *mvdata) size = mvdata->size; #define elfcheck(X) \ -do { if (unlikely(!(X))) { line = __LINE__; goto elfcheck_error; } } while(0) +do { if (unlikely(!(X))) { line = __LINE__; goto elfcheck_error; } } while (0) #define seccheck(X) \ -do { if (unlikely(!(X))) { line = __LINE__; goto seccheck_error; } } while(0) +do { if (unlikely(!(X))) { line = __LINE__; goto seccheck_error; } } while (0) #define symcheck(X) \ -do { if (unlikely(!(X))) { line = __LINE__; goto symcheck_error; } } while(0) +do { if (unlikely(!(X))) { line = __LINE__; goto symcheck_error; } } while (0) /* Validate the ELF header */ elfcheck(size > sizeof(Elf_Ehdr)); @@ -388,7 +388,7 @@ static int extract_elf_rel(struct module_verify_data *mvdata, #else #error unsupported module type #endif - } __attribute__((packed)) relocation; + } __packed relocation; const Elf_Rel *reloc; const Elf_Sym *symbols, *symbol; @@ -401,7 +401,9 @@ static int extract_elf_rel(struct module_verify_data *mvdata, strings = mvdata->buffer + mvdata->sections[mvdata->sections[secix].sh_link].sh_offset; - /* Contribute the relevant bits from a join of { REL, SYMBOL, SECTION } */ + /* Contribute the relevant bits from a join of + * { REL, SYMBOL, SECTION } + */ for (loop = 0; loop < nrels; loop++) { unsigned st_shndx; @@ -479,7 +481,7 @@ static int extract_elf_rela(struct module_verify_data *mvdata, #else #error unsupported module type #endif - } __attribute__((packed)) relocation; + } __packed relocation; const Elf_Shdr *relsec, *symsec, *strsec; const Elf_Rela *reloc; @@ -495,7 +497,9 @@ static int extract_elf_rela(struct module_verify_data *mvdata, symbols = mvdata->buffer + symsec->sh_offset; strings = mvdata->buffer + strsec->sh_offset; - /* Contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */ + /* Contribute the relevant bits from a join of + * { RELA, SYMBOL, SECTION } + */ for (loop = 0; loop < nrels; loop++) { unsigned st_shndx; diff --git a/kernel/module-verify.h b/kernel/module-verify.h index 6bb6b56..c640634 100644 --- a/kernel/module-verify.h +++ b/kernel/module-verify.h @@ -12,7 +12,8 @@ #ifdef CONFIG_MODULE_SIG extern int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok); #else -static inline int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok) +static inline int module_verify(const Elf_Ehdr *hdr, size_t size, + bool *_gpgsig_ok) { return 0; } diff --git a/security/keys/crypto/pgp_key_parser.c b/security/keys/crypto/pgp_key_parser.c index 1407e2e..d913538 100644 --- a/security/keys/crypto/pgp_key_parser.c +++ b/security/keys/crypto/pgp_key_parser.c @@ -94,7 +94,7 @@ static int pgp_calc_pkey_keyid(struct shash_desc *digest, if (pgp->version < PGP_KEY_VERSION_4) { u16 a16; - if( pgp->expires_at) + if (pgp->expires_at) a16 = (pgp->expires_at - pgp->creation_time) / 86400UL; else a16 = 0; -- 1.7.10.2 From 27ecab7d7ee104299133c9ffd51d00ea378ed56b Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 16 May 2012 15:13:41 +0100 Subject: [PATCH 31/36] PGPLIB: Preclear array on stack Preclear an array on the stack so that the error handling that frees what the array might point to won't crash. Reported-by: Tetsuo Handa Signed-off-by: David Howells --- security/keys/crypto/pgp_key_parser.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/security/keys/crypto/pgp_key_parser.c b/security/keys/crypto/pgp_key_parser.c index d913538..84ce457 100644 --- a/security/keys/crypto/pgp_key_parser.c +++ b/security/keys/crypto/pgp_key_parser.c @@ -71,6 +71,9 @@ static int pgp_calc_pkey_keyid(struct shash_desc *digest, kenter(""); + for (i = 0; i < ARRAY_SIZE(pp); i++) + pp[i] = NULL; + n = (pgp->version < PGP_KEY_VERSION_4) ? 8 : 6; for (i = 0; i < npkey; i++) { nb[i] = mpi_get_nbits(key->mpi[i]); -- 1.7.10.2 From a382a46fe70ee35cfb6fe97faa8abffd82368cbe Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 16 May 2012 15:19:24 +0100 Subject: [PATCH 32/36] PGPLIB: Check the length in a packet or subpacket Check the length in a packet or subpacket to make sure there isn't an overflow should the length not fit into the lower 31 bits of an integer. It is possible that both pgp_parse_packet_header() and pgp_parse_sig_subpkt_header() could see packets that purport to be >2G in size. Normally this will not be a problem because EBADMSG is indicated if the size indicated is greater than the remnant size of the data - but just in case we do end up parsing a >2G blob, a couple of simple checks can prevent an overflow from occurring. Reported-by: Stephan Mueller Signed-off-by: David Howells --- security/keys/crypto/pgp_library.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/security/keys/crypto/pgp_library.c b/security/keys/crypto/pgp_library.c index f6b831f..a9462f5 100644 --- a/security/keys/crypto/pgp_library.c +++ b/security/keys/crypto/pgp_library.c @@ -137,6 +137,8 @@ ssize_t pgp_parse_packet_header(const u8 **_data, size_t *_datalen, pr_devel("datalen=%zu size=%zu", datalen, size); if (datalen < size) goto short_packet; + if ((int)size < 0) + goto too_big; *_data = data; *_datalen = datalen; @@ -147,6 +149,9 @@ ssize_t pgp_parse_packet_header(const u8 **_data, size_t *_datalen, short_packet: pr_debug("Attempt to parse short packet\n"); return -EBADMSG; +too_big: + pr_debug("Signature subpacket size >2G\n"); + return -EMSGSIZE; } /** @@ -312,6 +317,8 @@ ssize_t pgp_parse_sig_subpkt_header(const u8 **_data, size_t *_datalen, pr_debug("Signature subpacket size can't be zero\n"); return -EBADMSG; } + if ((int)size < 0) + goto too_big; type = *data++ & ~PGP_SIG_SUBPKT_TYPE_CRITICAL_MASK; datalen--; @@ -330,6 +337,9 @@ ssize_t pgp_parse_sig_subpkt_header(const u8 **_data, size_t *_datalen, short_subpacket: pr_debug("Attempt to parse short signature subpacket\n"); return -EBADMSG; +too_big: + pr_debug("Signature subpacket size >2G\n"); + return -EMSGSIZE; } /** -- 1.7.10.2 From a577fc904c197d97b028863989d9a891c3e1ea17 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 17 May 2012 17:41:36 +0100 Subject: [PATCH 33/36] PGPLIB: Remnant length should be decreased in pgp_parse_sig_params() The remnant length of the signature packet should be decreased rather than being increased as we parse in pgp_parse_sig_params(). Signed-off-by: David Howells --- security/keys/crypto/pgp_library.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/keys/crypto/pgp_library.c b/security/keys/crypto/pgp_library.c index a9462f5..1ff3628 100644 --- a/security/keys/crypto/pgp_library.c +++ b/security/keys/crypto/pgp_library.c @@ -503,7 +503,7 @@ int pgp_parse_sig_params(const u8 **_data, size_t *_datalen, if (ret < 0) return ret; data += subdatalen; - datalen += subdatalen; + datalen -= subdatalen; } subdatalen = *data++ << 8; @@ -521,7 +521,7 @@ int pgp_parse_sig_params(const u8 **_data, size_t *_datalen, if (ret < 0) return ret; data += subdatalen; - datalen += subdatalen; + datalen -= subdatalen; } if (!ctx.got_the_issuer) { -- 1.7.10.2 From ab7204f60a1cedecb24bb2888db5d03bdcf20488 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 17 May 2012 17:41:36 +0100 Subject: [PATCH 34/36] PGPLIB: Parse 5-octet length new-format packet headers Parse 5-octet length new-format packet headers to extract the 32-bit length encoded therein [RFC4880 4.2.2.3]. Signed-off-by: David Howells --- security/keys/crypto/pgp_library.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/security/keys/crypto/pgp_library.c b/security/keys/crypto/pgp_library.c index 1ff3628..310ee2f 100644 --- a/security/keys/crypto/pgp_library.c +++ b/security/keys/crypto/pgp_library.c @@ -86,8 +86,17 @@ ssize_t pgp_parse_packet_header(const u8 **_data, size_t *_datalen, *_headerlen = 3; break; case 0xff: - pr_debug("Five-byte packet length not supported\n"); - return -EBADMSG; + /* Five-byte length */ + if (datalen < 5) + goto short_packet; + size = data[1] << 24; + size |= data[2] << 16; + size |= data[3] << 8; + size |= data[4]; + data += 5; + datalen -= 5; + *_headerlen = 6; + break; default: pr_debug("Error parsing packet length\n"); return -EBADMSG; -- 1.7.10.2 From df233b65b833e085f39d80dc6f77c383b32786ce Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 17 May 2012 17:41:36 +0100 Subject: [PATCH 35/36] PGPLIB: Change the debug message for Partial Body Length specifier Change the debug message displayed if we encounter a Partial Body Length specifier whilst parsing a PGP stream [RFC4880 4.2.2.4]. Signed-off-by: David Howells --- security/keys/crypto/pgp_library.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/keys/crypto/pgp_library.c b/security/keys/crypto/pgp_library.c index 310ee2f..111cbd7 100644 --- a/security/keys/crypto/pgp_library.c +++ b/security/keys/crypto/pgp_library.c @@ -98,7 +98,7 @@ ssize_t pgp_parse_packet_header(const u8 **_data, size_t *_datalen, *_headerlen = 6; break; default: - pr_debug("Error parsing packet length\n"); + pr_debug("Partial body length packet not supported\n"); return -EBADMSG; } } else { -- 1.7.10.2 From 3ac676c62cceabdbf814bbc3495f36abd1848a97 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 18 May 2012 16:44:14 +0100 Subject: [PATCH 36/36] PGPLIB: Adjust error handling Adjust the error handling in the following ways: (1) When parsing signature subpacket header, do the size checks before accessing the subpacket type (which is in the subpacket payload governed by the size). (2) Indicate ENOPKG when we are asked to use a public key algorithm we don't support rather than returning ENOKEY. (3) Indicate EKEYREJECTED if the key that matches the signature demands a different key algorithm to the signature. (4) Indicate ENOMSG if the signature blob does not contain a signature packet. Possibly this should be EBADMSG - though that causes the next packet parser to be tried if available. (5) Give a better debug message in the case of an unsupported hash. (6) Don't return keyring-related errors when searching for a key containing the public key (EACCES, ENOTDIR, EAGAIN), but rather map them to ENOKEY. Possibly EACCES should be passed through as you also get that if there *is* a matching key, but it cannot be accessed. Signed-off-by: David Howells --- Documentation/module-signing.txt | 2 ++ security/keys/crypto/pgp_library.c | 16 ++++++++-------- security/keys/crypto/pgp_pubkey_sig.c | 9 +++++---- security/keys/crypto/pgp_sig_parser.c | 15 ++++++++++++--- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/Documentation/module-signing.txt b/Documentation/module-signing.txt index d75d473..d3beb1e 100644 --- a/Documentation/module-signing.txt +++ b/Documentation/module-signing.txt @@ -185,10 +185,12 @@ This table indicates the behaviours of the various situations: MODULE STATE PERMISSIVE MODE ENFORCING MODE ======================================= =============== =============== Unsigned Ok EKEYREJECTED + No signature packet in the signature ENOMSG ENOMSG Signed, no public key ENOKEY ENOKEY Validly signed, public key Ok Ok Invalidly signed, public key EKEYREJECTED EKEYREJECTED Validly signed, expired key EKEYEXPIRED EKEYEXPIRED + Signed, pubkey algorithm unavailable ENOPKG ENOPKG Signed, hash algorithm unavailable ENOPKG ENOPKG Corrupt signature EBADMSG EBADMSG Corrupt ELF ELIBBAD ELIBBAD diff --git a/security/keys/crypto/pgp_library.c b/security/keys/crypto/pgp_library.c index 111cbd7..ee08b86 100644 --- a/security/keys/crypto/pgp_library.c +++ b/security/keys/crypto/pgp_library.c @@ -322,10 +322,11 @@ ssize_t pgp_parse_sig_subpkt_header(const u8 **_data, size_t *_datalen, } /* The type octet is included in the size */ - if (size == 0) { - pr_debug("Signature subpacket size can't be zero\n"); - return -EBADMSG; - } + pr_devel("datalen=%zu size=%zu", datalen, size); + if (datalen < size) + goto short_subpacket; + if (size == 0) + goto very_short_subpacket; if ((int)size < 0) goto too_big; @@ -333,16 +334,15 @@ ssize_t pgp_parse_sig_subpkt_header(const u8 **_data, size_t *_datalen, datalen--; size--; - pr_devel("datalen=%zu size=%zu", datalen, size); - if (datalen < size) - goto short_subpacket; - *_data = data; *_datalen = datalen; *_type = type; pr_devel("Found subpkt type=%u size=%zd\n", type, size); return size; +very_short_subpacket: + pr_debug("Signature subpacket size can't be zero\n"); + return -EBADMSG; short_subpacket: pr_debug("Attempt to parse short signature subpacket\n"); return -EBADMSG; diff --git a/security/keys/crypto/pgp_pubkey_sig.c b/security/keys/crypto/pgp_pubkey_sig.c index b4b7cb0..bc02dfa 100644 --- a/security/keys/crypto/pgp_pubkey_sig.c +++ b/security/keys/crypto/pgp_pubkey_sig.c @@ -86,12 +86,12 @@ struct crypto_key_verify_context *pgp_pkey_verify_sig_begin( !pgp_public_key_algorithms[p.params.pubkey_algo]) { pr_debug("Unsupported public key algorithm %u\n", p.params.pubkey_algo); - return ERR_PTR(-ENOKEY); + return ERR_PTR(-ENOPKG); } if (pgp_public_key_algorithms[p.params.pubkey_algo] != key->algo) { - kleave(" = -ENOKEY [wrong pk algo]"); - return ERR_PTR(-ENOKEY); + kleave(" = -EKEYREJECTED [wrong pk algo]"); + return ERR_PTR(-EKEYREJECTED); } if (!(key->capabilities & PKEY_CAN_VERIFY)) { @@ -101,7 +101,8 @@ struct crypto_key_verify_context *pgp_pkey_verify_sig_begin( if (p.params.hash_algo >= PGP_HASH__LAST || !pgp_hash_algorithms[p.params.hash_algo]) { - kleave(" = -ENOPKG [hash]"); + pr_debug("Unsupported hash algorithm %u\n", + p.params.hash_algo); return ERR_PTR(-ENOPKG); } diff --git a/security/keys/crypto/pgp_sig_parser.c b/security/keys/crypto/pgp_sig_parser.c index b72c505..3dd223f 100644 --- a/security/keys/crypto/pgp_sig_parser.c +++ b/security/keys/crypto/pgp_sig_parser.c @@ -66,7 +66,7 @@ static struct key *find_key_for_pgp_sig(struct key *keyring, return ERR_PTR(ret); if (!p.found_sig) - return ERR_PTR(-EINVAL); + return ERR_PTR(-ENOMSG); sprintf(criterion, "id:%08x%08x", be32_to_cpu(p.params.issuer32[0]), @@ -76,8 +76,17 @@ static struct key *find_key_for_pgp_sig(struct key *keyring, key = keyring_search(make_key_ref(keyring, 1), &key_type_crypto, criterion); - if (IS_ERR(key)) - return ERR_CAST(key); + 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_debug("Found key %x\n", key_serial(key_ref_to_ptr(key))); return key_ref_to_ptr(key); -- 1.7.10.2