| From bcb9a5e7e8108872ec9fd7083209cbf3a47ef952 Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 00:14:14 +0100 |
| Subject: [PATCH 01/32] KEYS: Add payload preparsing opportunity prior to key |
| instantiate or update |
| |
| Give the key type the opportunity to preparse the payload prior to the |
| instantiation and update routines being called. This is done with the |
| provision of two new key type operations: |
| |
| int (*preparse)(struct key_preparsed_payload *prep); |
| void (*free_preparse)(struct key_preparsed_payload *prep); |
| |
| If the first operation is present, then it is called before key creation (in |
| the add/update case) or before the key semaphore is taken (in the update and |
| instantiate cases). The second operation is called to clean up if the first |
| was called. |
| |
| preparse() is given the opportunity to fill in the following structure: |
| |
| struct key_preparsed_payload { |
| char *description; |
| void *type_data[2]; |
| void *payload; |
| const void *data; |
| size_t datalen; |
| size_t quotalen; |
| }; |
| |
| Before the preparser is called, the first three fields will have been cleared, |
| the payload pointer and size will be stored in data and datalen and the default |
| quota size from the key_type struct will be stored into quotalen. |
| |
| The preparser may parse the payload in any way it likes and may store data in |
| the type_data[] and payload fields for use by the instantiate() and update() |
| ops. |
| |
| The preparser may also propose a description for the key by attaching it as a |
| string to the description field. This can be used by passing a NULL or "" |
| description to the add_key() system call or the key_create_or_update() |
| function. This cannot work with request_key() as that required the description |
| to tell the upcall about the key to be created. |
| |
| This, for example permits keys that store PGP public keys to generate their own |
| name from the user ID and public key fingerprint in the key. |
| |
| The instantiate() and update() operations are then modified to look like this: |
| |
| int (*instantiate)(struct key *key, struct key_preparsed_payload *prep); |
| int (*update)(struct key *key, struct key_preparsed_payload *prep); |
| |
| and the new payload data is passed in *prep, whether or not it was preparsed. |
| |
| Signed-off-by: David Howells <dhowells@redhat.com> |
| |
| Documentation/security/keys.txt | 50 +++++++++++++- |
| fs/cifs/cifs_spnego.c | 6 +- |
| fs/cifs/cifsacl.c | 8 +-- |
| include/keys/user-type.h | 6 +- |
| include/linux/key-type.h | 35 +++++++++- |
| net/ceph/crypto.c | 9 +-- |
| net/dns_resolver/dns_key.c | 6 +- |
| net/rxrpc/ar-key.c | 40 ++++++------ |
| security/keys/encrypted-keys/encrypted.c | 16 +++-- |
| security/keys/key.c | 108 ++++++++++++++++++++++--------- |
| security/keys/keyctl.c | 18 ++++-- |
| security/keys/keyring.c | 6 +- |
| security/keys/request_key_auth.c | 8 +-- |
| security/keys/trusted.c | 16 +++-- |
| security/keys/user_defined.c | 14 ++-- |
| 15 files changed, 245 insertions(+), 101 deletions(-) |
| |
| diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt |
| index aa0dbd7..7d9ca92 100644 |
| |
| |
| @@ -412,6 +412,10 @@ The main syscalls are: |
| to the keyring. In this case, an error will be generated if the process |
| does not have permission to write to the keyring. |
| |
| + If the key type supports it, if the description is NULL or an empty |
| + string, the key type will try and generate a description from the content |
| + of the payload. |
| + |
| The payload is optional, and the pointer can be NULL if not required by |
| the type. The payload is plen in size, and plen can be zero for an empty |
| payload. |
| @@ -1114,12 +1118,53 @@ The structure has a number of fields, some of which are mandatory: |
| it should return 0. |
| |
| |
| - (*) int (*instantiate)(struct key *key, const void *data, size_t datalen); |
| + (*) int (*preparse)(struct key_preparsed_payload *prep); |
| + |
| + This optional method permits the key type to attempt to parse payload |
| + before a key is created (add key) or the key semaphore is taken (update or |
| + instantiate key). The structure pointed to by prep looks like: |
| + |
| + struct key_preparsed_payload { |
| + char *description; |
| + void *type_data[2]; |
| + void *payload; |
| + const void *data; |
| + size_t datalen; |
| + size_t quotalen; |
| + }; |
| + |
| + Before calling the method, the caller will fill in data and datalen with |
| + the payload blob parameters; quotalen will be filled in with the default |
| + quota size from the key type and the rest will be cleared. |
| + |
| + If a description can be proposed from the payload contents, that should be |
| + attached as a string to the description field. This will be used for the |
| + key description if the caller of add_key() passes NULL or "". |
| + |
| + The method can attach anything it likes to type_data[] and payload. These |
| + are merely passed along to the instantiate() or update() operations. |
| + |
| + The method should return 0 if success ful or a negative error code |
| + otherwise. |
| + |
| + |
| + (*) void (*free_preparse)(struct key_preparsed_payload *prep); |
| + |
| + This method is only required if the preparse() method is provided, |
| + otherwise it is unused. It cleans up anything attached to the |
| + description, type_data and payload fields of the key_preparsed_payload |
| + struct as filled in by the preparse() method. |
| + |
| + |
| + (*) int (*instantiate)(struct key *key, struct key_preparsed_payload *prep); |
| |
| This method is called to attach a payload to a key during construction. |
| The payload attached need not bear any relation to the data passed to this |
| function. |
| |
| + The prep->data and prep->datalen fields will define the original payload |
| + blob. If preparse() was supplied then other fields may be filled in also. |
| + |
| If the amount of data attached to the key differs from the size in |
| keytype->def_datalen, then key_payload_reserve() should be called. |
| |
| @@ -1135,6 +1180,9 @@ The structure has a number of fields, some of which are mandatory: |
| If this type of key can be updated, then this method should be provided. |
| It is called to update a key's payload from the blob of data provided. |
| |
| + The prep->data and prep->datalen fields will define the original payload |
| + blob. If preparse() was supplied then other fields may be filled in also. |
| + |
| key_payload_reserve() should be called if the data length might change |
| before any changes are actually made. Note that if this succeeds, the type |
| is committed to changing the key because it's already been altered, so all |
| diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c |
| index e622863..086f381 100644 |
| |
| |
| @@ -31,18 +31,18 @@ |
| |
| /* create a new cifs key */ |
| static int |
| -cifs_spnego_key_instantiate(struct key *key, const void *data, size_t datalen) |
| +cifs_spnego_key_instantiate(struct key *key, struct key_preparsed_payload *prep) |
| { |
| char *payload; |
| int ret; |
| |
| ret = -ENOMEM; |
| - payload = kmalloc(datalen, GFP_KERNEL); |
| + payload = kmalloc(prep->datalen, GFP_KERNEL); |
| if (!payload) |
| goto error; |
| |
| /* attach the data */ |
| - memcpy(payload, data, datalen); |
| + memcpy(payload, prep->data, prep->datalen); |
| key->payload.data = payload; |
| ret = 0; |
| |
| diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c |
| index 05f4dc2..f3c60e2 100644 |
| |
| |
| @@ -167,17 +167,17 @@ static struct shrinker cifs_shrinker = { |
| }; |
| |
| static int |
| -cifs_idmap_key_instantiate(struct key *key, const void *data, size_t datalen) |
| +cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep) |
| { |
| char *payload; |
| |
| - payload = kmalloc(datalen, GFP_KERNEL); |
| + payload = kmalloc(prep->datalen, GFP_KERNEL); |
| if (!payload) |
| return -ENOMEM; |
| |
| - memcpy(payload, data, datalen); |
| + memcpy(payload, prep->data, prep->datalen); |
| key->payload.data = payload; |
| - key->datalen = datalen; |
| + key->datalen = prep->datalen; |
| return 0; |
| } |
| |
| diff --git a/include/keys/user-type.h b/include/keys/user-type.h |
| index bc9ec1d..5e452c8 100644 |
| |
| |
| @@ -35,8 +35,10 @@ struct user_key_payload { |
| extern struct key_type key_type_user; |
| extern struct key_type key_type_logon; |
| |
| -extern int user_instantiate(struct key *key, const void *data, size_t datalen); |
| -extern int user_update(struct key *key, const void *data, size_t datalen); |
| +struct key_preparsed_payload; |
| + |
| +extern int user_instantiate(struct key *key, struct key_preparsed_payload *prep); |
| +extern int user_update(struct key *key, struct key_preparsed_payload *prep); |
| extern int user_match(const struct key *key, const void *criterion); |
| extern void user_revoke(struct key *key); |
| extern void user_destroy(struct key *key); |
| diff --git a/include/linux/key-type.h b/include/linux/key-type.h |
| index f0c651c..518a53a 100644 |
| |
| |
| @@ -26,6 +26,27 @@ struct key_construction { |
| struct key *authkey;/* authorisation for key being constructed */ |
| }; |
| |
| +/* |
| + * Pre-parsed payload, used by key add, update and instantiate. |
| + * |
| + * This struct will be cleared and data and datalen will be set with the data |
| + * and length parameters from the caller and quotalen will be set from |
| + * def_datalen from the key type. Then if the preparse() op is provided by the |
| + * key type, that will be called. Then the struct will be passed to the |
| + * instantiate() or the update() op. |
| + * |
| + * If the preparse() op is given, the free_preparse() op will be called to |
| + * clear the contents. |
| + */ |
| +struct key_preparsed_payload { |
| + char *description; /* Proposed key description (or NULL) */ |
| + void *type_data[2]; /* Private key-type data */ |
| + void *payload; /* Proposed payload */ |
| + const void *data; /* Raw data */ |
| + size_t datalen; /* Raw datalen */ |
| + size_t quotalen; /* Quota length for proposed payload */ |
| +}; |
| + |
| typedef int (*request_key_actor_t)(struct key_construction *key, |
| const char *op, void *aux); |
| |
| @@ -45,18 +66,28 @@ struct key_type { |
| /* vet a description */ |
| int (*vet_description)(const char *description); |
| |
| + /* Preparse the data blob from userspace that is to be the payload, |
| + * generating a proposed description and payload that will be handed to |
| + * the instantiate() and update() ops. |
| + */ |
| + int (*preparse)(struct key_preparsed_payload *prep); |
| + |
| + /* Free a preparse data structure. |
| + */ |
| + void (*free_preparse)(struct key_preparsed_payload *prep); |
| + |
| /* instantiate a key of this type |
| * - this method should call key_payload_reserve() to determine if the |
| * user's quota will hold the payload |
| */ |
| - int (*instantiate)(struct key *key, const void *data, size_t datalen); |
| + int (*instantiate)(struct key *key, struct key_preparsed_payload *prep); |
| |
| /* update a key of this type (optional) |
| * - this method should call key_payload_reserve() to recalculate the |
| * quota consumption |
| * - the key must be locked against read when modifying |
| */ |
| - int (*update)(struct key *key, const void *data, size_t datalen); |
| + int (*update)(struct key *key, struct key_preparsed_payload *prep); |
| |
| /* match a key against a description */ |
| int (*match)(const struct key *key, const void *desc); |
| diff --git a/net/ceph/crypto.c b/net/ceph/crypto.c |
| index 9da7fdd..af14cb4 100644 |
| |
| |
| @@ -423,14 +423,15 @@ int ceph_encrypt2(struct ceph_crypto_key *secret, void *dst, size_t *dst_len, |
| } |
| } |
| |
| -int ceph_key_instantiate(struct key *key, const void *data, size_t datalen) |
| +int ceph_key_instantiate(struct key *key, struct key_preparsed_payload *prep) |
| { |
| struct ceph_crypto_key *ckey; |
| + size_t datalen = prep->datalen; |
| int ret; |
| void *p; |
| |
| ret = -EINVAL; |
| - if (datalen <= 0 || datalen > 32767 || !data) |
| + if (datalen <= 0 || datalen > 32767 || !prep->data) |
| goto err; |
| |
| ret = key_payload_reserve(key, datalen); |
| @@ -443,8 +444,8 @@ int ceph_key_instantiate(struct key *key, const void *data, size_t datalen) |
| goto err; |
| |
| /* TODO ceph_crypto_key_decode should really take const input */ |
| - p = (void *)data; |
| - ret = ceph_crypto_key_decode(ckey, &p, (char*)data+datalen); |
| + p = (void *)prep->data; |
| + ret = ceph_crypto_key_decode(ckey, &p, (char*)prep->data+datalen); |
| if (ret < 0) |
| goto err_ckey; |
| |
| diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c |
| index d9507dd..859ab8b 100644 |
| |
| |
| @@ -59,13 +59,13 @@ const struct cred *dns_resolver_cache; |
| * "ip1,ip2,...#foo=bar" |
| */ |
| static int |
| -dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen) |
| +dns_resolver_instantiate(struct key *key, struct key_preparsed_payload *prep) |
| { |
| struct user_key_payload *upayload; |
| unsigned long derrno; |
| int ret; |
| - size_t result_len = 0; |
| - const char *data = _data, *end, *opt; |
| + size_t datalen = prep->datalen, result_len = 0; |
| + const char *data = prep->data, *end, *opt; |
| |
| kenter("%%%d,%s,'%*.*s',%zu", |
| key->serial, key->description, |
| diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c |
| index 8b1f9f4..106c5a6 100644 |
| |
| |
| @@ -26,8 +26,8 @@ |
| #include "ar-internal.h" |
| |
| static int rxrpc_vet_description_s(const char *); |
| -static int rxrpc_instantiate(struct key *, const void *, size_t); |
| -static int rxrpc_instantiate_s(struct key *, const void *, size_t); |
| +static int rxrpc_instantiate(struct key *, struct key_preparsed_payload *); |
| +static int rxrpc_instantiate_s(struct key *, struct key_preparsed_payload *); |
| static void rxrpc_destroy(struct key *); |
| static void rxrpc_destroy_s(struct key *); |
| static void rxrpc_describe(const struct key *, struct seq_file *); |
| @@ -678,7 +678,7 @@ error: |
| * |
| * if no data is provided, then a no-security key is made |
| */ |
| -static int rxrpc_instantiate(struct key *key, const void *data, size_t datalen) |
| +static int rxrpc_instantiate(struct key *key, struct key_preparsed_payload *prep) |
| { |
| const struct rxrpc_key_data_v1 *v1; |
| struct rxrpc_key_token *token, **pp; |
| @@ -686,26 +686,26 @@ static int rxrpc_instantiate(struct key *key, const void *data, size_t datalen) |
| u32 kver; |
| int ret; |
| |
| - _enter("{%x},,%zu", key_serial(key), datalen); |
| + _enter("{%x},,%zu", key_serial(key), prep->datalen); |
| |
| /* handle a no-security key */ |
| - if (!data && datalen == 0) |
| + if (!prep->data && prep->datalen == 0) |
| return 0; |
| |
| /* determine if the XDR payload format is being used */ |
| - if (datalen > 7 * 4) { |
| - ret = rxrpc_instantiate_xdr(key, data, datalen); |
| + if (prep->datalen > 7 * 4) { |
| + ret = rxrpc_instantiate_xdr(key, prep->data, prep->datalen); |
| if (ret != -EPROTO) |
| return ret; |
| } |
| |
| /* get the key interface version number */ |
| ret = -EINVAL; |
| - if (datalen <= 4 || !data) |
| + if (prep->datalen <= 4 || !prep->data) |
| goto error; |
| - memcpy(&kver, data, sizeof(kver)); |
| - data += sizeof(kver); |
| - datalen -= sizeof(kver); |
| + memcpy(&kver, prep->data, sizeof(kver)); |
| + prep->data += sizeof(kver); |
| + prep->datalen -= sizeof(kver); |
| |
| _debug("KEY I/F VERSION: %u", kver); |
| |
| @@ -715,11 +715,11 @@ static int rxrpc_instantiate(struct key *key, const void *data, size_t datalen) |
| |
| /* deal with a version 1 key */ |
| ret = -EINVAL; |
| - if (datalen < sizeof(*v1)) |
| + if (prep->datalen < sizeof(*v1)) |
| goto error; |
| |
| - v1 = data; |
| - if (datalen != sizeof(*v1) + v1->ticket_length) |
| + v1 = prep->data; |
| + if (prep->datalen != sizeof(*v1) + v1->ticket_length) |
| goto error; |
| |
| _debug("SCIX: %u", v1->security_index); |
| @@ -784,17 +784,17 @@ error: |
| * instantiate a server secret key |
| * data should be a pointer to the 8-byte secret key |
| */ |
| -static int rxrpc_instantiate_s(struct key *key, const void *data, |
| - size_t datalen) |
| +static int rxrpc_instantiate_s(struct key *key, |
| + struct key_preparsed_payload *prep) |
| { |
| struct crypto_blkcipher *ci; |
| |
| - _enter("{%x},,%zu", key_serial(key), datalen); |
| + _enter("{%x},,%zu", key_serial(key), prep->datalen); |
| |
| - if (datalen != 8) |
| + if (prep->datalen != 8) |
| return -EINVAL; |
| |
| - memcpy(&key->type_data, data, 8); |
| + memcpy(&key->type_data, prep->data, 8); |
| |
| ci = crypto_alloc_blkcipher("pcbc(des)", 0, CRYPTO_ALG_ASYNC); |
| if (IS_ERR(ci)) { |
| @@ -802,7 +802,7 @@ static int rxrpc_instantiate_s(struct key *key, const void *data, |
| return PTR_ERR(ci); |
| } |
| |
| - if (crypto_blkcipher_setkey(ci, data, 8) < 0) |
| + if (crypto_blkcipher_setkey(ci, prep->data, 8) < 0) |
| BUG(); |
| |
| key->payload.data = ci; |
| diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c |
| index 2d1bb8a..9e1e005 100644 |
| |
| |
| @@ -773,8 +773,8 @@ static int encrypted_init(struct encrypted_key_payload *epayload, |
| * |
| * On success, return 0. Otherwise return errno. |
| */ |
| -static int encrypted_instantiate(struct key *key, const void *data, |
| - size_t datalen) |
| +static int encrypted_instantiate(struct key *key, |
| + struct key_preparsed_payload *prep) |
| { |
| struct encrypted_key_payload *epayload = NULL; |
| char *datablob = NULL; |
| @@ -782,16 +782,17 @@ static int encrypted_instantiate(struct key *key, const void *data, |
| char *master_desc = NULL; |
| char *decrypted_datalen = NULL; |
| char *hex_encoded_iv = NULL; |
| + size_t datalen = prep->datalen; |
| int ret; |
| |
| - if (datalen <= 0 || datalen > 32767 || !data) |
| + if (datalen <= 0 || datalen > 32767 || !prep->data) |
| return -EINVAL; |
| |
| datablob = kmalloc(datalen + 1, GFP_KERNEL); |
| if (!datablob) |
| return -ENOMEM; |
| datablob[datalen] = 0; |
| - memcpy(datablob, data, datalen); |
| + memcpy(datablob, prep->data, datalen); |
| ret = datablob_parse(datablob, &format, &master_desc, |
| &decrypted_datalen, &hex_encoded_iv); |
| if (ret < 0) |
| @@ -834,16 +835,17 @@ static void encrypted_rcu_free(struct rcu_head *rcu) |
| * |
| * On success, return 0. Otherwise return errno. |
| */ |
| -static int encrypted_update(struct key *key, const void *data, size_t datalen) |
| +static int encrypted_update(struct key *key, struct key_preparsed_payload *prep) |
| { |
| struct encrypted_key_payload *epayload = key->payload.data; |
| struct encrypted_key_payload *new_epayload; |
| char *buf; |
| char *new_master_desc = NULL; |
| const char *format = NULL; |
| + size_t datalen = prep->datalen; |
| int ret = 0; |
| |
| - if (datalen <= 0 || datalen > 32767 || !data) |
| + if (datalen <= 0 || datalen > 32767 || !prep->data) |
| return -EINVAL; |
| |
| buf = kmalloc(datalen + 1, GFP_KERNEL); |
| @@ -851,7 +853,7 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen) |
| return -ENOMEM; |
| |
| buf[datalen] = 0; |
| - memcpy(buf, data, datalen); |
| + memcpy(buf, prep->data, datalen); |
| ret = datablob_parse(buf, &format, &new_master_desc, NULL, NULL); |
| if (ret < 0) |
| goto out; |
| diff --git a/security/keys/key.c b/security/keys/key.c |
| index 50d96d4..732a53e 100644 |
| |
| |
| @@ -412,8 +412,7 @@ EXPORT_SYMBOL(key_payload_reserve); |
| * key_construction_mutex. |
| */ |
| static int __key_instantiate_and_link(struct key *key, |
| - const void *data, |
| - size_t datalen, |
| + struct key_preparsed_payload *prep, |
| struct key *keyring, |
| struct key *authkey, |
| unsigned long *_prealloc) |
| @@ -431,7 +430,7 @@ static int __key_instantiate_and_link(struct key *key, |
| /* can't instantiate twice */ |
| if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) { |
| /* instantiate the key */ |
| - ret = key->type->instantiate(key, data, datalen); |
| + ret = key->type->instantiate(key, prep); |
| |
| if (ret == 0) { |
| /* mark the key as being instantiated */ |
| @@ -482,22 +481,37 @@ int key_instantiate_and_link(struct key *key, |
| struct key *keyring, |
| struct key *authkey) |
| { |
| + struct key_preparsed_payload prep; |
| unsigned long prealloc; |
| int ret; |
| |
| + memset(&prep, 0, sizeof(prep)); |
| + prep.data = data; |
| + prep.datalen = datalen; |
| + prep.quotalen = key->type->def_datalen; |
| + if (key->type->preparse) { |
| + ret = key->type->preparse(&prep); |
| + if (ret < 0) |
| + goto error; |
| + } |
| + |
| if (keyring) { |
| ret = __key_link_begin(keyring, key->type, key->description, |
| &prealloc); |
| if (ret < 0) |
| - return ret; |
| + goto error_free_preparse; |
| } |
| |
| - ret = __key_instantiate_and_link(key, data, datalen, keyring, authkey, |
| + ret = __key_instantiate_and_link(key, &prep, keyring, authkey, |
| &prealloc); |
| |
| if (keyring) |
| __key_link_end(keyring, key->type, prealloc); |
| |
| +error_free_preparse: |
| + if (key->type->preparse) |
| + key->type->free_preparse(&prep); |
| +error: |
| return ret; |
| } |
| |
| @@ -706,7 +720,7 @@ void key_type_put(struct key_type *ktype) |
| * if we get an error. |
| */ |
| static inline key_ref_t __key_update(key_ref_t key_ref, |
| - const void *payload, size_t plen) |
| + struct key_preparsed_payload *prep) |
| { |
| struct key *key = key_ref_to_ptr(key_ref); |
| int ret; |
| @@ -722,7 +736,7 @@ static inline key_ref_t __key_update(key_ref_t key_ref, |
| |
| down_write(&key->sem); |
| |
| - ret = key->type->update(key, payload, plen); |
| + ret = key->type->update(key, prep); |
| if (ret == 0) |
| /* updating a negative key instantiates it */ |
| clear_bit(KEY_FLAG_NEGATIVE, &key->flags); |
| @@ -774,6 +788,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, |
| unsigned long flags) |
| { |
| unsigned long prealloc; |
| + struct key_preparsed_payload prep; |
| const struct cred *cred = current_cred(); |
| struct key_type *ktype; |
| struct key *keyring, *key = NULL; |
| @@ -789,8 +804,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, |
| } |
| |
| key_ref = ERR_PTR(-EINVAL); |
| - if (!ktype->match || !ktype->instantiate) |
| - goto error_2; |
| + if (!ktype->match || !ktype->instantiate || |
| + (!description && !ktype->preparse)) |
| + goto error_put_type; |
| |
| keyring = key_ref_to_ptr(keyring_ref); |
| |
| @@ -798,18 +814,33 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, |
| |
| key_ref = ERR_PTR(-ENOTDIR); |
| if (keyring->type != &key_type_keyring) |
| - goto error_2; |
| + goto error_put_type; |
| + |
| + memset(&prep, 0, sizeof(prep)); |
| + prep.data = payload; |
| + prep.datalen = plen; |
| + prep.quotalen = ktype->def_datalen; |
| + if (ktype->preparse) { |
| + ret = ktype->preparse(&prep); |
| + if (ret < 0) |
| + goto error_put_type; |
| + if (!description) |
| + description = prep.description; |
| + ret = -EINVAL; |
| + if (!description) |
| + goto error_free_prep; |
| + } |
| |
| ret = __key_link_begin(keyring, ktype, description, &prealloc); |
| if (ret < 0) |
| - goto error_2; |
| + goto error_free_prep; |
| |
| /* if we're going to allocate a new key, we're going to have |
| * to modify the keyring */ |
| ret = key_permission(keyring_ref, KEY_WRITE); |
| if (ret < 0) { |
| key_ref = ERR_PTR(ret); |
| - goto error_3; |
| + goto error_link_end; |
| } |
| |
| /* if it's possible to update this type of key, search for an existing |
| @@ -840,25 +871,27 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, |
| perm, flags); |
| if (IS_ERR(key)) { |
| key_ref = ERR_CAST(key); |
| - goto error_3; |
| + goto error_link_end; |
| } |
| |
| /* instantiate it and link it into the target keyring */ |
| - ret = __key_instantiate_and_link(key, payload, plen, keyring, NULL, |
| - &prealloc); |
| + ret = __key_instantiate_and_link(key, &prep, keyring, NULL, &prealloc); |
| if (ret < 0) { |
| key_put(key); |
| key_ref = ERR_PTR(ret); |
| - goto error_3; |
| + goto error_link_end; |
| } |
| |
| key_ref = make_key_ref(key, is_key_possessed(keyring_ref)); |
| |
| - error_3: |
| +error_link_end: |
| __key_link_end(keyring, ktype, prealloc); |
| - error_2: |
| +error_free_prep: |
| + if (ktype->preparse) |
| + ktype->free_preparse(&prep); |
| +error_put_type: |
| key_type_put(ktype); |
| - error: |
| +error: |
| return key_ref; |
| |
| found_matching_key: |
| @@ -866,10 +899,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, |
| * - we can drop the locks first as we have the key pinned |
| */ |
| __key_link_end(keyring, ktype, prealloc); |
| - key_type_put(ktype); |
| |
| - key_ref = __key_update(key_ref, payload, plen); |
| - goto error; |
| + key_ref = __key_update(key_ref, &prep); |
| + goto error_free_prep; |
| } |
| EXPORT_SYMBOL(key_create_or_update); |
| |
| @@ -888,6 +920,7 @@ EXPORT_SYMBOL(key_create_or_update); |
| */ |
| int key_update(key_ref_t key_ref, const void *payload, size_t plen) |
| { |
| + struct key_preparsed_payload prep; |
| struct key *key = key_ref_to_ptr(key_ref); |
| int ret; |
| |
| @@ -900,18 +933,31 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen) |
| |
| /* attempt to update it if supported */ |
| ret = -EOPNOTSUPP; |
| - if (key->type->update) { |
| - down_write(&key->sem); |
| - |
| - ret = key->type->update(key, payload, plen); |
| - if (ret == 0) |
| - /* updating a negative key instantiates it */ |
| - clear_bit(KEY_FLAG_NEGATIVE, &key->flags); |
| + if (!key->type->update) |
| + goto error; |
| |
| - up_write(&key->sem); |
| + memset(&prep, 0, sizeof(prep)); |
| + prep.data = payload; |
| + prep.datalen = plen; |
| + prep.quotalen = key->type->def_datalen; |
| + if (key->type->preparse) { |
| + ret = key->type->preparse(&prep); |
| + if (ret < 0) |
| + goto error; |
| } |
| |
| - error: |
| + down_write(&key->sem); |
| + |
| + ret = key->type->update(key, &prep); |
| + if (ret == 0) |
| + /* updating a negative key instantiates it */ |
| + clear_bit(KEY_FLAG_NEGATIVE, &key->flags); |
| + |
| + up_write(&key->sem); |
| + |
| + if (key->type->preparse) |
| + key->type->free_preparse(&prep); |
| +error: |
| return ret; |
| } |
| EXPORT_SYMBOL(key_update); |
| diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c |
| index 3364fbf..505d40b 100644 |
| |
| |
| @@ -46,6 +46,9 @@ static int key_get_type_from_user(char *type, |
| * Extract the description of a new key from userspace and either add it as a |
| * new key to the specified keyring or update a matching key in that keyring. |
| * |
| + * If the description is NULL or an empty string, the key type is asked to |
| + * generate one from the payload. |
| + * |
| * The keyring must be writable so that we can attach the key to it. |
| * |
| * If successful, the new key's serial number is returned, otherwise an error |
| @@ -72,10 +75,17 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type, |
| if (ret < 0) |
| goto error; |
| |
| - description = strndup_user(_description, PAGE_SIZE); |
| - if (IS_ERR(description)) { |
| - ret = PTR_ERR(description); |
| - goto error; |
| + description = NULL; |
| + if (_description) { |
| + description = strndup_user(_description, PAGE_SIZE); |
| + if (IS_ERR(description)) { |
| + ret = PTR_ERR(description); |
| + goto error; |
| + } |
| + if (!*description) { |
| + kfree(description); |
| + description = NULL; |
| + } |
| } |
| |
| /* pull the payload in if one was supplied */ |
| diff --git a/security/keys/keyring.c b/security/keys/keyring.c |
| index 81e7852..f04d8cf 100644 |
| |
| |
| @@ -66,7 +66,7 @@ static inline unsigned keyring_hash(const char *desc) |
| * operations. |
| */ |
| static int keyring_instantiate(struct key *keyring, |
| - const void *data, size_t datalen); |
| + struct key_preparsed_payload *prep); |
| static int keyring_match(const struct key *keyring, const void *criterion); |
| static void keyring_revoke(struct key *keyring); |
| static void keyring_destroy(struct key *keyring); |
| @@ -121,12 +121,12 @@ static void keyring_publish_name(struct key *keyring) |
| * Returns 0 on success, -EINVAL if given any data. |
| */ |
| static int keyring_instantiate(struct key *keyring, |
| - const void *data, size_t datalen) |
| + struct key_preparsed_payload *prep) |
| { |
| int ret; |
| |
| ret = -EINVAL; |
| - if (datalen == 0) { |
| + if (prep->datalen == 0) { |
| /* make the keyring available by name if it has one */ |
| keyring_publish_name(keyring); |
| ret = 0; |
| diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c |
| index 60d4e3f..85730d5 100644 |
| |
| |
| @@ -19,7 +19,8 @@ |
| #include <asm/uaccess.h> |
| #include "internal.h" |
| |
| -static int request_key_auth_instantiate(struct key *, const void *, size_t); |
| +static int request_key_auth_instantiate(struct key *, |
| + struct key_preparsed_payload *); |
| static void request_key_auth_describe(const struct key *, struct seq_file *); |
| static void request_key_auth_revoke(struct key *); |
| static void request_key_auth_destroy(struct key *); |
| @@ -42,10 +43,9 @@ struct key_type key_type_request_key_auth = { |
| * Instantiate a request-key authorisation key. |
| */ |
| static int request_key_auth_instantiate(struct key *key, |
| - const void *data, |
| - size_t datalen) |
| + struct key_preparsed_payload *prep) |
| { |
| - key->payload.data = (struct request_key_auth *) data; |
| + key->payload.data = (struct request_key_auth *)prep->data; |
| return 0; |
| } |
| |
| diff --git a/security/keys/trusted.c b/security/keys/trusted.c |
| index 2d5d041..42036c7 100644 |
| |
| |
| @@ -927,22 +927,23 @@ static struct trusted_key_payload *trusted_payload_alloc(struct key *key) |
| * |
| * On success, return 0. Otherwise return errno. |
| */ |
| -static int trusted_instantiate(struct key *key, const void *data, |
| - size_t datalen) |
| +static int trusted_instantiate(struct key *key, |
| + struct key_preparsed_payload *prep) |
| { |
| struct trusted_key_payload *payload = NULL; |
| struct trusted_key_options *options = NULL; |
| + size_t datalen = prep->datalen; |
| char *datablob; |
| int ret = 0; |
| int key_cmd; |
| |
| - if (datalen <= 0 || datalen > 32767 || !data) |
| + if (datalen <= 0 || datalen > 32767 || !prep->data) |
| return -EINVAL; |
| |
| datablob = kmalloc(datalen + 1, GFP_KERNEL); |
| if (!datablob) |
| return -ENOMEM; |
| - memcpy(datablob, data, datalen); |
| + memcpy(datablob, prep->data, datalen); |
| datablob[datalen] = '\0'; |
| |
| options = trusted_options_alloc(); |
| @@ -1011,17 +1012,18 @@ static void trusted_rcu_free(struct rcu_head *rcu) |
| /* |
| * trusted_update - reseal an existing key with new PCR values |
| */ |
| -static int trusted_update(struct key *key, const void *data, size_t datalen) |
| +static int trusted_update(struct key *key, struct key_preparsed_payload *prep) |
| { |
| struct trusted_key_payload *p = key->payload.data; |
| struct trusted_key_payload *new_p; |
| struct trusted_key_options *new_o; |
| + size_t datalen = prep->datalen; |
| char *datablob; |
| int ret = 0; |
| |
| if (!p->migratable) |
| return -EPERM; |
| - if (datalen <= 0 || datalen > 32767 || !data) |
| + if (datalen <= 0 || datalen > 32767 || !prep->data) |
| return -EINVAL; |
| |
| datablob = kmalloc(datalen + 1, GFP_KERNEL); |
| @@ -1038,7 +1040,7 @@ static int trusted_update(struct key *key, const void *data, size_t datalen) |
| goto out; |
| } |
| |
| - memcpy(datablob, data, datalen); |
| + memcpy(datablob, prep->data, datalen); |
| datablob[datalen] = '\0'; |
| ret = datablob_parse(datablob, new_p, new_o); |
| if (ret != Opt_update) { |
| diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c |
| index c7660a2..55dc889 100644 |
| |
| |
| @@ -58,13 +58,14 @@ EXPORT_SYMBOL_GPL(key_type_logon); |
| /* |
| * instantiate a user defined key |
| */ |
| -int user_instantiate(struct key *key, const void *data, size_t datalen) |
| +int user_instantiate(struct key *key, struct key_preparsed_payload *prep) |
| { |
| struct user_key_payload *upayload; |
| + size_t datalen = prep->datalen; |
| int ret; |
| |
| ret = -EINVAL; |
| - if (datalen <= 0 || datalen > 32767 || !data) |
| + if (datalen <= 0 || datalen > 32767 || !prep->data) |
| goto error; |
| |
| ret = key_payload_reserve(key, datalen); |
| @@ -78,7 +79,7 @@ int user_instantiate(struct key *key, const void *data, size_t datalen) |
| |
| /* attach the data */ |
| upayload->datalen = datalen; |
| - memcpy(upayload->data, data, datalen); |
| + memcpy(upayload->data, prep->data, datalen); |
| rcu_assign_keypointer(key, upayload); |
| ret = 0; |
| |
| @@ -92,13 +93,14 @@ EXPORT_SYMBOL_GPL(user_instantiate); |
| * update a user defined key |
| * - the key's semaphore is write-locked |
| */ |
| -int user_update(struct key *key, const void *data, size_t datalen) |
| +int user_update(struct key *key, struct key_preparsed_payload *prep) |
| { |
| struct user_key_payload *upayload, *zap; |
| + size_t datalen = prep->datalen; |
| int ret; |
| |
| ret = -EINVAL; |
| - if (datalen <= 0 || datalen > 32767 || !data) |
| + if (datalen <= 0 || datalen > 32767 || !prep->data) |
| goto error; |
| |
| /* construct a replacement payload */ |
| @@ -108,7 +110,7 @@ int user_update(struct key *key, const void *data, size_t datalen) |
| goto error; |
| |
| upayload->datalen = datalen; |
| - memcpy(upayload->data, data, datalen); |
| + memcpy(upayload->data, prep->data, datalen); |
| |
| /* check the quota and attach the new data */ |
| zap = upayload; |
| -- |
| 1.7.11.4 |
| |
| |
| From 2f5f2d483565648dbe050fda8767edd5d65b1d98 Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 00:14:17 +0100 |
| Subject: [PATCH 02/32] MPILIB: Provide count_leading/trailing_zeros() based |
| on arch functions |
| |
| Provide count_leading/trailing_zeros() macros based on extant arch bit scanning |
| functions rather than reimplementing from scratch in MPILIB. |
| |
| Whilst we're at it, turn count_foo_zeros(n, x) into n = count_foo_zeros(x). |
| |
| Also move the definition to asm-generic as other people may be interested in |
| using it. |
| |
| Signed-off-by: David Howells <dhowells@redhat.com> |
| Cc: David S. Miller <davem@davemloft.net> |
| Cc: Dmitry Kasatkin <dmitry.kasatkin@intel.com> |
| Cc: Arnd Bergmann <arnd@arndb.com> |
| |
| include/asm-generic/bitops/count_zeros.h | 57 +++++++++++++ |
| lib/mpi/longlong.h | 138 +------------------------------ |
| lib/mpi/mpi-bit.c | 2 +- |
| lib/mpi/mpi-pow.c | 4 +- |
| 4 files changed, 62 insertions(+), 139 deletions(-) |
| create mode 100644 include/asm-generic/bitops/count_zeros.h |
| |
| diff --git a/include/asm-generic/bitops/count_zeros.h b/include/asm-generic/bitops/count_zeros.h |
| new file mode 100644 |
| index 0000000..97520d2 |
| |
| |
| @@ -0,0 +1,57 @@ |
| +/* Count leading and trailing zeros functions |
| + * |
| + * 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. |
| + */ |
| + |
| +#ifndef _ASM_GENERIC_BITOPS_COUNT_ZEROS_H_ |
| +#define _ASM_GENERIC_BITOPS_COUNT_ZEROS_H_ |
| + |
| +#include <asm/bitops.h> |
| + |
| +/** |
| + * count_leading_zeros - Count the number of zeros from the MSB back |
| + * @x: The value |
| + * |
| + * Count the number of leading zeros from the MSB going towards the LSB in @x. |
| + * |
| + * If the MSB of @x is set, the result is 0. |
| + * If only the LSB of @x is set, then the result is BITS_PER_LONG-1. |
| + * If @x is 0 then the result is COUNT_LEADING_ZEROS_0. |
| + */ |
| +static inline int count_leading_zeros(unsigned long x) |
| +{ |
| + if (sizeof(x) == 4) |
| + return BITS_PER_LONG - fls(x); |
| + else |
| + return BITS_PER_LONG - fls64(x); |
| +} |
| + |
| +#define COUNT_LEADING_ZEROS_0 BITS_PER_LONG |
| + |
| +/** |
| + * count_trailing_zeros - Count the number of zeros from the LSB forwards |
| + * @x: The value |
| + * |
| + * Count the number of trailing zeros from the LSB going towards the MSB in @x. |
| + * |
| + * If the LSB of @x is set, the result is 0. |
| + * If only the MSB of @x is set, then the result is BITS_PER_LONG-1. |
| + * If @x is 0 then the result is COUNT_TRAILING_ZEROS_0. |
| + */ |
| +static inline int count_trailing_zeros(unsigned long x) |
| +{ |
| +#define COUNT_TRAILING_ZEROS_0 (-1) |
| + |
| + if (sizeof(x) == 4) |
| + return ffs(x); |
| + else |
| + return (x != 0) ? __ffs(x) : COUNT_TRAILING_ZEROS_0; |
| +} |
| + |
| +#endif /* _ASM_GENERIC_BITOPS_COUNT_ZEROS_H_ */ |
| diff --git a/lib/mpi/longlong.h b/lib/mpi/longlong.h |
| index 29f9862..678ce4f 100644 |
| |
| |
| @@ -19,6 +19,8 @@ |
| * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, |
| * MA 02111-1307, USA. */ |
| |
| +#include <asm-generic/bitops/count_zeros.h> |
| + |
| /* You have to define the following before including this file: |
| * |
| * UWtype -- An unsigned type, default type for operations (typically a "word") |
| @@ -146,12 +148,6 @@ do { \ |
| : "1" ((USItype)(n1)), \ |
| "r" ((USItype)(n0)), \ |
| "r" ((USItype)(d))) |
| - |
| -#define count_leading_zeros(count, x) \ |
| - __asm__ ("clz %0,%1" \ |
| - : "=r" ((USItype)(count)) \ |
| - : "r" ((USItype)(x))) |
| -#define COUNT_LEADING_ZEROS_0 32 |
| #endif /* __a29k__ */ |
| |
| #if defined(__alpha) && W_TYPE_SIZE == 64 |
| @@ -298,11 +294,6 @@ extern UDItype __udiv_qrnnd(); |
| : "1" ((USItype)(nh)), \ |
| "0" ((USItype)(nl)), \ |
| "g" ((USItype)(d))) |
| -#define count_leading_zeros(count, x) \ |
| - __asm__ ("bsch/1 %1,%0" \ |
| - : "=g" (count) \ |
| - : "g" ((USItype)(x)), \ |
| - "0" ((USItype)0)) |
| #endif |
| |
| / |
| @@ -354,27 +345,6 @@ do { USItype __r; \ |
| } while (0) |
| extern USItype __udiv_qrnnd(); |
| #endif /* LONGLONG_STANDALONE */ |
| -#define count_leading_zeros(count, x) \ |
| -do { \ |
| - USItype __tmp; \ |
| - __asm__ ( \ |
| - "ldi 1,%0\n" \ |
| - "extru,= %1,15,16,%%r0 ; Bits 31..16 zero?\n" \ |
| - "extru,tr %1,15,16,%1 ; No. Shift down, skip add.\n" \ |
| - "ldo 16(%0),%0 ; Yes. Perform add.\n" \ |
| - "extru,= %1,23,8,%%r0 ; Bits 15..8 zero?\n" \ |
| - "extru,tr %1,23,8,%1 ; No. Shift down, skip add.\n" \ |
| - "ldo 8(%0),%0 ; Yes. Perform add.\n" \ |
| - "extru,= %1,27,4,%%r0 ; Bits 7..4 zero?\n" \ |
| - "extru,tr %1,27,4,%1 ; No. Shift down, skip add.\n" \ |
| - "ldo 4(%0),%0 ; Yes. Perform add.\n" \ |
| - "extru,= %1,29,2,%%r0 ; Bits 3..2 zero?\n" \ |
| - "extru,tr %1,29,2,%1 ; No. Shift down, skip add.\n" \ |
| - "ldo 2(%0),%0 ; Yes. Perform add.\n" \ |
| - "extru %1,30,1,%1 ; Extract bit 1.\n" \ |
| - "sub %0,%1,%0 ; Subtract it. " \ |
| - : "=r" (count), "=r" (__tmp) : "1" (x)); \ |
| -} while (0) |
| #endif /* hppa */ |
| |
| / |
| @@ -457,15 +427,6 @@ do { \ |
| : "0" ((USItype)(n0)), \ |
| "1" ((USItype)(n1)), \ |
| "rm" ((USItype)(d))) |
| -#define count_leading_zeros(count, x) \ |
| -do { \ |
| - USItype __cbtmp; \ |
| - __asm__ ("bsrl %1,%0" \ |
| - : "=r" (__cbtmp) : "rm" ((USItype)(x))); \ |
| - (count) = __cbtmp ^ 31; \ |
| -} while (0) |
| -#define count_trailing_zeros(count, x) \ |
| - __asm__ ("bsfl %1,%0" : "=r" (count) : "rm" ((USItype)(x))) |
| #ifndef UMUL_TIME |
| #define UMUL_TIME 40 |
| #endif |
| @@ -536,15 +497,6 @@ do { \ |
| "dI" ((USItype)(d))); \ |
| (r) = __rq.__i.__l; (q) = __rq.__i.__h; \ |
| } while (0) |
| -#define count_leading_zeros(count, x) \ |
| -do { \ |
| - USItype __cbtmp; \ |
| - __asm__ ("scanbit %1,%0" \ |
| - : "=r" (__cbtmp) \ |
| - : "r" ((USItype)(x))); \ |
| - (count) = __cbtmp ^ 31; \ |
| -} while (0) |
| -#define COUNT_LEADING_ZEROS_0 (-32) /* sic */ |
| #if defined(__i960mx) /* what is the proper symbol to test??? */ |
| #define rshift_rhlc(r, h, l, c) \ |
| do { \ |
| @@ -603,11 +555,6 @@ do { \ |
| : "0" ((USItype)(n0)), \ |
| "1" ((USItype)(n1)), \ |
| "dmi" ((USItype)(d))) |
| -#define count_leading_zeros(count, x) \ |
| - __asm__ ("bfffo %1{%b2:%b2},%0" \ |
| - : "=d" ((USItype)(count)) \ |
| - : "od" ((USItype)(x)), "n" (0)) |
| -#define COUNT_LEADING_ZEROS_0 32 |
| #else /* not mc68020 */ |
| #define umul_ppmm(xh, xl, a, b) \ |
| do { USItype __umul_tmp1, __umul_tmp2; \ |
| @@ -664,15 +611,6 @@ do { USItype __umul_tmp1, __umul_tmp2; \ |
| "rJ" ((USItype)(bh)), \ |
| "rJ" ((USItype)(al)), \ |
| "rJ" ((USItype)(bl))) |
| -#define count_leading_zeros(count, x) \ |
| -do { \ |
| - USItype __cbtmp; \ |
| - __asm__ ("ff1 %0,%1" \ |
| - : "=r" (__cbtmp) \ |
| - : "r" ((USItype)(x))); \ |
| - (count) = __cbtmp ^ 31; \ |
| -} while (0) |
| -#define COUNT_LEADING_ZEROS_0 63 /* sic */ |
| #if defined(__m88110__) |
| #define umul_ppmm(wh, wl, u, v) \ |
| do { \ |
| @@ -779,12 +717,6 @@ do { \ |
| : "0" (__xx.__ll), \ |
| "g" ((USItype)(d))); \ |
| (r) = __xx.__i.__l; (q) = __xx.__i.__h; }) |
| -#define count_trailing_zeros(count, x) \ |
| -do { \ |
| - __asm__("ffsd %2,%0" \ |
| - : "=r"((USItype) (count)) \ |
| - : "0"((USItype) 0), "r"((USItype) (x))); \ |
| - } while (0) |
| #endif /* __ns32000__ */ |
| |
| / |
| @@ -855,11 +787,6 @@ do { \ |
| "rI" ((USItype)(al)), \ |
| "r" ((USItype)(bl))); \ |
| } while (0) |
| -#define count_leading_zeros(count, x) \ |
| - __asm__ ("{cntlz|cntlzw} %0,%1" \ |
| - : "=r" ((USItype)(count)) \ |
| - : "r" ((USItype)(x))) |
| -#define COUNT_LEADING_ZEROS_0 32 |
| #if defined(_ARCH_PPC) |
| #define umul_ppmm(ph, pl, m0, m1) \ |
| do { \ |
| @@ -1001,19 +928,6 @@ do { \ |
| } while (0) |
| #define UMUL_TIME 20 |
| #define UDIV_TIME 200 |
| -#define count_leading_zeros(count, x) \ |
| -do { \ |
| - if ((x) >= 0x10000) \ |
| - __asm__ ("clz %0,%1" \ |
| - : "=r" ((USItype)(count)) \ |
| - : "r" ((USItype)(x) >> 16)); \ |
| - else { \ |
| - __asm__ ("clz %0,%1" \ |
| - : "=r" ((USItype)(count)) \ |
| - : "r" ((USItype)(x))); \ |
| - (count) += 16; \ |
| - } \ |
| -} while (0) |
| #endif /* RT/ROMP */ |
| |
| / |
| @@ -1142,13 +1056,6 @@ do { \ |
| "rI" ((USItype)(d)) \ |
| : "%g1" __AND_CLOBBER_CC) |
| #define UDIV_TIME 37 |
| -#define count_leading_zeros(count, x) \ |
| - __asm__ ("scan %1,0,%0" \ |
| - : "=r" ((USItype)(x)) \ |
| - : "r" ((USItype)(count))) |
| -/* Early sparclites return 63 for an argument of 0, but they warn that future |
| - implementations might change this. Therefore, leave COUNT_LEADING_ZEROS_0 |
| - undefined. */ |
| #endif /* __sparclite__ */ |
| #endif /* __sparc_v8__ */ |
| /* Default to sparc v7 versions of umul_ppmm and udiv_qrnnd. */ |
| @@ -1454,47 +1361,6 @@ do { \ |
| #define udiv_qrnnd __udiv_qrnnd_c |
| #endif |
| |
| -#undef count_leading_zeros |
| -#if !defined(count_leading_zeros) |
| - extern |
| -#ifdef __STDC__ |
| - const |
| -#endif |
| - unsigned char __clz_tab[]; |
| -#define count_leading_zeros(count, x) \ |
| -do { \ |
| - UWtype __xr = (x); \ |
| - UWtype __a; \ |
| - \ |
| - if (W_TYPE_SIZE <= 32) { \ |
| - __a = __xr < ((UWtype) 1 << 2*__BITS4) \ |
| - ? (__xr < ((UWtype) 1 << __BITS4) ? 0 : __BITS4) \ |
| - : (__xr < ((UWtype) 1 << 3*__BITS4) ? 2*__BITS4 : 3*__BITS4); \ |
| - } \ |
| - else { \ |
| - for (__a = W_TYPE_SIZE - 8; __a > 0; __a -= 8) \ |
| - if (((__xr >> __a) & 0xff) != 0) \ |
| - break; \ |
| - } \ |
| - \ |
| - (count) = W_TYPE_SIZE - (__clz_tab[__xr >> __a] + __a); \ |
| -} while (0) |
| - /* This version gives a well-defined value for zero. */ |
| -#define COUNT_LEADING_ZEROS_0 W_TYPE_SIZE |
| -#endif |
| - |
| -#if !defined(count_trailing_zeros) |
| -/* Define count_trailing_zeros using count_leading_zeros. The latter might be |
| - defined in asm, but if it is not, the C version above is good enough. */ |
| -#define count_trailing_zeros(count, x) \ |
| -do { \ |
| - UWtype __ctz_x = (x); \ |
| - UWtype __ctz_c; \ |
| - count_leading_zeros(__ctz_c, __ctz_x & -__ctz_x); \ |
| - (count) = W_TYPE_SIZE - 1 - __ctz_c; \ |
| -} while (0) |
| -#endif |
| - |
| #ifndef UDIV_NEEDS_NORMALIZATION |
| #define UDIV_NEEDS_NORMALIZATION 0 |
| #endif |
| diff --git a/lib/mpi/mpi-bit.c b/lib/mpi/mpi-bit.c |
| index 5687248..503537e 100644 |
| |
| |
| @@ -45,7 +45,7 @@ unsigned mpi_get_nbits(MPI a) |
| if (a->nlimbs) { |
| mpi_limb_t alimb = a->d[a->nlimbs - 1]; |
| if (alimb) |
| - count_leading_zeros(n, alimb); |
| + n = count_leading_zeros(alimb); |
| else |
| n = BITS_PER_MPI_LIMB; |
| n = BITS_PER_MPI_LIMB - n + (a->nlimbs - 1) * BITS_PER_MPI_LIMB; |
| diff --git a/lib/mpi/mpi-pow.c b/lib/mpi/mpi-pow.c |
| index 67f3e79..5464c87 100644 |
| |
| |
| @@ -77,7 +77,7 @@ int mpi_powm(MPI res, MPI base, MPI exp, MPI mod) |
| mp = mp_marker = mpi_alloc_limb_space(msize); |
| if (!mp) |
| goto enomem; |
| - count_leading_zeros(mod_shift_cnt, mod->d[msize - 1]); |
| + mod_shift_cnt = count_leading_zeros(mod->d[msize - 1]); |
| if (mod_shift_cnt) |
| mpihelp_lshift(mp, mod->d, msize, mod_shift_cnt); |
| else |
| @@ -169,7 +169,7 @@ int mpi_powm(MPI res, MPI base, MPI exp, MPI mod) |
| |
| i = esize - 1; |
| e = ep[i]; |
| - count_leading_zeros(c, e); |
| + c = count_leading_zeros(e); |
| e = (e << c) << 1; /* shift the exp bits to the left, lose msb */ |
| c = BITS_PER_MPI_LIMB - 1 - c; |
| |
| -- |
| 1.7.11.4 |
| |
| |
| From 15b76403afcc626b91e5fcee8b6a950a51f284ef Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 00:14:18 +0100 |
| Subject: [PATCH 03/32] 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 <dhowells@redhat.com> |
| |
| Documentation/security/keys-crypto.txt | 181 ++++++++++++++++++++++ |
| include/keys/crypto-subtype.h | 57 +++++++ |
| 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 | 272 +++++++++++++++++++++++++++++++++ |
| 9 files changed, 580 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 |
| |
| |
| @@ -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:<hexdigits>" 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 "<subtype>:<hexdigits>" 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 <keys/crypto-type.h> |
| + |
| +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..1f546e6 |
| |
| |
| @@ -0,0 +1,57 @@ |
| +/* 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 <linux/seq_file.h> |
| +#include <keys/crypto-type.h> |
| + |
| +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 parse a key from the data blob passed to add_key() or |
| + * keyctl_instantiate(). Should also generate a proposed description |
| + * that the caller can optionally use for the key. |
| + * |
| + * Return EBADMSG if not recognised. |
| + */ |
| + int (*preparse)(struct key_preparsed_payload *prep); |
| +}; |
| + |
| +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 |
| |
| |
| @@ -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 <linux/key-type.h> |
| + |
| +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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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..eb11788 |
| |
| |
| @@ -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 const 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..e8f83a6 |
| |
| |
| @@ -0,0 +1,272 @@ |
| +/* 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 <keys/crypto-subtype.h> |
| +#include <linux/seq_file.h> |
| +#include <linux/module.h> |
| +#include <linux/slab.h> |
| +#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: |
| + * |
| + * "<desc>" - request a key by description |
| + * "id:<id>" - request a key matching the ID |
| + * "<subtype>:<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, ']'); |
| + } |
| +} |
| + |
| +/* |
| + * Preparse a crypto payload to get format the contents appropriately for the |
| + * internal payload to cut down on the number of scans of the data performed. |
| + * |
| + * We also generate a proposed description from the contents of the key that |
| + * can be used to name the key if the user doesn't want to provide one. |
| + */ |
| +static int crypto_key_preparse(struct key_preparsed_payload *prep) |
| +{ |
| + struct crypto_key_parser *parser; |
| + int ret; |
| + |
| + pr_devel("==>%s()\n", __func__); |
| + |
| + if (prep->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->preparse(prep); |
| + 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; |
| +} |
| + |
| +/* |
| + * Clean up the preparse data |
| + */ |
| +static void crypto_key_free_preparse(struct key_preparsed_payload *prep) |
| +{ |
| + struct crypto_key_subtype *subtype = prep->type_data[0]; |
| + |
| + pr_devel("==>%s()\n", __func__); |
| + |
| + if (subtype) { |
| + subtype->destroy(prep->payload); |
| + module_put(subtype->owner); |
| + } |
| + kfree(prep->type_data[1]); |
| + kfree(prep->description); |
| +} |
| + |
| +/* |
| + * Instantiate a crypto_key defined key |
| + */ |
| +static int crypto_key_instantiate(struct key *key, struct key_preparsed_payload *prep) |
| +{ |
| + int ret; |
| + |
| + pr_devel("==>%s()\n", __func__); |
| + |
| + ret = key_payload_reserve(key, prep->quotalen); |
| + if (ret == 0) { |
| + key->type_data.p[0] = prep->type_data[0]; |
| + key->type_data.p[1] = prep->type_data[1]; |
| + key->payload.data = prep->payload; |
| + prep->type_data[0] = NULL; |
| + prep->type_data[1] = NULL; |
| + prep->payload = NULL; |
| + } |
| + 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", |
| + .preparse = crypto_key_preparse, |
| + .free_preparse = crypto_key_free_preparse, |
| + .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.11.4 |
| |
| |
| From a4e8ef15db9c013c4d3141424120c5ba74376483 Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 00:14:18 +0100 |
| Subject: [PATCH 04/32] 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 <dhowells@redhat.com> |
| |
| Documentation/security/keys-crypto.txt | 100 +++++++++++++++++++++ |
| include/keys/crypto-subtype.h | 36 +++++++- |
| include/keys/crypto-type.h | 9 ++ |
| security/keys/crypto/Makefile | 2 +- |
| security/keys/crypto/crypto_keys.h | 1 - |
| security/keys/crypto/crypto_type.c | 2 +- |
| security/keys/crypto/crypto_verify.c | 159 +++++++++++++++++++++++++++++++++ |
| 7 files changed, 304 insertions(+), 5 deletions(-) |
| 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..0a886ec 100644 |
| |
| |
| @@ -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 |
| |
| @@ -107,6 +167,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 +197,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 1f546e6..61a5338 100644 |
| |
| |
| @@ -34,8 +34,7 @@ struct crypto_key_subtype { |
| }; |
| |
| /* |
| - * Data parser. Called during instantiation and signature verification |
| - * initiation. |
| + * Key data parser. Called during key instantiation. |
| */ |
| struct crypto_key_parser { |
| struct list_head link; |
| @@ -54,4 +53,37 @@ struct crypto_key_parser { |
| extern int register_crypto_key_parser(struct crypto_key_parser *); |
| extern void unregister_crypto_key_parser(struct crypto_key_parser *); |
| |
| +/* |
| + * Context base for signature verification methods. Allocated by the subtype |
| + * and presumably embedded in something appropriate. |
| + */ |
| +struct crypto_sig_verify_context { |
| + struct key *key; |
| + struct crypto_sig_parser *parser; |
| + int (*add_data)(struct crypto_sig_verify_context *ctx, |
| + const void *data, size_t datalen); |
| + int (*end)(struct crypto_sig_verify_context *ctx, |
| + const u8 *sig, size_t siglen); |
| + void (*cancel)(struct crypto_sig_verify_context *ctx); |
| +}; |
| + |
| +/* |
| + * Signature data parser. Called during signature verification initiation. |
| + */ |
| +struct crypto_sig_parser { |
| + struct list_head link; |
| + struct module *owner; |
| + const char *name; |
| + |
| + /* Attempt to recognise a signature blob and find a matching key. |
| + * |
| + * Return EBADMSG if not recognised. |
| + */ |
| + struct crypto_sig_verify_context *(*verify_sig_begin)( |
| + struct key *keyring, const u8 *sig, size_t siglen); |
| +}; |
| + |
| +extern int register_crypto_sig_parser(struct crypto_sig_parser *); |
| +extern void unregister_crypto_sig_parser(struct crypto_sig_parser *); |
| + |
| #endif /* _KEYS_CRYPTO_SUBTYPE_H */ |
| diff --git a/include/keys/crypto-type.h b/include/keys/crypto-type.h |
| index 47c00c7..0fb362a 100644 |
| |
| |
| @@ -18,6 +18,15 @@ |
| |
| extern struct key_type key_type_crypto; |
| |
| +struct crypto_sig_verify_context; |
| +extern struct crypto_sig_verify_context *verify_sig_begin( |
| + struct key *key, const void *sig, size_t siglen); |
| +extern int verify_sig_add_data(struct crypto_sig_verify_context *ctx, |
| + const void *data, size_t datalen); |
| +extern int verify_sig_end(struct crypto_sig_verify_context *ctx, |
| + const void *sig, size_t siglen); |
| +extern void verify_sig_cancel(struct crypto_sig_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 |
| |
| |
| @@ -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_keys.h b/security/keys/crypto/crypto_keys.h |
| index eb11788..ab9b381 100644 |
| |
| |
| @@ -24,5 +24,4 @@ static inline const char *crypto_key_id(const struct key *key) |
| /* |
| * 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 |
| index e8f83a6..821db37 100644 |
| |
| |
| @@ -18,7 +18,7 @@ |
| |
| MODULE_LICENSE("GPL"); |
| |
| -LIST_HEAD(crypto_key_parsers); |
| +static LIST_HEAD(crypto_key_parsers); |
| DECLARE_RWSEM(crypto_key_parsers_sem); |
| |
| /* |
| diff --git a/security/keys/crypto/crypto_verify.c b/security/keys/crypto/crypto_verify.c |
| new file mode 100644 |
| index 0000000..d64f1c7 |
| |
| |
| @@ -0,0 +1,159 @@ |
| +/* 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 <keys/crypto-subtype.h> |
| +#include <linux/module.h> |
| +#include <linux/err.h> |
| +#include "crypto_keys.h" |
| + |
| +static LIST_HEAD(crypto_sig_parsers); |
| + |
| +/** |
| + * 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_sig_verify_context *verify_sig_begin( |
| + struct key *keyring, const void *sig, size_t siglen) |
| +{ |
| + struct crypto_sig_verify_context *ret; |
| + struct crypto_sig_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_sig_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_sig_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_sig_verify_context *ctx, |
| + const void *sig, size_t siglen) |
| +{ |
| + struct crypto_sig_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_sig_verify_context *ctx) |
| +{ |
| + struct crypto_sig_parser *parser = ctx->parser; |
| + |
| + ctx->cancel(ctx); |
| + module_put(parser->owner); |
| +} |
| +EXPORT_SYMBOL_GPL(verify_sig_cancel); |
| + |
| +/** |
| + * register_crypto_sig_parser - Register a crypto sig blob parser |
| + * @parser: The parser to register |
| + */ |
| +int register_crypto_sig_parser(struct crypto_sig_parser *parser) |
| +{ |
| + struct crypto_sig_parser *cursor; |
| + int ret; |
| + |
| + down_write(&crypto_key_parsers_sem); |
| + |
| + list_for_each_entry(cursor, &crypto_sig_parsers, link) { |
| + if (strcmp(cursor->name, parser->name) == 0) { |
| + pr_err("Crypto signature parser '%s' already registered\n", |
| + parser->name); |
| + ret = -EEXIST; |
| + goto out; |
| + } |
| + } |
| + |
| + list_add_tail(&parser->link, &crypto_sig_parsers); |
| + |
| + pr_notice("Crypto signature parser '%s' registered\n", parser->name); |
| + ret = 0; |
| + |
| +out: |
| + up_write(&crypto_key_parsers_sem); |
| + return ret; |
| +} |
| +EXPORT_SYMBOL_GPL(register_crypto_sig_parser); |
| + |
| +/** |
| + * unregister_crypto_sig_parser - Unregister a crypto sig blob parser |
| + * @parser: The parser to unregister |
| + */ |
| +void unregister_crypto_sig_parser(struct crypto_sig_parser *parser) |
| +{ |
| + down_write(&crypto_key_parsers_sem); |
| + list_del(&parser->link); |
| + up_write(&crypto_key_parsers_sem); |
| + |
| + pr_notice("Crypto signature parser '%s' unregistered\n", parser->name); |
| +} |
| +EXPORT_SYMBOL_GPL(unregister_crypto_sig_parser); |
| -- |
| 1.7.11.4 |
| |
| |
| From d8cb66a6f69869c8c2992f67915a7c238066f2fb Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 00:14:18 +0100 |
| Subject: [PATCH 05/32] 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 <dhowells@redhat.com> |
| |
| security/keys/crypto/Kconfig | 10 ++++ |
| security/keys/crypto/Makefile | 3 +- |
| security/keys/crypto/public_key.c | 82 +++++++++++++++++++++++++ |
| security/keys/crypto/public_key.h | 123 ++++++++++++++++++++++++++++++++++++++ |
| 4 files changed, 217 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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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..ebb31ec |
| |
| |
| @@ -0,0 +1,82 @@ |
| +/* 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 <linux/module.h> |
| +#include <linux/export.h> |
| +#include <linux/kernel.h> |
| +#include "public_key.h" |
| + |
| +MODULE_LICENSE("GPL"); |
| + |
| +const char *const pkey_algo[PKEY_ALGO__LAST] = { |
| + [PKEY_ALGO_DSA] = "DSA", |
| + [PKEY_ALGO_RSA] = "RSA", |
| +}; |
| +EXPORT_SYMBOL_GPL(pkey_algo); |
| + |
| +const char *const pkey_hash_algo[PKEY_HASH__LAST] = { |
| + [PKEY_HASH_MD4] = "md4", |
| + [PKEY_HASH_MD5] = "md5", |
| + [PKEY_HASH_SHA1] = "sha1", |
| + [PKEY_HASH_RIPE_MD_160] = "rmd160", |
| + [PKEY_HASH_SHA256] = "sha256", |
| + [PKEY_HASH_SHA384] = "sha384", |
| + [PKEY_HASH_SHA512] = "sha512", |
| + [PKEY_HASH_SHA224] = "sha224", |
| +}; |
| +EXPORT_SYMBOL_GPL(pkey_hash_algo); |
| + |
| +const char *const pkey_id_type[PKEY_ID_TYPE__LAST] = { |
| + [PKEY_ID_PGP] = "PGP", |
| + [PKEY_ID_X509] = "X509", |
| +}; |
| +EXPORT_SYMBOL_GPL(pkey_id_type); |
| + |
| +/* |
| + * 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_printf(m, "%s.%s", |
| + pkey_id_type[key->id_type], key->algo->name); |
| +} |
| + |
| +/* |
| + * Destroy a public key algorithm key |
| + */ |
| +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); |
| + } |
| +} |
| +EXPORT_SYMBOL_GPL(public_key_destroy); |
| + |
| +/* |
| + * 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..228090d |
| |
| |
| @@ -0,0 +1,123 @@ |
| +/* 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 <linux/mpi.h> |
| +#include <crypto/hash.h> |
| +#include <keys/crypto-subtype.h> |
| + |
| +struct public_key; |
| +struct public_key_signature; |
| + |
| +enum pkey_algo { |
| + PKEY_ALGO_DSA, |
| + PKEY_ALGO_RSA, |
| + PKEY_ALGO__LAST |
| +}; |
| + |
| +extern const char *const pkey_algo[PKEY_ALGO__LAST]; |
| + |
| +enum pkey_hash_algo { |
| + PKEY_HASH_MD4, |
| + 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 |
| +}; |
| + |
| +extern const char *const pkey_hash_algo[PKEY_HASH__LAST]; |
| + |
| +enum pkey_id_type { |
| + PKEY_ID_PGP, /* OpenPGP generated key ID */ |
| + PKEY_ID_X509, /* X.509 arbitrary subjectKeyIdentifier */ |
| + PKEY_ID_TYPE__LAST |
| +}; |
| + |
| +extern const char *const pkey_id_type[PKEY_ID_TYPE__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) |
| + enum pkey_id_type id_type : 8; |
| + 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_sig_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_subtype public_key_crypto_key_subtype; |
| + |
| +#endif /* _LINUX_PUBLIC_KEY_H */ |
| -- |
| 1.7.11.4 |
| |
| |
| From 76b6843d2be046bd5ab8fbae8a1cc5b5d2d48a80 Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 00:14:18 +0100 |
| Subject: [PATCH 06/32] MPILIB: Reinstate mpi_cmp[_ui]() and export for RSA |
| signature verification |
| |
| Reinstate and export mpi_cmp() and mpi_cmp_ui() from the MPI library for use by |
| RSA signature verification as per RFC3447 section 5.2.2 step 1. |
| |
| Signed-off-by: David Howells <dhowells@redhat.com> |
| |
| lib/mpi/Makefile | 1 + |
| lib/mpi/mpi-cmp.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| 2 files changed, 71 insertions(+) |
| create mode 100644 lib/mpi/mpi-cmp.c |
| |
| diff --git a/lib/mpi/Makefile b/lib/mpi/Makefile |
| index 45ca90a..019a68c 100644 |
| |
| |
| @@ -14,6 +14,7 @@ mpi-y = \ |
| generic_mpih-add1.o \ |
| mpicoder.o \ |
| mpi-bit.o \ |
| + mpi-cmp.o \ |
| mpih-cmp.o \ |
| mpih-div.o \ |
| mpih-mul.o \ |
| diff --git a/lib/mpi/mpi-cmp.c b/lib/mpi/mpi-cmp.c |
| new file mode 100644 |
| index 0000000..1871e7b |
| |
| |
| @@ -0,0 +1,70 @@ |
| +/* mpi-cmp.c - MPI functions |
| + * Copyright (C) 1998, 1999 Free Software Foundation, Inc. |
| + * |
| + * This file is part of GnuPG. |
| + * |
| + * GnuPG 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. |
| + * |
| + * GnuPG is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA |
| + */ |
| + |
| +#include "mpi-internal.h" |
| + |
| +int mpi_cmp_ui(MPI u, unsigned long v) |
| +{ |
| + mpi_limb_t limb = v; |
| + |
| + mpi_normalize(u); |
| + if (!u->nlimbs && !limb) |
| + return 0; |
| + if (u->sign) |
| + return -1; |
| + if (u->nlimbs > 1) |
| + return 1; |
| + |
| + if (u->d[0] == limb) |
| + return 0; |
| + else if (u->d[0] > limb) |
| + return 1; |
| + else |
| + return -1; |
| +} |
| +EXPORT_SYMBOL_GPL(mpi_cmp_ui); |
| + |
| +int mpi_cmp(MPI u, MPI v) |
| +{ |
| + mpi_size_t usize, vsize; |
| + int cmp; |
| + |
| + mpi_normalize(u); |
| + mpi_normalize(v); |
| + usize = u->nlimbs; |
| + vsize = v->nlimbs; |
| + if (!u->sign && v->sign) |
| + return 1; |
| + if (u->sign && !v->sign) |
| + return -1; |
| + if (usize != vsize && !u->sign && !v->sign) |
| + return usize - vsize; |
| + if (usize != vsize && u->sign && v->sign) |
| + return vsize + usize; |
| + if (!usize) |
| + return 0; |
| + cmp = mpihelp_cmp(u->d, v->d, usize); |
| + if (!cmp) |
| + return 0; |
| + if ((cmp < 0 ? 1 : 0) == (u->sign ? 1 : 0)) |
| + return 1; |
| + return -1; |
| +} |
| +EXPORT_SYMBOL_GPL(mpi_cmp); |
| -- |
| 1.7.11.4 |
| |
| |
| From 48c4be1c886e51edbbbebdb65421a7ca120a8d21 Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 00:14:18 +0100 |
| Subject: [PATCH 07/32] KEYS: RSA: Implement signature verification algorithm |
| [PKCS#1 / RFC3447] |
| |
| Implement RSA public key cryptography [PKCS#1 / RFC3447]. At this time, only |
| the signature verification algorithm is supported. This uses the asymmetric |
| public key subtype to hold its key data. |
| |
| Signed-off-by: David Howells <dhowells@redhat.com> |
| |
| security/keys/crypto/Kconfig | 7 + |
| security/keys/crypto/Makefile | 1 + |
| security/keys/crypto/crypto_rsa.c | 267 ++++++++++++++++++++++++++++++++++++++ |
| security/keys/crypto/public_key.h | 2 + |
| 4 files changed, 277 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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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..6e95e60 |
| |
| |
| @@ -0,0 +1,267 @@ |
| +/* 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 <linux/module.h> |
| +#include <linux/kernel.h> |
| +#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 *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(""); |
| + |
| + if (!RSA_ASN1_templates[sig->pkey_hash_algo].data) |
| + return -ENOTSUPP; |
| + |
| + /* (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; |
| + |
| + 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 228090d..947817b 100644 |
| |
| |
| @@ -61,6 +61,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.11.4 |
| |
| |
| From 311305a6e970236bd30d8942552a26e6f93730ce Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 00:14:19 +0100 |
| Subject: [PATCH 08/32] KEYS: RSA: 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 <mbroz@redhat.com> |
| Signed-off-by: David Howells <dhowells@redhat.com> |
| |
| 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 6e95e60..796ed1d 100644 |
| |
| |
| @@ -222,15 +222,23 @@ static int RSA_verify_signature(const struct public_key *key, |
| return -ENOTSUPP; |
| |
| /* (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.11.4 |
| |
| |
| From a9b954d2c225dc99ecc319ea760576f525f0a623 Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 00:14:19 +0100 |
| Subject: [PATCH 09/32] 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 <dhowells@redhat.com> |
| |
| 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 |
| |
| |
| @@ -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 <linux/types.h> |
| + |
| +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.11.4 |
| |
| |
| From 1937e5a7e8ae1abdb7f1dc72f7b128e33c31d644 Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 01:33:13 +0100 |
| Subject: [PATCH 10/32] 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 <dhowells@redhat.com> |
| |
| include/linux/pgplib.h | 47 +++++++ |
| security/keys/crypto/Kconfig | 6 + |
| security/keys/crypto/Makefile | 1 + |
| security/keys/crypto/pgp_library.c | 268 +++++++++++++++++++++++++++++++++++++ |
| 4 files changed, 322 insertions(+) |
| create mode 100644 include/linux/pgplib.h |
| create mode 100644 security/keys/crypto/pgp_library.c |
| |
| diff --git a/include/linux/pgplib.h b/include/linux/pgplib.h |
| new file mode 100644 |
| index 0000000..a045b3a |
| |
| |
| @@ -0,0 +1,47 @@ |
| +/* PGP library definitions (RFC 4880) |
| + * |
| + * 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. |
| + */ |
| + |
| +#ifndef _LINUX_PGPLIB_H |
| +#define _LINUX_PGPLIB_H |
| + |
| +#if defined(CONFIG_PGP_LIBRARY) || defined(CONFIG_PGP_LIBRARY_MODULE) |
| + |
| +#include <linux/pgp.h> |
| + |
| +/* |
| + * 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 /* CONFIG_PGP_LIBRARY */ |
| + |
| +#endif /* _LINUX_PGPLIB_H */ |
| diff --git a/security/keys/crypto/Kconfig b/security/keys/crypto/Kconfig |
| index 4e3777e..88ce0e2 100644 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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..39d2878 |
| |
| |
| @@ -0,0 +1,268 @@ |
| +/* 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 <linux/pgplib.h> |
| +#include <linux/errno.h> |
| +#include <linux/kernel.h> |
| +#include <linux/module.h> |
| + |
| +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. |
| + */ |
| +static 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,,)\n", 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: |
| + /* 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("Partial body length packet not supported\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; |
| + if ((int)size < 0) |
| + goto too_big; |
| + |
| + *_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; |
| +too_big: |
| + pr_debug("Signature subpacket size >2G\n"); |
| + return -EMSGSIZE; |
| +} |
| + |
| +/** |
| + * 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.11.4 |
| |
| |
| From 3379efc21d2ecc93de135e3265baabbe1f326d5a Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 01:33:17 +0100 |
| Subject: [PATCH 11/32] 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 <dhowells@redhat.com> |
| |
| include/linux/pgplib.h | 25 ++++ |
| security/keys/crypto/pgp_library.c | 280 +++++++++++++++++++++++++++++++++++++ |
| 2 files changed, 305 insertions(+) |
| |
| diff --git a/include/linux/pgplib.h b/include/linux/pgplib.h |
| index a045b3a..34594a9 100644 |
| |
| |
| @@ -41,6 +41,31 @@ 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_version version : 8; |
| + enum pgp_signature_type signature_type : 8; |
| + enum pgp_pubkey_algo pubkey_algo : 8; |
| + enum pgp_hash_algo hash_algo : 8; |
| + union { |
| + struct pgp_key_ID issuer; |
| + __be32 issuer32[2]; |
| + }; |
| +}; |
| + |
| +extern int pgp_parse_sig_params(const u8 **_data, size_t *_datalen, |
| + struct pgp_sig_parameters *p); |
| + |
| |
| #endif /* CONFIG_PGP_LIBRARY */ |
| |
| diff --git a/security/keys/crypto/pgp_library.c b/security/keys/crypto/pgp_library.c |
| index 39d2878..50b7fa0 100644 |
| |
| |
| @@ -266,3 +266,283 @@ 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. |
| + */ |
| +static 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 */ |
| + 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; |
| + |
| + type = *data++ & ~PGP_SIG_SUBPKT_TYPE_CRITICAL_MASK; |
| + datalen--; |
| + size--; |
| + |
| + *_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; |
| +too_big: |
| + pr_debug("Signature subpacket size >2G\n"); |
| + return -EMSGSIZE; |
| +} |
| + |
| +/** |
| + * 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]. |
| + */ |
| +static 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; |
| +} |
| + |
| +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 hash 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) |
| +{ |
| + const u8 *data = *_data; |
| + size_t datalen = *_datalen; |
| + int ret; |
| + |
| + pr_devel("-->pgp_parse_sig_params(,%zu,,)", datalen); |
| + |
| + if (datalen < 1) |
| + return -EBADMSG; |
| + p->version = *data; |
| + |
| + if (p->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 (p->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", |
| + p->version); |
| + return -EBADMSG; |
| + } |
| + |
| + *_data = data; |
| + *_datalen = datalen; |
| + return 0; |
| +} |
| +EXPORT_SYMBOL_GPL(pgp_parse_sig_params); |
| -- |
| 1.7.11.4 |
| |
| |
| From 39b6dd66338812a1e1806489d3fed50620011b14 Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 01:33:17 +0100 |
| Subject: [PATCH 12/32] 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. |
| |
| Thanks to Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> for pointing out |
| some errors. |
| |
| Signed-off-by: David Howells <dhowells@redhat.com> |
| |
| security/keys/crypto/Kconfig | 12 ++ |
| security/keys/crypto/Makefile | 4 + |
| security/keys/crypto/pgp_parser.h | 23 +++ |
| security/keys/crypto/pgp_public_key.c | 344 ++++++++++++++++++++++++++++++++++ |
| 4 files changed, 383 insertions(+) |
| create mode 100644 security/keys/crypto/pgp_parser.h |
| create mode 100644 security/keys/crypto/pgp_public_key.c |
| |
| diff --git a/security/keys/crypto/Kconfig b/security/keys/crypto/Kconfig |
| index 88ce0e2..1c2ae55 100644 |
| |
| |
| @@ -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..35733fc 100644 |
| |
| |
| @@ -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_key_parser.o |
| +pgp_key_parser-y := \ |
| + pgp_public_key.o |
| diff --git a/security/keys/crypto/pgp_parser.h b/security/keys/crypto/pgp_parser.h |
| new file mode 100644 |
| index 0000000..1cda231 |
| |
| |
| @@ -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 <linux/pgp.h> |
| + |
| +#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]; |
| diff --git a/security/keys/crypto/pgp_public_key.c b/security/keys/crypto/pgp_public_key.c |
| new file mode 100644 |
| index 0000000..c260e02 |
| |
| |
| @@ -0,0 +1,344 @@ |
| +/* Instantiate a public key crypto key from PGP format 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 <keys/crypto-subtype.h> |
| +#include <linux/module.h> |
| +#include <linux/kernel.h> |
| +#include <linux/slab.h> |
| +#include <linux/mpi.h> |
| +#include <linux/pgplib.h> |
| +#include <crypto/hash.h> |
| +#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(""); |
| + |
| + 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]); |
| + 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; |
| + key->id_type = PKEY_ID_PGP; |
| + |
| + 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); |
| + |
| + for (i = 0; i < algo->n_pub_mpi; i++) { |
| + unsigned int remaining = datalen; |
| + if (remaining == 0) { |
| + pr_debug("short %zu mpi %d\n", datalen, i); |
| + goto cleanup_badmsg; |
| + } |
| + key->mpi[i] = mpi_read_from_buffer(data, &remaining); |
| + if (!key->mpi[i]) |
| + goto cleanup_nomem; |
| + data += remaining; |
| + datalen -= remaining; |
| + } |
| + |
| + if (datalen != 0) { |
| + pr_debug("excess %zu\n", datalen); |
| + goto cleanup_badmsg; |
| + } |
| + |
| + 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_nomem: |
| + ret = -ENOMEM; |
| + goto cleanup; |
| +cleanup_badmsg: |
| + ret = -EBADMSG; |
| +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_preparse(struct key_preparsed_payload *prep) |
| +{ |
| + struct pgp_key_data_parse_context ctx; |
| + int ret; |
| + |
| + kenter(""); |
| + |
| + 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(prep->data, prep->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); |
| + return ret; |
| + } |
| + |
| + prep->type_data[0] = ctx.subtype; |
| + prep->type_data[1] = ctx.fingerprint; |
| + prep->payload = ctx.payload; |
| + prep->quotalen = prep->datalen; |
| + return 0; |
| +} |
| + |
| +static struct crypto_key_parser pgp_key_parser = { |
| + .owner = THIS_MODULE, |
| + .name = "pgp", |
| + .preparse = pgp_key_preparse, |
| +}; |
| + |
| +/* |
| + * 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); |
| -- |
| 1.7.11.4 |
| |
| |
| From 1095ff02a49868cf8f3706dd2c83474bcf2081f8 Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 01:33:17 +0100 |
| Subject: [PATCH 13/32] 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 <dhowells@redhat.com> |
| |
| security/keys/crypto/Makefile | 3 +- |
| security/keys/crypto/pgp_parser.h | 6 + |
| security/keys/crypto/pgp_sig_verify.c | 325 ++++++++++++++++++++++++++++++++++ |
| 3 files changed, 333 insertions(+), 1 deletion(-) |
| create mode 100644 security/keys/crypto/pgp_sig_verify.c |
| |
| diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile |
| index 35733fc..0c8b8a1 100644 |
| |
| |
| @@ -11,4 +11,5 @@ obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o |
| |
| obj-$(CONFIG_CRYPTO_KEY_PGP_PARSER) += pgp_key_parser.o |
| pgp_key_parser-y := \ |
| - pgp_public_key.o |
| + pgp_public_key.o \ |
| + pgp_sig_verify.o |
| diff --git a/security/keys/crypto/pgp_parser.h b/security/keys/crypto/pgp_parser.h |
| index 1cda231..6f5b3af 100644 |
| |
| |
| @@ -21,3 +21,9 @@ |
| */ |
| extern const |
| struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST]; |
| + |
| +/* |
| + * pgp_pubkey_sig.c |
| + */ |
| +extern struct crypto_sig_verify_context *pgp_pkey_verify_sig_begin( |
| + struct key *crypto_key, const u8 *sigdata, size_t siglen); |
| diff --git a/security/keys/crypto/pgp_sig_verify.c b/security/keys/crypto/pgp_sig_verify.c |
| new file mode 100644 |
| index 0000000..f9bb949 |
| |
| |
| @@ -0,0 +1,325 @@ |
| +/* PGP public key signature verification [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 <linux/module.h> |
| +#include <linux/kernel.h> |
| +#include <linux/slab.h> |
| +#include <linux/pgplib.h> |
| +#include <linux/err.h> |
| +#include "public_key.h" |
| +#include "pgp_parser.h" |
| + |
| +static 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_sig_verify_context *ctx, |
| + const void *data, size_t datalen); |
| +static int pgp_pkey_verify_sig_end(struct crypto_sig_verify_context *ctx, |
| + const u8 *sig, size_t siglen); |
| +static void pgp_pkey_verify_sig_cancel(struct crypto_sig_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_sig_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(-ENOPKG); |
| + } |
| + |
| + if (pgp_public_key_algorithms[p.params.pubkey_algo] != key->algo) { |
| + kleave(" = -EKEYREJECTED [wrong pk algo]"); |
| + return ERR_PTR(-EKEYREJECTED); |
| + } |
| + |
| + 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]) { |
| + pr_debug("Unsupported hash algorithm %u\n", |
| + p.params.hash_algo); |
| + 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_sig_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_sig_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_sig_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.11.4 |
| |
| |
| From 69d9f31b888090b5705b7148760143a6b706a116 Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 01:33:17 +0100 |
| Subject: [PATCH 14/32] 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 <dhowells@redhat.com> |
| |
| security/keys/crypto/Makefile | 1 + |
| security/keys/crypto/pgp_sig_parser.c | 136 ++++++++++++++++++++++++++++++++++ |
| 2 files changed, 137 insertions(+) |
| create mode 100644 security/keys/crypto/pgp_sig_parser.c |
| |
| diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile |
| index 0c8b8a1..a9a34c6 100644 |
| |
| |
| @@ -12,4 +12,5 @@ obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o |
| obj-$(CONFIG_CRYPTO_KEY_PGP_PARSER) += pgp_key_parser.o |
| pgp_key_parser-y := \ |
| pgp_public_key.o \ |
| + pgp_sig_parser.o \ |
| pgp_sig_verify.o |
| diff --git a/security/keys/crypto/pgp_sig_parser.c b/security/keys/crypto/pgp_sig_parser.c |
| new file mode 100644 |
| index 0000000..683eb53 |
| |
| |
| @@ -0,0 +1,136 @@ |
| +/* 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 <linux/module.h> |
| +#include <linux/kernel.h> |
| +#include <linux/pgplib.h> |
| +#include <linux/err.h> |
| +#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(-ENOMSG); |
| + |
| + 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)) { |
| + 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); |
| +} |
| + |
| +/* |
| + * Attempt to parse a signature as a PGP packet format blob and find a |
| + * matching key. |
| + */ |
| +static struct crypto_sig_verify_context *pgp_verify_sig_begin( |
| + struct key *keyring, const u8 *sig, size_t siglen) |
| +{ |
| + struct crypto_sig_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; |
| +} |
| + |
| +static struct crypto_sig_parser pgp_sig_parser = { |
| + .owner = THIS_MODULE, |
| + .name = "pgp", |
| + .verify_sig_begin = pgp_verify_sig_begin, |
| +}; |
| + |
| +/* |
| + * Module stuff |
| + */ |
| +static int __init pgp_sig_init(void) |
| +{ |
| + return register_crypto_sig_parser(&pgp_sig_parser); |
| +} |
| + |
| +static void __exit pgp_sig_exit(void) |
| +{ |
| + unregister_crypto_sig_parser(&pgp_sig_parser); |
| +} |
| + |
| +module_init(pgp_sig_init); |
| +module_exit(pgp_sig_exit); |
| -- |
| 1.7.11.4 |
| |
| |
| From fe109b5adbb22b3503cb1f72f5585543218ba990 Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 01:33:17 +0100 |
| Subject: [PATCH 15/32] KEYS: Provide PGP key description autogeneration |
| |
| Provide a facility to autogenerate the name of PGP keys from the contents of |
| the payload. If add_key() is given a blank description, a description is |
| constructed from the last user ID packet in the payload data plus the last 8 |
| hex digits of the key ID. For instance: |
| |
| keyctl padd crypto "" @s </tmp/key.pub |
| |
| might create a key with a constructed description that can be seen in |
| /proc/keys: |
| |
| 2f674b96 I--Q--- 1 perm 39390000 0 0 crypto Sample kernel key 31f0ae93: PGP.RSA 31f0ae93 [] |
| |
| Signed-off-by: David Howells <dhowells@redhat.com> |
| |
| security/keys/crypto/pgp_public_key.c | 64 +++++++++++++++++++++++++++++------ |
| 1 file changed, 53 insertions(+), 11 deletions(-) |
| |
| diff --git a/security/keys/crypto/pgp_public_key.c b/security/keys/crypto/pgp_public_key.c |
| index c260e02..2347ecd 100644 |
| |
| |
| @@ -52,6 +52,9 @@ struct pgp_key_data_parse_context { |
| struct crypto_key_subtype *subtype; |
| char *fingerprint; |
| void *payload; |
| + const char *user_id; |
| + size_t user_id_len; |
| + size_t fingerprint_len; |
| }; |
| |
| /* |
| @@ -166,6 +169,7 @@ static int pgp_generate_fingerprint(struct pgp_key_data_parse_context *ctx, |
| if (ret < 0) |
| goto cleanup_raw_fingerprint; |
| |
| + ctx->fingerprint_len = digest_size * 2; |
| fingerprint = kmalloc(digest_size * 2 + 1, GFP_KERNEL); |
| if (!fingerprint) |
| goto cleanup_raw_fingerprint; |
| @@ -212,6 +216,13 @@ static int pgp_process_public_key(struct pgp_parse_context *context, |
| |
| kenter(",%u,%u,,%zu", type, headerlen, datalen); |
| |
| + if (type == PGP_PKT_USER_ID) { |
| + ctx->user_id = data; |
| + ctx->user_id_len = datalen; |
| + kleave(" = 0 [user ID]"); |
| + return 0; |
| + } |
| + |
| if (ctx->subtype) { |
| kleave(" = -ENOKEY [already]"); |
| return -EBADMSG; |
| @@ -297,21 +308,44 @@ static int pgp_key_preparse(struct key_preparsed_payload *prep) |
| |
| kenter(""); |
| |
| + memset(&ctx, 0, sizeof(ctx)); |
| ctx.pgp.types_of_interest = |
| - (1 << PGP_PKT_PUBLIC_KEY) | (1 << PGP_PKT_PUBLIC_SUBKEY); |
| + (1 << PGP_PKT_PUBLIC_KEY) | (1 << PGP_PKT_PUBLIC_SUBKEY) | |
| + (1 << PGP_PKT_USER_ID); |
| ctx.pgp.process_packet = pgp_process_public_key; |
| - ctx.subtype = NULL; |
| - ctx.fingerprint = NULL; |
| - ctx.payload = NULL; |
| |
| ret = pgp_parse_packets(prep->data, prep->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); |
| - return ret; |
| + if (ret < 0) |
| + goto error; |
| + |
| + if (ctx.user_id && ctx.user_id_len > 0) { |
| + /* Propose a description for the key (user ID without the comment) */ |
| + size_t ulen = ctx.user_id_len, flen = ctx.fingerprint_len; |
| + const char *p; |
| + |
| + p = memchr(ctx.user_id, '(', ulen); |
| + if (p) { |
| + /* Remove the comment */ |
| + do { |
| + p--; |
| + } while (*p == ' ' && p > ctx.user_id); |
| + if (*p != ' ') |
| + p++; |
| + ulen = p - ctx.user_id; |
| + } |
| + |
| + if (ulen > 255 - 9) |
| + ulen = 255 - 9; |
| + prep->description = kmalloc(ulen + 1 + 8 + 1, GFP_KERNEL); |
| + ret = -ENOMEM; |
| + if (!prep->description) |
| + goto error; |
| + memcpy(prep->description, ctx.user_id, ulen); |
| + prep->description[ulen] = ' '; |
| + memcpy(prep->description + ulen + 1, |
| + ctx.fingerprint + flen - 8, 8); |
| + prep->description[ulen + 9] = 0; |
| + pr_debug("desc '%s'\n", prep->description); |
| } |
| |
| prep->type_data[0] = ctx.subtype; |
| @@ -319,6 +353,14 @@ static int pgp_key_preparse(struct key_preparsed_payload *prep) |
| prep->payload = ctx.payload; |
| prep->quotalen = prep->datalen; |
| return 0; |
| + |
| +error: |
| + if (ctx.payload) |
| + ctx.subtype->destroy(ctx.payload); |
| + if (ctx.subtype) |
| + module_put(ctx.subtype->owner); |
| + kfree(ctx.fingerprint); |
| + return ret; |
| } |
| |
| static struct crypto_key_parser pgp_key_parser = { |
| -- |
| 1.7.11.4 |
| |
| |
| From 77b00423d002eb013293177310644c8284b6ea2f Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 01:33:18 +0100 |
| Subject: [PATCH 16/32] 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 |
| |
| Thanks to Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> for some pointing |
| out some errors. |
| |
| Signed-off-by: David Howells <dhowells@redhat.com> |
| |
| 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 | 115 +++++++++++++++++++++++++++++++++ |
| 5 files changed, 148 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 0a886ec..be5067e 100644 |
| |
| |
| @@ -10,6 +10,7 @@ Contents: |
| - Signature verification. |
| - Implementing crypto parsers. |
| - Implementing crypto subtypes. |
| + - Initial PGP key preloading. |
| |
| |
| |
| @@ -279,3 +280,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); |
| + |
| +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 a |
| +description generated from the fingerprint and last user ID of each 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 0fb362a..ed9b203 100644 |
| |
| |
| @@ -31,4 +31,7 @@ extern void verify_sig_cancel(struct crypto_sig_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); |
| + |
| #endif /* _KEYS_CRYPTO_TYPE_H */ |
| diff --git a/security/keys/crypto/Kconfig b/security/keys/crypto/Kconfig |
| index 1c2ae55..8af0155 100644 |
| |
| |
| @@ -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 a9a34c6..c873674 100644 |
| |
| |
| @@ -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_key_parser.o |
| pgp_key_parser-y := \ |
| diff --git a/security/keys/crypto/pgp_preload.c b/security/keys/crypto/pgp_preload.c |
| new file mode 100644 |
| index 0000000..ca4cfe6 |
| |
| |
| @@ -0,0 +1,115 @@ |
| +/* 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 <linux/module.h> |
| +#include <linux/key.h> |
| +#include <linux/pgplib.h> |
| +#include <linux/err.h> |
| +#include <keys/crypto-type.h> |
| +#include "crypto_keys.h" |
| + |
| +struct preload_pgp_keys_context { |
| + struct pgp_parse_context pgp; |
| + key_ref_t keyring; |
| + const u8 *key_start; |
| + const u8 *key_end; |
| + bool found_key; |
| +}; |
| + |
| +/* |
| + * Create a key. |
| + */ |
| +static int __init create_pgp_key(struct preload_pgp_keys_context *ctx) |
| +{ |
| + key_ref_t key; |
| + |
| + key = key_create_or_update(ctx->keyring, "crypto", NULL, |
| + ctx->key_start, |
| + ctx->key_end - ctx->key_start, |
| + 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; |
| +} |
| + |
| +/* |
| + * 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); |
| + int ret; |
| + |
| + if (ctx->found_key) { |
| + ctx->key_end = data - headerlen; |
| + ret = create_pgp_key(ctx); |
| + if (ret < 0) |
| + return ret; |
| + } |
| + |
| + ctx->key_start = data - headerlen; |
| + ctx->found_key = true; |
| + 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. |
| + * |
| + * Preload a pack of keys from a PGP keyring blob. |
| + * |
| + * The keys have their descriptions generated from the user ID and fingerprint |
| + * in the PGP stream. 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) |
| +{ |
| + struct preload_pgp_keys_context ctx; |
| + int ret; |
| + |
| + 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.found_key = false; |
| + |
| + ret = pgp_parse_packets(pgpdata, pgpdatalen, &ctx.pgp); |
| + if (ret < 0) |
| + return ret; |
| + |
| + if (ctx.found_key) { |
| + ctx.key_end = pgpdata + pgpdatalen; |
| + return create_pgp_key(&ctx); |
| + } |
| + return 0; |
| +} |
| -- |
| 1.7.11.4 |
| |
| |
| From d569964b0037289f291f5ac48df54a6b90b3435a Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 01:33:48 +0100 |
| Subject: [PATCH 17/32] Make most arch asm/module.h files use |
| asm-generic/module.h |
| |
| Use the mapping of Elf_[SPE]hdr, Elf_Addr, Elf_Sym, Elf_Dyn, Elf_Rel/Rela, |
| ELF_R_TYPE() and ELF_R_SYM() to either the 32-bit version or the 64-bit version |
| into asm-generic/module.h for all arches bar MIPS. |
| |
| Also, use the generic definition mod_arch_specific where possible. |
| |
| To this end, I've defined three new config bools: |
| |
| (*) HAVE_MOD_ARCH_SPECIFIC |
| |
| Arches define this if they don't want to use the empty generic |
| mod_arch_specific struct. |
| |
| (*) MODULES_USE_ELF_RELA |
| |
| Arches define this if their modules can contain RELA records. This causes |
| the Elf_Rela mapping to be emitted and allows apply_relocate_add() to be |
| defined by the arch rather than have the core emit an error message. |
| |
| (*) MODULES_USE_ELF_REL |
| |
| Arches define this if their modules can contain REL records. This causes |
| the Elf_Rel mapping to be emitted and allows apply_relocate() to be |
| defined by the arch rather than have the core emit an error message. |
| |
| Note that it is possible to allow both REL and RELA records: m68k and mips are |
| two arches that do this. |
| |
| With this, some arch asm/module.h files can be deleted entirely and replaced |
| with a generic-y marker in the arch Kbuild file. |
| |
| Additionally, I have removed the bits from m32r and score that handle the |
| unsupported type of relocation record as that's now handled centrally. |
| |
| Thanks to Jonas Gorski <jonas.gorski@gmail.com> for some MIPS fixes. |
| |
| Signed-off-by: David Howells <dhowells@redhat.com> |
| Acked-by: Sam Ravnborg <sam@ravnborg.org> |
| |
| arch/Kconfig | 19 ++++++++++++++++++ |
| arch/alpha/Kconfig | 2 ++ |
| arch/alpha/include/asm/module.h | 10 ++-------- |
| arch/arm/Kconfig | 2 ++ |
| arch/arm/include/asm/module.h | 8 ++------ |
| arch/avr32/Kconfig | 2 ++ |
| arch/avr32/include/asm/module.h | 6 ++---- |
| arch/blackfin/Kconfig | 2 ++ |
| arch/blackfin/include/asm/module.h | 4 +--- |
| arch/c6x/Kconfig | 1 + |
| arch/c6x/include/asm/module.h | 12 +----------- |
| arch/cris/Kconfig | 1 + |
| arch/cris/include/asm/Kbuild | 2 ++ |
| arch/cris/include/asm/module.h | 9 --------- |
| arch/frv/include/asm/module.h | 8 +------- |
| arch/h8300/Kconfig | 1 + |
| arch/h8300/include/asm/Kbuild | 2 ++ |
| arch/h8300/include/asm/module.h | 11 ----------- |
| arch/hexagon/Kconfig | 1 + |
| arch/ia64/Kconfig | 2 ++ |
| arch/ia64/include/asm/module.h | 6 ++---- |
| arch/m32r/Kconfig | 1 + |
| arch/m32r/include/asm/Kbuild | 2 ++ |
| arch/m32r/include/asm/module.h | 10 ---------- |
| arch/m32r/kernel/module.c | 15 -------------- |
| arch/m68k/Kconfig | 3 +++ |
| arch/m68k/include/asm/module.h | 6 ++---- |
| arch/microblaze/Kconfig | 1 + |
| arch/mips/Kconfig | 3 +++ |
| arch/mips/include/asm/module.h | 10 ++++++++-- |
| arch/mips/kernel/module.c | 2 ++ |
| arch/mn10300/Kconfig | 1 + |
| arch/mn10300/include/asm/module.h | 7 +------ |
| arch/openrisc/Kconfig | 1 + |
| arch/parisc/Kconfig | 2 ++ |
| arch/parisc/include/asm/module.h | 16 +++------------ |
| arch/powerpc/Kconfig | 2 ++ |
| arch/powerpc/include/asm/module.h | 7 +------ |
| arch/s390/Kconfig | 2 ++ |
| arch/s390/include/asm/module.h | 18 +++-------------- |
| arch/score/Kconfig | 2 ++ |
| arch/score/include/asm/module.h | 6 +----- |
| arch/score/kernel/module.c | 10 ---------- |
| arch/sh/Kconfig | 2 ++ |
| arch/sh/include/asm/module.h | 14 +++---------- |
| arch/sparc/Kconfig | 1 + |
| arch/sparc/include/asm/Kbuild | 1 + |
| arch/sparc/include/asm/module.h | 24 ----------------------- |
| arch/tile/Kconfig | 1 + |
| arch/unicore32/Kconfig | 1 + |
| arch/x86/Kconfig | 2 ++ |
| arch/x86/um/Kconfig | 2 ++ |
| arch/xtensa/Kconfig | 1 + |
| arch/xtensa/include/asm/module.h | 9 +-------- |
| include/asm-generic/module.h | 40 +++++++++++++++++++++++++++++++------- |
| include/linux/moduleloader.h | 36 ++++++++++++++++++++++++++++++---- |
| kernel/module.c | 20 ------------------- |
| 57 files changed, 169 insertions(+), 223 deletions(-) |
| delete mode 100644 arch/cris/include/asm/module.h |
| delete mode 100644 arch/h8300/include/asm/module.h |
| delete mode 100644 arch/m32r/include/asm/module.h |
| delete mode 100644 arch/sparc/include/asm/module.h |
| |
| diff --git a/arch/Kconfig b/arch/Kconfig |
| index 72f2fa1..3450115 100644 |
| |
| |
| @@ -281,4 +281,23 @@ config SECCOMP_FILTER |
| |
| See Documentation/prctl/seccomp_filter.txt for details. |
| |
| +config HAVE_MOD_ARCH_SPECIFIC |
| + bool |
| + help |
| + The arch uses struct mod_arch_specific to store data. Many arches |
| + just need a simple module loader without arch specific data - those |
| + should not enable this. |
| + |
| +config MODULES_USE_ELF_RELA |
| + bool |
| + help |
| + Modules only use ELF RELA relocations. Modules with ELF REL |
| + relocations will give an error. |
| + |
| +config MODULES_USE_ELF_REL |
| + bool |
| + help |
| + Modules only use ELF REL relocations. Modules with ELF RELA |
| + relocations will give an error. |
| + |
| source "kernel/gcov/Kconfig" |
| diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig |
| index 9944ded..7e3710c 100644 |
| |
| |
| @@ -20,6 +20,8 @@ config ALPHA |
| select GENERIC_CMOS_UPDATE |
| select GENERIC_STRNCPY_FROM_USER |
| select GENERIC_STRNLEN_USER |
| + select HAVE_MOD_ARCH_SPECIFIC |
| + select MODULES_USE_ELF_RELA |
| help |
| The Alpha is a 64-bit general-purpose processor designed and |
| marketed by the Digital Equipment Corporation of blessed memory, |
| diff --git a/arch/alpha/include/asm/module.h b/arch/alpha/include/asm/module.h |
| index 7b63743..9cd13b5 100644 |
| |
| |
| @@ -1,19 +1,13 @@ |
| #ifndef _ALPHA_MODULE_H |
| #define _ALPHA_MODULE_H |
| |
| +#include <asm-generic/module.h> |
| + |
| struct mod_arch_specific |
| { |
| unsigned int gotsecindex; |
| }; |
| |
| -#define Elf_Sym Elf64_Sym |
| -#define Elf_Shdr Elf64_Shdr |
| -#define Elf_Ehdr Elf64_Ehdr |
| -#define Elf_Phdr Elf64_Phdr |
| -#define Elf_Dyn Elf64_Dyn |
| -#define Elf_Rel Elf64_Rel |
| -#define Elf_Rela Elf64_Rela |
| - |
| #define ARCH_SHF_SMALL SHF_ALPHA_GPREL |
| |
| #ifdef MODULE |
| diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig |
| index e91c7cd..c75c217 100644 |
| |
| |
| @@ -50,6 +50,8 @@ config ARM |
| select GENERIC_STRNCPY_FROM_USER |
| select GENERIC_STRNLEN_USER |
| select DCACHE_WORD_ACCESS if (CPU_V6 || CPU_V6K || CPU_V7) && !CPU_BIG_ENDIAN |
| + select HAVE_MOD_ARCH_SPECIFIC if ARM_UNWIND |
| + select MODULES_USE_ELF_REL |
| help |
| The ARM series is a line of low-power-consumption RISC chip designs |
| licensed by ARM Ltd and targeted at embedded applications and |
| diff --git a/arch/arm/include/asm/module.h b/arch/arm/include/asm/module.h |
| index 6c6809f..0d3a28d 100644 |
| |
| |
| @@ -1,9 +1,7 @@ |
| #ifndef _ASM_ARM_MODULE_H |
| #define _ASM_ARM_MODULE_H |
| |
| -#define Elf_Shdr Elf32_Shdr |
| -#define Elf_Sym Elf32_Sym |
| -#define Elf_Ehdr Elf32_Ehdr |
| +#include <asm-generic/module.h> |
| |
| struct unwind_table; |
| |
| @@ -16,13 +14,11 @@ enum { |
| ARM_SEC_DEVEXIT, |
| ARM_SEC_MAX, |
| }; |
| -#endif |
| |
| struct mod_arch_specific { |
| -#ifdef CONFIG_ARM_UNWIND |
| struct unwind_table *unwind[ARM_SEC_MAX]; |
| -#endif |
| }; |
| +#endif |
| |
| /* |
| * Add the ARM architecture version to the version magic string |
| diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig |
| index 5ade51c..06e73bf 100644 |
| |
| |
| @@ -15,6 +15,8 @@ config AVR32 |
| select ARCH_WANT_IPC_PARSE_VERSION |
| select ARCH_HAVE_NMI_SAFE_CMPXCHG |
| select GENERIC_CLOCKEVENTS |
| + select HAVE_MOD_ARCH_SPECIFIC |
| + select MODULES_USE_ELF_RELA |
| help |
| AVR32 is a high-performance 32-bit RISC microprocessor core, |
| designed for cost-sensitive embedded applications, with particular |
| diff --git a/arch/avr32/include/asm/module.h b/arch/avr32/include/asm/module.h |
| index 4514445..3f083d3 100644 |
| |
| |
| @@ -1,6 +1,8 @@ |
| #ifndef __ASM_AVR32_MODULE_H |
| #define __ASM_AVR32_MODULE_H |
| |
| +#include <asm-generic/module.h> |
| + |
| struct mod_arch_syminfo { |
| unsigned long got_offset; |
| int got_initialized; |
| @@ -17,10 +19,6 @@ struct mod_arch_specific { |
| struct mod_arch_syminfo *syminfo; |
| }; |
| |
| -#define Elf_Shdr Elf32_Shdr |
| -#define Elf_Sym Elf32_Sym |
| -#define Elf_Ehdr Elf32_Ehdr |
| - |
| #define MODULE_PROC_FAMILY "AVR32v1" |
| |
| #define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY |
| diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig |
| index f348619..a48d8be 100644 |
| |
| |
| @@ -41,6 +41,8 @@ config BLACKFIN |
| select HAVE_NMI_WATCHDOG if NMI_WATCHDOG |
| select GENERIC_SMP_IDLE_THREAD |
| select ARCH_USES_GETTIMEOFFSET if !GENERIC_CLOCKEVENTS |
| + select HAVE_MOD_ARCH_SPECIFIC |
| + select MODULES_USE_ELF_RELA |
| |
| config GENERIC_CSUM |
| def_bool y |
| diff --git a/arch/blackfin/include/asm/module.h b/arch/blackfin/include/asm/module.h |
| index ed5689b..231a149 100644 |
| |
| |
| @@ -7,9 +7,7 @@ |
| #ifndef _ASM_BFIN_MODULE_H |
| #define _ASM_BFIN_MODULE_H |
| |
| -#define Elf_Shdr Elf32_Shdr |
| -#define Elf_Sym Elf32_Sym |
| -#define Elf_Ehdr Elf32_Ehdr |
| +#include <asm-generic/module.h> |
| |
| struct mod_arch_specific { |
| Elf_Shdr *text_l1; |
| diff --git a/arch/c6x/Kconfig b/arch/c6x/Kconfig |
| index 052f81a..8f3a304 100644 |
| |
| |
| @@ -16,6 +16,7 @@ config C6X |
| select OF |
| select OF_EARLY_FLATTREE |
| select GENERIC_CLOCKEVENTS |
| + select MODULES_USE_ELF_RELA |
| |
| config MMU |
| def_bool n |
| diff --git a/arch/c6x/include/asm/module.h b/arch/c6x/include/asm/module.h |
| index a453f97..5c7269c 100644 |
| |
| |
| @@ -13,17 +13,7 @@ |
| #ifndef _ASM_C6X_MODULE_H |
| #define _ASM_C6X_MODULE_H |
| |
| -#define Elf_Shdr Elf32_Shdr |
| -#define Elf_Sym Elf32_Sym |
| -#define Elf_Ehdr Elf32_Ehdr |
| -#define Elf_Addr Elf32_Addr |
| -#define Elf_Word Elf32_Word |
| - |
| -/* |
| - * This file contains the C6x architecture specific module code. |
| - */ |
| -struct mod_arch_specific { |
| -}; |
| +#include <asm-generic/module.h> |
| |
| struct loaded_sections { |
| unsigned int new_vaddr; |
| diff --git a/arch/cris/Kconfig b/arch/cris/Kconfig |
| index e922154..7bb8cf9 100644 |
| |
| |
| @@ -47,6 +47,7 @@ config CRIS |
| select GENERIC_IOMAP |
| select GENERIC_SMP_IDLE_THREAD if ETRAX_ARCH_V32 |
| select GENERIC_CMOS_UPDATE |
| + select MODULES_USE_ELF_RELA |
| |
| config HZ |
| int |
| diff --git a/arch/cris/include/asm/Kbuild b/arch/cris/include/asm/Kbuild |
| index 04d02a5..28b690d 100644 |
| |
| |
| @@ -7,3 +7,5 @@ header-y += ethernet.h |
| header-y += etraxgpio.h |
| header-y += rs485.h |
| header-y += sync_serial.h |
| + |
| +generic-y += module.h |
| diff --git a/arch/cris/include/asm/module.h b/arch/cris/include/asm/module.h |
| deleted file mode 100644 |
| index 7ee7231..0000000 |
| |
| |
| @@ -1,9 +0,0 @@ |
| -#ifndef _ASM_CRIS_MODULE_H |
| -#define _ASM_CRIS_MODULE_H |
| -/* cris is simple */ |
| -struct mod_arch_specific { }; |
| - |
| -#define Elf_Shdr Elf32_Shdr |
| -#define Elf_Sym Elf32_Sym |
| -#define Elf_Ehdr Elf32_Ehdr |
| -#endif /* _ASM_CRIS_MODULE_H */ |
| diff --git a/arch/frv/include/asm/module.h b/arch/frv/include/asm/module.h |
| index 3d5c636..a8848f0 100644 |
| |
| |
| @@ -11,13 +11,7 @@ |
| #ifndef _ASM_MODULE_H |
| #define _ASM_MODULE_H |
| |
| -struct mod_arch_specific |
| -{ |
| -}; |
| - |
| -#define Elf_Shdr Elf32_Shdr |
| -#define Elf_Sym Elf32_Sym |
| -#define Elf_Ehdr Elf32_Ehdr |
| +#include <asm-generic/module.h> |
| |
| /* |
| * Include the architecture version. |
| diff --git a/arch/h8300/Kconfig b/arch/h8300/Kconfig |
| index 5e8a0d9..c149d3b29 100644 |
| |
| |
| @@ -6,6 +6,7 @@ config H8300 |
| select ARCH_WANT_IPC_PARSE_VERSION |
| select GENERIC_IRQ_SHOW |
| select GENERIC_CPU_DEVICES |
| + select MODULES_USE_ELF_RELA |
| |
| config SYMBOL_PREFIX |
| string |
| diff --git a/arch/h8300/include/asm/Kbuild b/arch/h8300/include/asm/Kbuild |
| index c68e168..871382d 100644 |
| |
| |
| @@ -1 +1,3 @@ |
| include include/asm-generic/Kbuild.asm |
| + |
| +generic-y += module.h |
| diff --git a/arch/h8300/include/asm/module.h b/arch/h8300/include/asm/module.h |
| deleted file mode 100644 |
| index 8e46724..0000000 |
| |
| |
| @@ -1,11 +0,0 @@ |
| -#ifndef _ASM_H8300_MODULE_H |
| -#define _ASM_H8300_MODULE_H |
| -/* |
| - * This file contains the H8/300 architecture specific module code. |
| - */ |
| -struct mod_arch_specific { }; |
| -#define Elf_Shdr Elf32_Shdr |
| -#define Elf_Sym Elf32_Sym |
| -#define Elf_Ehdr Elf32_Ehdr |
| - |
| -#endif /* _ASM_H8/300_MODULE_H */ |
| diff --git a/arch/hexagon/Kconfig b/arch/hexagon/Kconfig |
| index b2fdfb7..0744f7d 100644 |
| |
| |
| @@ -30,6 +30,7 @@ config HEXAGON |
| select KTIME_SCALAR |
| select GENERIC_CLOCKEVENTS |
| select GENERIC_CLOCKEVENTS_BROADCAST |
| + select MODULES_USE_ELF_RELA |
| ---help--- |
| Qualcomm Hexagon is a processor architecture designed for high |
| performance and low power across a wide variety of applications. |
| diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig |
| index 310cf57..6881464 100644 |
| |
| |
| @@ -39,6 +39,8 @@ config IA64 |
| select ARCH_THREAD_INFO_ALLOCATOR |
| select ARCH_CLOCKSOURCE_DATA |
| select GENERIC_TIME_VSYSCALL |
| + select HAVE_MOD_ARCH_SPECIFIC |
| + select MODULES_USE_ELF_RELA |
| default y |
| help |
| The Itanium Processor Family is Intel's 64-bit successor to |
| diff --git a/arch/ia64/include/asm/module.h b/arch/ia64/include/asm/module.h |
| index 908eaef..dfba22a 100644 |
| |
| |
| @@ -1,6 +1,8 @@ |
| #ifndef _ASM_IA64_MODULE_H |
| #define _ASM_IA64_MODULE_H |
| |
| +#include <asm-generic/module.h> |
| + |
| /* |
| * IA-64-specific support for kernel module loader. |
| * |
| @@ -29,10 +31,6 @@ struct mod_arch_specific { |
| unsigned int next_got_entry; /* index of next available got entry */ |
| }; |
| |
| -#define Elf_Shdr Elf64_Shdr |
| -#define Elf_Sym Elf64_Sym |
| -#define Elf_Ehdr Elf64_Ehdr |
| - |
| #define MODULE_PROC_FAMILY "ia64" |
| #define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY \ |
| "gcc-" __stringify(__GNUC__) "." __stringify(__GNUC_MINOR__) |
| diff --git a/arch/m32r/Kconfig b/arch/m32r/Kconfig |
| index 49498bb..fc61533 100644 |
| |
| |
| @@ -13,6 +13,7 @@ config M32R |
| select GENERIC_IRQ_SHOW |
| select GENERIC_ATOMIC64 |
| select ARCH_USES_GETTIMEOFFSET |
| + select MODULES_USE_ELF_RELA |
| |
| config SBUS |
| bool |
| diff --git a/arch/m32r/include/asm/Kbuild b/arch/m32r/include/asm/Kbuild |
| index c68e168..871382d 100644 |
| |
| |
| @@ -1 +1,3 @@ |
| include include/asm-generic/Kbuild.asm |
| + |
| +generic-y += module.h |
| diff --git a/arch/m32r/include/asm/module.h b/arch/m32r/include/asm/module.h |
| deleted file mode 100644 |
| index eb73ee0..0000000 |
| |
| |
| @@ -1,10 +0,0 @@ |
| -#ifndef _ASM_M32R_MODULE_H |
| -#define _ASM_M32R_MODULE_H |
| - |
| -struct mod_arch_specific { }; |
| - |
| -#define Elf_Shdr Elf32_Shdr |
| -#define Elf_Sym Elf32_Sym |
| -#define Elf_Ehdr Elf32_Ehdr |
| - |
| -#endif /* _ASM_M32R_MODULE_H */ |
| diff --git a/arch/m32r/kernel/module.c b/arch/m32r/kernel/module.c |
| index 3071fe8..38233b6 100644 |
| |
| |
| @@ -201,18 +201,3 @@ int apply_relocate_add(Elf32_Shdr *sechdrs, |
| } |
| return 0; |
| } |
| - |
| -int apply_relocate(Elf32_Shdr *sechdrs, |
| - const char *strtab, |
| - unsigned int symindex, |
| - unsigned int relsec, |
| - struct module *me) |
| -{ |
| -#if 0 |
| - printk(KERN_ERR "module %s: REL RELOCATION unsupported\n", |
| - me->name); |
| - return -ENOEXEC; |
| -#endif |
| - return 0; |
| - |
| -} |
| diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig |
| index 4a46990..714a850 100644 |
| |
| |
| @@ -12,6 +12,9 @@ config M68K |
| select FPU if MMU |
| select ARCH_WANT_IPC_PARSE_VERSION |
| select ARCH_USES_GETTIMEOFFSET if MMU && !COLDFIRE |
| + select HAVE_MOD_ARCH_SPECIFIC |
| + select MODULES_USE_ELF_REL |
| + select MODULES_USE_ELF_RELA |
| |
| config RWSEM_GENERIC_SPINLOCK |
| bool |
| diff --git a/arch/m68k/include/asm/module.h b/arch/m68k/include/asm/module.h |
| index edffe66..8b58fce 100644 |
| |
| |
| @@ -1,6 +1,8 @@ |
| #ifndef _ASM_M68K_MODULE_H |
| #define _ASM_M68K_MODULE_H |
| |
| +#include <asm-generic/module.h> |
| + |
| enum m68k_fixup_type { |
| m68k_fixup_memoffset, |
| m68k_fixup_vnode_shift, |
| @@ -36,8 +38,4 @@ struct module; |
| extern void module_fixup(struct module *mod, struct m68k_fixup_info *start, |
| struct m68k_fixup_info *end); |
| |
| -#define Elf_Shdr Elf32_Shdr |
| -#define Elf_Sym Elf32_Sym |
| -#define Elf_Ehdr Elf32_Ehdr |
| - |
| #endif /* _ASM_M68K_MODULE_H */ |
| diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig |
| index ab9afca..b4f409f 100644 |
| |
| |
| @@ -24,6 +24,7 @@ config MICROBLAZE |
| select GENERIC_CPU_DEVICES |
| select GENERIC_ATOMIC64 |
| select GENERIC_CLOCKEVENTS |
| + select MODULES_USE_ELF_RELA |
| |
| config SWAP |
| def_bool n |
| diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig |
| index 331d574..5ff52db 100644 |
| |
| |
| @@ -36,6 +36,9 @@ config MIPS |
| select BUILDTIME_EXTABLE_SORT |
| select GENERIC_CLOCKEVENTS |
| select GENERIC_CMOS_UPDATE |
| + select HAVE_MOD_ARCH_SPECIFIC |
| + select MODULES_USE_ELF_REL |
| + select MODULES_USE_ELF_RELA if 64BIT |
| |
| menu "Machine selection" |
| |
| diff --git a/arch/mips/include/asm/module.h b/arch/mips/include/asm/module.h |
| index 7531ecd..c93b62b 100644 |
| |
| |
| @@ -34,11 +34,14 @@ typedef struct { |
| } Elf64_Mips_Rela; |
| |
| #ifdef CONFIG_32BIT |
| - |
| #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 |
| @@ -49,11 +52,14 @@ typedef struct { |
| #endif |
| |
| #ifdef CONFIG_64BIT |
| - |
| #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/mips/kernel/module.c b/arch/mips/kernel/module.c |
| index 4f8c3cb..9f102cf 100644 |
| |
| |
| @@ -324,6 +324,7 @@ int apply_relocate(Elf_Shdr *sechdrs, const char *strtab, |
| return 0; |
| } |
| |
| +#ifdef CONFIG_MODULES_USE_ELF_RELA |
| int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, |
| unsigned int symindex, unsigned int relsec, |
| struct module *me) |
| @@ -363,6 +364,7 @@ int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, |
| |
| return 0; |
| } |
| +#endif |
| |
| /* Given an address, look for it in the module exception tables. */ |
| const struct exception_table_entry *search_module_dbetables(unsigned long addr) |
| diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig |
| index 5cfb086..aa03f2e 100644 |
| |
| |
| @@ -8,6 +8,7 @@ config MN10300 |
| select HAVE_ARCH_KGDB |
| select HAVE_NMI_WATCHDOG if MN10300_WD_TIMER |
| select GENERIC_CLOCKEVENTS |
| + select MODULES_USE_ELF_RELA |
| |
| config AM33_2 |
| def_bool n |
| diff --git a/arch/mn10300/include/asm/module.h b/arch/mn10300/include/asm/module.h |
| index 5d7057d..6571103 100644 |
| |
| |
| @@ -12,12 +12,7 @@ |
| #ifndef _ASM_MODULE_H |
| #define _ASM_MODULE_H |
| |
| -struct mod_arch_specific { |
| -}; |
| - |
| -#define Elf_Shdr Elf32_Shdr |
| -#define Elf_Sym Elf32_Sym |
| -#define Elf_Ehdr Elf32_Ehdr |
| +#include <asm-generic/module.h> |
| |
| /* |
| * Include the MN10300 architecture version. |
| diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig |
| index 49765b5..05f2ba4 100644 |
| |
| |
| @@ -21,6 +21,7 @@ config OPENRISC |
| select GENERIC_CLOCKEVENTS |
| select GENERIC_STRNCPY_FROM_USER |
| select GENERIC_STRNLEN_USER |
| + select MODULES_USE_ELF_RELA |
| |
| config MMU |
| def_bool y |
| diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig |
| index 3ff21b5..166d991 100644 |
| |
| |
| @@ -19,6 +19,8 @@ config PARISC |
| select ARCH_HAVE_NMI_SAFE_CMPXCHG |
| select GENERIC_SMP_IDLE_THREAD |
| select GENERIC_STRNCPY_FROM_USER |
| + select HAVE_MOD_ARCH_SPECIFIC |
| + select MODULES_USE_ELF_RELA |
| |
| help |
| The PA-RISC microprocessor is designed by Hewlett-Packard and used |
| diff --git a/arch/parisc/include/asm/module.h b/arch/parisc/include/asm/module.h |
| index 1f41234..bab37e9 100644 |
| |
| |
| @@ -1,21 +1,11 @@ |
| #ifndef _ASM_PARISC_MODULE_H |
| #define _ASM_PARISC_MODULE_H |
| + |
| +#include <asm-generic/module.h> |
| + |
| /* |
| * This file contains the parisc architecture specific module code. |
| */ |
| -#ifdef CONFIG_64BIT |
| -#define Elf_Shdr Elf64_Shdr |
| -#define Elf_Sym Elf64_Sym |
| -#define Elf_Ehdr Elf64_Ehdr |
| -#define Elf_Addr Elf64_Addr |
| -#define Elf_Rela Elf64_Rela |
| -#else |
| -#define Elf_Shdr Elf32_Shdr |
| -#define Elf_Sym Elf32_Sym |
| -#define Elf_Ehdr Elf32_Ehdr |
| -#define Elf_Addr Elf32_Addr |
| -#define Elf_Rela Elf32_Rela |
| -#endif |
| |
| struct unwind_table; |
| |
| diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig |
| index 352f416..74f8478 100644 |
| |
| |
| @@ -139,6 +139,8 @@ config PPC |
| select GENERIC_CLOCKEVENTS |
| select GENERIC_STRNCPY_FROM_USER |
| select GENERIC_STRNLEN_USER |
| + select HAVE_MOD_ARCH_SPECIFIC |
| + select MODULES_USE_ELF_RELA |
| |
| config EARLY_PRINTK |
| bool |
| diff --git a/arch/powerpc/include/asm/module.h b/arch/powerpc/include/asm/module.h |
| index 0192a4e..c1df590 100644 |
| |
| |
| @@ -11,6 +11,7 @@ |
| |
| #include <linux/list.h> |
| #include <asm/bug.h> |
| +#include <asm-generic/module.h> |
| |
| |
| #ifndef __powerpc64__ |
| @@ -60,16 +61,10 @@ struct mod_arch_specific { |
| */ |
| |
| #ifdef __powerpc64__ |
| -# define Elf_Shdr Elf64_Shdr |
| -# define Elf_Sym Elf64_Sym |
| -# define Elf_Ehdr Elf64_Ehdr |
| # ifdef MODULE |
| asm(".section .stubs,\"ax\",@nobits; .align 3; .previous"); |
| # endif |
| #else |
| -# define Elf_Shdr Elf32_Shdr |
| -# define Elf_Sym Elf32_Sym |
| -# define Elf_Ehdr Elf32_Ehdr |
| # ifdef MODULE |
| asm(".section .plt,\"ax\",@nobits; .align 3; .previous"); |
| asm(".section .init.plt,\"ax\",@nobits; .align 3; .previous"); |
| diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig |
| index 107610e..c76a052 100644 |
| |
| |
| @@ -125,6 +125,8 @@ config S390 |
| select GENERIC_CLOCKEVENTS |
| select KTIME_SCALAR if 32BIT |
| select HAVE_ARCH_SECCOMP_FILTER |
| + select HAVE_MOD_ARCH_SPECIFIC |
| + select MODULES_USE_ELF_RELA |
| |
| config SCHED_OMIT_FRAME_POINTER |
| def_bool y |
| diff --git a/arch/s390/include/asm/module.h b/arch/s390/include/asm/module.h |
| index f0b6b26..df1f861 100644 |
| |
| |
| @@ -1,5 +1,8 @@ |
| #ifndef _ASM_S390_MODULE_H |
| #define _ASM_S390_MODULE_H |
| + |
| +#include <asm-generic/module.h> |
| + |
| /* |
| * This file contains the s390 architecture specific module code. |
| */ |
| @@ -28,19 +31,4 @@ struct mod_arch_specific |
| struct mod_arch_syminfo *syminfo; |
| }; |
| |
| -#ifdef CONFIG_64BIT |
| -#define ElfW(x) Elf64_ ## x |
| -#define ELFW(x) ELF64_ ## x |
| -#else |
| -#define ElfW(x) Elf32_ ## x |
| -#define ELFW(x) ELF32_ ## x |
| -#endif |
| - |
| -#define Elf_Addr ElfW(Addr) |
| -#define Elf_Rela ElfW(Rela) |
| -#define Elf_Shdr ElfW(Shdr) |
| -#define Elf_Sym ElfW(Sym) |
| -#define Elf_Ehdr ElfW(Ehdr) |
| -#define ELF_R_SYM ELFW(R_SYM) |
| -#define ELF_R_TYPE ELFW(R_TYPE) |
| #endif /* _ASM_S390_MODULE_H */ |
| diff --git a/arch/score/Kconfig b/arch/score/Kconfig |
| index ba0f412..e2c8db4 100644 |
| |
| |
| @@ -10,6 +10,8 @@ config SCORE |
| select ARCH_DISCARD_MEMBLOCK |
| select GENERIC_CPU_DEVICES |
| select GENERIC_CLOCKEVENTS |
| + select HAVE_MOD_ARCH_SPECIFIC |
| + select MODULES_USE_ELF_REL |
| |
| choice |
| prompt "System type" |
| diff --git a/arch/score/include/asm/module.h b/arch/score/include/asm/module.h |
| index f0b5dc0..abf395b 100644 |
| |
| |
| @@ -3,6 +3,7 @@ |
| |
| #include <linux/list.h> |
| #include <asm/uaccess.h> |
| +#include <asm-generic/module.h> |
| |
| struct mod_arch_specific { |
| /* Data Bus Error exception tables */ |
| @@ -13,11 +14,6 @@ struct mod_arch_specific { |
| |
| typedef uint8_t Elf64_Byte; /* Type for a 8-bit quantity. */ |
| |
| -#define Elf_Shdr Elf32_Shdr |
| -#define Elf_Sym Elf32_Sym |
| -#define Elf_Ehdr Elf32_Ehdr |
| -#define Elf_Addr Elf32_Addr |
| - |
| /* Given an address, look for it in the exception tables. */ |
| #ifdef CONFIG_MODULES |
| const struct exception_table_entry *search_module_dbetables(unsigned long addr); |
| diff --git a/arch/score/kernel/module.c b/arch/score/kernel/module.c |
| index 469e3b6..1378d99 100644 |
| |
| |
| @@ -125,16 +125,6 @@ int apply_relocate(Elf_Shdr *sechdrs, const char *strtab, |
| return 0; |
| } |
| |
| -int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, |
| - unsigned int symindex, unsigned int relsec, |
| - struct module *me) |
| -{ |
| - /* Non-standard return value... most other arch's return -ENOEXEC |
| - * for an unsupported relocation variant |
| - */ |
| - return 0; |
| -} |
| - |
| /* Given an address, look for it in the module exception tables. */ |
| const struct exception_table_entry *search_module_dbetables(unsigned long addr) |
| { |
| diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig |
| index 36f5141..656329a 100644 |
| |
| |
| @@ -35,6 +35,8 @@ config SUPERH |
| select GENERIC_CMOS_UPDATE if SH_SH03 || SH_DREAMCAST |
| select GENERIC_STRNCPY_FROM_USER |
| select GENERIC_STRNLEN_USER |
| + select HAVE_MOD_ARCH_SPECIFIC if DWARF_UNWINDER |
| + select MODULES_USE_ELF_RELA |
| help |
| The SuperH is a RISC processor targeted for use in embedded systems |
| and consumer electronics; it was also used in the Sega Dreamcast |
| diff --git a/arch/sh/include/asm/module.h b/arch/sh/include/asm/module.h |
| index b7927de..81300d8b 100644 |
| |
| |
| @@ -1,21 +1,13 @@ |
| #ifndef _ASM_SH_MODULE_H |
| #define _ASM_SH_MODULE_H |
| |
| -struct mod_arch_specific { |
| +#include <asm-generic/module.h> |
| + |
| #ifdef CONFIG_DWARF_UNWINDER |
| +struct mod_arch_specific { |
| struct list_head fde_list; |
| struct list_head cie_list; |
| -#endif |
| }; |
| - |
| -#ifdef CONFIG_64BIT |
| -#define Elf_Shdr Elf64_Shdr |
| -#define Elf_Sym Elf64_Sym |
| -#define Elf_Ehdr Elf64_Ehdr |
| -#else |
| -#define Elf_Shdr Elf32_Shdr |
| -#define Elf_Sym Elf32_Sym |
| -#define Elf_Ehdr Elf32_Ehdr |
| #endif |
| |
| #ifdef CONFIG_CPU_LITTLE_ENDIAN |
| diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig |
| index 67f1f6f..a244e70 100644 |
| |
| |
| @@ -37,6 +37,7 @@ config SPARC |
| select GENERIC_CLOCKEVENTS |
| select GENERIC_STRNCPY_FROM_USER |
| select GENERIC_STRNLEN_USER |
| + select MODULES_USE_ELF_RELA |
| |
| config SPARC32 |
| def_bool !64BIT |
| diff --git a/arch/sparc/include/asm/Kbuild b/arch/sparc/include/asm/Kbuild |
| index 67f83e0..fbe1cb5 100644 |
| |
| |
| @@ -21,4 +21,5 @@ generic-y += div64.h |
| generic-y += local64.h |
| generic-y += irq_regs.h |
| generic-y += local.h |
| +generic-y += module.h |
| generic-y += word-at-a-time.h |
| diff --git a/arch/sparc/include/asm/module.h b/arch/sparc/include/asm/module.h |
| deleted file mode 100644 |
| index ff8e02d..0000000 |
| |
| |
| @@ -1,24 +0,0 @@ |
| -#ifndef __SPARC_MODULE_H |
| -#define __SPARC_MODULE_H |
| -struct mod_arch_specific { }; |
| - |
| -/* |
| - * Use some preprocessor magic to define the correct symbol |
| - * for sparc32 and sparc64. |
| - * Elf_Addr becomes Elf32_Addr for sparc32 and Elf64_Addr for sparc64 |
| - */ |
| -#define ___ELF(a, b, c) a##b##c |
| -#define __ELF(a, b, c) ___ELF(a, b, c) |
| -#define _Elf(t) __ELF(Elf, CONFIG_BITS, t) |
| -#define _ELF(t) __ELF(ELF, CONFIG_BITS, t) |
| - |
| -#define Elf_Shdr _Elf(_Shdr) |
| -#define Elf_Sym _Elf(_Sym) |
| -#define Elf_Ehdr _Elf(_Ehdr) |
| -#define Elf_Rela _Elf(_Rela) |
| -#define Elf_Addr _Elf(_Addr) |
| - |
| -#define ELF_R_SYM _ELF(_R_SYM) |
| -#define ELF_R_TYPE _ELF(_R_TYPE) |
| - |
| -#endif /* __SPARC_MODULE_H */ |
| diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig |
| index 932e443..1603f30 100644 |
| |
| |
| @@ -17,6 +17,7 @@ config TILE |
| select SYS_HYPERVISOR |
| select ARCH_HAVE_NMI_SAFE_CMPXCHG |
| select GENERIC_CLOCKEVENTS |
| + select MODULES_USE_ELF_RELA |
| |
| # FIXME: investigate whether we need/want these options. |
| # select HAVE_IOREMAP_PROT |
| diff --git a/arch/unicore32/Kconfig b/arch/unicore32/Kconfig |
| index b0a4743..5ef0814 100644 |
| |
| |
| @@ -14,6 +14,7 @@ config UNICORE32 |
| select GENERIC_IRQ_SHOW |
| select ARCH_WANT_FRAME_POINTERS |
| select GENERIC_IOMAP |
| + select MODULES_USE_ELF_REL |
| help |
| UniCore-32 is 32-bit Instruction Set Architecture, |
| including a series of low-power-consumption RISC chip |
| diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig |
| index 8ec3a1a..01726cb 100644 |
| |
| |
| @@ -97,6 +97,8 @@ config X86 |
| select KTIME_SCALAR if X86_32 |
| select GENERIC_STRNCPY_FROM_USER |
| select GENERIC_STRNLEN_USER |
| + select MODULES_USE_ELF_REL if X86_32 |
| + select MODULES_USE_ELF_RELA if X86_64 |
| |
| config INSTRUCTION_DECODER |
| def_bool (KPROBES || PERF_EVENTS || UPROBES) |
| diff --git a/arch/x86/um/Kconfig b/arch/x86/um/Kconfig |
| index 9926e11..a4b0c10 100644 |
| |
| |
| @@ -21,9 +21,11 @@ config 64BIT |
| config X86_32 |
| def_bool !64BIT |
| select HAVE_AOUT |
| + select MODULES_USE_ELF_REL |
| |
| config X86_64 |
| def_bool 64BIT |
| + select MODULES_USE_ELF_RELA |
| |
| config RWSEM_XCHGADD_ALGORITHM |
| def_bool X86_XADD && 64BIT |
| diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig |
| index 8ed64cf..4816e44 100644 |
| |
| |
| @@ -11,6 +11,7 @@ config XTENSA |
| select HAVE_GENERIC_HARDIRQS |
| select GENERIC_IRQ_SHOW |
| select GENERIC_CPU_DEVICES |
| + select MODULES_USE_ELF_RELA |
| help |
| Xtensa processors are 32-bit RISC machines designed by Tensilica |
| primarily for embedded systems. These processors are both |
| diff --git a/arch/xtensa/include/asm/module.h b/arch/xtensa/include/asm/module.h |
| index d9b34be..488b40c 100644 |
| |
| |
| @@ -13,15 +13,8 @@ |
| #ifndef _XTENSA_MODULE_H |
| #define _XTENSA_MODULE_H |
| |
| -struct mod_arch_specific |
| -{ |
| - /* No special elements, yet. */ |
| -}; |
| - |
| #define MODULE_ARCH_VERMAGIC "xtensa-" __stringify(XCHAL_CORE_ID) " " |
| |
| -#define Elf_Shdr Elf32_Shdr |
| -#define Elf_Sym Elf32_Sym |
| -#define Elf_Ehdr Elf32_Ehdr |
| +#include <asm-generic/module.h> |
| |
| #endif /* _XTENSA_MODULE_H */ |
| diff --git a/include/asm-generic/module.h b/include/asm-generic/module.h |
| index ed5b44d..14dc41d 100644 |
| |
| |
| @@ -5,18 +5,44 @@ |
| * Many architectures just need a simple module |
| * loader without arch specific data. |
| */ |
| +#ifndef CONFIG_HAVE_MOD_ARCH_SPECIFIC |
| struct mod_arch_specific |
| { |
| }; |
| +#endif |
| |
| #ifdef CONFIG_64BIT |
| -#define Elf_Shdr Elf64_Shdr |
| -#define Elf_Sym Elf64_Sym |
| -#define Elf_Ehdr Elf64_Ehdr |
| -#else |
| -#define Elf_Shdr Elf32_Shdr |
| -#define Elf_Sym Elf32_Sym |
| -#define Elf_Ehdr Elf32_Ehdr |
| +#define Elf_Shdr Elf64_Shdr |
| +#define Elf_Phdr Elf64_Phdr |
| +#define Elf_Sym Elf64_Sym |
| +#define Elf_Dyn Elf64_Dyn |
| +#define Elf_Ehdr Elf64_Ehdr |
| +#define Elf_Addr Elf64_Addr |
| +#ifdef CONFIG_MODULES_USE_ELF_REL |
| +#define Elf_Rel Elf64_Rel |
| +#endif |
| +#ifdef CONFIG_MODULES_USE_ELF_RELA |
| +#define Elf_Rela Elf64_Rela |
| +#endif |
| +#define ELF_R_TYPE(X) ELF64_R_TYPE(X) |
| +#define ELF_R_SYM(X) ELF64_R_SYM(X) |
| + |
| +#else /* CONFIG_64BIT */ |
| + |
| +#define Elf_Shdr Elf32_Shdr |
| +#define Elf_Phdr Elf32_Phdr |
| +#define Elf_Sym Elf32_Sym |
| +#define Elf_Dyn Elf32_Dyn |
| +#define Elf_Ehdr Elf32_Ehdr |
| +#define Elf_Addr Elf32_Addr |
| +#ifdef CONFIG_MODULES_USE_ELF_REL |
| +#define Elf_Rel Elf32_Rel |
| +#endif |
| +#ifdef CONFIG_MODULES_USE_ELF_RELA |
| +#define Elf_Rela Elf32_Rela |
| +#endif |
| +#define ELF_R_TYPE(X) ELF32_R_TYPE(X) |
| +#define ELF_R_SYM(X) ELF32_R_SYM(X) |
| #endif |
| |
| #endif /* __ASM_GENERIC_MODULE_H */ |
| diff --git a/include/linux/moduleloader.h b/include/linux/moduleloader.h |
| index b2be02e..560ca53 100644 |
| |
| |
| @@ -28,21 +28,49 @@ void *module_alloc(unsigned long size); |
| /* Free memory returned from module_alloc. */ |
| void module_free(struct module *mod, void *module_region); |
| |
| -/* Apply the given relocation to the (simplified) ELF. Return -error |
| - or 0. */ |
| +/* |
| + * Apply the given relocation to the (simplified) ELF. Return -error |
| + * or 0. |
| + */ |
| +#ifdef CONFIG_MODULES_USE_ELF_REL |
| int apply_relocate(Elf_Shdr *sechdrs, |
| const char *strtab, |
| unsigned int symindex, |
| unsigned int relsec, |
| struct module *mod); |
| +#else |
| +static inline int apply_relocate(Elf_Shdr *sechdrs, |
| + const char *strtab, |
| + unsigned int symindex, |
| + unsigned int relsec, |
| + struct module *me) |
| +{ |
| + printk(KERN_ERR "module %s: REL relocation unsupported\n", me->name); |
| + return -ENOEXEC; |
| +} |
| +#endif |
| |
| -/* Apply the given add relocation to the (simplified) ELF. Return |
| - -error or 0 */ |
| +/* |
| + * Apply the given add relocation to the (simplified) ELF. Return |
| + * -error or 0 |
| + */ |
| +#ifdef CONFIG_MODULES_USE_ELF_RELA |
| int apply_relocate_add(Elf_Shdr *sechdrs, |
| const char *strtab, |
| unsigned int symindex, |
| unsigned int relsec, |
| struct module *mod); |
| +#else |
| +static inline int apply_relocate_add(Elf_Shdr *sechdrs, |
| + const char *strtab, |
| + unsigned int symindex, |
| + unsigned int relsec, |
| + struct module *me) |
| +{ |
| + printk(KERN_ERR "module %s: REL relocation unsupported\n", me->name); |
| + return -ENOEXEC; |
| +} |
| +#endif |
| |
| /* Any final processing of module before access. Return -error or 0. */ |
| int module_finalize(const Elf_Ehdr *hdr, |
| diff --git a/kernel/module.c b/kernel/module.c |
| index 4edbd9c..087aeed 100644 |
| |
| |
| @@ -1949,26 +1949,6 @@ static int simplify_symbols(struct module *mod, const struct load_info *info) |
| return ret; |
| } |
| |
| -int __weak apply_relocate(Elf_Shdr *sechdrs, |
| - const char *strtab, |
| - unsigned int symindex, |
| - unsigned int relsec, |
| - struct module *me) |
| -{ |
| - pr_err("module %s: REL relocation unsupported\n", me->name); |
| - return -ENOEXEC; |
| -} |
| - |
| -int __weak apply_relocate_add(Elf_Shdr *sechdrs, |
| - const char *strtab, |
| - unsigned int symindex, |
| - unsigned int relsec, |
| - struct module *me) |
| -{ |
| - pr_err("module %s: RELA relocation unsupported\n", me->name); |
| - return -ENOEXEC; |
| -} |
| - |
| static int apply_relocations(struct module *mod, const struct load_info *info) |
| { |
| unsigned int i; |
| -- |
| 1.7.11.4 |
| |
| |
| From c6cfa3260f1e2961e69a2e3240695954aed24976 Mon Sep 17 00:00:00 2001 |
| From: Ralf Baechle <ralf@linux-mips.org> |
| Date: Thu, 16 Aug 2012 01:38:43 +0100 |
| Subject: [PATCH 18/32] MIPS: Fix module.c build for 32 bit |
| |
| Fixes build failure introduced by "Make most arch asm/module.h files use |
| asm-generic/module.h" by moving all the RELA processing code to a |
| separate file to be used only for RELA processing on 64-bit kernels. |
| |
| CC arch/mips/kernel/module.o |
| arch/mips/kernel/module.c:250:14: error: 'reloc_handlers_rela' defined but not |
| used [-Werror=unused-variable] |
| cc1: all warnings being treated as errors |
| |
| make[6]: *** [arch/mips/kernel/module.o] Error 1 |
| |
| Signed-off-by: Ralf Baechle <ralf@linux-mips.org> |
| Signed-off-by: David Howells <dhowells@redhat.com> |
| |
| arch/mips/kernel/Makefile | 1 + |
| arch/mips/kernel/module-rela.c | 144 +++++++++++++++++++++++++++++++++++++++++ |
| arch/mips/kernel/module.c | 124 +---------------------------------- |
| arch/mips/kernel/module.h | 12 ++++ |
| 4 files changed, 159 insertions(+), 122 deletions(-) |
| create mode 100644 arch/mips/kernel/module-rela.c |
| create mode 100644 arch/mips/kernel/module.h |
| |
| diff --git a/arch/mips/kernel/module-rela.c b/arch/mips/kernel/module-rela.c |
| new file mode 100644 |
| index 0000000..4e784a8 |
| |
| |
| @@ -0,0 +1,144 @@ |
| +/* |
| + * 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. |
| + * |
| + * This program is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this program; if not, write to the Free Software |
| + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| + * |
| + * Copyright (C) 2001 Rusty Russell. |
| + * Copyright (C) 2003, 2004 Ralf Baechle (ralf@linux-mips.org) |
| + * Copyright (C) 2005 Thiemo Seufer |
| + */ |
| + |
| +#include <linux/elf.h> |
| +#include <linux/err.h> |
| +#include <linux/errno.h> |
| +#include <linux/moduleloader.h> |
| +#include "module.h" |
| + |
| +static int apply_r_mips_32_rela(struct module *me, u32 *location, Elf_Addr v) |
| +{ |
| + *location = v; |
| + |
| + return 0; |
| +} |
| + |
| +static int apply_r_mips_26_rela(struct module *me, u32 *location, Elf_Addr v) |
| +{ |
| + if (v % 4) { |
| + pr_err("module %s: dangerous R_MIPS_26 RELArelocation\n", |
| + me->name); |
| + return -ENOEXEC; |
| + } |
| + |
| + if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) { |
| + printk(KERN_ERR |
| + "module %s: relocation overflow\n", |
| + me->name); |
| + return -ENOEXEC; |
| + } |
| + |
| + *location = (*location & ~0x03ffffff) | ((v >> 2) & 0x03ffffff); |
| + |
| + return 0; |
| +} |
| + |
| +static int apply_r_mips_hi16_rela(struct module *me, u32 *location, Elf_Addr v) |
| +{ |
| + *location = (*location & 0xffff0000) | |
| + ((((long long) v + 0x8000LL) >> 16) & 0xffff); |
| + |
| + return 0; |
| +} |
| + |
| +static int apply_r_mips_lo16_rela(struct module *me, u32 *location, Elf_Addr v) |
| +{ |
| + *location = (*location & 0xffff0000) | (v & 0xffff); |
| + |
| + return 0; |
| +} |
| + |
| +static int apply_r_mips_64_rela(struct module *me, u32 *location, Elf_Addr v) |
| +{ |
| + *(Elf_Addr *)location = v; |
| + |
| + return 0; |
| +} |
| + |
| +static int apply_r_mips_higher_rela(struct module *me, u32 *location, |
| + Elf_Addr v) |
| +{ |
| + *location = (*location & 0xffff0000) | |
| + ((((long long) v + 0x80008000LL) >> 32) & 0xffff); |
| + |
| + return 0; |
| +} |
| + |
| +static int apply_r_mips_highest_rela(struct module *me, u32 *location, |
| + Elf_Addr v) |
| +{ |
| + *location = (*location & 0xffff0000) | |
| + ((((long long) v + 0x800080008000LL) >> 48) & 0xffff); |
| + |
| + return 0; |
| +} |
| + |
| +static int (*reloc_handlers_rela[]) (struct module *me, u32 *location, |
| + Elf_Addr v) = { |
| + [R_MIPS_NONE] = apply_r_mips_none, |
| + [R_MIPS_32] = apply_r_mips_32_rela, |
| + [R_MIPS_26] = apply_r_mips_26_rela, |
| + [R_MIPS_HI16] = apply_r_mips_hi16_rela, |
| + [R_MIPS_LO16] = apply_r_mips_lo16_rela, |
| + [R_MIPS_64] = apply_r_mips_64_rela, |
| + [R_MIPS_HIGHER] = apply_r_mips_higher_rela, |
| + [R_MIPS_HIGHEST] = apply_r_mips_highest_rela |
| +}; |
| + |
| +int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, |
| + unsigned int symindex, unsigned int relsec, |
| + struct module *me) |
| +{ |
| + Elf_Mips_Rela *rel = (void *) sechdrs[relsec].sh_addr; |
| + Elf_Sym *sym; |
| + u32 *location; |
| + unsigned int i; |
| + Elf_Addr v; |
| + int res; |
| + |
| + pr_debug("Applying relocate section %u to %u\n", relsec, |
| + sechdrs[relsec].sh_info); |
| + |
| + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { |
| + /* This is where to make the change */ |
| + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr |
| + + rel[i].r_offset; |
| + /* This is the symbol it is referring to */ |
| + sym = (Elf_Sym *)sechdrs[symindex].sh_addr |
| + + ELF_MIPS_R_SYM(rel[i]); |
| + if (IS_ERR_VALUE(sym->st_value)) { |
| + /* Ignore unresolved weak symbol */ |
| + if (ELF_ST_BIND(sym->st_info) == STB_WEAK) |
| + continue; |
| + printk(KERN_WARNING "%s: Unknown symbol %s\n", |
| + me->name, strtab + sym->st_name); |
| + return -ENOENT; |
| + } |
| + |
| + v = sym->st_value + rel[i].r_addend; |
| + |
| + res = reloc_handlers_rela[ELF_MIPS_R_TYPE(rel[i])](me, location, v); |
| + if (res) |
| + return res; |
| + } |
| + |
| + return 0; |
| +} |
| diff --git a/arch/mips/kernel/module.h b/arch/mips/kernel/module.h |
| new file mode 100644 |
| index 0000000..675d091 |
| |
| |
| @@ -0,0 +1,12 @@ |
| +/* Internal definitions for MIPS module code |
| + * |
| + * 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. |
| + */ |
| + |
| +/* |
| + * module.c |
| + */ |
| +extern int apply_r_mips_none(struct module *me, u32 *location, Elf_Addr v); |
| diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile |
| index fdaf65e..cd1e6c2 100644 |
| |
| |
| @@ -31,6 +31,7 @@ obj-$(CONFIG_SYNC_R4K) += sync-r4k.o |
| |
| obj-$(CONFIG_STACKTRACE) += stacktrace.o |
| obj-$(CONFIG_MODULES) += mips_ksyms.o module.o |
| +obj-$(CONFIG_MODULES_USE_ELF_RELA) += module-rela.o |
| |
| obj-$(CONFIG_FUNCTION_TRACER) += mcount.o ftrace.o |
| |
| diff --git a/arch/mips/kernel/module.c b/arch/mips/kernel/module.c |
| index 9f102cf..e7dc80b 100644 |
| |
| |
| @@ -30,6 +30,7 @@ |
| #include <linux/kernel.h> |
| #include <linux/spinlock.h> |
| #include <linux/jump_label.h> |
| +#include "module.h" |
| |
| #include <asm/pgtable.h> /* MODULE_START */ |
| |
| @@ -51,7 +52,7 @@ void *module_alloc(unsigned long size) |
| } |
| #endif |
| |
| -static int apply_r_mips_none(struct module *me, u32 *location, Elf_Addr v) |
| +int apply_r_mips_none(struct module *me, u32 *location, Elf_Addr v) |
| { |
| return 0; |
| } |
| @@ -63,13 +64,6 @@ static int apply_r_mips_32_rel(struct module *me, u32 *location, Elf_Addr v) |
| return 0; |
| } |
| |
| -static int apply_r_mips_32_rela(struct module *me, u32 *location, Elf_Addr v) |
| -{ |
| - *location = v; |
| - |
| - return 0; |
| -} |
| - |
| static int apply_r_mips_26_rel(struct module *me, u32 *location, Elf_Addr v) |
| { |
| if (v % 4) { |
| @@ -91,26 +85,6 @@ static int apply_r_mips_26_rel(struct module *me, u32 *location, Elf_Addr v) |
| return 0; |
| } |
| |
| -static int apply_r_mips_26_rela(struct module *me, u32 *location, Elf_Addr v) |
| -{ |
| - if (v % 4) { |
| - pr_err("module %s: dangerous R_MIPS_26 RELArelocation\n", |
| - me->name); |
| - return -ENOEXEC; |
| - } |
| - |
| - if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) { |
| - printk(KERN_ERR |
| - "module %s: relocation overflow\n", |
| - me->name); |
| - return -ENOEXEC; |
| - } |
| - |
| - *location = (*location & ~0x03ffffff) | ((v >> 2) & 0x03ffffff); |
| - |
| - return 0; |
| -} |
| - |
| static int apply_r_mips_hi16_rel(struct module *me, u32 *location, Elf_Addr v) |
| { |
| struct mips_hi16 *n; |
| @@ -132,14 +106,6 @@ static int apply_r_mips_hi16_rel(struct module *me, u32 *location, Elf_Addr v) |
| return 0; |
| } |
| |
| -static int apply_r_mips_hi16_rela(struct module *me, u32 *location, Elf_Addr v) |
| -{ |
| - *location = (*location & 0xffff0000) | |
| - ((((long long) v + 0x8000LL) >> 16) & 0xffff); |
| - |
| - return 0; |
| -} |
| - |
| static void free_relocation_chain(struct mips_hi16 *l) |
| { |
| struct mips_hi16 *next; |
| @@ -217,38 +183,6 @@ out_danger: |
| return -ENOEXEC; |
| } |
| |
| -static int apply_r_mips_lo16_rela(struct module *me, u32 *location, Elf_Addr v) |
| -{ |
| - *location = (*location & 0xffff0000) | (v & 0xffff); |
| - |
| - return 0; |
| -} |
| - |
| -static int apply_r_mips_64_rela(struct module *me, u32 *location, Elf_Addr v) |
| -{ |
| - *(Elf_Addr *)location = v; |
| - |
| - return 0; |
| -} |
| - |
| -static int apply_r_mips_higher_rela(struct module *me, u32 *location, |
| - Elf_Addr v) |
| -{ |
| - *location = (*location & 0xffff0000) | |
| - ((((long long) v + 0x80008000LL) >> 32) & 0xffff); |
| - |
| - return 0; |
| -} |
| - |
| -static int apply_r_mips_highest_rela(struct module *me, u32 *location, |
| - Elf_Addr v) |
| -{ |
| - *location = (*location & 0xffff0000) | |
| - ((((long long) v + 0x800080008000LL) >> 48) & 0xffff); |
| - |
| - return 0; |
| -} |
| - |
| static int (*reloc_handlers_rel[]) (struct module *me, u32 *location, |
| Elf_Addr v) = { |
| [R_MIPS_NONE] = apply_r_mips_none, |
| @@ -258,18 +192,6 @@ static int (*reloc_handlers_rel[]) (struct module *me, u32 *location, |
| [R_MIPS_LO16] = apply_r_mips_lo16_rel |
| }; |
| |
| -static int (*reloc_handlers_rela[]) (struct module *me, u32 *location, |
| - Elf_Addr v) = { |
| - [R_MIPS_NONE] = apply_r_mips_none, |
| - [R_MIPS_32] = apply_r_mips_32_rela, |
| - [R_MIPS_26] = apply_r_mips_26_rela, |
| - [R_MIPS_HI16] = apply_r_mips_hi16_rela, |
| - [R_MIPS_LO16] = apply_r_mips_lo16_rela, |
| - [R_MIPS_64] = apply_r_mips_64_rela, |
| - [R_MIPS_HIGHER] = apply_r_mips_higher_rela, |
| - [R_MIPS_HIGHEST] = apply_r_mips_highest_rela |
| -}; |
| - |
| int apply_relocate(Elf_Shdr *sechdrs, const char *strtab, |
| unsigned int symindex, unsigned int relsec, |
| struct module *me) |
| @@ -324,48 +246,6 @@ int apply_relocate(Elf_Shdr *sechdrs, const char *strtab, |
| return 0; |
| } |
| |
| -#ifdef CONFIG_MODULES_USE_ELF_RELA |
| -int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, |
| - unsigned int symindex, unsigned int relsec, |
| - struct module *me) |
| -{ |
| - Elf_Mips_Rela *rel = (void *) sechdrs[relsec].sh_addr; |
| - Elf_Sym *sym; |
| - u32 *location; |
| - unsigned int i; |
| - Elf_Addr v; |
| - int res; |
| - |
| - pr_debug("Applying relocate section %u to %u\n", relsec, |
| - sechdrs[relsec].sh_info); |
| - |
| - for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { |
| - /* This is where to make the change */ |
| - location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr |
| - + rel[i].r_offset; |
| - /* This is the symbol it is referring to */ |
| - sym = (Elf_Sym *)sechdrs[symindex].sh_addr |
| - + ELF_MIPS_R_SYM(rel[i]); |
| - if (IS_ERR_VALUE(sym->st_value)) { |
| - /* Ignore unresolved weak symbol */ |
| - if (ELF_ST_BIND(sym->st_info) == STB_WEAK) |
| - continue; |
| - printk(KERN_WARNING "%s: Unknown symbol %s\n", |
| - me->name, strtab + sym->st_name); |
| - return -ENOENT; |
| - } |
| - |
| - v = sym->st_value + rel[i].r_addend; |
| - |
| - res = reloc_handlers_rela[ELF_MIPS_R_TYPE(rel[i])](me, location, v); |
| - if (res) |
| - return res; |
| - } |
| - |
| - return 0; |
| -} |
| -#endif |
| - |
| /* Given an address, look for it in the module exception tables. */ |
| const struct exception_table_entry *search_module_dbetables(unsigned long addr) |
| { |
| -- |
| 1.7.11.4 |
| |
| |
| From 661f0147e9414fb2237f56d88d1f92d8a42345c9 Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 01:38:45 +0100 |
| Subject: [PATCH 19/32] 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 <dhowells@redhat.com> |
| |
| 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 |
| |
| |
| @@ -58,6 +58,7 @@ |
| ELFNOTE_END |
| |
| #else /* !__ASSEMBLER__ */ |
| +#include <linux/stringify.h> |
| #include <linux/elf.h> |
| /* |
| * 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.11.4 |
| |
| |
| From 544f02e192a8a38153d7dedc61bc107545666c0d Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 01:38:45 +0100 |
| Subject: [PATCH 20/32] 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 <dhowells@redhat.com> |
| |
| .gitignore | 12 ++++++++++++ |
| Makefile | 1 + |
| scripts/mod/.gitignore | 1 + |
| 3 files changed, 14 insertions(+) |
| |
| diff --git a/.gitignore b/.gitignore |
| index 57af07c..7948eeb 100644 |
| |
| |
| @@ -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 ddf5be9..70a6b5b 100644 |
| |
| |
| @@ -1239,6 +1239,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 |
| |
| |
| @@ -1,4 +1,5 @@ |
| elfconfig.h |
| mk_elfconfig |
| modpost |
| +mod-extract |
| |
| -- |
| 1.7.11.4 |
| |
| |
| From 6e21809168e7b45a830ec354ec9fc1582fcffe4f Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 01:38:45 +0100 |
| Subject: [PATCH 21/32] 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 <dhowells@redhat.com> |
| |
| Documentation/module-signing.txt | 194 +++++++++++++++++++++++++++++++++++++++ |
| include/linux/modsign.h | 27 ++++++ |
| init/Kconfig | 54 +++++++++++ |
| 3 files changed, 275 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 |
| |
| |
| @@ -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 <<EOF |
| + %pubring modsign.pub |
| + %secring modsign.sec |
| + Key-Type: RSA |
| + Key-Length: 4096 |
| + Name-Real: A. N. Other |
| + Name-Comment: Kernel Module GPG key |
| + %commit |
| + EOF |
| + gpg --homedir . --batch --gen-key genkey |
| + |
| +The above generates fresh keys using /dev/random. If there's insufficient data |
| +in /dev/random, more can be provided using the rngd program if there's a |
| +hardware random number generator available. |
| + |
| +Note that no GPG password is used in the above scriptlet. |
| + |
| +The modsign.pub file is compiled into the kernel directly by the assembler by |
| +means of an ".incbin" directive in kernel/modsign-pubkey.c. |
| + |
| +Once the kernel is running, the keys are visible to root as kernel crypto keys |
| +in /proc/keys in a keyring called .module_sign: |
| + |
| +335ab517 I----- 1 perm 1f030000 0 0 keyring .module_sign: 2/4 |
| +38d7d169 I----- 1 perm 3f010000 0 0 crypto modsign.0: rsa 57532ca5 [] |
| +195fa736 I----- 1 perm 3f010000 0 0 crypto modsign.1: dsa 5acc2142 [] |
| + |
| +This keyring can be listed with the keyctl program. See: |
| + |
| + Documentation/security/keys-crypto.txt |
| + |
| +for more information of crypto keys. |
| + |
| + |
| +============================ |
| +SELECTING THE HASH ALGORITHM |
| +============================ |
| + |
| +The hash algorithm to be used is selected by a multiple choice configuration |
| +item that enables one of the following variables: |
| + |
| + CONFIG_SIG_SHA1 |
| + CONFIG_SIG_SHA224 |
| + CONFIG_SIG_SHA256 |
| + CONFIG_SIG_SHA384 |
| + CONFIG_SIG_SHA512 |
| + |
| +These cause an appropriate "--digest-algo=" parameter to be passed to gpg when |
| +signing a module and force the appropriate hash algorithm to be compiled |
| +directly into the kernel rather than being built as a module. |
| + |
| + |
| +============== |
| +MODULE SIGNING |
| +============== |
| + |
| +Modules will then be signed automatically. The kernel make command line can |
| +include the following options: |
| + |
| + (*) MODSECKEY=<secret-key-ring-path> |
| + |
| + This indicates the whereabouts of the GPG keyring that is the source of |
| + the secret key to be used. The default is "./modsign.sec". |
| + |
| + (*) MODPUBKEY=<public-key-ring-path> |
| + |
| + 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=<key-name> |
| + |
| + 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=<hash-algorithm> |
| + |
| + 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 |
| |
| |
| @@ -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 <linux/elfnote.h> |
| + |
| +/* |
| + * 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 af6c7f8..e23ed83 100644 |
| |
| |
| @@ -1585,6 +1585,60 @@ 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 KEYS |
| + 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.11.4 |
| |
| |
| From 7733934d34b7f03574b4578edfad4a60d6fe3d56 Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 01:38:45 +0100 |
| Subject: [PATCH 22/32] 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 <dhowells@redhat.com> |
| |
| 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 |
| |
| |
| @@ -14,7 +14,8 @@ |
| # 3) create one <module>.mod.c file pr. module |
| # 4) create one Module.symvers file with CRC for all exported symbols |
| # 5) compile all <module>.mod.c files |
| -# 6) final link of the module to a <module.ko> file |
| +# 6) final link of the module to a <module.ko> (or <module.unsigned>) file |
| +# 7) signs the modules to a <module.ko> 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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 <stdio.h> |
| +#include <stdlib.h> |
| +#include <stdint.h> |
| +#include <stdarg.h> |
| +#include <string.h> |
| +#include <unistd.h> |
| +#include <fcntl.h> |
| +#include <sys/mman.h> |
| +#include <sys/stat.h> |
| +#include <elf.h> |
| +#include <asm/byteorder.h> |
| + |
| +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] <modulefile> <extractfile>\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 |
| |
| |
| @@ -0,0 +1,16 @@ |
| +#!/bin/sh |
| +# |
| +# Generate a module signature note source file |
| +# |
| +# mod-sign.sh <sig-file> ><note-src-file> |
| +# |
| + |
| +SIG=$1 |
| + |
| +cat <<EOF |
| +#include <linux/modsign.h> |
| + |
| +ELFNOTE(MODSIGN_NOTE_NAME, MODSIGN_NOTE_TYPE, .incbin "$SIG") |
| +EOF |
| + |
| +exit 0 |
| -- |
| 1.7.11.4 |
| |
| |
| From ee3ca99bcf972f0d072d91f9256c39a197153b8e Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 01:38:45 +0100 |
| Subject: [PATCH 23/32] 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 <dhowells@redhat.com> |
| |
| include/linux/module.h | 3 ++ |
| kernel/Makefile | 1 + |
| kernel/module-verify-defs.h | 77 +++++++++++++++++++++++++++++++ |
| kernel/module-verify.c | 110 ++++++++++++++++++++++++++++++++++++++++++++ |
| kernel/module-verify.h | 20 ++++++++ |
| kernel/module.c | 26 +++++++++-- |
| 6 files changed, 232 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 |
| |
| |
| @@ -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 c0cc67a..cec222a 100644 |
| |
| |
| @@ -55,6 +55,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..141ddab |
| |
| |
| @@ -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..4bf857e |
| |
| |
| @@ -0,0 +1,110 @@ |
| +/* 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 <linux/kernel.h> |
| +#include <linux/module.h> |
| +#include <linux/slab.h> |
| +#include <linux/err.h> |
| +#include <linux/elf.h> |
| +#include <linux/elfnote.h> |
| +#include <linux/sched.h> |
| +#include <linux/cred.h> |
| +#include <linux/modsign.h> |
| +#include <linux/moduleparam.h> |
| +#include <keys/crypto-type.h> |
| +#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..c640634 |
| |
| |
| @@ -0,0 +1,20 @@ |
| +/* 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 087aeed..a59a9da 100644 |
| |
| |
| @@ -58,6 +58,7 @@ |
| #include <linux/jump_label.h> |
| #include <linux/pfn.h> |
| #include <linux/bsearch.h> |
| +#include "module-verify.h" |
| |
| #define CREATE_TRACE_POINTS |
| #include <trace/events/module.h> |
| @@ -2382,7 +2383,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; |
| @@ -2415,6 +2417,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; |
| @@ -2757,7 +2765,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; |
| @@ -2767,6 +2776,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) |
| @@ -2850,17 +2860,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; |
| @@ -3497,8 +3508,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.11.4 |
| |
| |
| From 0f8f372047d8220e1d918797972746bb9fe345d9 Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 01:38:45 +0100 |
| Subject: [PATCH 24/32] 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 <dhowells@redhat.com> |
| |
| kernel/Makefile | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ |
| 1 file changed, 49 insertions(+) |
| |
| diff --git a/kernel/Makefile b/kernel/Makefile |
| index cec222a..28cd248 100644 |
| |
| |
| @@ -132,3 +132,52 @@ 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) |
| +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 "###" >&2 |
| + @echo "### Now generating a sample key generation script." >&2 |
| + @echo "###" >&2 |
| + @echo "### IT IS STRONGLY RECOMMENDED THAT YOU SUPPLY YOUR OWN" >&2 |
| + @echo "### SCRIPT WITH APPROPRIATE NAME FIELDS FILLED IN." >&2 |
| + @echo "###" >&2 |
| + @echo "### If you have a hardware random number generator feeding" >&2 |
| + @echo "### into /dev/random, you should drop the %no-protection" >&2 |
| + @echo "### and %transient-key lines from the script." >&2 |
| + @echo "###" >&2 |
| + 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.11.4 |
| |
| |
| From be5544dce081ccb49fd452a6273c5024208b2f06 Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 01:38:45 +0100 |
| Subject: [PATCH 25/32] 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 <dhowells@redhat.com> |
| |
| kernel/Makefile | 25 ++++++++------- |
| kernel/modsign-pubkey.c | 75 +++++++++++++++++++++++++++++++++++++++++++++ |
| kernel/module-verify-defs.h | 4 +++ |
| kernel/module-verify.c | 2 -- |
| 4 files changed, 93 insertions(+), 13 deletions(-) |
| create mode 100644 kernel/modsign-pubkey.c |
| |
| diff --git a/kernel/Makefile b/kernel/Makefile |
| index 28cd248..1d20704 100644 |
| |
| |
| @@ -55,7 +55,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 |
| @@ -159,16 +160,18 @@ modsign.pub modsign.sec: genkey |
| rm -f pubring.gpg secring.gpg trustdb.gpg |
| |
| genkey: |
| - @echo "###" >&2 |
| - @echo "### Now generating a sample key generation script." >&2 |
| - @echo "###" >&2 |
| - @echo "### IT IS STRONGLY RECOMMENDED THAT YOU SUPPLY YOUR OWN" >&2 |
| - @echo "### SCRIPT WITH APPROPRIATE NAME FIELDS FILLED IN." >&2 |
| - @echo "###" >&2 |
| - @echo "### If you have a hardware random number generator feeding" >&2 |
| - @echo "### into /dev/random, you should drop the %no-protection" >&2 |
| - @echo "### and %transient-key lines from the script." >&2 |
| - @echo "###" >&2 |
| + @echo "kernel/Makefile:163: ###" >&2 |
| + @echo "kernel/Makefile:163: ### CONFIG_MODULE_SIG is enabled so a public key is needed." >&2 |
| + @echo "kernel/Makefile:163: ###" >&2 |
| + @echo "kernel/Makefile:163: ### Now generating a sample key generation script." >&2 |
| + @echo "kernel/Makefile:163: ###" >&2 |
| + @echo "kernel/Makefile:163: ### IT IS STRONGLY RECOMMENDED THAT YOU SUPPLY YOUR OWN" >&2 |
| + @echo "kernel/Makefile:163: ### SCRIPT WITH APPROPRIATE NAME FIELDS FILLED IN." >&2 |
| + @echo "kernel/Makefile:163: ###" >&2 |
| + @echo "kernel/Makefile:163: ### If you have a hardware random number generator feeding" >&2 |
| + @echo "kernel/Makefile:163: ### into /dev/random, you should drop the %no-protection" >&2 |
| + @echo "kernel/Makefile:163: ### and %transient-key lines from the script." >&2 |
| + @echo "kernel/Makefile:163: ###" >&2 |
| echo "%pubring modsign.pub" >genkey |
| echo "%secring modsign.sec" >>genkey |
| echo "%no-protection: yes" >> genkey |
| diff --git a/kernel/modsign-pubkey.c b/kernel/modsign-pubkey.c |
| new file mode 100644 |
| index 0000000..5fdb082 |
| |
| |
| @@ -0,0 +1,75 @@ |
| +/* 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 <linux/kernel.h> |
| +#include <linux/sched.h> |
| +#include <linux/cred.h> |
| +#include <linux/err.h> |
| +#include <keys/crypto-type.h> |
| +#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) < 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 141ddab..2fe31e1 100644 |
| |
| |
| @@ -11,6 +11,10 @@ |
| |
| #ifdef CONFIG_MODULE_SIG |
| |
| +#include <linux/module.h> |
| + |
| +extern struct key *modsign_keyring; |
| + |
| /* |
| * Internal state |
| */ |
| diff --git a/kernel/module-verify.c b/kernel/module-verify.c |
| index 4bf857e..05473e6 100644 |
| |
| |
| @@ -28,8 +28,6 @@ |
| #include <linux/err.h> |
| #include <linux/elf.h> |
| #include <linux/elfnote.h> |
| -#include <linux/sched.h> |
| -#include <linux/cred.h> |
| #include <linux/modsign.h> |
| #include <linux/moduleparam.h> |
| #include <keys/crypto-type.h> |
| -- |
| 1.7.11.4 |
| |
| |
| From 34c918aacc002f8a7226a26a0d8af614c6f4430e Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 01:38:45 +0100 |
| Subject: [PATCH 26/32] 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 <dhowells@redhat.com> |
| |
| kernel/module-verify.c | 230 +++++++++++++++++++++++++++++++++++++++++++++++++ |
| 1 file changed, 230 insertions(+) |
| |
| diff --git a/kernel/module-verify.c b/kernel/module-verify.c |
| index 05473e6..2161d11 100644 |
| |
| |
| @@ -51,6 +51,228 @@ 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, 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 */ |
| + section = &mvdata->sections[SHN_UNDEF]; |
| + seccheck(section->sh_type == SHT_NULL); |
| + |
| + /* We also want access to the section name table */ |
| + section = &mvdata->sections[hdr->e_shstrndx]; |
| + seccheck(section->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); |
| + if (section->sh_addralign > 1) |
| + 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: |
| +#ifdef Elf_Rel |
| + seccheck(section->sh_entsize == sizeof(Elf_Rel)); |
| + seccheck(section->sh_addralign % sizeof(long) == 0); |
| + break; |
| +#else |
| + seccheck(false); |
| + break; |
| +#endif |
| + case SHT_RELA: |
| +#ifdef Elf_Rela |
| + 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; |
| + |
| +#ifdef Elf_Rel |
| + case SHT_REL: |
| +#endif |
| +#ifdef Elf_Rela |
| + 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) |
| @@ -62,6 +284,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.11.4 |
| |
| |
| From 2de4559e24c416e6813c10edbe3cc433ecd0dd50 Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 01:38:46 +0100 |
| Subject: [PATCH 27/32] 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 <dhowells@redhat.com> |
| |
| kernel/module-verify.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++ |
| 1 file changed, 80 insertions(+) |
| |
| diff --git a/kernel/module-verify.c b/kernel/module-verify.c |
| index 2161d11..646b104 100644 |
| |
| |
| @@ -273,6 +273,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) |
| @@ -303,7 +377,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.11.4 |
| |
| |
| From 3c8e71a46663f1fc3ee49fe3f6fa5c3bb85b704c Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 01:38:46 +0100 |
| Subject: [PATCH 28/32] 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 <dhowells@redhat.com> |
| |
| kernel/module-verify-defs.h | 13 +- |
| kernel/module-verify.c | 332 +++++++++++++++++++++++++++++++++++++++++++- |
| 2 files changed, 338 insertions(+), 7 deletions(-) |
| |
| diff --git a/kernel/module-verify-defs.h b/kernel/module-verify-defs.h |
| index 2fe31e1..cb477a2 100644 |
| |
| |
| @@ -19,7 +19,7 @@ extern struct key *modsign_keyring; |
| * Internal state |
| */ |
| struct module_verify_data { |
| - struct crypto_key_verify_context *mod_sig; /* Module signing context */ |
| + struct crypto_sig_verify_context *mod_sig; /* Module signing context */ |
| union { |
| const void *buffer; /* module buffer */ |
| const Elf_Ehdr *hdr; /* ELF header */ |
| @@ -42,15 +42,16 @@ struct module_verify_data { |
| /* |
| * Whether or not we support various types of ELF relocation record |
| */ |
| -#if defined(MODULE_HAS_ELF_REL_ONLY) |
| +#ifdef Elf_Rel |
| #define is_elf_rel(sh_type) ((sh_type) == SHT_REL) |
| -#define is_elf_rela(sh_type) (0) |
| -#elif defined(MODULE_HAS_ELF_RELA_ONLY) |
| +#else |
| #define is_elf_rel(sh_type) (0) |
| +#endif |
| + |
| +#ifdef Elf_Rela |
| #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) |
| +#define is_elf_rela(sh_type) (0) |
| #endif |
| |
| /* |
| diff --git a/kernel/module-verify.c b/kernel/module-verify.c |
| index 646b104..bee7e04 100644 |
| |
| |
| @@ -50,6 +50,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. |
| @@ -346,6 +362,320 @@ static int module_verify_canonicalise(struct module_verify_data *mvdata) |
| return 0; |
| } |
| |
| +#ifdef Elf_Rel |
| +/* |
| + * 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 { |
| +#ifdef CONFIG_64BIT |
| + 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 |
| + 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; |
| +#endif |
| + } __packed relocation; |
| + |
| + const Elf_Shdr *relsec, *symsec, *strsec; |
| + const Elf_Rel *reloc; |
| + const Elf_Sym *symbols, *symbol; |
| + const char *strings; |
| + unsigned long r_sym; |
| + 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 |
| + * { 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; |
| +} |
| +#endif |
| + |
| +#ifdef Elf_Rela |
| +/* |
| + * 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 { |
| +#ifdef CONFIG_64BIT |
| + 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 |
| + 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; |
| +#endif |
| + } __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; |
| +} |
| +#endif |
| + |
| +/* |
| + * Verify a module's signature |
| + */ |
| +static noinline int module_verify_signature(struct module_verify_data *mvdata) |
| +{ |
| + struct crypto_sig_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. |
| + */ |
| +#ifdef Elf_Rel |
| + 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; |
| + } |
| +#endif |
| + |
| +#ifdef Elf_Rela |
| + 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; |
| + } |
| +#endif |
| + |
| + /* 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 |
| */ |
| @@ -382,7 +712,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.11.4 |
| |
| |
| From 53142a9c74e2922885d03555d26213fc38553b90 Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 01:38:46 +0100 |
| Subject: [PATCH 29/32] 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 <dhowells@redhat.com> |
| |
| kernel/module-verify.c | 6 +++--- |
| 1 file changed, 3 insertions(+), 3 deletions(-) |
| |
| diff --git a/kernel/module-verify.c b/kernel/module-verify.c |
| index bee7e04..f3a694f 100644 |
| |
| |
| @@ -97,11 +97,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.11.4 |
| |
| |
| From 14d36171021b1c16f6c664bd4ab31e1d989ab282 Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Thu, 16 Aug 2012 01:38:46 +0100 |
| Subject: [PATCH 30/32] MODSIGN: Panic the kernel if FIPS is enabled upon |
| module signing failure |
| |
| If module signing fails when the kernel is running with FIPS enabled then the |
| kernel should panic lest the crypto layer be compromised. Possibly a panic |
| shouldn't happen on cases like ENOMEM. |
| |
| Reported-by: Stephan Mueller <stephan.mueller@atsec.com> |
| Signed-off-by: David Howells <dhowells@redhat.com> |
| |
| kernel/module-verify.c | 5 +++++ |
| 1 file changed, 5 insertions(+) |
| |
| diff --git a/kernel/module-verify.c b/kernel/module-verify.c |
| index f3a694f..896c0ff 100644 |
| |
| |
| @@ -30,6 +30,7 @@ |
| #include <linux/elfnote.h> |
| #include <linux/modsign.h> |
| #include <linux/moduleparam.h> |
| +#include <linux/fips.h> |
| #include <keys/crypto-type.h> |
| #include "module-verify.h" |
| #include "module-verify-defs.h" |
| @@ -716,6 +717,10 @@ int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok) |
| kfree(mvdata.canonlist); |
| |
| out: |
| + if (ret < 0 && fips_enabled) |
| + panic("Module verification failed with error %d in FIPS mode\n", |
| + ret); |
| + |
| switch (ret) { |
| case 0: /* Good signature */ |
| *_gpgsig_ok = true; |
| -- |
| 1.7.11.4 |
| |
| |
| From 1e8e625508f013acfb8ade3b5c30dcc7ff710ce9 Mon Sep 17 00:00:00 2001 |
| From: Josh Boyer <jwboyer@redhat.com> |
| Date: Thu, 16 Aug 2012 01:38:46 +0100 |
| Subject: [PATCH 31/32] MODSIGN: Allow modules to be signed with an unknown |
| key unless enforcing |
| |
| Currently we fail the loading of modules that are signed with a public key |
| that is not in the modsign keyring even if we are not in enforcing mode. |
| This is somewhat at odds with the fact that we allow a completely unsigned |
| module to load in such a case. |
| |
| We should allow modules signed with an unknown key to load in cases |
| where we are not enforcing and not in FIPS mode. |
| |
| Signed-off-by: Josh Boyer <jwboyer@redhat.com> |
| Signed-off-by: David Howells <dhowells@redhat.com> |
| |
| kernel/module-verify.c | 7 +++++++ |
| 1 file changed, 7 insertions(+) |
| |
| diff --git a/kernel/module-verify.c b/kernel/module-verify.c |
| index 896c0ff..041506f 100644 |
| |
| |
| @@ -736,6 +736,13 @@ out: |
| break; |
| case -ENOKEY: /* Signed, but we don't have the public key */ |
| pr_err("Module signed with unknown public key\n"); |
| + if (!modsign_signedonly) { |
| + /* Allow a module to be signed with an unknown public |
| + * key unless we're enforcing. |
| + */ |
| + pr_info("Allowing\n"); |
| + ret = 0; |
| + } |
| break; |
| default: /* Other error (probably ENOMEM) */ |
| break; |
| -- |
| 1.7.11.4 |
| |
| |
| From 7ac7095ee6624789c6a971d16f5ca823ebbde3c7 Mon Sep 17 00:00:00 2001 |
| From: Peter Jones <pjones@redhat.com> |
| Date: Thu, 16 Aug 2012 01:38:46 +0100 |
| Subject: [PATCH 32/32] MODSIGN: Fix documentation of signed-nokey behavior |
| when not enforcing. |
| |
| jwboyer's previous commit changes the behavior of module signing when |
| there's a valid signature but we don't know the public key and are in |
| permissive mode. This updates the documentation to match. |
| |
| Signed-off-by: Peter Jones <pjones@redhat.com> |
| Acked-by: Josh Boyer <jwboyer@redhat.com> |
| Signed-off-by: David Howells <dhowells@redhat.com> |
| |
| Documentation/module-signing.txt | 2 +- |
| 1 file changed, 1 insertion(+), 1 deletion(-) |
| |
| diff --git a/Documentation/module-signing.txt b/Documentation/module-signing.txt |
| index d75d473..8c4bef9 100644 |
| |
| |
| @@ -185,7 +185,7 @@ This table indicates the behaviours of the various situations: |
| MODULE STATE PERMISSIVE MODE ENFORCING MODE |
| |
| Unsigned Ok EKEYREJECTED |
| - Signed, no public key ENOKEY ENOKEY |
| + Signed, no public key Ok ENOKEY |
| Validly signed, public key Ok Ok |
| Invalidly signed, public key EKEYREJECTED EKEYREJECTED |
| Validly signed, expired key EKEYEXPIRED EKEYEXPIRED |
| -- |
| 1.7.11.4 |
| |