From 711fd460b3d44d666fbddd80a91ae5f825c7ebb6 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 13:59:15 +0100 Subject: [PATCH 01/28] 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 Cc: David S. Miller Cc: Dmitry Kasatkin Cc: Arnd Bergmann --- 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 --- /dev/null +++ b/include/asm-generic/bitops/count_zeros.h @@ -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 + +/** + * 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 --- a/lib/mpi/longlong.h +++ b/lib/mpi/longlong.h @@ -19,6 +19,8 @@ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. */ +#include + /* 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 --- a/lib/mpi/mpi-bit.c +++ b/lib/mpi/mpi-bit.c @@ -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 --- a/lib/mpi/mpi-pow.c +++ b/lib/mpi/mpi-pow.c @@ -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.2 From 1d6e2f2b87e6651bead1c0ccca699681f92dd52c Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 13:59:51 +0100 Subject: [PATCH 02/28] KEYS: Create a key type that can be used for general cryptographic operations Create a key type that can be used for general cryptographic operations, such as encryption, decryption, signature generation and signature verification. The key type is "crypto" and can provide access to a variety of cryptographic algorithms. Signed-off-by: David Howells --- Documentation/security/keys-crypto.txt | 181 ++++++++++++++++++++++++++ include/keys/crypto-subtype.h | 56 ++++++++ include/keys/crypto-type.h | 25 ++++ security/keys/Kconfig | 2 + security/keys/Makefile | 1 + security/keys/crypto/Kconfig | 7 + security/keys/crypto/Makefile | 7 + security/keys/crypto/crypto_keys.h | 28 ++++ security/keys/crypto/crypto_type.c | 228 +++++++++++++++++++++++++++++++++ 9 files changed, 535 insertions(+) create mode 100644 Documentation/security/keys-crypto.txt create mode 100644 include/keys/crypto-subtype.h create mode 100644 include/keys/crypto-type.h create mode 100644 security/keys/crypto/Kconfig create mode 100644 security/keys/crypto/Makefile create mode 100644 security/keys/crypto/crypto_keys.h create mode 100644 security/keys/crypto/crypto_type.c diff --git a/Documentation/security/keys-crypto.txt b/Documentation/security/keys-crypto.txt new file mode 100644 index 0000000..97dee80 --- /dev/null +++ b/Documentation/security/keys-crypto.txt @@ -0,0 +1,181 @@ + ====================== + CRYPTOGRAPHIC KEY TYPE + ====================== + +Contents: + + - Overview. + - Key identification. + - Accessing crypto keys. + - Implementing crypto parsers. + - Implementing crypto subtypes. + + +======== +OVERVIEW +======== + +The "crypto" key type is designed to be a container for cryptographic keys, +without imposing any particular restrictions on the form of the cryptography or +the key. + +The crypto key is given a subtype that defines what sort of data is associated +with the key and provides operations to describe and destroy it. However, no +requirement is made that the key data actually be loaded into the key. + +The crypto key also has a number of data parsers registered with it. The data +parsers are responsible for extracing information the blobs of data passed to +the instantiator function. The first data parser that recognises the blob gets +to set the subtype of the key and define the operations that can be done on +that key. + +Completely in-kernel key retention and operation subtypes and parsers can be +defined, but it would also be possible to provide access to cryptographic +hardware (such as a TPM) that might be used to both retain the relevant key and +perform operations using that key. In such a case, the crypto key would then +merely be an interface to the TPM driver. + + +================== +KEY IDENTIFICATION +================== + +Because the identity of a key is not necessarily known and may not be easily +calculated when a crypto key is allocated, it may not be a simple matter to set +a key description to something that's useful for determining whether this is +the key you're looking for. Furthermore, it may be necessary to perform a +partial match upon the key identity. + +To help with this, when a key is loaded, the parser calculates the key +fingerprint and stores a copy in the key structure. + +The crypto key type's key matching function then performs more checks than just +the straightforward comparison of the description with the criterion string: + + (1) If the criterion string is of the form "id:" then the match + function will examine a key's fingerprint to see if the hex digits given + after the "id:" match the tail. For instance: + + keyctl search @s crypto id:5acc2142 + + will match a key with fingerprint: + + 1A00 2040 7601 7889 DE11 882C 3823 04AD 5ACC 2142 + + (2) If the criterion string is of the form ":" then the + match will match the ID as in (1), but with the added restriction that + only keys of the specified subtype (e.g. dsa or rsa) will be matched. For + instance: + + keyctl search @s crypto dsa:5acc2142 + +Looking in /proc/keys, the last 8 hex digits of the key fingerprint are +displayed, along with the subtype: + + 1a39e171 I----- 1 perm 3f010000 0 0 crypto modsign.0: DSA 5acc2142 [] + + +===================== +ACCESSING CRYPTO KEYS +===================== + +To access crypto keys from within the kernel, the following inclusion is +required: + + #include + +This gives access to the key type: + + struct key_type key_type_crypto; + + +=========================== +IMPLEMENTING CRYPTO PARSERS +=========================== + +The crypto key type keeps a list of registered data parsers. An example of +such a parser is one that parses OpenPGP packet formatted data [RFC 4880]. + +During key instantiation each parser in the list is tried until one doesn't +return -EBADMSG. + +The parser definition structure looks like the following: + + struct crypto_key_parser { + struct module *owner; + const char *name; + + int (*instantiate)(struct key *key, + const void *data, size_t datalen); + }; + +The owner and name fields should be set to the owning module and the name of +the parser. + +There are a number of operations defined by the parser. They are all optional, +but it is expected that at least one will be defined. + + (1) instantiate(). + + The arguments are the same as for the instantiate function in the key + type. 'key' is the crypto key being instantiated; data and datalen are + the instantiation data, presumably containing cryptographic key data, and + the length of that data. + + If the data format is not recognised, -EBADMSG should be returned. If it + is recognised, but the key cannot for some reason be set up, some other + negative error code should be returned. + + If the key can be successfully set up, then key->payload should be set to + point to the retained data, key->type_data.p[0] should be set to point to + the subtype chosen and key->type_data.p[1] should be set to point to a + copy of the key's identity string and 0 should be returned. + + The key's identity string may be partially matched upon. For a public-key + algorithm such as RSA and DSA this will likely be a printable hex version + of the key's fingerprint. + +Functions are provided to register and unregister parsers: + + int register_crypto_key_parser(struct crypto_key_parser *parser); + void unregister_crypto_key_parser(struct crypto_key_parser *subtype); + +Parsers may not have the same name. The names are only used for displaying in +debugging messages. + + +============================ +IMPLEMENTING CRYPTO SUBTYPES +============================ + +The parser selects the appropriate subtype directly and sets it on the key; the +crypto key then retains a reference on the subtype module (which means the +parser can be removed thereafter). + +The subtype definition structure looks like the following: + + struct crypto_key_subtype { + struct module *owner; + const char *name; + + void (*describe)(const struct key *key, struct seq_file *m); + void (*destroy)(void *payload); + }; + +The owner and name fields should be set to the owning module and the name of +the subtype. + +There are a number of operations defined by the subtype: + + (1) describe(). + + Mandatory. This allows the subtype to display something in /proc/keys + against the key. For instance the name of the public key algorithm type + could be displayed. The key type will display the tail of the key + identity string after this. + + (2) destroy(). + + Mandatory. This should free the memory associated with the key. The + crypto key will look after freeing the fingerprint and releasing the + reference on the subtype module. diff --git a/include/keys/crypto-subtype.h b/include/keys/crypto-subtype.h new file mode 100644 index 0000000..fa87555 --- /dev/null +++ b/include/keys/crypto-subtype.h @@ -0,0 +1,56 @@ +/* Cryptographic key subtype + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + * + * See Documentation/security/keys-crypto.txt + */ + +#ifndef _KEYS_CRYPTO_SUBTYPE_H +#define _KEYS_CRYPTO_SUBTYPE_H + +#include +#include + +extern struct key_type key_type_crypto; + +/* + * Keys of this type declare a subtype that indicates the handlers and + * capabilities. + */ +struct crypto_key_subtype { + struct module *owner; + const char *name; + unsigned short name_len; /* length of name */ + + void (*describe)(const struct key *key, struct seq_file *m); + + void (*destroy)(void *payload); +}; + +/* + * Data parser. Called during instantiation and signature verification + * initiation. + */ +struct crypto_key_parser { + struct list_head link; + struct module *owner; + const char *name; + + /* Attempt to instantiate a key from the data blob passed to add_key() + * or keyctl_instantiate(). + * + * Return EBADMSG if not recognised. + */ + int (*instantiate)(struct key *key, const void *data, size_t datalen); +}; + +extern int register_crypto_key_parser(struct crypto_key_parser *); +extern void unregister_crypto_key_parser(struct crypto_key_parser *); + +#endif /* _KEYS_CRYPTO_SUBTYPE_H */ diff --git a/include/keys/crypto-type.h b/include/keys/crypto-type.h new file mode 100644 index 0000000..47c00c7 --- /dev/null +++ b/include/keys/crypto-type.h @@ -0,0 +1,25 @@ +/* Cryptographic key type interface + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + * + * See Documentation/security/keys-crypto.txt + */ + +#ifndef _KEYS_CRYPTO_TYPE_H +#define _KEYS_CRYPTO_TYPE_H + +#include + +extern struct key_type key_type_crypto; + +/* + * The payload is at the discretion of the subtype. + */ + +#endif /* _KEYS_CRYPTO_TYPE_H */ diff --git a/security/keys/Kconfig b/security/keys/Kconfig index a90d6d3..992fe52 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -69,3 +69,5 @@ config KEYS_DEBUG_PROC_KEYS the resulting table. If you are unsure as to whether this is required, answer N. + +source security/keys/crypto/Kconfig diff --git a/security/keys/Makefile b/security/keys/Makefile index 504aaa0..67dae73 100644 --- a/security/keys/Makefile +++ b/security/keys/Makefile @@ -24,3 +24,4 @@ obj-$(CONFIG_SYSCTL) += sysctl.o # obj-$(CONFIG_TRUSTED_KEYS) += trusted.o obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/ +obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto/ diff --git a/security/keys/crypto/Kconfig b/security/keys/crypto/Kconfig new file mode 100644 index 0000000..3d15710 --- /dev/null +++ b/security/keys/crypto/Kconfig @@ -0,0 +1,7 @@ +config CRYPTO_KEY_TYPE + tristate "Cryptographic key type" + depends on KEYS + help + This option provides support for a type of key that holds the keys + required for cryptographic operations such as encryption, decryption, + signature generation and signature verification. diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile new file mode 100644 index 0000000..36db1d5 --- /dev/null +++ b/security/keys/crypto/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for cryptographic keys +# + +obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o + +crypto_keys-y := crypto_type.o diff --git a/security/keys/crypto/crypto_keys.h b/security/keys/crypto/crypto_keys.h new file mode 100644 index 0000000..a339ce0 --- /dev/null +++ b/security/keys/crypto/crypto_keys.h @@ -0,0 +1,28 @@ +/* Internal crypto type stuff + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +static inline +struct crypto_key_subtype *crypto_key_subtype(const struct key *key) +{ + return key->type_data.p[0]; +} + +static inline char *crypto_key_id(const struct key *key) +{ + return key->type_data.p[1]; +} + + +/* + * crypto_type.c + */ +extern struct list_head crypto_key_parsers; +extern struct rw_semaphore crypto_key_parsers_sem; diff --git a/security/keys/crypto/crypto_type.c b/security/keys/crypto/crypto_type.c new file mode 100644 index 0000000..33d279b --- /dev/null +++ b/security/keys/crypto/crypto_type.c @@ -0,0 +1,228 @@ +/* Cryptographic key type + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + * + * See Documentation/security/keys-crypto.txt + */ +#include +#include +#include +#include +#include "crypto_keys.h" + +MODULE_LICENSE("GPL"); + +LIST_HEAD(crypto_key_parsers); +DECLARE_RWSEM(crypto_key_parsers_sem); + +/* + * Match crypto_keys on (part of) their name + * We have some shorthand methods for matching keys. We allow: + * + * "" - request a key by description + * "id:" - request a key matching the ID + * ":" - request a key of a subtype + */ +static int crypto_key_match(const struct key *key, const void *description) +{ + const struct crypto_key_subtype *subtype = crypto_key_subtype(key); + const char *spec = description; + const char *id, *kid; + ptrdiff_t speclen; + size_t idlen, kidlen; + + if (!subtype || !spec || !*spec) + return 0; + + /* See if the full key description matches as is */ + if (key->description && strcmp(key->description, description) == 0) + return 1; + + /* All tests from here on break the criterion description into a + * specifier, a colon and then an identifier. + */ + id = strchr(spec, ':'); + if (!id) + return 0; + + speclen = id - spec; + id++; + + /* Anything after here requires a partial match on the ID string */ + kid = crypto_key_id(key); + if (!kid) + return 0; + + idlen = strlen(id); + kidlen = strlen(kid); + if (idlen > kidlen) + return 0; + + kid += kidlen - idlen; + if (strcasecmp(id, kid) != 0) + return 0; + + if (speclen == 2 && + memcmp(spec, "id", 2) == 0) + return 1; + + if (speclen == subtype->name_len && + memcmp(spec, subtype->name, speclen) == 0) + return 1; + + return 0; +} + +/* + * Describe the crypto key + */ +static void crypto_key_describe(const struct key *key, struct seq_file *m) +{ + const struct crypto_key_subtype *subtype = crypto_key_subtype(key); + const char *kid = crypto_key_id(key); + size_t n; + + seq_puts(m, key->description); + + if (subtype) { + seq_puts(m, ": "); + subtype->describe(key, m); + + if (kid) { + seq_putc(m, ' '); + n = strlen(kid); + if (n <= 8) + seq_puts(m, kid); + else + seq_puts(m, kid + n - 8); + } + + seq_puts(m, " ["); + /* put something here to indicate the key's capabilities */ + seq_putc(m, ']'); + } +} + +/* + * Instantiate a crypto_key defined key + */ +static int crypto_key_instantiate(struct key *key, + const void *data, size_t datalen) +{ + struct crypto_key_parser *parser; + int ret; + + pr_devel("==>%s()\n", __func__); + + if (datalen == 0) + return -EINVAL; + + down_read(&crypto_key_parsers_sem); + + ret = -EBADMSG; + list_for_each_entry(parser, &crypto_key_parsers, link) { + pr_debug("Trying parser '%s'\n", parser->name); + + ret = parser->instantiate(key, data, datalen); + if (ret != -EBADMSG) { + pr_debug("Parser recognised the format (ret %d)\n", + ret); + break; + } + } + + up_read(&crypto_key_parsers_sem); + pr_devel("<==%s() = %d\n", __func__, ret); + return ret; +} + +/* + * dispose of the data dangling from the corpse of a crypto key + */ +static void crypto_key_destroy(struct key *key) +{ + struct crypto_key_subtype *subtype = crypto_key_subtype(key); + if (subtype) { + subtype->destroy(key->payload.data); + module_put(subtype->owner); + key->type_data.p[0] = NULL; + } + kfree(key->type_data.p[1]); + key->type_data.p[1] = NULL; +} + +struct key_type key_type_crypto = { + .name = "crypto", + .instantiate = crypto_key_instantiate, + .match = crypto_key_match, + .destroy = crypto_key_destroy, + .describe = crypto_key_describe, +}; +EXPORT_SYMBOL_GPL(key_type_crypto); + +/** + * register_crypto_key_parser - Register a crypto key blob parser + * @parser: The parser to register + */ +int register_crypto_key_parser(struct crypto_key_parser *parser) +{ + struct crypto_key_parser *cursor; + int ret; + + down_write(&crypto_key_parsers_sem); + + list_for_each_entry(cursor, &crypto_key_parsers, link) { + if (strcmp(cursor->name, parser->name) == 0) { + pr_err("Crypto key parser '%s' already registered\n", + parser->name); + ret = -EEXIST; + goto out; + } + } + + list_add_tail(&parser->link, &crypto_key_parsers); + + pr_notice("Crypto key parser '%s' registered\n", parser->name); + ret = 0; + +out: + up_write(&crypto_key_parsers_sem); + return ret; +} +EXPORT_SYMBOL_GPL(register_crypto_key_parser); + +/** + * unregister_crypto_key_parser - Unregister a crypto key blob parser + * @parser: The parser to unregister + */ +void unregister_crypto_key_parser(struct crypto_key_parser *parser) +{ + down_write(&crypto_key_parsers_sem); + list_del(&parser->link); + up_write(&crypto_key_parsers_sem); + + pr_notice("Crypto key parser '%s' unregistered\n", parser->name); +} +EXPORT_SYMBOL_GPL(unregister_crypto_key_parser); + +/* + * Module stuff + */ +static int __init crypto_key_init(void) +{ + return register_key_type(&key_type_crypto); +} + +static void __exit crypto_key_cleanup(void) +{ + unregister_key_type(&key_type_crypto); +} + +module_init(crypto_key_init); +module_exit(crypto_key_cleanup); -- 1.7.11.2 From 24d9655ce0fc046012078867baaedd3bf2eaedd2 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 13:59:51 +0100 Subject: [PATCH 03/28] KEYS: Add signature verification facility Add a facility whereby a key subtype may be asked to verify a signature against the data it is purported to have signed. This adds four routines: (1) struct crypto_key_verify_context * verify_sig_begin(struct key *keyring, const void *sig, size_t siglen); This sets up a verification context for the given signature using information in that signature to select a key from the specified keyring and to request a hash algorithm from the crypto layer. (2) int verify_sig_add_data(struct crypto_key_verify_context *ctx, const void *data, size_t datalen); Incrementally supply data to be signed. May be called multiple times. (3) int verify_sig_end(struct crypto_key_verify_context *ctx, const void *sig, size_t siglen); Complete the verification process and return the result. -EKEYREJECTED will indicate that the verification failed and 0 will indicate success. Other errors are also possible. (4) void verify_sig_cancel(struct crypto_key_verify_context *ctx); Cancel the verification process. Signed-off-by: David Howells --- Documentation/security/keys-crypto.txt | 101 +++++++++++++++++++++++++++++ include/keys/crypto-subtype.h | 21 +++++++ include/keys/crypto-type.h | 9 +++ security/keys/crypto/Makefile | 2 +- security/keys/crypto/crypto_verify.c | 112 +++++++++++++++++++++++++++++++++ 5 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 security/keys/crypto/crypto_verify.c diff --git a/Documentation/security/keys-crypto.txt b/Documentation/security/keys-crypto.txt index 97dee80..a964717 100644 --- a/Documentation/security/keys-crypto.txt +++ b/Documentation/security/keys-crypto.txt @@ -7,6 +7,7 @@ Contents: - Overview. - Key identification. - Accessing crypto keys. + - Signature verification. - Implementing crypto parsers. - Implementing crypto subtypes. @@ -89,6 +90,65 @@ This gives access to the key type: struct key_type key_type_crypto; +SIGNATURE VERIFICATION +---------------------- + +The four operations that can perform cryptographic signature verification, +using one of a set of keys to provide the public key: + + (1) Begin verification procedure. + + struct crypto_key_verify_context * + verify_sig_begin(struct key *keyring, const void *sig, size_t siglen); + + This function sets up a verification context from the information in the + signature and looks for a suitable key in the keyring. The signature blob + must be presented again at the end of the procedure. The keys will be + checked against parameters in the signature, and if the matching one is + not found then -ENOKEY will be returned. + + The hashing algorithm, if such a thing applies, will be determined from + information in the signature and the appropriate crypto module will be + used. -ENOPKG will be returned if the hash algorithm is unavailable. + + The return value is an opaque pointer to be passed to the other functions, + or a negative error code. + + (2) Indicate data to be verified. + + int verify_sig_add_data(struct crypto_key_verify_context *ctx, + const void *data, size_t datalen); + + This function is used to shovel data to the verification procedure so that + it can load it into the hash, pass it to hardware or whatever is + appropriate for the algorithm being employed. + + The data is not canonicalised for the document type specified in the + signature. The caller must do that. + + It will return 0 if successful and a negative error code if not. + + (3) Complete the verification process. + + int verify_sig_end(struct crypto_key_verify_context *ctx, + const void *sig, size_t siglen); + + This function performs the actual signature verification step and cleans + up the resources allocated at the beginning. The signature must be + presented again as some of the data therein may need to be added to the + internal hash. + + It will return -EKEYREJECTED if the signature didn't match, 0 if + successful and may return other errors as appropriate. + + (4) Cancel the verification process. + + void verify_sig_cancel(struct crypto_key_verify_context *ctx); + + This function cleans up the resources allocated at the beginning. This is + not necessary if verify_sig_end() was called. + + =========================== IMPLEMENTING CRYPTO PARSERS =========================== @@ -96,6 +156,7 @@ IMPLEMENTING CRYPTO PARSERS The crypto key type keeps a list of registered data parsers. An example of such a parser is one that parses OpenPGP packet formatted data [RFC 4880]. + During key instantiation each parser in the list is tried until one doesn't return -EBADMSG. @@ -107,6 +168,8 @@ The parser definition structure looks like the following: int (*instantiate)(struct key *key, const void *data, size_t datalen); + struct crypto_key_verify_context *(*verify_sig_begin)( + struct key *keyring, const u8 *sig, size_t siglen); }; The owner and name fields should be set to the owning module and the name of @@ -135,6 +198,44 @@ but it is expected that at least one will be defined. algorithm such as RSA and DSA this will likely be a printable hex version of the key's fingerprint. + (2) verify_sig_begin(). + + This is similar in concept to the instantiate() function, except that it + is given a signature blob to parse rather than a key data blob. + + If the data format is not recognised, -EBADMSG should be returned. If it + is recognised, but the signature verification process cannot for some + reason be set up, some other negative error code should be returned. + -ENOKEY should be used to indicate that no matching key is available and + -ENOPKG should be returned if the hash algorithm or the verification + algorithm are unavailable. + + If successful, the parser should allocate a verification context and embed + the following struct in it: + + struct crypto_key_verify_context { + struct key *key; + int (*add_data)(struct crypto_key_verify_context *ctx, + const void *data, size_t datalen); + int (*end)(struct crypto_key_verify_context *ctx, + const u8 *sig, size_t siglen); + void (*cancel)(struct crypto_key_verify_context *ctx); + }; + + and return a pointer to this to the caller, who will then pass it to the + verification operation wrappers described in the "Signature Verification" + section. The three operation pointers here correspond exactly to those + wrappers and are all mandatory. container_of() should be used to retrieve + the actual context. + + Note that the crypto key type retains a reference on the parser module for + the lifetime of this context, though the operation pointers need not point + into this module. + + The parser should also record a pointer to the key selected and take a + reference on that key with key_get(). + + Functions are provided to register and unregister parsers: int register_crypto_key_parser(struct crypto_key_parser *parser); diff --git a/include/keys/crypto-subtype.h b/include/keys/crypto-subtype.h index fa87555..f2b927a 100644 --- a/include/keys/crypto-subtype.h +++ b/include/keys/crypto-subtype.h @@ -20,6 +20,20 @@ extern struct key_type key_type_crypto; /* + * Context base for signature verification methods. Allocated by the subtype + * and presumably embedded in something appropriate. + */ +struct crypto_key_verify_context { + struct key *key; + struct crypto_key_parser *parser; + int (*add_data)(struct crypto_key_verify_context *ctx, + const void *data, size_t datalen); + int (*end)(struct crypto_key_verify_context *ctx, + const u8 *sig, size_t siglen); + void (*cancel)(struct crypto_key_verify_context *ctx); +}; + +/* * Keys of this type declare a subtype that indicates the handlers and * capabilities. */ @@ -48,6 +62,13 @@ struct crypto_key_parser { * Return EBADMSG if not recognised. */ int (*instantiate)(struct key *key, const void *data, size_t datalen); + + /* Attempt to recognise a signature blob and find a matching key. + * + * Return EBADMSG if not recognised. + */ + struct crypto_key_verify_context *(*verify_sig_begin)( + struct key *keyring, const u8 *sig, size_t siglen); }; extern int register_crypto_key_parser(struct crypto_key_parser *); diff --git a/include/keys/crypto-type.h b/include/keys/crypto-type.h index 47c00c7..6b93366 100644 --- a/include/keys/crypto-type.h +++ b/include/keys/crypto-type.h @@ -18,6 +18,15 @@ extern struct key_type key_type_crypto; +struct crypto_key_verify_context; +extern struct crypto_key_verify_context *verify_sig_begin( + struct key *key, const void *sig, size_t siglen); +extern int verify_sig_add_data(struct crypto_key_verify_context *ctx, + const void *data, size_t datalen); +extern int verify_sig_end(struct crypto_key_verify_context *ctx, + const void *sig, size_t siglen); +extern void verify_sig_cancel(struct crypto_key_verify_context *ctx); + /* * The payload is at the discretion of the subtype. */ diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile index 36db1d5..67001bc 100644 --- a/security/keys/crypto/Makefile +++ b/security/keys/crypto/Makefile @@ -4,4 +4,4 @@ obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o -crypto_keys-y := crypto_type.o +crypto_keys-y := crypto_type.o crypto_verify.o diff --git a/security/keys/crypto/crypto_verify.c b/security/keys/crypto/crypto_verify.c new file mode 100644 index 0000000..3f2964b --- /dev/null +++ b/security/keys/crypto/crypto_verify.c @@ -0,0 +1,112 @@ +/* Signature verification with a crypto key + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + * + * See Documentation/security/keys-crypto.txt + */ + +#include +#include +#include +#include "crypto_keys.h" + +/** + * verify_sig_begin - Initiate the use of a crypto key to verify a signature + * @keyring: The public keys to verify against + * @sig: The signature data + * @siglen: The signature length + * + * Returns a context or an error. + */ +struct crypto_key_verify_context *verify_sig_begin( + struct key *keyring, const void *sig, size_t siglen) +{ + struct crypto_key_verify_context *ret; + struct crypto_key_parser *parser; + + pr_devel("==>%s()\n", __func__); + + if (siglen == 0 || !sig) + return ERR_PTR(-EINVAL); + + down_read(&crypto_key_parsers_sem); + + ret = ERR_PTR(-EBADMSG); + list_for_each_entry(parser, &crypto_key_parsers, link) { + if (parser->verify_sig_begin) { + if (!try_module_get(parser->owner)) + continue; + + pr_debug("Trying parser '%s'\n", parser->name); + + ret = parser->verify_sig_begin(keyring, sig, siglen); + if (IS_ERR(ret)) + module_put(parser->owner); + else + ret->parser = parser; + if (ret != ERR_PTR(-EBADMSG)) { + pr_debug("Parser recognised the format" + " (ret %ld)\n", + PTR_ERR(ret)); + break; + } + } + } + + up_read(&crypto_key_parsers_sem); + pr_devel("<==%s() = %p\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL_GPL(verify_sig_begin); + +/** + * verify_sig_add_data - Incrementally provide data to be verified + * @ctx: The context from verify_sig_begin() + * @data: Data + * @datalen: The amount of @data + * + * This may be called multiple times. + */ +int verify_sig_add_data(struct crypto_key_verify_context *ctx, + const void *data, size_t datalen) +{ + return ctx->add_data(ctx, data, datalen); +} +EXPORT_SYMBOL_GPL(verify_sig_add_data); + +/** + * verify_sig_end - Finalise signature verification and return result + * @ctx: The context from verify_sig_begin() + * @sig: The signature data + * @siglen: The signature length + */ +int verify_sig_end(struct crypto_key_verify_context *ctx, + const void *sig, size_t siglen) +{ + struct crypto_key_parser *parser = ctx->parser; + int ret; + + ret = ctx->end(ctx, sig, siglen); + module_put(parser->owner); + return ret; +} +EXPORT_SYMBOL_GPL(verify_sig_end); + +/** + * verify_sig_end - Cancel signature verification + * @ctx: The context from verify_sig_begin() + */ +void verify_sig_cancel(struct crypto_key_verify_context *ctx) +{ + struct crypto_key_parser *parser = ctx->parser; + + ctx->cancel(ctx); + module_put(parser->owner); +} +EXPORT_SYMBOL_GPL(verify_sig_cancel); -- 1.7.11.2 From a0fe6700fba7b7497cf137dc6a969d299ee59c67 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 13:59:52 +0100 Subject: [PATCH 04/28] KEYS: Asymmetric public-key algorithm crypto key subtype Add a subtype for supporting asymmetric public-key encryption algorithms such as DSA (FIPS-186) and RSA (PKCS#1 / RFC1337). Signed-off-by: David Howells --- security/keys/crypto/Kconfig | 10 ++++ security/keys/crypto/Makefile | 3 +- security/keys/crypto/public_key.c | 55 ++++++++++++++++++++ security/keys/crypto/public_key.h | 106 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 security/keys/crypto/public_key.c create mode 100644 security/keys/crypto/public_key.h diff --git a/security/keys/crypto/Kconfig b/security/keys/crypto/Kconfig index 3d15710..5f2b8ac 100644 --- a/security/keys/crypto/Kconfig +++ b/security/keys/crypto/Kconfig @@ -5,3 +5,13 @@ config CRYPTO_KEY_TYPE This option provides support for a type of key that holds the keys required for cryptographic operations such as encryption, decryption, signature generation and signature verification. + +config CRYPTO_KEY_PUBLIC_KEY_SUBTYPE + tristate "Asymmetric public-key crypto algorithm subtype" + depends on CRYPTO_KEY_TYPE + select MPILIB + help + This option provides support for asymmetric public key type handling. + If signature generation and/or verification are to be used, + appropriate hash algorithms (such as SHA-1) must be available. + ENOPKG will be reported if the requisite algorithm is unavailable. diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile index 67001bc..6384306 100644 --- a/security/keys/crypto/Makefile +++ b/security/keys/crypto/Makefile @@ -3,5 +3,6 @@ # obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o - crypto_keys-y := crypto_type.o crypto_verify.o + +obj-$(CONFIG_CRYPTO_KEY_PUBLIC_KEY_SUBTYPE) += public_key.o diff --git a/security/keys/crypto/public_key.c b/security/keys/crypto/public_key.c new file mode 100644 index 0000000..c00ddac --- /dev/null +++ b/security/keys/crypto/public_key.c @@ -0,0 +1,55 @@ +/* Asymmetric public key crypto subtype + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "PKEY: "fmt +#include +#include +#include "public_key.h" + +MODULE_LICENSE("GPL"); + +/* + * Provide a part of a description of the key for /proc/keys. + */ +static void public_key_describe(const struct key *crypto_key, + struct seq_file *m) +{ + struct public_key *key = crypto_key->payload.data; + + if (key) + seq_puts(m, key->algo->name); +} + +/* + * Destroy a public key algorithm key + */ +static void public_key_destroy(void *payload) +{ + struct public_key *key = payload; + int i; + + if (key) { + for (i = 0; i < ARRAY_SIZE(key->mpi); i++) + mpi_free(key->mpi[i]); + kfree(key); + } +} + +/* + * Public key algorithm crypto key subtype + */ +struct crypto_key_subtype public_key_crypto_key_subtype = { + .owner = THIS_MODULE, + .name = "public_key", + .describe = public_key_describe, + .destroy = public_key_destroy, +}; +EXPORT_SYMBOL_GPL(public_key_crypto_key_subtype); diff --git a/security/keys/crypto/public_key.h b/security/keys/crypto/public_key.h new file mode 100644 index 0000000..81ed603 --- /dev/null +++ b/security/keys/crypto/public_key.h @@ -0,0 +1,106 @@ +/* Asymmetric public-key algorithm definitions + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _LINUX_PUBLIC_KEY_H +#define _LINUX_PUBLIC_KEY_H + +#include +#include +#include + +struct public_key; +struct public_key_signature; + +enum pkey_hash_algo { + PKEY_HASH_MD5, + PKEY_HASH_SHA1, + PKEY_HASH_RIPE_MD_160, + PKEY_HASH_SHA256, + PKEY_HASH_SHA384, + PKEY_HASH_SHA512, + PKEY_HASH_SHA224, + PKEY_HASH__LAST +}; + +/* + * Public key type definition + */ +struct public_key_algorithm { + const char *name; + u8 n_pub_mpi; /* Number of MPIs in public key */ + u8 n_sec_mpi; /* Number of MPIs in secret key */ + u8 n_sig_mpi; /* Number of MPIs in a signature */ + int (*verify)(const struct public_key *key, + const struct public_key_signature *sig); +}; + +/* + * Asymmetric public key data + */ +struct public_key { + const struct public_key_algorithm *algo; + u8 capabilities; +#define PKEY_CAN_ENCRYPT 0x01 +#define PKEY_CAN_DECRYPT 0x02 +#define PKEY_CAN_ENCDEC (PKEY_CAN_ENCRYPT | PKEY_CAN_DECRYPT) +#define PKEY_CAN_SIGN 0x04 +#define PKEY_CAN_VERIFY 0x08 +#define PKEY_CAN_SIGVER (PKEY_CAN_SIGN | PKEY_CAN_VERIFY) + union { + MPI mpi[5]; + struct { + MPI p; /* DSA prime */ + MPI q; /* DSA group order */ + MPI g; /* DSA group generator */ + MPI y; /* DSA public-key value = g^x mod p */ + MPI x; /* DSA secret exponent (if present) */ + } dsa; + struct { + MPI n; /* RSA public modulus */ + MPI e; /* RSA public encryption exponent */ + MPI d; /* RSA secret encryption exponent (if present) */ + MPI p; /* RSA secret prime (if present) */ + MPI q; /* RSA secret prime (if present) */ + } rsa; + }; + + u8 key_id[8]; /* ID of this key pair */ + u8 key_id_size; /* Number of bytes in key_id */ +}; + +/* + * Asymmetric public key algorithm signature data + */ +struct public_key_signature { + struct crypto_key_verify_context base; + u8 *digest; + enum pkey_hash_algo pkey_hash_algo : 8; + u8 signed_hash_msw[2]; + u8 digest_size; /* Number of bytes in digest */ + union { + MPI mpi[2]; + struct { + MPI s; /* m^d mod n */ + } rsa; + struct { + MPI r; + MPI s; + } dsa; + }; + struct shash_desc hash; /* This must go last! */ +}; + +extern struct crypto_key_verify_context *pgp_pkey_verify_sig_begin( + struct key *crypto_key, const u8 *sigdata, size_t siglen); + +extern struct crypto_key_subtype public_key_crypto_key_subtype; + +#endif /* _LINUX_PUBLIC_KEY_H */ -- 1.7.11.2 From 39eaf7c28e0ca07dcb5e1e2a12db62815890f0e7 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:10:37 +0100 Subject: [PATCH 05/28] 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 --- 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 --- a/lib/mpi/Makefile +++ b/lib/mpi/Makefile @@ -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 --- /dev/null +++ b/lib/mpi/mpi-cmp.c @@ -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.2 From c995ac0765cfffe9b293327717e080c2cd253779 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:10:39 +0100 Subject: [PATCH 06/28] 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 --- security/keys/crypto/Kconfig | 7 + security/keys/crypto/Makefile | 1 + security/keys/crypto/crypto_rsa.c | 264 ++++++++++++++++++++++++++++++++++++++ security/keys/crypto/public_key.h | 2 + 4 files changed, 274 insertions(+) create mode 100644 security/keys/crypto/crypto_rsa.c diff --git a/security/keys/crypto/Kconfig b/security/keys/crypto/Kconfig index 5f2b8ac..4e3777e 100644 --- a/security/keys/crypto/Kconfig +++ b/security/keys/crypto/Kconfig @@ -15,3 +15,10 @@ config CRYPTO_KEY_PUBLIC_KEY_SUBTYPE If signature generation and/or verification are to be used, appropriate hash algorithms (such as SHA-1) must be available. ENOPKG will be reported if the requisite algorithm is unavailable. + +config CRYPTO_KEY_PKEY_ALGO_RSA + tristate "RSA public-key algorithm" + depends on CRYPTO_KEY_PUBLIC_KEY_SUBTYPE + select MPILIB_EXTRA + help + This option enables support for the RSA algorithm (PKCS#1, RFC3447). diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile index 6384306..b6b1a5a 100644 --- a/security/keys/crypto/Makefile +++ b/security/keys/crypto/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o crypto_keys-y := crypto_type.o crypto_verify.o obj-$(CONFIG_CRYPTO_KEY_PUBLIC_KEY_SUBTYPE) += public_key.o +obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) += crypto_rsa.o diff --git a/security/keys/crypto/crypto_rsa.c b/security/keys/crypto/crypto_rsa.c new file mode 100644 index 0000000..845285c --- /dev/null +++ b/security/keys/crypto/crypto_rsa.c @@ -0,0 +1,264 @@ +/* RSA asymmetric public-key algorithm [RFC3447] + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "RSA: "fmt +#include +#include +#include "public_key.h" + +MODULE_LICENSE("GPL"); + +#define kenter(FMT, ...) \ + pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__) +#define kleave(FMT, ...) \ + pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__) + +/* + * Hash algorithm OIDs plus ASN.1 DER wrappings [RFC4880 sec 5.2.2]. + */ +static const u8 RSA_digest_info_MD5[] = { + 0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, /* OID */ + 0x05, 0x00, 0x04, 0x10 +}; + +static const u8 RSA_digest_info_SHA1[] = { + 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, + 0x2B, 0x0E, 0x03, 0x02, 0x1A, + 0x05, 0x00, 0x04, 0x14 +}; + +static const u8 RSA_digest_info_RIPE_MD_160[] = { + 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, + 0x2B, 0x24, 0x03, 0x02, 0x01, + 0x05, 0x00, 0x04, 0x14 +}; + +static const u8 RSA_digest_info_SHA224[] = { + 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, + 0x05, 0x00, 0x04, 0x1C +}; + +static const u8 RSA_digest_info_SHA256[] = { + 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, + 0x05, 0x00, 0x04, 0x20 +}; + +static const u8 RSA_digest_info_SHA384[] = { + 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, + 0x05, 0x00, 0x04, 0x30 +}; + +static const u8 RSA_digest_info_SHA512[] = { + 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, + 0x05, 0x00, 0x04, 0x40 +}; + +static const struct { + const u8 *data; + size_t size; +} RSA_ASN1_templates[PKEY_HASH__LAST] = { +#define _(X) { RSA_digest_info_##X, sizeof(RSA_digest_info_##X) } + [PKEY_HASH_MD5] = _(MD5), + [PKEY_HASH_SHA1] = _(SHA1), + [PKEY_HASH_RIPE_MD_160] = _(RIPE_MD_160), + [PKEY_HASH_SHA256] = _(SHA256), + [PKEY_HASH_SHA384] = _(SHA384), + [PKEY_HASH_SHA512] = _(SHA512), + [PKEY_HASH_SHA224] = _(SHA224), +#undef _ +}; + +/* + * RSAVP1() function [RFC3447 sec 5.2.2] + */ +static int RSAVP1(const struct public_key *key, MPI s, MPI *_m) +{ + MPI m; + int ret; + + /* (1) Validate 0 <= s < n */ + if (mpi_cmp_ui(s, 0) < 0) { + kleave(" = -EBADMSG [s < 0]"); + return -EBADMSG; + } + if (mpi_cmp(s, key->rsa.n) >= 0) { + kleave(" = -EBADMSG [s >= n]"); + return -EBADMSG; + } + + m = mpi_alloc(0); + if (!m) + return -ENOMEM; + + /* (2) m = s^e mod n */ + ret = mpi_powm(m, s, key->rsa.e, key->rsa.n); + if (ret < 0) { + mpi_free(m); + return ret; + } + + *_m = m; + return 0; +} + +/* + * Integer to Octet String conversion [RFC3447 sec 4.1] + */ +static int RSA_I2OSP(MPI x, size_t xLen, u8 **_X) +{ + unsigned X_size, x_size; + int X_sign; + u8 *X; + + /* Make sure the string is the right length. The number should begin + * with { 0x00, 0x01, ... } so we have to account for 15 leading zero + * bits not being reported by MPI. + */ + x_size = mpi_get_nbits(x); + pr_devel("size(x)=%u xLen*8=%zu\n", x_size, xLen * 8); + if (x_size != xLen * 8 - 15) + return -ERANGE; + + X = mpi_get_buffer(x, &X_size, &X_sign); + if (!X) + return -ENOMEM; + if (X_sign < 0) { + kfree(X); + return -EBADMSG; + } + if (X_size != xLen - 1) { + kfree(X); + return -EBADMSG; + } + + *_X = X; + return 0; +} + +/* + * Perform the RSA signature verification. + * @H: Value of hash of data and metadata + * @EM: The computed signature value + * @k: The size of EM (EM[0] is an invalid location but should hold 0x00) + * @hash_size: The size of H + * @asn1_template: The DigestInfo ASN.1 template + * @asn1_size: Size of asm1_template[] + */ +static int RSA_verify(const u8 *H, const u8 *EM, size_t k, size_t hash_size, + const u8 *asn1_template, size_t asn1_size) +{ + unsigned PS_end, T_offset, i; + + kenter(",,%zu,%zu,%zu", k, hash_size, asn1_size); + + if (k < 2 + 1 + asn1_size + hash_size) + return -EBADMSG; + + /* Decode the EMSA-PKCS1-v1_5 */ + if (EM[1] != 0x01) { + kleave(" = -EBADMSG [EM[1] == %02u]", EM[1]); + return -EBADMSG; + } + + T_offset = k - (asn1_size + hash_size); + PS_end = T_offset - 1; + if (EM[PS_end] != 0x00) { + kleave(" = -EBADMSG [EM[T-1] == %02u]", EM[PS_end]); + return -EBADMSG; + } + + for (i = 2; i < PS_end; i++) { + if (EM[i] != 0xff) { + kleave(" = -EBADMSG [EM[PS%x] == %02u]", i - 2, EM[i]); + return -EBADMSG; + } + } + + if (memcmp(asn1_template, EM + T_offset, asn1_size) != 0) { + kleave(" = -EBADMSG [EM[T] ASN.1 mismatch]"); + return -EBADMSG; + } + + if (memcmp(H, EM + T_offset + asn1_size, hash_size) != 0) { + kleave(" = -EKEYREJECTED [EM[T] hash mismatch]"); + return -EKEYREJECTED; + } + + kleave(" = 0"); + return 0; +} + +/* + * Perform the verification step [RFC3447 sec 8.2.2]. + */ +static int RSA_verify_signature(const struct public_key *key, + const struct public_key_signature *sig) +{ + size_t tsize; + int ret; + + /* Variables as per RFC3447 sec 8.2.2 */ + const u8 *H = sig->digest; + u8 *EM = NULL; + MPI m = NULL; + size_t k; + + kenter(""); + + /* (1) Check the signature size against the public key modulus size */ + k = (mpi_get_nbits(key->rsa.n) + 7) / 8; + + tsize = (mpi_get_nbits(sig->rsa.s) + 7) / 8; + pr_devel("step 1: k=%zu size(S)=%zu\n", k, tsize); + if (tsize != k) { + ret = -EBADMSG; + goto error; + } + + /* (2b) Apply the RSAVP1 verification primitive to the public key */ + ret = RSAVP1(key, sig->rsa.s, &m); + if (ret < 0) + goto error; + + /* (2c) Convert the message representative (m) to an encoded message + * (EM) of length k octets. + * + * NOTE! The leading zero byte is suppressed by MPI, so we pass a + * pointer to the _preceding_ byte to RSA_verify()! + */ + ret = RSA_I2OSP(m, k, &EM); + if (ret < 0) + goto error; + + ret = RSA_verify(H, EM - 1, k, sig->digest_size, + RSA_ASN1_templates[sig->pkey_hash_algo].data, + RSA_ASN1_templates[sig->pkey_hash_algo].size); + +error: + kfree(EM); + mpi_free(m); + kleave(" = %d", ret); + return ret; +} + +const struct public_key_algorithm RSA_public_key_algorithm = { + .name = "RSA", + .n_pub_mpi = 2, + .n_sec_mpi = 3, + .n_sig_mpi = 1, + .verify = RSA_verify_signature, +}; +EXPORT_SYMBOL_GPL(RSA_public_key_algorithm); diff --git a/security/keys/crypto/public_key.h b/security/keys/crypto/public_key.h index 81ed603..7913615 100644 --- a/security/keys/crypto/public_key.h +++ b/security/keys/crypto/public_key.h @@ -42,6 +42,8 @@ struct public_key_algorithm { const struct public_key_signature *sig); }; +extern const struct public_key_algorithm RSA_public_key_algorithm; + /* * Asymmetric public key data */ -- 1.7.11.2 From d9acf3806acdc9ab5e26a1c604989070a7ae6840 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:11:19 +0100 Subject: [PATCH 07/28] 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 Signed-off-by: David Howells --- security/keys/crypto/crypto_rsa.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/security/keys/crypto/crypto_rsa.c b/security/keys/crypto/crypto_rsa.c index 845285c..a4a63be 100644 --- a/security/keys/crypto/crypto_rsa.c +++ b/security/keys/crypto/crypto_rsa.c @@ -219,15 +219,23 @@ static int RSA_verify_signature(const struct public_key *key, kenter(""); /* (1) Check the signature size against the public key modulus size */ - k = (mpi_get_nbits(key->rsa.n) + 7) / 8; + k = mpi_get_nbits(key->rsa.n); + tsize = mpi_get_nbits(sig->rsa.s); - tsize = (mpi_get_nbits(sig->rsa.s) + 7) / 8; + /* According to RFC 4880 sec 3.2, length of MPI is computed starting + * from most significant bit. So the RFC 3447 sec 8.2.2 size check + * must be relaxed to conform with shorter signatures - so we fail here + * only if signature length is longer than modulus size. + */ pr_devel("step 1: k=%zu size(S)=%zu\n", k, tsize); - if (tsize != k) { + if (k < tsize) { ret = -EBADMSG; goto error; } + /* Round up and convert to octets */ + k = (k + 7) / 8; + /* (2b) Apply the RSAVP1 verification primitive to the public key */ ret = RSAVP1(key, sig->rsa.s, &m); if (ret < 0) -- 1.7.11.2 From 9a2a2b1faa27be883b3aa2c47bbc367bd1a1f653 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:11:19 +0100 Subject: [PATCH 08/28] PGPLIB: PGP definitions (RFC 4880) Provide some useful PGP definitions from RFC 4880. These describe details of public key crypto as used by crypto keys for things like signature verification. Signed-off-by: David Howells --- include/linux/pgp.h | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 include/linux/pgp.h diff --git a/include/linux/pgp.h b/include/linux/pgp.h new file mode 100644 index 0000000..1359f64 --- /dev/null +++ b/include/linux/pgp.h @@ -0,0 +1,206 @@ +/* PGP definitions (RFC 4880) + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _LINUX_PGP_H +#define _LINUX_PGP_H + +#include + +struct pgp_key_ID { + u8 id[8]; +}; + +struct pgp_time { + u8 time[4]; +}; + +/* + * PGP public-key algorithm identifiers [RFC4880: 9.1] + */ +enum pgp_pubkey_algo { + PGP_PUBKEY_RSA_ENC_OR_SIG = 1, + PGP_PUBKEY_RSA_ENC_ONLY = 2, + PGP_PUBKEY_RSA_SIG_ONLY = 3, + PGP_PUBKEY_ELGAMAL = 16, + PGP_PUBKEY_DSA = 17, + PGP_PUBKEY__LAST +}; + +/* + * PGP symmetric-key algorithm identifiers [RFC4880: 9.2] + */ +enum pgp_symkey_algo { + PGP_SYMKEY_PLAINTEXT = 0, + PGP_SYMKEY_IDEA = 1, + PGP_SYMKEY_3DES = 2, + PGP_SYMKEY_CAST5 = 3, + PGP_SYMKEY_BLOWFISH = 4, + PGP_SYMKEY_AES_128KEY = 7, + PGP_SYMKEY_AES_192KEY = 8, + PGP_SYMKEY_AES_256KEY = 9, + PGP_SYMKEY_TWOFISH_256KEY = 10, +}; + +/* + * PGP compression algorithm identifiers [RFC4880: 9.3] + */ +enum pgp_compr_algo { + PGP_COMPR_UNCOMPRESSED = 0, + PGP_COMPR_ZIP = 1, + PGP_COMPR_ZLIB = 2, + PGP_COMPR_BZIP2 = 3, +}; + +/* + * PGP hash algorithm identifiers [RFC4880: 9.4] + */ +enum pgp_hash_algo { + PGP_HASH_MD5 = 1, + PGP_HASH_SHA1 = 2, + PGP_HASH_RIPE_MD_160 = 3, + PGP_HASH_SHA256 = 8, + PGP_HASH_SHA384 = 9, + PGP_HASH_SHA512 = 10, + PGP_HASH_SHA224 = 11, + PGP_HASH__LAST +}; + +extern const char *const pgp_hash_algorithms[PGP_HASH__LAST]; + +/* + * PGP packet type tags [RFC4880: 4.3]. + */ +enum pgp_packet_tag { + PGP_PKT_RESERVED = 0, + PGP_PKT_PUBKEY_ENC_SESSION_KEY = 1, + PGP_PKT_SIGNATURE = 2, + PGP_PKT_SYMKEY_ENC_SESSION_KEY = 3, + PGP_PKT_ONEPASS_SIGNATURE = 4, + PGP_PKT_SECRET_KEY = 5, + PGP_PKT_PUBLIC_KEY = 6, + PGP_PKT_SECRET_SUBKEY = 7, + PGP_PKT_COMPRESSED_DATA = 8, + PGP_PKT_SYM_ENC_DATA = 9, + PGP_PKT_MARKER = 10, + PGP_PKT_LITERAL_DATA = 11, + PGP_PKT_TRUST = 12, + PGP_PKT_USER_ID = 13, + PGP_PKT_PUBLIC_SUBKEY = 14, + PGP_PKT_USER_ATTRIBUTE = 17, + PGP_PKT_SYM_ENC_AND_INTEG_DATA = 18, + PGP_PKT_MODIFY_DETECT_CODE = 19, + PGP_PKT_PRIVATE_0 = 60, + PGP_PKT_PRIVATE_3 = 63, + PGP_PKT__HIGHEST = 63 +}; + +/* + * Signature (tag 2) packet [RFC4880: 5.2]. + */ +enum pgp_signature_version { + PGP_SIG_VERSION_3 = 3, + PGP_SIG_VERSION_4 = 4, +}; + +enum pgp_signature_type { + PGP_SIG_BINARY_DOCUMENT_SIG = 0x00, + PGP_SIG_CANONICAL_TEXT_DOCUMENT_SIG = 0x01, + PGP_SIG_STANDALONE_SIG = 0x02, + PGP_SIG_GENERAL_CERT_OF_UID_PUBKEY = 0x10, + PGP_SIG_PERSONAL_CERT_OF_UID_PUBKEY = 0x11, + PGP_SIG_CASUAL_CERT_OF_UID_PUBKEY = 0x12, + PGP_SIG_POSTITIVE_CERT_OF_UID_PUBKEY = 0x13, + PGP_SIG_SUBKEY_BINDING_SIG = 0x18, + PGP_SIG_PRIMARY_KEY_BINDING_SIG = 0x19, + PGP_SIG_DIRECTLY_ON_KEY = 0x1F, + PGP_SIG_KEY_REVOCATION_SIG = 0x20, + PGP_SIG_SUBKEY_REVOCATION_SIG = 0x28, + PGP_SIG_CERT_REVOCATION_SIG = 0x30, + PGP_SIG_TIMESTAMP_SIG = 0x40, + PGP_SIG_THIRD_PARTY_CONFIRM_SIG = 0x50, +}; + +struct pgp_signature_v3_packet { + enum pgp_signature_version version : 8; /* == PGP_SIG_VERSION_3 */ + u8 length_of_hashed; /* == 5 */ + struct { + enum pgp_signature_type signature_type : 8; + struct pgp_time creation_time; + } hashed; + struct pgp_key_ID issuer; + enum pgp_pubkey_algo pubkey_algo : 8; + enum pgp_hash_algo hash_algo : 8; +} __packed; + +struct pgp_signature_v4_packet { + enum pgp_signature_version version : 8; /* == PGP_SIG_VERSION_4 */ + enum pgp_signature_type signature_type : 8; + enum pgp_pubkey_algo pubkey_algo : 8; + enum pgp_hash_algo hash_algo : 8; +} __packed; + +/* + * V4 signature subpacket types [RFC4880: 5.2.3.1]. + */ +enum pgp_sig_subpkt_type { + PGP_SIG_CREATION_TIME = 2, + PGP_SIG_EXPIRATION_TIME = 3, + PGP_SIG_EXPORTABLE_CERT = 4, + PGP_SIG_TRUST_SIG = 5, + PGP_SIG_REGEXP = 6, + PGP_SIG_REVOCABLE = 7, + PGP_SIG_KEY_EXPIRATION_TIME = 9, + PGP_SIG_PREF_SYM_ALGO = 11, + PGP_SIG_REVOCATION_KEY = 12, + PGP_SIG_ISSUER = 16, + PGP_SIG_NOTATION_DATA = 20, + PGP_SIG_PREF_HASH_ALGO = 21, + PGP_SIG_PREF_COMPR_ALGO = 22, + PGP_SIG_KEY_SERVER_PREFS = 23, + PGP_SIG_PREF_KEY_SERVER = 24, + PGP_SIG_PRIMARY_USER_ID = 25, + PGP_SIG_POLICY_URI = 26, + PGP_SIG_KEY_FLAGS = 27, + PGP_SIG_SIGNERS_USER_ID = 28, + PGP_SIG_REASON_FOR_REVOCATION = 29, + PGP_SIG_FEATURES = 30, + PGP_SIG_TARGET = 31, + PGP_SIG_EMBEDDED_SIG = 32, + PGP_SIG__LAST +}; + +#define PGP_SIG_SUBPKT_TYPE_CRITICAL_MASK 0x80 + +/* + * Key (tag 5, 6, 7 and 14) packet + */ +enum pgp_key_version { + PGP_KEY_VERSION_2 = 2, + PGP_KEY_VERSION_3 = 3, + PGP_KEY_VERSION_4 = 4, +}; + +struct pgp_key_v3_packet { + enum pgp_key_version version : 8; + struct pgp_time creation_time; + u8 expiry[2]; /* 0 or time in days till expiry */ + enum pgp_pubkey_algo pubkey_algo : 8; + u8 key_material[0]; +} __packed; + +struct pgp_key_v4_packet { + enum pgp_key_version version : 8; + struct pgp_time creation_time; + enum pgp_pubkey_algo pubkey_algo : 8; + u8 key_material[0]; +} __packed; + +#endif /* _LINUX_PGP_H */ -- 1.7.11.2 From 0b8ec95fe7220288c143a820b8d8996c356129f1 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:11:20 +0100 Subject: [PATCH 09/28] PGPLIB: Basic packet parser Provide a simple parser that extracts the packets from a PGP packet blob and passes the desirous ones to the given processor function: struct pgp_parse_context { u64 types_of_interest; int (*process_packet)(struct pgp_parse_context *context, enum pgp_packet_tag type, u8 headerlen, const u8 *data, size_t datalen); }; int pgp_parse_packets(const u8 *data, size_t datalen, struct pgp_parse_context *ctx); This is configured on with CONFIG_PGP_LIBRARY. Signed-off-by: David Howells --- include/linux/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 --- /dev/null +++ b/include/linux/pgplib.h @@ -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 + +/* + * 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 --- a/security/keys/crypto/Kconfig +++ b/security/keys/crypto/Kconfig @@ -22,3 +22,9 @@ config CRYPTO_KEY_PKEY_ALGO_RSA select MPILIB_EXTRA help This option enables support for the RSA algorithm (PKCS#1, RFC3447). + +config PGP_LIBRARY + tristate "PGP parsing library" + help + This option enables a library that provides a number of simple + utility functions for parsing PGP (RFC 4880) packet-based messages. diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile index b6b1a5a..5fbe54e 100644 --- a/security/keys/crypto/Makefile +++ b/security/keys/crypto/Makefile @@ -7,3 +7,4 @@ crypto_keys-y := crypto_type.o crypto_verify.o obj-$(CONFIG_CRYPTO_KEY_PUBLIC_KEY_SUBTYPE) += public_key.o obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) += crypto_rsa.o +obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o diff --git a/security/keys/crypto/pgp_library.c b/security/keys/crypto/pgp_library.c new file mode 100644 index 0000000..af396d6 --- /dev/null +++ b/security/keys/crypto/pgp_library.c @@ -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 +#include +#include +#include + +MODULE_LICENSE("GPL"); + +const char *const pgp_hash_algorithms[PGP_HASH__LAST] = { + [PGP_HASH_MD5] = "md5", + [PGP_HASH_SHA1] = "sha1", + [PGP_HASH_RIPE_MD_160] = "rmd160", + [PGP_HASH_SHA256] = "sha256", + [PGP_HASH_SHA384] = "sha384", + [PGP_HASH_SHA512] = "sha512", + [PGP_HASH_SHA224] = "sha224", +}; +EXPORT_SYMBOL_GPL(pgp_hash_algorithms); + +/** + * pgp_parse_packet_header - Parse a PGP packet header + * @_data: Start of the PGP packet (updated to PGP packet data) + * @_datalen: Amount of data remaining in buffer (decreased) + * @_type: Where the packet type will be returned + * @_headerlen: Where the header length will be returned + * + * Parse a set of PGP packet header [RFC 4880: 4.2]. + * + * Returns packet data size on success; non-zero on error. If successful, + * *_data and *_datalen will have been updated and *_headerlen will be set to + * hold the length of the packet header. + */ +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,,)", 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.2 From a3673ac73f4634bcdd97d642b3bdd87998eb2100 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:11:20 +0100 Subject: [PATCH 10/28] PGPLIB: Signature parser Provide some PGP signature parsing helpers: (1) A function to parse V4 signature subpackets and pass the desired ones to a processor function: int pgp_parse_sig_subpkts(const u8 *data, size_t datalen, struct pgp_parse_sig_context *ctx); (2) A function to parse out basic signature parameters from any PGP signature such that the algorithms and public key can be selected: int pgp_parse_sig_params(const u8 **_data, size_t *_datalen, struct pgp_sig_parameters *p); Signed-off-by: David Howells --- include/linux/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 --- a/include/linux/pgplib.h +++ b/include/linux/pgplib.h @@ -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 af396d6..c9218df 100644 --- a/security/keys/crypto/pgp_library.c +++ b/security/keys/crypto/pgp_library.c @@ -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.2 From dd59f49ce7179b145f55bdca3b43f4761ae0769d Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:11:21 +0100 Subject: [PATCH 11/28] 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 for pointing out some errors. Signed-off-by: David Howells --- security/keys/crypto/Kconfig | 12 ++ security/keys/crypto/Makefile | 4 + security/keys/crypto/pgp_parser.h | 23 +++ security/keys/crypto/pgp_public_key.c | 348 ++++++++++++++++++++++++++++++++++ 4 files changed, 387 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 --- a/security/keys/crypto/Kconfig +++ b/security/keys/crypto/Kconfig @@ -28,3 +28,15 @@ config PGP_LIBRARY help This option enables a library that provides a number of simple utility functions for parsing PGP (RFC 4880) packet-based messages. + +config CRYPTO_KEY_PGP_PARSER + tristate "PGP key blob parser" + depends on CRYPTO_KEY_TYPE + select CRYPTO_KEY_PUBLIC_KEY_SUBTYPE + select PGP_LIBRARY + select MD5 # V3 fingerprint generation + select SHA1 # V4 fingerprint generation + help + This option provides support for parsing PGP (RFC 4880) format blobs + for key data and provides the ability to instantiate a crypto key + from a public key packet found inside the blob. diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile index 5fbe54e..35733fc 100644 --- a/security/keys/crypto/Makefile +++ b/security/keys/crypto/Makefile @@ -8,3 +8,7 @@ crypto_keys-y := crypto_type.o crypto_verify.o obj-$(CONFIG_CRYPTO_KEY_PUBLIC_KEY_SUBTYPE) += public_key.o obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) += crypto_rsa.o obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o + +obj-$(CONFIG_CRYPTO_KEY_PGP_PARSER) += pgp_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 --- /dev/null +++ b/security/keys/crypto/pgp_parser.h @@ -0,0 +1,23 @@ +/* PGP crypto data parser internal definitions + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include + +#define kenter(FMT, ...) \ + pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__) +#define kleave(FMT, ...) \ + pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__) + +/* + * pgp_key_parser.c + */ +extern const +struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST]; diff --git a/security/keys/crypto/pgp_public_key.c b/security/keys/crypto/pgp_public_key.c new file mode 100644 index 0000000..8a8b7c0 --- /dev/null +++ b/security/keys/crypto/pgp_public_key.c @@ -0,0 +1,348 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include "public_key.h" +#include "pgp_parser.h" + +MODULE_LICENSE("GPL"); + +const +struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST] = { +#if defined(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) || \ + defined(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA_MODULE) + [PGP_PUBKEY_RSA_ENC_OR_SIG] = &RSA_public_key_algorithm, + [PGP_PUBKEY_RSA_ENC_ONLY] = &RSA_public_key_algorithm, + [PGP_PUBKEY_RSA_SIG_ONLY] = &RSA_public_key_algorithm, +#endif + [PGP_PUBKEY_ELGAMAL] = NULL, + [PGP_PUBKEY_DSA] = NULL, +}; + +static const u8 pgp_public_key_capabilities[PGP_PUBKEY__LAST] = { + [PGP_PUBKEY_RSA_ENC_OR_SIG] = PKEY_CAN_ENCDEC | PKEY_CAN_SIGVER, + [PGP_PUBKEY_RSA_ENC_ONLY] = PKEY_CAN_ENCDEC, + [PGP_PUBKEY_RSA_SIG_ONLY] = PKEY_CAN_SIGVER, + [PGP_PUBKEY_ELGAMAL] = 0, + [PGP_PUBKEY_DSA] = 0, +}; + +static inline void digest_putc(struct shash_desc *digest, uint8_t ch) +{ + crypto_shash_update(digest, &ch, 1); +} + +struct pgp_key_data_parse_context { + struct pgp_parse_context pgp; + struct crypto_key_subtype *subtype; + char *fingerprint; + void *payload; +}; + +/* + * Calculate the public key ID (RFC4880 12.2) + */ +static int pgp_calc_pkey_keyid(struct shash_desc *digest, + struct pgp_parse_pubkey *pgp, + struct public_key *key) +{ + unsigned nb[ARRAY_SIZE(key->mpi)]; + unsigned nn[ARRAY_SIZE(key->mpi)]; + unsigned n; + u8 *pp[ARRAY_SIZE(key->mpi)]; + u32 a32; + int npkey = key->algo->n_pub_mpi; + int i, ret = -ENOMEM; + + kenter(""); + + 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; + + 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_instantiate(struct key *key, + const void *data, size_t datalen) +{ + struct pgp_key_data_parse_context ctx; + int ret; + + kenter(""); + + ret = key_payload_reserve(key, datalen); + if (ret < 0) + return ret; + + ctx.pgp.types_of_interest = + (1 << PGP_PKT_PUBLIC_KEY) | (1 << PGP_PKT_PUBLIC_SUBKEY); + ctx.pgp.process_packet = pgp_process_public_key; + ctx.subtype = NULL; + ctx.fingerprint = NULL; + ctx.payload = NULL; + + ret = pgp_parse_packets(data, datalen, &ctx.pgp); + if (ret < 0) { + if (ctx.payload) + ctx.subtype->destroy(ctx.payload); + if (ctx.subtype) + module_put(ctx.subtype->owner); + kfree(ctx.fingerprint); + key_payload_reserve(key, 0); + return ret; + } + + key->type_data.p[0] = ctx.subtype; + key->type_data.p[1] = ctx.fingerprint; + key->payload.data = ctx.payload; + return 0; +} + +static struct crypto_key_parser pgp_key_parser = { + .owner = THIS_MODULE, + .name = "pgp", + .instantiate = pgp_key_instantiate, +}; + +/* + * Module stuff + */ +static int __init pgp_key_init(void) +{ + return register_crypto_key_parser(&pgp_key_parser); +} + +static void __exit pgp_key_exit(void) +{ + unregister_crypto_key_parser(&pgp_key_parser); +} + +module_init(pgp_key_init); +module_exit(pgp_key_exit); -- 1.7.11.2 From 80437db0342877f06d689d33babcc99175d34b82 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:11:21 +0100 Subject: [PATCH 12/28] KEYS: PGP-based public key signature verification Provide handlers for PGP-based public-key algorithm signature verification. This does most of the work involved in signature verification as most of it is public-key algorithm agnostic. The public-key verification algorithm itself is just the last little bit and is supplied the complete hash data to process. This requires glue logic putting on top to make use of it - something the next patch provides. Signed-off-by: David Howells --- security/keys/crypto/Makefile | 3 +- security/keys/crypto/pgp_parser.h | 6 + security/keys/crypto/pgp_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 --- a/security/keys/crypto/Makefile +++ b/security/keys/crypto/Makefile @@ -11,4 +11,5 @@ obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o obj-$(CONFIG_CRYPTO_KEY_PGP_PARSER) += pgp_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..a6192ce 100644 --- a/security/keys/crypto/pgp_parser.h +++ b/security/keys/crypto/pgp_parser.h @@ -21,3 +21,9 @@ */ extern const struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST]; + +/* + * pgp_pubkey_sig.c + */ +extern struct crypto_key_verify_context *pgp_pkey_verify_sig_begin( + struct key *crypto_key, const u8 *sigdata, size_t siglen); diff --git a/security/keys/crypto/pgp_sig_verify.c b/security/keys/crypto/pgp_sig_verify.c new file mode 100644 index 0000000..82c89da --- /dev/null +++ b/security/keys/crypto/pgp_sig_verify.c @@ -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 +#include +#include +#include +#include +#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_key_verify_context *ctx, + const void *data, size_t datalen); +static int pgp_pkey_verify_sig_end(struct crypto_key_verify_context *ctx, + const u8 *sig, size_t siglen); +static void pgp_pkey_verify_sig_cancel(struct crypto_key_verify_context *ctx); + +struct pgp_pkey_sig_parse_context { + struct pgp_parse_context pgp; + struct pgp_sig_parameters params; +}; + +static int pgp_pkey_parse_signature(struct pgp_parse_context *context, + enum pgp_packet_tag type, + u8 headerlen, + const u8 *data, + size_t datalen) +{ + struct pgp_pkey_sig_parse_context *ctx = + container_of(context, struct pgp_pkey_sig_parse_context, pgp); + + return pgp_parse_sig_params(&data, &datalen, &ctx->params); +} + +/* + * Begin the process of verifying a DSA signature. + * + * This involves allocating the hash into which first the data and then the + * metadata will be put, and parsing the signature to check that it matches the + * key. + */ +struct crypto_key_verify_context *pgp_pkey_verify_sig_begin( + struct key *crypto_key, const u8 *sigdata, size_t siglen) +{ + struct pgp_pkey_sig_parse_context p; + struct public_key_signature *sig; + struct crypto_shash *tfm; + const struct public_key *key = crypto_key->payload.data; + size_t digest_size, desc_size; + int ret; + + kenter("{%d},,%zu", key_serial(crypto_key), siglen); + + if (!key) { + kleave(" = -ENOKEY [no public key]"); + return ERR_PTR(-ENOKEY); + } + + p.pgp.types_of_interest = (1 << PGP_PKT_SIGNATURE); + p.pgp.process_packet = pgp_pkey_parse_signature; + ret = pgp_parse_packets(sigdata, siglen, &p.pgp); + if (ret < 0) + return ERR_PTR(ret); + + if (p.params.pubkey_algo >= PGP_PUBKEY__LAST || + !pgp_public_key_algorithms[p.params.pubkey_algo]) { + pr_debug("Unsupported public key algorithm %u\n", + p.params.pubkey_algo); + return ERR_PTR(-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_key_verify_context *ctx, + const void *data, size_t datalen) +{ + struct public_key_signature *sig = + container_of(ctx, struct public_key_signature, base); + + return crypto_shash_update(&sig->hash, data, datalen); +} + +struct pgp_pkey_sig_digest_context { + struct pgp_parse_context pgp; + const struct public_key *key; + struct public_key_signature *sig; +}; + +/* + * Extract required metadata from the signature packet and add what we need to + * to the hash. + */ +static int pgp_pkey_digest_signature(struct pgp_parse_context *context, + enum pgp_packet_tag type, + u8 headerlen, + const u8 *data, + size_t datalen) +{ + struct pgp_pkey_sig_digest_context *ctx = + container_of(context, struct pgp_pkey_sig_digest_context, pgp); + enum pgp_signature_version version; + int i; + + kenter(",%u,%u,,%zu", type, headerlen, datalen); + + version = *data; + if (version == PGP_SIG_VERSION_3) { + /* We just include an excerpt of the metadata from a V3 + * signature. + */ + crypto_shash_update(&ctx->sig->hash, data + 1, 5); + data += sizeof(struct pgp_signature_v3_packet); + datalen -= sizeof(struct pgp_signature_v3_packet); + } else if (version == PGP_SIG_VERSION_4) { + /* We add the whole metadata header and some of the hashed data + * for a V4 signature, plus a trailer. + */ + size_t hashedsz, unhashedsz; + u8 trailer[6]; + + hashedsz = 4 + 2 + (data[4] << 8) + data[5]; + crypto_shash_update(&ctx->sig->hash, data, hashedsz); + + trailer[0] = version; + trailer[1] = 0xffU; + trailer[2] = hashedsz >> 24; + trailer[3] = hashedsz >> 16; + trailer[4] = hashedsz >> 8; + trailer[5] = hashedsz; + + crypto_shash_update(&ctx->sig->hash, trailer, 6); + data += hashedsz; + datalen -= hashedsz; + + unhashedsz = 2 + (data[0] << 8) + data[1]; + data += unhashedsz; + datalen -= unhashedsz; + } + + if (datalen <= 2) { + kleave(" = -EBADMSG"); + return -EBADMSG; + } + + /* There's a quick check on the hash available. */ + ctx->sig->signed_hash_msw[0] = *data++; + ctx->sig->signed_hash_msw[1] = *data++; + datalen -= 2; + + /* And then the cryptographic data, which we'll need for the + * algorithm. + */ + for (i = 0; i < ctx->key->algo->n_sig_mpi; i++) { + unsigned int remaining = datalen; + if (remaining == 0) { + pr_debug("short %zu mpi %d\n", datalen, i); + return -EBADMSG; + } + ctx->sig->mpi[i] = mpi_read_from_buffer(data, &remaining); + if (!ctx->sig->mpi[i]) + return -ENOMEM; + data += remaining; + datalen -= remaining; + } + + if (datalen != 0) { + kleave(" = -EBADMSG [trailer %zu]", datalen); + return -EBADMSG; + } + + kleave(" = 0"); + return 0; +} + +/* + * The data is now all loaded into the hash; load the metadata, finalise the + * hash and perform the verification step. + */ +static int pgp_pkey_verify_sig_end(struct crypto_key_verify_context *ctx, + const u8 *sigdata, size_t siglen) +{ + struct public_key_signature *sig = + container_of(ctx, struct public_key_signature, base); + const struct public_key *key = sig->base.key->payload.data; + struct pgp_pkey_sig_digest_context p; + int ret; + + kenter(""); + + /* Firstly we add metadata, starting with some of the data from the + * signature packet */ + p.pgp.types_of_interest = (1 << PGP_PKT_SIGNATURE); + p.pgp.process_packet = pgp_pkey_digest_signature; + p.key = key; + p.sig = sig; + ret = pgp_parse_packets(sigdata, siglen, &p.pgp); + if (ret < 0) + goto error_free_ctx; + + crypto_shash_final(&sig->hash, sig->digest); + + ret = key->algo->verify(key, sig); + +error_free_ctx: + pgp_pkey_verify_sig_cancel(ctx); + kleave(" = %d", ret); + return ret; +} + +/* + * Cancel an in-progress data loading + */ +static void pgp_pkey_verify_sig_cancel(struct crypto_key_verify_context *ctx) +{ + struct public_key_signature *sig = + container_of(ctx, struct public_key_signature, base); + int i; + + kenter(""); + + /* !!! Do we need to tell the crypto layer to cancel too? */ + crypto_free_shash(sig->hash.tfm); + key_put(sig->base.key); + for (i = 0; i < ARRAY_SIZE(sig->mpi); i++) + mpi_free(sig->mpi[i]); + kfree(sig); + + kleave(""); +} -- 1.7.11.2 From 1826f7b562237c008c66ad63b7d7d4c7c44b98fb Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:11:21 +0100 Subject: [PATCH 13/28] KEYS: PGP format signature parser Implement a signature parser that will attempt to parse a signature blob as a PGP packet format message. If it can, it will find an appropriate crypto key and set the public-key algorithm according to the data in the signature. Signed-off-by: David Howells --- security/keys/crypto/Makefile | 1 + security/keys/crypto/pgp_parser.h | 6 ++ security/keys/crypto/pgp_public_key.c | 1 + security/keys/crypto/pgp_sig_parser.c | 114 ++++++++++++++++++++++++++++++++++ 4 files changed, 122 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 --- a/security/keys/crypto/Makefile +++ b/security/keys/crypto/Makefile @@ -12,4 +12,5 @@ obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o obj-$(CONFIG_CRYPTO_KEY_PGP_PARSER) += pgp_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_parser.h b/security/keys/crypto/pgp_parser.h index a6192ce..73c900e 100644 --- a/security/keys/crypto/pgp_parser.h +++ b/security/keys/crypto/pgp_parser.h @@ -23,6 +23,12 @@ extern const struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST]; /* + * pgp_sig_parser.c + */ +extern struct crypto_key_verify_context *pgp_verify_sig_begin( + struct key *keyring, const u8 *sig, size_t siglen); + +/* * pgp_pubkey_sig.c */ extern struct crypto_key_verify_context *pgp_pkey_verify_sig_begin( diff --git a/security/keys/crypto/pgp_public_key.c b/security/keys/crypto/pgp_public_key.c index 8a8b7c0..5ab926d 100644 --- a/security/keys/crypto/pgp_public_key.c +++ b/security/keys/crypto/pgp_public_key.c @@ -329,6 +329,7 @@ static struct crypto_key_parser pgp_key_parser = { .owner = THIS_MODULE, .name = "pgp", .instantiate = pgp_key_instantiate, + .verify_sig_begin = pgp_verify_sig_begin, }; /* diff --git a/security/keys/crypto/pgp_sig_parser.c b/security/keys/crypto/pgp_sig_parser.c new file mode 100644 index 0000000..f5feb2b --- /dev/null +++ b/security/keys/crypto/pgp_sig_parser.c @@ -0,0 +1,114 @@ +/* Handling for PGP public key signature data [RFC 4880] + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "PGPSIG: "fmt +#include +#include +#include +#include +#include "public_key.h" +#include "pgp_parser.h" + +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. + */ +struct crypto_key_verify_context *pgp_verify_sig_begin( + struct key *keyring, const u8 *sig, size_t siglen) +{ + struct crypto_key_verify_context *ctx; + struct key *key; + + key = find_key_for_pgp_sig(keyring, sig, siglen); + if (IS_ERR(key)) + return ERR_CAST(key); + + /* We only handle in-kernel public key signatures for the moment */ + ctx = pgp_pkey_verify_sig_begin(key, sig, siglen); + key_put(key); + return ctx; +} -- 1.7.11.2 From 68b4585107d4d014b4de3536c972c63f617c48f5 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:11:21 +0100 Subject: [PATCH 14/28] 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 for some pointing out some errors. Signed-off-by: David Howells --- Documentation/security/keys-crypto.txt | 20 +++++++ include/keys/crypto-type.h | 3 ++ security/keys/crypto/Kconfig | 9 ++++ security/keys/crypto/Makefile | 1 + security/keys/crypto/pgp_preload.c | 96 ++++++++++++++++++++++++++++++++++ 5 files changed, 129 insertions(+) create mode 100644 security/keys/crypto/pgp_preload.c diff --git a/Documentation/security/keys-crypto.txt b/Documentation/security/keys-crypto.txt index a964717..ba2ab55 100644 --- a/Documentation/security/keys-crypto.txt +++ b/Documentation/security/keys-crypto.txt @@ -10,6 +10,7 @@ Contents: - Signature verification. - Implementing crypto parsers. - Implementing crypto subtypes. + - Initial PGP key preloading. ======== @@ -280,3 +281,22 @@ There are a number of operations defined by the subtype: Mandatory. This should free the memory associated with the key. The crypto key will look after freeing the fingerprint and releasing the reference on the subtype module. + + +======================= +INITIAL PGP KEY LOADING +======================= + +A function is provided to perform an initial load of a set of public keys bound +into a PGP packet format blob: + + int preload_pgp_keys(const u8 *pgpdata, size_t pgpdatalen, + struct key *keyring, const char *descprefix); + +This takes the blob of data defined by pgpdata and pgpdatalen, extracts keys +from them and adds them to the specified keyring. The keys are labelled with +descprefix plus a simple uniquifier - it is not expected that the description +will be used to identify the key. The description is required to prevent all +but the last key being discarded when the keys are linked into the keyring. + +This function is only available during initial kernel set up. diff --git a/include/keys/crypto-type.h b/include/keys/crypto-type.h index 6b93366..710e77f 100644 --- a/include/keys/crypto-type.h +++ b/include/keys/crypto-type.h @@ -31,4 +31,7 @@ extern void verify_sig_cancel(struct crypto_key_verify_context *ctx); * The payload is at the discretion of the subtype. */ +extern __init int preload_pgp_keys(const u8 *pgpdata, size_t pgpdatalen, + struct key *keyring, const char *descprefix); + #endif /* _KEYS_CRYPTO_TYPE_H */ diff --git a/security/keys/crypto/Kconfig b/security/keys/crypto/Kconfig index 1c2ae55..8af0155 100644 --- a/security/keys/crypto/Kconfig +++ b/security/keys/crypto/Kconfig @@ -40,3 +40,12 @@ config CRYPTO_KEY_PGP_PARSER This option provides support for parsing PGP (RFC 4880) format blobs for key data and provides the ability to instantiate a crypto key from a public key packet found inside the blob. + +config PGP_PRELOAD + bool "PGP public key preloading facility" + select PGP_LIBRARY + select CRYPTO_KEY_PGP_PARSER + help + This option provides a facility for the kernel to preload PGP-wrapped + bundles of keys during boot. It is used by module signing to load + the module signing keys for example. diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile index a9a34c6..c873674 100644 --- a/security/keys/crypto/Makefile +++ b/security/keys/crypto/Makefile @@ -8,6 +8,7 @@ crypto_keys-y := crypto_type.o crypto_verify.o obj-$(CONFIG_CRYPTO_KEY_PUBLIC_KEY_SUBTYPE) += public_key.o obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) += crypto_rsa.o obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o +obj-$(CONFIG_PGP_PRELOAD) += pgp_preload.o obj-$(CONFIG_CRYPTO_KEY_PGP_PARSER) += pgp_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..9028788 --- /dev/null +++ b/security/keys/crypto/pgp_preload.c @@ -0,0 +1,96 @@ +/* Cryptographic key request handling + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + * + * See Documentation/security/keys-crypto.txt + */ + +#include +#include +#include +#include +#include +#include "crypto_keys.h" + +struct preload_pgp_keys_context { + struct pgp_parse_context pgp; + key_ref_t keyring; + char descbuf[20]; + u8 key_n; + u8 dsize; +}; + +/* + * Extract a public key or subkey from the PGP stream. + */ +static int __init found_pgp_key(struct pgp_parse_context *context, + enum pgp_packet_tag type, u8 headerlen, + const u8 *data, size_t datalen) +{ + struct preload_pgp_keys_context *ctx = + container_of(context, struct preload_pgp_keys_context, pgp); + key_ref_t key; + + if (ctx->key_n >= 255) + return 0; /* Don't overrun descbuf */ + + sprintf(ctx->descbuf + ctx->dsize, "%d", ctx->key_n++); + + key = key_create_or_update(ctx->keyring, "crypto", ctx->descbuf, + data - headerlen, datalen + headerlen, + KEY_POS_ALL | KEY_USR_VIEW, + KEY_ALLOC_NOT_IN_QUOTA); + + if (IS_ERR(key)) + return PTR_ERR(key); + + pr_notice("Loaded %s key: %s\n", + key_ref_to_ptr(key)->description, + crypto_key_id(key_ref_to_ptr(key))); + + key_ref_put(key); + return 0; +} + +/** + * preload_pgp_keys - Load keys from a PGP keyring blob + * @pgpdata: The PGP keyring blob containing the keys. + * @pgpdatalen: The size of the @pgpdata blob. + * @keyring: The keyring to add the new keys to. + * @descprefix: The key description prefix. + * + * Preload a pack of keys from a PGP keyring blob. + * + * The keys are given description of @descprefix + the number of the key in the + * list. Since keys can be matched on their key IDs independently of the key + * description, the description is mostly irrelevant apart from the fact that + * keys of the same description displace one another from a keyring. + * + * The caller should override the current creds if they want the keys to be + * owned by someone other than the current process's owner. Keys will not be + * accounted towards the owner's quota. + * + * This function may only be called whilst the kernel is booting. + */ +int __init preload_pgp_keys(const u8 *pgpdata, size_t pgpdatalen, + struct key *keyring, const char *descprefix) +{ + struct preload_pgp_keys_context ctx; + + ctx.pgp.types_of_interest = + (1 << PGP_PKT_PUBLIC_KEY) | (1 << PGP_PKT_PUBLIC_SUBKEY); + ctx.pgp.process_packet = found_pgp_key; + ctx.keyring = make_key_ref(keyring, 1); + ctx.key_n = 0; + ctx.dsize = strlen(descprefix); + BUG_ON(ctx.dsize > sizeof(ctx.descbuf) - 4); + strcpy(ctx.descbuf, descprefix); + + return pgp_parse_packets(pgpdata, pgpdatalen, &ctx.pgp); +} -- 1.7.11.2 From c9455441e0482bb5eb0ea8f1e2cfbe2e7d630560 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:13:56 +0100 Subject: [PATCH 15/28] 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. Signed-off-by: David Howells --- 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 | 4 ++++ 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/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 ------------------- 56 files changed, 168 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 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -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 d5b9b5e..e73a1a7 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -18,6 +18,8 @@ config ALPHA select ARCH_HAVE_NMI_SAFE_CMPXCHG select GENERIC_SMP_IDLE_THREAD select GENERIC_CMOS_UPDATE + 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 --- a/arch/alpha/include/asm/module.h +++ b/arch/alpha/include/asm/module.h @@ -1,19 +1,13 @@ #ifndef _ALPHA_MODULE_H #define _ALPHA_MODULE_H +#include + 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 7980873..f447a89 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -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 --- a/arch/arm/include/asm/module.h +++ b/arch/arm/include/asm/module.h @@ -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 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 --- a/arch/avr32/Kconfig +++ b/arch/avr32/Kconfig @@ -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 --- a/arch/avr32/include/asm/module.h +++ b/arch/avr32/include/asm/module.h @@ -1,6 +1,8 @@ #ifndef __ASM_AVR32_MODULE_H #define __ASM_AVR32_MODULE_H +#include + 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 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig @@ -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 --- a/arch/blackfin/include/asm/module.h +++ b/arch/blackfin/include/asm/module.h @@ -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 struct mod_arch_specific { Elf_Shdr *text_l1; diff --git a/arch/c6x/Kconfig b/arch/c6x/Kconfig index 052f81a..8f3a304 100644 --- a/arch/c6x/Kconfig +++ b/arch/c6x/Kconfig @@ -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 --- a/arch/c6x/include/asm/module.h +++ b/arch/c6x/include/asm/module.h @@ -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 struct loaded_sections { unsigned int new_vaddr; diff --git a/arch/cris/Kconfig b/arch/cris/Kconfig index e922154..7bb8cf9 100644 --- a/arch/cris/Kconfig +++ b/arch/cris/Kconfig @@ -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 --- a/arch/cris/include/asm/Kbuild +++ b/arch/cris/include/asm/Kbuild @@ -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 --- a/arch/cris/include/asm/module.h +++ /dev/null @@ -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 --- a/arch/frv/include/asm/module.h +++ b/arch/frv/include/asm/module.h @@ -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 /* * Include the architecture version. diff --git a/arch/h8300/Kconfig b/arch/h8300/Kconfig index 5e8a0d9..c149d3b29 100644 --- a/arch/h8300/Kconfig +++ b/arch/h8300/Kconfig @@ -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 --- a/arch/h8300/include/asm/Kbuild +++ b/arch/h8300/include/asm/Kbuild @@ -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 --- a/arch/h8300/include/asm/module.h +++ /dev/null @@ -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 --- a/arch/hexagon/Kconfig +++ b/arch/hexagon/Kconfig @@ -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 --- a/arch/ia64/Kconfig +++ b/arch/ia64/Kconfig @@ -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 --- a/arch/ia64/include/asm/module.h +++ b/arch/ia64/include/asm/module.h @@ -1,6 +1,8 @@ #ifndef _ASM_IA64_MODULE_H #define _ASM_IA64_MODULE_H +#include + /* * 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 --- a/arch/m32r/Kconfig +++ b/arch/m32r/Kconfig @@ -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 --- a/arch/m32r/include/asm/Kbuild +++ b/arch/m32r/include/asm/Kbuild @@ -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 --- a/arch/m32r/include/asm/module.h +++ /dev/null @@ -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 --- a/arch/m32r/kernel/module.c +++ b/arch/m32r/kernel/module.c @@ -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 0b0f8b8..fcc5a65 100644 --- a/arch/m68k/Kconfig +++ b/arch/m68k/Kconfig @@ -12,6 +12,10 @@ 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 --- a/arch/m68k/include/asm/module.h +++ b/arch/m68k/include/asm/module.h @@ -1,6 +1,8 @@ #ifndef _ASM_M68K_MODULE_H #define _ASM_M68K_MODULE_H +#include + 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 --- a/arch/microblaze/Kconfig +++ b/arch/microblaze/Kconfig @@ -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 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -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 --- a/arch/mips/include/asm/module.h +++ b/arch/mips/include/asm/module.h @@ -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 a5066b1..1500c80 100644 --- a/arch/mips/kernel/module.c +++ b/arch/mips/kernel/module.c @@ -299,6 +299,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) @@ -338,6 +339,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 --- a/arch/mn10300/Kconfig +++ b/arch/mn10300/Kconfig @@ -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 --- a/arch/mn10300/include/asm/module.h +++ b/arch/mn10300/include/asm/module.h @@ -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 /* * Include the MN10300 architecture version. diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig index 49765b5..05f2ba4 100644 --- a/arch/openrisc/Kconfig +++ b/arch/openrisc/Kconfig @@ -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 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -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 --- a/arch/parisc/include/asm/module.h +++ b/arch/parisc/include/asm/module.h @@ -1,21 +1,11 @@ #ifndef _ASM_PARISC_MODULE_H #define _ASM_PARISC_MODULE_H + +#include + /* * 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 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -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 --- a/arch/powerpc/include/asm/module.h +++ b/arch/powerpc/include/asm/module.h @@ -11,6 +11,7 @@ #include #include +#include #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 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -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 --- a/arch/s390/include/asm/module.h +++ b/arch/s390/include/asm/module.h @@ -1,5 +1,8 @@ #ifndef _ASM_S390_MODULE_H #define _ASM_S390_MODULE_H + +#include + /* * 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 --- a/arch/score/Kconfig +++ b/arch/score/Kconfig @@ -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 --- a/arch/score/include/asm/module.h +++ b/arch/score/include/asm/module.h @@ -3,6 +3,7 @@ #include #include +#include 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 --- a/arch/score/kernel/module.c +++ b/arch/score/kernel/module.c @@ -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 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -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 --- a/arch/sh/include/asm/module.h +++ b/arch/sh/include/asm/module.h @@ -1,21 +1,13 @@ #ifndef _ASM_SH_MODULE_H #define _ASM_SH_MODULE_H -struct mod_arch_specific { +#include + #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 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -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 --- a/arch/sparc/include/asm/Kbuild +++ b/arch/sparc/include/asm/Kbuild @@ -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 --- a/arch/sparc/include/asm/module.h +++ /dev/null @@ -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 --- a/arch/tile/Kconfig +++ b/arch/tile/Kconfig @@ -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 --- a/arch/unicore32/Kconfig +++ b/arch/unicore32/Kconfig @@ -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 ba2657c..afea8c7 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -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/xtensa/Kconfig b/arch/xtensa/Kconfig index 8ed64cf..4816e44 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -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 --- a/arch/xtensa/include/asm/module.h +++ b/arch/xtensa/include/asm/module.h @@ -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 #endif /* _XTENSA_MODULE_H */ diff --git a/include/asm-generic/module.h b/include/asm-generic/module.h index ed5b44d..14dc41d 100644 --- a/include/asm-generic/module.h +++ b/include/asm-generic/module.h @@ -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 --- a/include/linux/moduleloader.h +++ b/include/linux/moduleloader.h @@ -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 --- a/kernel/module.c +++ b/kernel/module.c @@ -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.2 From 45c9f5b2992c100a9183f753d933d3141ae4e951 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:13:57 +0100 Subject: [PATCH 16/28] Provide macros for forming the name of an ELF note and its section Provide macros for stringifying the name of an ELF note and its section appropriately so that the macro can be used in both C and assembly. Signed-off-by: David Howells --- include/linux/elfnote.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/linux/elfnote.h b/include/linux/elfnote.h index 278e3ef..949d494 100644 --- a/include/linux/elfnote.h +++ b/include/linux/elfnote.h @@ -58,6 +58,7 @@ ELFNOTE_END #else /* !__ASSEMBLER__ */ +#include #include /* * Use an anonymous structure which matches the shape of @@ -93,6 +94,9 @@ #define ELFNOTE32(name, type, desc) ELFNOTE(32, name, type, desc) #define ELFNOTE64(name, type, desc) ELFNOTE(64, name, type, desc) + +#define ELFNOTE_NAME(name) __stringify(name) +#define ELFNOTE_SECTION(name) ".note."ELFNOTE_NAME(name) #endif /* __ASSEMBLER__ */ #endif /* _LINUX_ELFNOTE_H */ -- 1.7.11.2 From 1d83fa4cf20b3b6f7ffd471459dcad47d6e2ac64 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:14:00 +0100 Subject: [PATCH 17/28] MODSIGN: Provide gitignore and make clean rules for extra files Provide gitignore and make clean rules for extra files to hide and clean up the extra files produced by module signing stuff once it is added. Also add a clean up rule for the module content extractor program used to extract the data to be signed. Signed-off-by: David Howells --- .gitignore | 12 ++++++++++++ Makefile | 1 + scripts/mod/.gitignore | 1 + 3 files changed, 14 insertions(+) diff --git a/.gitignore b/.gitignore index 57af07c..7948eeb 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,9 @@ *.o.* *.a *.s +*.ko.unsigned +*.ko.digest +*.ko.digest.sig *.ko *.so *.so.dbg @@ -84,3 +87,12 @@ GTAGS *.orig *~ \#*# + +# +# GPG leavings from module signing +# +genkey +modsign.pub +modsign.sec +random_seed +trustdb.gpg diff --git a/Makefile b/Makefile index 8e4c0a7..4db9629 100644 --- a/Makefile +++ b/Makefile @@ -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 --- a/scripts/mod/.gitignore +++ b/scripts/mod/.gitignore @@ -1,4 +1,5 @@ elfconfig.h mk_elfconfig modpost +mod-extract -- 1.7.11.2 From a284aee7526543a96a6e5694425ec7a2001d5c32 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:14:01 +0100 Subject: [PATCH 18/28] MODSIGN: Provide Documentation and Kconfig options Provide documentation and kernel configuration options for module signing. The documentation can be found in: Documentation/module-signing.txt The following configuration options are added: (1) CONFIG_MODULE_SIG Enable module signing. This will both cause the build process to sign modules and the kernel to check modules when they're loaded. (2) CONFIG_MODULE_SIG_SHA1 CONFIG_MODULE_SIG_SHA224 CONFIG_MODULE_SIG_SHA256 CONFIG_MODULE_SIG_SHA384 CONFIG_MODULE_SIG_SHA512 Select the cryptographic hash used to digest the data prior to signing. Additionally, the crypto module selected will be built into the kernel as it won't be possible to load it as a module without incurring a circular dependency when the kernel tries to check its signature. (3) CONFIG_MODULE_SIG_FORCE Require that any module loaded must be signed with a key compiled into the kernel. All other modules are rejected with EKEYREJECTED. Signed-off-by: David Howells --- Documentation/module-signing.txt | 194 +++++++++++++++++++++++++++++++++++++++ include/linux/modsign.h | 27 ++++++ init/Kconfig | 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 --- /dev/null +++ b/Documentation/module-signing.txt @@ -0,0 +1,194 @@ + ============================== + KERNEL MODULE SIGNING FACILITY + ============================== + +The module signing facility applies cryptographic signature checking to modules +on module load, checking the signature against a ring of public keys compiled +into the kernel. GPG is used to do the cryptographic work and determines the +format of the signature and key data. The facility uses GPG's MPI library to +handle the huge numbers involved. + +This facility is enabled through CONFIG_MODULE_SIG. Turning on signature +checking will also force the module's ELF metadata to be verified before the +signature is checked. + +The signature checker in the kernel is capable of handling multiple keys of +either DSA or RSA type, and can support any of MD5, RIPE-MD-160, SHA-1, +SHA-224, SHA-256, SHA-384 and SHA-512 hashes - PROVIDED(!) the requisite +algorithms are compiled into the kernel. + +(!) NOTE: Modules may only be verified initially with algorithms compiled into +the kernel. Further algorithm modules may be loaded and used - but these must +first pass a verification step using already loaded/compiled-in algorithms. + + +===================== +SUPPLYING PUBLIC KEYS +===================== + +A set of public keys must be supplied at kernel image build time. This is done +by taking a GPG public key file and placing it in the base of the kernel +directory in a file called modsign.pub. + +For example, a throwaway key could be generated automatically by something like +the following: + + cat >genkey < + + This indicates the whereabouts of the GPG keyring that is the source of + the public key to be used. The default is "./modsign.pub". + + (*) MODKEYNAME= + + The name of the key pair to be used from the aforementioned keyrings. + This defaults to being unset, thus leaving the choice of default key to + gpg. + + (*) KEYFLAGS="gpg-options" + + Override the complete gpg command line, including the preceding three + options. The default options supplied to gpg are: + + --no-default-keyring + --secret-keyring $(MODSECKEY) + --keyring $(MODPUBKEY) + --no-default-keyring + --homedir . + --no-options + --no-auto-check-trustdb + --no-permission-warning + --digest-algo= + + with: + + --default-key $(MODKEYNAME) + + being added if requested. + +The resulting module.ko file will be the signed module. + + +======================== +STRIPPING SIGNED MODULES +======================== + +Signed modules may be safely stripped with any of the following: + + strip -x + strip -g + eu-strip + +as the signature only covers those parts of the module the kernel actually uses +and any ELF metadata required to deal with them. Any necessary ELF metadata +that is affected by stripping is canonicalised by the sig generator and the sig +checker to hide strip effects. + +This permits the debuginfo to be detached from the module and placed in another +spot so that gdb can find it when referring to that module without the need for +multiple signed versions of the module. Such is done by rpmbuild when +producing RPMs. + +It also permits the module to be stripped as far as possible for when modules +are being reduced prior to being included in an initial ramdisk composition. + +Note that "strip" and "strip -s" may not be used on a module, signed or +otherwise, as they remove the symbol table and render the relocation tables +unusable. + + +====================== +LOADING SIGNED MODULES +====================== + +Modules are loaded with insmod, exactly as for unsigned modules. The signature +is inserted into the module object file during the build process as an ELF note +called "module.sig" in an ELF section called ".note.module.sig". The signature +checker will detect it and apply signature checking. + + +========================================= +NON-VALID SIGNATURES AND UNSIGNED MODULES +========================================= + +If CONFIG_MODULE_SIG_FORCE is enabled or "enforcemodulesig=1" is supplied on +the kernel command line, the kernel will _only_ load validly signed modules +for which it has a public key. Otherwise, it will also load modules that are +unsigned. Any module for which the kernel has a key, but which proves to have +a signature mismatch will not be permitted to load (returning EKEYREJECTED). + +This table indicates the behaviours of the various situations: + + MODULE STATE PERMISSIVE MODE ENFORCING MODE + ======================================= =============== =============== + Unsigned Ok EKEYREJECTED + Signed, no public key ENOKEY ENOKEY + Validly signed, public key Ok Ok + Invalidly signed, public key EKEYREJECTED EKEYREJECTED + Validly signed, expired key EKEYEXPIRED EKEYEXPIRED + Signed, hash algorithm unavailable ENOPKG ENOPKG + Corrupt signature EBADMSG EBADMSG + Corrupt ELF ELIBBAD ELIBBAD diff --git a/include/linux/modsign.h b/include/linux/modsign.h new file mode 100644 index 0000000..c5ac87a --- /dev/null +++ b/include/linux/modsign.h @@ -0,0 +1,27 @@ +/* Module signing definitions + * + * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _LINUX_MODSIGN_H +#define _LINUX_MODSIGN_H + +#ifdef CONFIG_MODULE_SIG + +#include + +/* + * The parameters of the ELF note used to carry the signature + */ +#define MODSIGN_NOTE_NAME module.sig +#define MODSIGN_NOTE_TYPE 100 + +#endif + +#endif /* _LINUX_MODSIGN_H */ diff --git a/init/Kconfig b/init/Kconfig index af6c7f8..e23ed83 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -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.2 From 509093b115e362fd50584c5852c922926c2395bd Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:14:01 +0100 Subject: [PATCH 19/28] MODSIGN: Sign modules during the build process If CONFIG_MODULE_SIG is set, then this patch will cause the module to get a signature installed. The following steps will occur: (1) The module will be linked to foo.ko.unsigned instead of foo.ko (2) The module's signable content will be extracted to foo.ko.digest by the mod-extract program. (3) The signature will be generated on foo.ko.digest by gpg and placed in foo.ko.digest.sig (4) The signature will be encapsulated into an ELF note and placed into a file called foo.ko.note.o using the output from modsign-note.sh piped into the assembler. (5) The unsigned module from (1) and the signature ELF note from (4) will be linked together to produce foo.ko Step (3) requires private and public keys to be available. By default these are expected to be found in PGP keyring files called modsign.sec (the secret key) and modsign.pub (the public key) in the build root. If the secret key is not found then signing will be skipped and the unsigned module from (1) will just be copied to foo.ko. If signing occurs, lines like the following will be seen: LD [M] fs/foo/foo.ko.unsigned SIGN [M] fs/foo/foo.ko will appear in the build log. If it is skipped, the following will be seen: LD [M] fs/foo/foo.ko.unsigned NO SIGN [M] fs/foo/foo.ko Signed-off-by: David Howells --- scripts/Makefile.modpost | 87 ++++- scripts/mod/Makefile | 2 +- scripts/mod/mod-extract.c | 913 ++++++++++++++++++++++++++++++++++++++++++++ scripts/mod/modsign-note.sh | 16 + 4 files changed, 1016 insertions(+), 2 deletions(-) create mode 100644 scripts/mod/mod-extract.c create mode 100644 scripts/mod/modsign-note.sh diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost index 08dce14..17465d8 100644 --- a/scripts/Makefile.modpost +++ b/scripts/Makefile.modpost @@ -14,7 +14,8 @@ # 3) create one .mod.c file pr. module # 4) create one Module.symvers file with CRC for all exported symbols # 5) compile all .mod.c files -# 6) final link of the module to a file +# 6) final link of the module to a (or ) file +# 7) signs the modules to a file # Step 3 is used to place certain information in the module's ELF # section, including information such as: @@ -32,6 +33,8 @@ # Step 4 is solely used to allow module versioning in external modules, # where the CRC of each module is retrieved from the Module.symvers file. +# Step 7 is dependent on CONFIG_MODULE_SIG being enabled. + # KBUILD_MODPOST_WARN can be set to avoid error out in case of undefined # symbols in the final module linking stage # KBUILD_MODPOST_NOFINAL can be set to skip the final link of modules. @@ -116,6 +119,7 @@ $(modules:.ko=.mod.o): %.mod.o: %.mod.c FORCE targets += $(modules:.ko=.mod.o) # Step 6), final link of the modules +ifneq ($(CONFIG_MODULE_SIG),y) quiet_cmd_ld_ko_o = LD [M] $@ cmd_ld_ko_o = $(LD) -r $(LDFLAGS) \ $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \ @@ -125,7 +129,88 @@ $(modules): %.ko :%.o %.mod.o FORCE $(call if_changed,ld_ko_o) targets += $(modules) +else +quiet_cmd_ld_ko_unsigned_o = LD [M] $@ + cmd_ld_ko_unsigned_o = \ + $(LD) -r $(LDFLAGS) \ + $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \ + -o $@ $(filter-out FORCE,$^) \ + $(if $(AFTER_LINK),; $(AFTER_LINK)) + +$(modules:.ko=.ko.unsigned): %.ko.unsigned :%.o %.mod.o FORCE + $(call if_changed,ld_ko_unsigned_o) + +targets += $(modules:.ko=.ko.unsigned) + +# Step 7), sign the modules +MODSECKEY = ./modsign.sec +MODPUBKEY = ./modsign.pub +KEYFLAGS = --no-default-keyring --secret-keyring $(MODSECKEY) --keyring $(MODPUBKEY) --no-default-keyring --homedir . --no-options --no-auto-check-trustdb --no-permission-warning + +ifdef CONFIG_MODULE_SIG_SHA1 +KEYFLAGS += --digest-algo=SHA1 +else +ifdef CONFIG_MODULE_SIG_SHA224 +KEYFLAGS += --digest-algo=SHA224 +else +ifdef CONFIG_MODULE_SIG_SHA256 +KEYFLAGS += --digest-algo=SHA256 +else +ifdef CONFIG_MODULE_SIG_SHA384 +KEYFLAGS += --digest-algo=SHA384 +else +ifdef CONFIG_MODULE_SIG_SHA512 +KEYFLAGS += --digest-algo=SHA512 +else +endif +endif +endif +endif +endif + +ifdef MODKEYNAME +KEYFLAGS += --default-key $(MODKEYNAME) +endif +ifeq ($(wildcard $(MODSECKEY))+$(wildcard $(MODPUBKEY)),$(MODSECKEY)+$(MODPUBKEY)) +ifeq ($(KBUILD_SRC),) + # no O= is being used + SCRIPTS_DIR := scripts +else + SCRIPTS_DIR := $(KBUILD_SRC)/scripts +endif +SIGN_MODULES := 1 +else +SIGN_MODULES := 0 +endif + +# only sign if it's an in-tree module +ifneq ($(KBUILD_EXTMOD),) +SIGN_MODULES := 0 +endif + +ifeq ($(SIGN_MODULES),1) +KEYRING_DEP := modsign.sec modsign.pub +quiet_cmd_sign_ko_ko_unsigned = SIGN [M] $@ + cmd_sign_ko_ko_unsigned = \ + scripts/mod/mod-extract $< $@.digest && \ + rm -f $@.digest.sig && \ + gpg --batch --no-greeting $(KEYFLAGS) -b $@.digest && \ + sh $(SCRIPTS_DIR)/mod/modsign-note.sh $@.digest.sig | \ + $(CC) -x assembler-with-cpp $(c_flags) $(CFLAGS_MODULE) -c -o $@.note.o - && \ + $(LD) -r $(LDFLAGS) -o $@ $< $@.note.o +else +KEYRING_DEP := +quiet_cmd_sign_ko_ko_unsigned = NO SIGN [M] $@ + cmd_sign_ko_ko_unsigned = \ + cp $< $@ +endif + +$(modules): %.ko :%.ko.unsigned $(KEYRING_DEP) FORCE + $(call if_changed,sign_ko_ko_unsigned) + +targets += $(modules) +endif # Add FORCE to the prequisites of a target to force it to be always rebuilt. # --------------------------------------------------------------------------- diff --git a/scripts/mod/Makefile b/scripts/mod/Makefile index ff954f8..4654e3b 100644 --- a/scripts/mod/Makefile +++ b/scripts/mod/Makefile @@ -1,4 +1,4 @@ -hostprogs-y := modpost mk_elfconfig +hostprogs-y := modpost mk_elfconfig mod-extract always := $(hostprogs-y) empty.o modpost-objs := modpost.o file2alias.o sumversion.o diff --git a/scripts/mod/mod-extract.c b/scripts/mod/mod-extract.c new file mode 100644 index 0000000..0c0e3e3 --- /dev/null +++ b/scripts/mod/mod-extract.c @@ -0,0 +1,913 @@ +/* mod-extract.c: module extractor for signing + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void extract_elf64(void *buffer, size_t size, Elf64_Ehdr *hdr); +static void extract_elf32(void *buffer, size_t size, Elf32_Ehdr *hdr); + +struct byteorder { + uint16_t (*get16)(const uint16_t *); + uint32_t (*get32)(const uint32_t *); + uint64_t (*get64)(const uint64_t *); + void (*set16)(uint16_t *, uint16_t); + void (*set32)(uint32_t *, uint32_t); + void (*set64)(uint64_t *, uint64_t); +}; + +static uint16_t get16_le(const uint16_t *p) { return __le16_to_cpu(*p); } +static uint32_t get32_le(const uint32_t *p) { return __le32_to_cpu(*p); } +static uint64_t get64_le(const uint64_t *p) { return __le64_to_cpu(*p); } +static uint16_t get16_be(const uint16_t *p) { return __be16_to_cpu(*p); } +static uint32_t get32_be(const uint32_t *p) { return __be32_to_cpu(*p); } +static uint64_t get64_be(const uint64_t *p) { return __be64_to_cpu(*p); } + +static void set16_le(uint16_t *p, uint16_t n) { *p = __cpu_to_le16(n); } +static void set32_le(uint32_t *p, uint32_t n) { *p = __cpu_to_le32(n); } +static void set64_le(uint64_t *p, uint64_t n) { *p = __cpu_to_le64(n); } +static void set16_be(uint16_t *p, uint16_t n) { *p = __cpu_to_be16(n); } +static void set32_be(uint32_t *p, uint32_t n) { *p = __cpu_to_be32(n); } +static void set64_be(uint64_t *p, uint64_t n) { *p = __cpu_to_be64(n); } + +static const struct byteorder byteorder_le = { + get16_le, get32_le, get64_le, + set16_le, set32_le, set64_le +}; +static const struct byteorder byteorder_be = { + get16_be, get32_be, get64_be, + set16_be, set32_be, set64_be +}; +static const struct byteorder *order; + +static inline uint16_t get16(const uint16_t *p) { return order->get16(p); } +static inline uint32_t get32(const uint32_t *p) { return order->get32(p); } +static inline uint64_t get64(const uint64_t *p) { return order->get64(p); } +static inline void set16(uint16_t *p, uint16_t n) { order->set16(p, n); } +static inline void set32(uint32_t *p, uint32_t n) { order->set32(p, n); } +static inline void set64(uint64_t *p, uint64_t n) { order->set64(p, n); } + +static FILE *outfd; +static uint8_t csum, xcsum; + +static void write_out(const void *data, size_t size) +{ + const uint8_t *p = data; + size_t loop; + + for (loop = 0; loop < size; loop++) { + csum += p[loop]; + xcsum += p[loop]; + } + + if (fwrite(data, 1, size, outfd) != size) { + perror("write"); + exit(1); + } +} + +#define write_out_val(VAL) write_out(&(VAL), sizeof(VAL)) + +static int is_verbose; + +static __attribute__((format(printf, 1, 2))) +void verbose(const char *fmt, ...) +{ + va_list va; + + if (is_verbose) { + va_start(va, fmt); + vprintf(fmt, va); + va_end(va); + } +} + +static __attribute__((noreturn)) +void usage(void) +{ + fprintf(stderr, "Usage: mod-extract [-v] \n"); + exit(2); +} + +/* + * + */ +int main(int argc, char **argv) +{ + struct stat st; + Elf32_Ehdr *hdr32; + Elf64_Ehdr *hdr64; + size_t len; + void *buffer; + int fd, be, b64; + + while (argc > 1 && strcmp("-v", argv[1]) == 0) { + argv++; + argc--; + is_verbose++; + } + + if (argc != 3) + usage(); + + /* map the module into memory */ + fd = open(argv[1], O_RDONLY); + if (fd < 0) { + perror("open input"); + exit(1); + } + + if (fstat(fd, &st) < 0) { + perror("fstat"); + exit(1); + } + + len = st.st_size; + + buffer = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); + if (buffer == MAP_FAILED) { + perror("mmap"); + exit(1); + } + + if (close(fd) < 0) { + perror("close input"); + exit(1); + } + + /* check it's an ELF object */ + hdr32 = buffer; + hdr64 = buffer; + + if (hdr32->e_ident[EI_MAG0] != ELFMAG0 || + hdr32->e_ident[EI_MAG1] != ELFMAG1 || + hdr32->e_ident[EI_MAG2] != ELFMAG2 || + hdr32->e_ident[EI_MAG3] != ELFMAG3 + ) { + fprintf(stderr, "Module does not appear to be ELF\n"); + exit(3); + } + + /* determine endianness and word size */ + b64 = (hdr32->e_ident[EI_CLASS] == ELFCLASS64); + be = (hdr32->e_ident[EI_DATA] == ELFDATA2MSB); + order = be ? &byteorder_be : &byteorder_le; + + verbose("Module is %s-bit %s-endian\n", + b64 ? "64" : "32", + be ? "big" : "little"); + + /* open the output file */ + outfd = fopen(argv[2], "w"); + if (!outfd) { + perror("open output"); + exit(1); + } + + /* perform the extraction */ + if (b64) + extract_elf64(buffer, len, hdr64); + else + extract_elf32(buffer, len, hdr32); + + /* done */ + if (fclose(outfd) == EOF) { + perror("close output"); + exit(1); + } + + return 0; +} + +/* + * extract a RELA table + * - need to canonicalise the entries in case section addition/removal has + * rearranged the symbol table and the section table + */ +static void extract_elf64_rela(const void *buffer, int secix, int targetix, + const Elf64_Rela *relatab, size_t nrels, + const Elf64_Sym *symbols, size_t nsyms, + const Elf64_Shdr *sections, size_t nsects, int *canonmap, + const char *strings, size_t nstrings, + const char *sh_name) +{ + struct { + uint64_t r_offset; + uint64_t r_addend; + uint64_t st_value; + uint64_t st_size; + uint32_t r_type; + uint16_t st_shndx; + uint8_t st_info; + uint8_t st_other; + + } __attribute__((packed)) relocation; + + const Elf64_Sym *symbol; + size_t loop; + + /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */ + for (loop = 0; loop < nrels; loop++) { + Elf64_Section st_shndx; + Elf64_Xword r_info; + + /* decode the relocation */ + r_info = get64(&relatab[loop].r_info); + relocation.r_offset = relatab[loop].r_offset; + relocation.r_addend = relatab[loop].r_addend; + set32(&relocation.r_type, ELF64_R_TYPE(r_info)); + + if (ELF64_R_SYM(r_info) >= nsyms) { + fprintf(stderr, "Invalid symbol ID %zx in relocation %zu\n", + (size_t)ELF64_R_SYM(r_info), loop); + exit(1); + } + + /* decode the symbol referenced by the relocation */ + symbol = &symbols[ELF64_R_SYM(r_info)]; + relocation.st_info = symbol->st_info; + relocation.st_other = symbol->st_other; + relocation.st_value = symbol->st_value; + relocation.st_size = symbol->st_size; + relocation.st_shndx = symbol->st_shndx; + st_shndx = get16(&symbol->st_shndx); + + /* canonicalise the section used by the symbol */ + if (st_shndx > SHN_UNDEF && st_shndx < nsects) + set16(&relocation.st_shndx, canonmap[st_shndx]); + + write_out_val(relocation); + + /* undefined symbols must be named if referenced */ + if (st_shndx == SHN_UNDEF) { + const char *name = strings + get32(&symbol->st_name); + write_out(name, strlen(name) + 1); + } + } + + verbose("%02x %4d %s [canon]\n", csum, secix, sh_name); +} + +/* + * extract a REL table + * - need to canonicalise the entries in case section addition/removal has + * rearranged the symbol table and the section table + */ +static void extract_elf64_rel(const void *buffer, int secix, int targetix, + const Elf64_Rel *relatab, size_t nrels, + const Elf64_Sym *symbols, size_t nsyms, + const Elf64_Shdr *sections, size_t nsects, int *canonmap, + const char *strings, size_t nstrings, + const char *sh_name) +{ + struct { + uint64_t r_offset; + uint64_t st_value; + uint64_t st_size; + uint32_t r_type; + uint16_t st_shndx; + uint8_t st_info; + uint8_t st_other; + + } __attribute__((packed)) relocation; + + const Elf64_Sym *symbol; + size_t loop; + + /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */ + for (loop = 0; loop < nrels; loop++) { + Elf64_Section st_shndx; + Elf64_Xword r_info; + + /* decode the relocation */ + r_info = get64(&relatab[loop].r_info); + relocation.r_offset = relatab[loop].r_offset; + set32(&relocation.r_type, ELF64_R_TYPE(r_info)); + + if (ELF64_R_SYM(r_info) >= nsyms) { + fprintf(stderr, "Invalid symbol ID %zx in relocation %zu\n", + (size_t)ELF64_R_SYM(r_info), loop); + exit(1); + } + + /* decode the symbol referenced by the relocation */ + symbol = &symbols[ELF64_R_SYM(r_info)]; + relocation.st_info = symbol->st_info; + relocation.st_other = symbol->st_other; + relocation.st_value = symbol->st_value; + relocation.st_size = symbol->st_size; + relocation.st_shndx = symbol->st_shndx; + st_shndx = get16(&symbol->st_shndx); + + /* canonicalise the section used by the symbol */ + if (st_shndx > SHN_UNDEF && st_shndx < nsects) + set16(&relocation.st_shndx, canonmap[st_shndx]); + + write_out_val(relocation); + + /* undefined symbols must be named if referenced */ + if (st_shndx == SHN_UNDEF) { + const char *name = strings + get32(&symbol->st_name); + write_out(name, strlen(name) + 1); + } + } + + verbose("%02x %4d %s [canon]\n", csum, secix, sh_name); +} + +/* + * extract the data from a 64-bit module + */ +static void extract_elf64(void *buffer, size_t len, Elf64_Ehdr *hdr) +{ + const Elf64_Sym *symbols; + Elf64_Shdr *sections; + const char *secstrings, *strings; + size_t nsyms, nstrings; + int loop, shnum, *canonlist, *canonmap, canon, changed, tmp; + + sections = buffer + get64(&hdr->e_shoff); + secstrings = buffer + get64(§ions[get16(&hdr->e_shstrndx)].sh_offset); + shnum = get16(&hdr->e_shnum); + + /* find the symbol table and the string table and produce a list of + * index numbers of sections that contribute to the kernel's module + * image + */ + canonlist = calloc(sizeof(int), shnum * 2); + if (!canonlist) { + perror("calloc"); + exit(1); + } + canonmap = canonlist + shnum; + canon = 0; + + symbols = NULL; + strings = NULL; + nstrings = 0; + nsyms = 0; + + for (loop = 1; loop < shnum; loop++) { + const char *sh_name = secstrings + get32(§ions[loop].sh_name); + Elf64_Word sh_type = get32(§ions[loop].sh_type); + Elf64_Xword sh_size = get64(§ions[loop].sh_size); + Elf64_Xword sh_flags = get64(§ions[loop].sh_flags); + Elf64_Word sh_info = get32(§ions[loop].sh_info); + Elf64_Off sh_offset = get64(§ions[loop].sh_offset); + void *data = buffer + sh_offset; + + /* quick sanity check */ + if (sh_type != SHT_NOBITS && len < sh_offset + sh_size) { + fprintf(stderr, "Section goes beyond EOF\n"); + exit(3); + } + + /* we only need to canonicalise allocatable sections */ + if (sh_flags & SHF_ALLOC) + canonlist[canon++] = loop; + else if ((sh_type == SHT_REL || sh_type == SHT_RELA) && + get64(§ions[sh_info].sh_flags) & SHF_ALLOC) + canonlist[canon++] = loop; + + /* keep track of certain special sections */ + switch (sh_type) { + case SHT_SYMTAB: + if (strcmp(sh_name, ".symtab") == 0) { + symbols = data; + nsyms = sh_size / sizeof(Elf64_Sym); + } + break; + + case SHT_STRTAB: + if (strcmp(sh_name, ".strtab") == 0) { + strings = data; + nstrings = sh_size; + } + break; + + default: + break; + } + } + + if (!symbols) { + fprintf(stderr, "Couldn't locate symbol table\n"); + exit(3); + } + + if (!strings) { + fprintf(stderr, "Couldn't locate strings table\n"); + exit(3); + } + + /* canonicalise the index numbers of the contributing section */ + do { + changed = 0; + + for (loop = 0; loop < canon - 1; loop++) { + const char *x = secstrings + get32(§ions[canonlist[loop + 0]].sh_name); + const char *y = secstrings + get32(§ions[canonlist[loop + 1]].sh_name); + if (strcmp(x, y) > 0) { + tmp = canonlist[loop + 0]; + canonlist[loop + 0] = canonlist[loop + 1]; + canonlist[loop + 1] = tmp; + changed = 1; + } + } + + } while (changed); + + for (loop = 0; loop < canon; loop++) + canonmap[canonlist[loop]] = loop + 1; + + if (is_verbose > 1) { + printf("\nSection canonicalisation map:\n"); + for (loop = 1; loop < shnum; loop++) { + const char *x = secstrings + get32(§ions[loop].sh_name); + printf("%4d %s\n", canonmap[loop], x); + } + + printf("\nAllocated section list in canonical order:\n"); + for (loop = 0; loop < canon; loop++) { + const char *x = secstrings + get32(§ions[canonlist[loop]].sh_name); + printf("%4d %s\n", canonlist[loop], x); + } + } + + /* iterate through the section table looking for sections we want to + * contribute to the signature */ + verbose("\n"); + verbose("CAN FILE POS CS SECT NAME\n"); + verbose("=== ======== == ==== ==============================\n"); + + for (loop = 0; loop < canon; loop++) { + int sect = canonlist[loop]; + const char *sh_name = secstrings + get32(§ions[sect].sh_name); + Elf64_Word sh_type = get32(§ions[sect].sh_type); + Elf64_Xword sh_size = get64(§ions[sect].sh_size); + Elf64_Xword sh_flags = get64(§ions[sect].sh_flags); + Elf64_Word sh_info = get32(§ions[sect].sh_info); + Elf64_Off sh_offset = get64(§ions[sect].sh_offset); + void *data = buffer + sh_offset; + + csum = 0; + + /* include canonicalised relocation sections */ + if (sh_type == SHT_REL || sh_type == SHT_RELA) { + Elf32_Word canon_sh_info; + + if (sh_info <= 0 && sh_info >= hdr->e_shnum) { + fprintf(stderr, + "Invalid ELF - REL/RELA sh_info does" + " not refer to a valid section\n"); + exit(3); + } + + verbose("%3u %08lx ", loop, ftell(outfd)); + + set32(&canon_sh_info, canonmap[sh_info]); + + /* write out selected portions of the section header */ + write_out(sh_name, strlen(sh_name)); + write_out_val(sections[sect].sh_type); + write_out_val(sections[sect].sh_flags); + write_out_val(sections[sect].sh_size); + write_out_val(sections[sect].sh_addralign); + write_out_val(canon_sh_info); + + if (sh_type == SHT_RELA) + extract_elf64_rela(buffer, sect, sh_info, + data, sh_size / sizeof(Elf64_Rela), + symbols, nsyms, + sections, shnum, canonmap, + strings, nstrings, + sh_name); + else + extract_elf64_rel(buffer, sect, sh_info, + data, sh_size / sizeof(Elf64_Rel), + symbols, nsyms, + sections, shnum, canonmap, + strings, nstrings, + sh_name); + continue; + } + + /* include the headers of BSS sections */ + if (sh_type == SHT_NOBITS && sh_flags & SHF_ALLOC) { + verbose("%3u %08lx ", loop, ftell(outfd)); + + /* write out selected portions of the section header */ + write_out(sh_name, strlen(sh_name)); + write_out_val(sections[sect].sh_type); + write_out_val(sections[sect].sh_flags); + write_out_val(sections[sect].sh_size); + write_out_val(sections[sect].sh_addralign); + + verbose("%02x %4d %s\n", csum, sect, sh_name); + } + + /* include allocatable loadable sections */ + if (sh_type != SHT_NOBITS && sh_flags & SHF_ALLOC) + goto include_section; + + /* not this section */ + continue; + + include_section: + verbose("%3u %08lx ", loop, ftell(outfd)); + + /* write out selected portions of the section header */ + write_out(sh_name, strlen(sh_name)); + write_out_val(sections[sect].sh_type); + write_out_val(sections[sect].sh_flags); + write_out_val(sections[sect].sh_size); + write_out_val(sections[sect].sh_addralign); + + /* write out the section data */ + write_out(data, sh_size); + + verbose("%02x %4d %s\n", csum, sect, sh_name); + } + + verbose("%08lx (%lu bytes csum 0x%02x)\n", + ftell(outfd), ftell(outfd), xcsum); +} + +/* + * extract a RELA table + * - need to canonicalise the entries in case section addition/removal has + * rearranged the symbol table and the section table + */ +static void extract_elf32_rela(const void *buffer, int secix, int targetix, + const Elf32_Rela *relatab, size_t nrels, + const Elf32_Sym *symbols, size_t nsyms, + const Elf32_Shdr *sections, size_t nsects, + int *canonmap, + const char *strings, size_t nstrings, + const char *sh_name) +{ + struct { + uint32_t r_offset; + uint32_t r_addend; + uint32_t st_value; + uint32_t st_size; + uint16_t st_shndx; + uint8_t r_type; + uint8_t st_info; + uint8_t st_other; + + } __attribute__((packed)) relocation; + + const Elf32_Sym *symbol; + size_t loop; + + /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */ + for (loop = 0; loop < nrels; loop++) { + Elf32_Section st_shndx; + Elf32_Word r_info; + + /* decode the relocation */ + r_info = get32(&relatab[loop].r_info); + relocation.r_offset = relatab[loop].r_offset; + relocation.r_addend = relatab[loop].r_addend; + relocation.r_type = ELF32_R_TYPE(r_info); + + if (ELF32_R_SYM(r_info) >= nsyms) { + fprintf(stderr, "Invalid symbol ID %x in relocation %zu\n", + ELF32_R_SYM(r_info), loop); + exit(1); + } + + /* decode the symbol referenced by the relocation */ + symbol = &symbols[ELF32_R_SYM(r_info)]; + relocation.st_info = symbol->st_info; + relocation.st_other = symbol->st_other; + relocation.st_value = symbol->st_value; + relocation.st_size = symbol->st_size; + relocation.st_shndx = symbol->st_shndx; + st_shndx = get16(&symbol->st_shndx); + + /* canonicalise the section used by the symbol */ + if (st_shndx > SHN_UNDEF && st_shndx < nsects) + set16(&relocation.st_shndx, canonmap[st_shndx]); + + write_out_val(relocation); + + /* undefined symbols must be named if referenced */ + if (st_shndx == SHN_UNDEF) { + const char *name = strings + get32(&symbol->st_name); + write_out(name, strlen(name) + 1); + } + } + + verbose("%02x %4d %s [canon]\n", csum, secix, sh_name); +} + +/* + * extract a REL table + * - need to canonicalise the entries in case section addition/removal has + * rearranged the symbol table and the section table + */ +static void extract_elf32_rel(const void *buffer, int secix, int targetix, + const Elf32_Rel *relatab, size_t nrels, + const Elf32_Sym *symbols, size_t nsyms, + const Elf32_Shdr *sections, size_t nsects, + int *canonmap, + const char *strings, size_t nstrings, + const char *sh_name) +{ + struct { + uint32_t r_offset; + uint32_t st_value; + uint32_t st_size; + uint16_t st_shndx; + uint8_t r_type; + uint8_t st_info; + uint8_t st_other; + + } __attribute__((packed)) relocation; + + const Elf32_Sym *symbol; + size_t loop; + + /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */ + for (loop = 0; loop < nrels; loop++) { + Elf32_Section st_shndx; + Elf32_Word r_info; + + /* decode the relocation */ + r_info = get32(&relatab[loop].r_info); + relocation.r_offset = relatab[loop].r_offset; + relocation.r_type = ELF32_R_TYPE(r_info); + + if (ELF32_R_SYM(r_info) >= nsyms) { + fprintf(stderr, "Invalid symbol ID %x in relocation %zu\n", + ELF32_R_SYM(r_info), loop); + exit(1); + } + + /* decode the symbol referenced by the relocation */ + symbol = &symbols[ELF32_R_SYM(r_info)]; + relocation.st_info = symbol->st_info; + relocation.st_other = symbol->st_other; + relocation.st_value = symbol->st_value; + relocation.st_size = symbol->st_size; + relocation.st_shndx = symbol->st_shndx; + st_shndx = get16(&symbol->st_shndx); + + /* canonicalise the section used by the symbol */ + if (st_shndx > SHN_UNDEF && st_shndx < nsects) + set16(&relocation.st_shndx, canonmap[st_shndx]); + + write_out_val(relocation); + + /* undefined symbols must be named if referenced */ + if (st_shndx == SHN_UNDEF) { + const char *name = strings + get32(&symbol->st_name); + write_out(name, strlen(name) + 1); + } + } + + verbose("%02x %4d %s [canon]\n", csum, secix, sh_name); +} + +/* + * extract the data from a 32-bit module + */ +static void extract_elf32(void *buffer, size_t len, Elf32_Ehdr *hdr) +{ + const Elf32_Sym *symbols; + Elf32_Shdr *sections; + const char *secstrings, *strings; + size_t nsyms, nstrings; + int loop, shnum, *canonlist, *canonmap, canon, changed, tmp; + + sections = buffer + get32(&hdr->e_shoff); + secstrings = buffer + get32(§ions[get16(&hdr->e_shstrndx)].sh_offset); + shnum = get16(&hdr->e_shnum); + + /* find the symbol table and the string table and produce a list of + * index numbers of sections that contribute to the kernel's module + * image + */ + canonlist = calloc(sizeof(int), shnum * 2); + if (!canonlist) { + perror("calloc"); + exit(1); + } + canonmap = canonlist + shnum; + canon = 0; + + symbols = NULL; + strings = NULL; + nstrings = 0; + nsyms = 0; + + for (loop = 1; loop < shnum; loop++) { + const char *sh_name = secstrings + get32(§ions[loop].sh_name); + Elf32_Word sh_type = get32(§ions[loop].sh_type); + Elf32_Xword sh_size = get32(§ions[loop].sh_size); + Elf32_Xword sh_flags = get32(§ions[loop].sh_flags); + Elf64_Word sh_info = get32(§ions[loop].sh_info); + Elf32_Off sh_offset = get32(§ions[loop].sh_offset); + void *data = buffer + sh_offset; + + /* quick sanity check */ + if (sh_type != SHT_NOBITS && len < sh_offset + sh_size) { + fprintf(stderr, "Section goes beyond EOF\n"); + exit(3); + } + + /* we only need to canonicalise allocatable sections */ + if (sh_flags & SHF_ALLOC) + canonlist[canon++] = loop; + else if ((sh_type == SHT_REL || sh_type == SHT_RELA) && + get32(§ions[sh_info].sh_flags) & SHF_ALLOC) + canonlist[canon++] = loop; + + /* keep track of certain special sections */ + switch (sh_type) { + case SHT_SYMTAB: + if (strcmp(sh_name, ".symtab") == 0) { + symbols = data; + nsyms = sh_size / sizeof(Elf32_Sym); + } + break; + + case SHT_STRTAB: + if (strcmp(sh_name, ".strtab") == 0) { + strings = data; + nstrings = sh_size; + } + break; + + default: + break; + } + } + + if (!symbols) { + fprintf(stderr, "Couldn't locate symbol table\n"); + exit(3); + } + + if (!strings) { + fprintf(stderr, "Couldn't locate strings table\n"); + exit(3); + } + + /* canonicalise the index numbers of the contributing section */ + do { + changed = 0; + + for (loop = 0; loop < canon - 1; loop++) { + const char *x = secstrings + get32(§ions[canonlist[loop + 0]].sh_name); + const char *y = secstrings + get32(§ions[canonlist[loop + 1]].sh_name); + if (strcmp(x, y) > 0) { + tmp = canonlist[loop + 0]; + canonlist[loop + 0] = canonlist[loop + 1]; + canonlist[loop + 1] = tmp; + changed = 1; + } + } + + } while (changed); + + for (loop = 0; loop < canon; loop++) + canonmap[canonlist[loop]] = loop + 1; + + if (is_verbose > 1) { + printf("\nSection canonicalisation map:\n"); + for (loop = 1; loop < shnum; loop++) { + const char *x = secstrings + get32(§ions[loop].sh_name); + printf("%4d %s\n", canonmap[loop], x); + } + + printf("\nAllocated section list in canonical order:\n"); + for (loop = 0; loop < canon; loop++) { + const char *x = secstrings + get32(§ions[canonlist[loop]].sh_name); + printf("%4d %s\n", canonlist[loop], x); + } + } + + /* iterate through the section table looking for sections we want to + * contribute to the signature */ + verbose("\n"); + verbose("CAN FILE POS CS SECT NAME\n"); + verbose("=== ======== == ==== ==============================\n"); + + for (loop = 0; loop < canon; loop++) { + int sect = canonlist[loop]; + const char *sh_name = secstrings + get32(§ions[sect].sh_name); + Elf32_Word sh_type = get32(§ions[sect].sh_type); + Elf32_Xword sh_size = get32(§ions[sect].sh_size); + Elf32_Xword sh_flags = get32(§ions[sect].sh_flags); + Elf32_Word sh_info = get32(§ions[sect].sh_info); + Elf32_Off sh_offset = get32(§ions[sect].sh_offset); + void *data = buffer + sh_offset; + + csum = 0; + + /* quick sanity check */ + if (sh_type != SHT_NOBITS && len < sh_offset + sh_size) { + fprintf(stderr, "section goes beyond EOF\n"); + exit(3); + } + + /* include canonicalised relocation sections */ + if (sh_type == SHT_REL || sh_type == SHT_RELA) { + Elf32_Word canon_sh_info; + + if (sh_info <= 0 && sh_info >= hdr->e_shnum) { + fprintf(stderr, + "Invalid ELF - REL/RELA sh_info does" + " not refer to a valid section\n"); + exit(3); + } + + verbose("%3u %08lx ", loop, ftell(outfd)); + + set32(&canon_sh_info, canonmap[sh_info]); + + /* write out selected portions of the section header */ + write_out(sh_name, strlen(sh_name)); + write_out_val(sections[sect].sh_type); + write_out_val(sections[sect].sh_flags); + write_out_val(sections[sect].sh_size); + write_out_val(sections[sect].sh_addralign); + write_out_val(canon_sh_info); + + if (sh_type == SHT_RELA) + extract_elf32_rela(buffer, sect, sh_info, + data, sh_size / sizeof(Elf32_Rela), + symbols, nsyms, + sections, shnum, canonmap, + strings, nstrings, + sh_name); + else + extract_elf32_rel(buffer, sect, sh_info, + data, sh_size / sizeof(Elf32_Rel), + symbols, nsyms, + sections, shnum, canonmap, + strings, nstrings, + sh_name); + continue; + } + + /* include the headers of BSS sections */ + if (sh_type == SHT_NOBITS && sh_flags & SHF_ALLOC) { + verbose("%3u %08lx ", loop, ftell(outfd)); + + /* write out selected portions of the section header */ + write_out(sh_name, strlen(sh_name)); + write_out_val(sections[sect].sh_type); + write_out_val(sections[sect].sh_flags); + write_out_val(sections[sect].sh_size); + write_out_val(sections[sect].sh_addralign); + + verbose("%02x %4d %s\n", csum, sect, sh_name); + } + + /* include allocatable loadable sections */ + if (sh_type != SHT_NOBITS && sh_flags & SHF_ALLOC) + goto include_section; + + /* not this section */ + continue; + + include_section: + verbose("%3u %08lx ", loop, ftell(outfd)); + + /* write out selected portions of the section header */ + write_out(sh_name, strlen(sh_name)); + write_out_val(sections[sect].sh_type); + write_out_val(sections[sect].sh_flags); + write_out_val(sections[sect].sh_size); + write_out_val(sections[sect].sh_addralign); + + /* write out the section data */ + write_out(data, sh_size); + + verbose("%02x %4d %s\n", csum, sect, sh_name); + } + + verbose("%08lx (%lu bytes csum 0x%02x)\n", + ftell(outfd), ftell(outfd), xcsum); +} diff --git a/scripts/mod/modsign-note.sh b/scripts/mod/modsign-note.sh new file mode 100644 index 0000000..bca67c0 --- /dev/null +++ b/scripts/mod/modsign-note.sh @@ -0,0 +1,16 @@ +#!/bin/sh +# +# Generate a module signature note source file +# +# mod-sign.sh > +# + +SIG=$1 + +cat < + +ELFNOTE(MODSIGN_NOTE_NAME, MODSIGN_NOTE_TYPE, .incbin "$SIG") +EOF + +exit 0 -- 1.7.11.2 From 6a2e8f0245dadda42c355eda278110f496e3a6d5 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:14:01 +0100 Subject: [PATCH 20/28] MODSIGN: Module signature verification stub Create a stub for the module signature verifier and link it into module.c so that it gets called. A field is added to struct module to record whether or not a valid module signature was detected. The stub also implements the policy for handling unsigned modules and the printing of error messages to indicate various problems with the module. If CONFIG_MODULE_SIG_FORCE is enabled or "enforcemodulesig=1" is supplied on the kernel command line, the kernel will _only_ load validly signed modules for which it has a public key. Otherwise, it will also load modules that are unsigned. Any module for which the kernel has a key, but which proves to have a signature mismatch will not be permitted to load. This table indicates the behaviours in the various situations: MODULE STATE PERMISSIVE MODE ENFORCING MODE ======================================= =============== =============== Unsigned Ok EKEYREJECTED Signed, no public key ENOKEY ENOKEY Validly signed, public key Ok Ok Invalidly signed, public key EKEYREJECTED EKEYREJECTED Validly signed, expired key EKEYEXPIRED EKEYEXPIRED Signed, hash algorithm unavailable ENOPKG ENOPKG Corrupt signature EBADMSG EBADMSG Corrupt ELF ELIBBAD ELIBBAD Signed-off-by: David Howells --- include/linux/module.h | 3 ++ kernel/Makefile | 1 + kernel/module-verify-defs.h | 77 +++++++++++++++++++++++++++++++ kernel/module-verify.c | 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 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -227,6 +227,9 @@ struct module /* Unique handle for this module */ char name[MODULE_NAME_LEN]; + /* Is this module GPG signed */ + bool gpgsig_ok; + /* Sysfs stuff. */ struct module_kobject mkobj; struct module_attribute *modinfo_attrs; diff --git a/kernel/Makefile b/kernel/Makefile index c0cc67a..cec222a 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -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 --- /dev/null +++ b/kernel/module-verify-defs.h @@ -0,0 +1,77 @@ +/* Module verification internal definitions + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifdef CONFIG_MODULE_SIG + +/* + * Internal state + */ +struct module_verify_data { + struct crypto_key_verify_context *mod_sig; /* Module signing context */ + union { + const void *buffer; /* module buffer */ + const Elf_Ehdr *hdr; /* ELF header */ + }; + const Elf_Shdr *sections; /* ELF section table */ + const char *secstrings; /* ELF section string table */ + const void *sig; /* Signature note content */ + size_t size; /* module object size */ + size_t nsects; /* number of sections */ + size_t sig_size; /* Size of signature */ + size_t signed_size; /* count of bytes contributed to digest */ + unsigned *canonlist; /* list of canonicalised sections */ + unsigned *canonmap; /* section canonicalisation map */ + unsigned ncanon; /* number of canonicalised sections */ + unsigned sig_index; /* module signature section index */ + uint8_t xcsum; /* checksum of bytes contributed to digest */ + uint8_t csum; /* checksum of bytes representing a section */ +}; + +/* + * Whether or not we support various types of ELF relocation record + */ +#if defined(MODULE_HAS_ELF_REL_ONLY) +#define is_elf_rel(sh_type) ((sh_type) == SHT_REL) +#define is_elf_rela(sh_type) (0) +#elif defined(MODULE_HAS_ELF_RELA_ONLY) +#define is_elf_rel(sh_type) (0) +#define is_elf_rela(sh_type) ((sh_type) == SHT_RELA) +#else +#define is_elf_rel(sh_type) ((sh_type) == SHT_REL) +#define is_elf_rela(sh_type) ((sh_type) == SHT_RELA) +#endif + +/* + * Debugging. Define DEBUG to enable. + */ +#define _debug(FMT, ...) \ + do { \ + if (unlikely(modsign_debug)) \ + pr_debug(FMT, ##__VA_ARGS__); \ + } while (0) + +#ifdef DEBUG +#define count_and_csum(C, __p, __n) \ + do { \ + int __loop; \ + for (__loop = 0; __loop < __n; __loop++) { \ + (C)->csum += __p[__loop]; \ + (C)->xcsum += __p[__loop]; \ + } \ + (C)->signed_size += __n; \ + } while (0) +#else +#define count_and_csum(C, __p, __n) \ + do { \ + } while (0) +#endif + +#endif /* CONFIG_MODULE_SIG */ diff --git a/kernel/module-verify.c b/kernel/module-verify.c new file mode 100644 index 0000000..4bf857e --- /dev/null +++ b/kernel/module-verify.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "module-verify.h" +#include "module-verify-defs.h" + +#ifdef DEBUG +static int modsign_debug; +core_param(modsign_debug, modsign_debug, int, 0644); +#else +#define modsign_debug false +#endif + +#ifdef CONFIG_MODULE_SIG_FORCE +#define modsign_signedonly true +#else +static bool modsign_signedonly; +#endif + +static const char modsign_note_name[] = ELFNOTE_NAME(MODSIGN_NOTE_NAME); +static const char modsign_note_section[] = ELFNOTE_SECTION(MODSIGN_NOTE_NAME); + +/* + * Verify a module's integrity + */ +int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok) +{ + struct module_verify_data mvdata; + int ret; + + memset(&mvdata, 0, sizeof(mvdata)); + mvdata.buffer = hdr; + mvdata.size = size; + + if (mvdata.sig_index <= 0) { + /* Deal with an unsigned module */ + if (modsign_signedonly) { + pr_err("An attempt to load unsigned module was rejected\n"); + return -EKEYREJECTED; + } else { + return 0; + } + goto out; + } + + ret = 0; + +out: + switch (ret) { + case 0: /* Good signature */ + *_gpgsig_ok = true; + break; + case -ELIBBAD: + pr_err("Module format error encountered\n"); + break; + case -EBADMSG: + pr_err("Module signature error encountered\n"); + break; + case -EKEYREJECTED: /* Signature mismatch or number format error */ + pr_err("Module signature verification failed\n"); + break; + case -ENOKEY: /* Signed, but we don't have the public key */ + pr_err("Module signed with unknown public key\n"); + break; + default: /* Other error (probably ENOMEM) */ + break; + } + return ret; +} + +static int __init sign_setup(char *str) +{ +#ifndef CONFIG_MODULE_SIG_FORCE + modsign_signedonly = true; +#endif + return 0; +} +__setup("enforcemodulesig", sign_setup); diff --git a/kernel/module-verify.h b/kernel/module-verify.h new file mode 100644 index 0000000..c640634 --- /dev/null +++ b/kernel/module-verify.h @@ -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 --- a/kernel/module.c +++ b/kernel/module.c @@ -58,6 +58,7 @@ #include #include #include +#include "module-verify.h" #define CREATE_TRACE_POINTS #include @@ -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.2 From 62c90369e58486688303c4803e39d7df44a932f9 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:14:02 +0100 Subject: [PATCH 21/28] MODSIGN: Automatically generate module signing keys if missing Automatically generate keys for module signing if they're absent so that allyesconfig doesn't break. The builder should consider generating their own keyrings, however, so that the keys are appropriately named and any extra keys required get imported. Also change the names of the keyring files to modsign.pub and modsign.sec so that they are then a more obvious what they're about and add a dependency for the signing rules on the keyring files so that the signatures get regenerated if the keyrings change. Signed-off-by: David Howells --- kernel/Makefile | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/kernel/Makefile b/kernel/Makefile index cec222a..28cd248 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -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.2 From 00ce30147994ed4a503bdb051350a4601c565dcc Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:14:02 +0100 Subject: [PATCH 22/28] MODSIGN: Provide module signing public keys to the kernel Include a PGP keyring containing the public keys required to perform module verification in the kernel image during build and create a special keyring during boot which is then populated with keys of crypto type holding the public keys found in the PGP keyring. These can be seen by root: [root@andromeda ~]# cat /proc/keys 07ad4ee0 I----- 1 perm 3f010000 0 0 crypto modsign.0: RSA 87b9b3bd [] 15c7f8c3 I----- 1 perm 1f030000 0 0 keyring .module_sign: 1/4 ... It is probably worth permitting root to invalidate these keys, resulting in their removal and preventing further modules from being loaded with that key. Signed-off-by: David Howells --- kernel/Makefile | 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 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -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..17e02f5 --- /dev/null +++ b/kernel/modsign-pubkey.c @@ -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 +#include +#include +#include +#include +#include "module-verify-defs.h" + +struct key *modsign_keyring; + +extern __initdata const u8 modsign_public_keys[]; +extern __initdata const u8 modsign_public_keys_end[]; +asm(".section .init.data,\"aw\"\n" + "modsign_public_keys:\n" + ".incbin \"modsign.pub\"\n" + "modsign_public_keys_end:" + ); + +/* + * We need to make sure ccache doesn't cache the .o file as it doesn't notice + * if modsign.pub changes. + */ +static __initdata const char annoy_ccache[] = __TIME__ "foo"; + +/* + * Load the compiled-in keys + */ +static __init int module_verify_init(void) +{ + pr_notice("Initialise module verification\n"); + + modsign_keyring = key_alloc(&key_type_keyring, ".module_sign", + 0, 0, current_cred(), + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ, + KEY_ALLOC_NOT_IN_QUOTA); + if (IS_ERR(modsign_keyring)) + panic("Can't allocate module signing keyring\n"); + + if (key_instantiate_and_link(modsign_keyring, NULL, 0, NULL, NULL) < 0) + panic("Can't instantiate module signing keyring\n"); + + return 0; +} + +/* + * Must be initialised before we try and load the keys into the keyring. + */ +device_initcall(module_verify_init); + +/* + * Load the compiled-in keys + */ +static __init int modsign_pubkey_init(void) +{ + pr_notice("Load module verification keys\n"); + + if (preload_pgp_keys(modsign_public_keys, + modsign_public_keys_end - modsign_public_keys, + modsign_keyring, "modsign.") < 0) + panic("Can't load module signing keys\n"); + + return 0; +} +late_initcall(modsign_pubkey_init); diff --git a/kernel/module-verify-defs.h b/kernel/module-verify-defs.h index 141ddab..2fe31e1 100644 --- a/kernel/module-verify-defs.h +++ b/kernel/module-verify-defs.h @@ -11,6 +11,10 @@ #ifdef CONFIG_MODULE_SIG +#include + +extern struct key *modsign_keyring; + /* * Internal state */ diff --git a/kernel/module-verify.c b/kernel/module-verify.c index 4bf857e..05473e6 100644 --- a/kernel/module-verify.c +++ b/kernel/module-verify.c @@ -28,8 +28,6 @@ #include #include #include -#include -#include #include #include #include -- 1.7.11.2 From 9b94f77eea94d028df6a041e6772f9f142eb89e7 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:14:02 +0100 Subject: [PATCH 23/28] MODSIGN: Check the ELF container Check the ELF container of the kernel module to prevent the kernel from crashing or getting corrupted whilst trying to use it and locate the module signature note if present. We try to check as little as possible. We check the metadata that the signature checker actually has to use, and leave anything that it doesn't actually need to the signature to catch. The stuff we need to check is: (1) The locations and offsets in the ELF header of important parts like the section table. (2) The section table. Note that we only check sh_info for section types that we're actually interested in (string, symbol and relocation tables). We also check that alignments are what we expect for those tables. (3) That non-empty string tables have the required NUL at the end so that we can be sure that all strings therein are NUL-terminated. We don't bother checking for the required NUL at the beginning as it shouldn't cause a problem to us. (4) The name offset and section index in each symbol. We could defer this to when we deal with the relocation tables so that we only check symbols that are used by relocations - but we would then end up checking some symbols multiple times. (5) The module signature note section and the first note in it if present. (6) That relocations applied to an allocatable section only refer to symbols in allocatable sections and absolute symbols (done in the module signing code rather than here). Note that these checks survive "strip -x", "strip -g" and "eu-strip" being applied to a module and detect if the module was given to "strip" or "strip -s" and report an error. We can skip some direct checks that turn out unnecessary or redundant: (1) That sh_link has a greater than 0 value for symbol tables and relocation tables. These require the index of a string table and a symbol table respectively - and since we have already checked section 0 is of SHT_NULL type, checking the symbol type renders the sh_link > 0 check redundant. (2) That a non-empty string table begins with a NUL. Since we check the string table ends with a NUL, any string in there will be NUL-terminated and shouldn't cause us to transgress beyond the bounds of the string table when using strlen(). (3) That strings in a string table actually make sense. We don't care, so long as it is NUL terminated. Any string that refers to an undefined symbol is added to the crypto digest and will be checked that way. Strings that we directly look for (such as ".modinfo") will be validated by that. (4) That sections don't overlap. We don't actually care if sections overlap in the file, provided we don't see bad metadata. If the sections holding the allocatable content overlap, then the signature check is likely to fail. (5) That symbol values and relocation offsets and addends make sense. We just add this data to the digest if it pertains to an allocatable section. (6) That allocatable note sections, other than the signature note, make sense. The contents of these get added to the digest in their entirety, so we don't need to check them manually. If bad ELF is detected, ELIBBAD is indicated. Note! The "noinline" attribute on the module_verify_elf() function results in somewhat smaller code. Similarly, having separate loops to check basic section parameters and to check type-specific features of sections results in smaller code, presumably because some local variables can be discarded. Signed-off-by: David Howells --- kernel/module-verify.c | 230 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) diff --git a/kernel/module-verify.c b/kernel/module-verify.c index 05473e6..2161d11 100644 --- a/kernel/module-verify.c +++ b/kernel/module-verify.c @@ -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.2 From 60ca7dc263084abcf68325ed86d2765148f60225 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:14:02 +0100 Subject: [PATCH 24/28] MODSIGN: Produce a filtered and canonicalised section list Build a list of the sections in which we're interested and canonicalise the section indices to avoid the problems of the section table being altered by ld when the signature is linked into the binary and by strip. The only sections in which we're actually interested are those that are marked allocatable (which will be kept in memory) and relocation tables that are applicable to those sections. Canonicalisation is done by sorting the filtered list in order of section name. Signed-off-by: David Howells --- kernel/module-verify.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/kernel/module-verify.c b/kernel/module-verify.c index 2161d11..646b104 100644 --- a/kernel/module-verify.c +++ b/kernel/module-verify.c @@ -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.2 From b847c539c4fb7d71ab7383e79b3e6c0683a23a7e Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:14:03 +0100 Subject: [PATCH 25/28] MODSIGN: Create digest of module content and check signature Apply signature checking to modules on module load, checking the signature against the ring of public keys compiled into the kernel (if enabled by CONFIG_MODULE_SIG). Turning on signature checking will also force the module's ELF metadata to be verified first. There are several reasons why these patches are useful, amongst which are: (1) to prevent accidentally corrupted modules from causing damage; (2) to prevent maliciously modified modules from causing damage; (3) to allow a sysadmin (or more likely an IT department) to enforce a policy that only known and approved modules shall be loaded onto machines which they're expected to support; (4) to allow other support providers to do likewise, or at least to _detect_ the fact that unsupported modules are loaded; (5) to allow the detection of modules replaced by a second-order distro or a preloaded Linux purveyor. These patches have two main appeals: (a) preventing malicious modules from being loaded, and (b) reducing support workload by pointing out modules on a crashing box that aren't what they're expected to be. Note that this is not a complete solution by any means: the core kernel is not protected, and nor are /dev/mem or /dev/kmem, but it denies (or at least controls) one relatively simple attack vector. To protect the kernel image would be the responsibility of the boot loader or the system BIOS. This facility is optional: the builder of a kernel is by no means under any requirement to actually enable it, let alone force the set of loadable modules to be restricted to just those that the builder provides (there are degrees of restriction available). Note! The "noinline" attribute on module_verify_signature() results in somewhat smaller code. Signed-off-by: David Howells --- kernel/module-verify-defs.h | 11 +- kernel/module-verify.c | 332 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 337 insertions(+), 6 deletions(-) diff --git a/kernel/module-verify-defs.h b/kernel/module-verify-defs.h index 2fe31e1..82952b0 100644 --- a/kernel/module-verify-defs.h +++ b/kernel/module-verify-defs.h @@ -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..e275759 100644 --- a/kernel/module-verify.c +++ b/kernel/module-verify.c @@ -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_key_verify_context *mod_sig; + const Elf_Shdr *sechdrs = mvdata->sections; + const char *secstrings = mvdata->secstrings; + const u8 *sig = mvdata->sig; + size_t sig_size = mvdata->sig_size; + int loop, ret; + + _debug("sig in section %u (size %zu)\n", + mvdata->sig_index, mvdata->sig_size); + _debug("%02x%02x%02x%02x%02x%02x%02x%02x\n", + sig[0], sig[1], sig[2], sig[3], + sig[4], sig[5], sig[6], sig[7]); + + /* Find the crypto key for the module signature + * - !!! if this tries to load the required hash algorithm module, + * we will deadlock!!! + */ + mod_sig = verify_sig_begin(modsign_keyring, sig, sig_size); + if (IS_ERR(mod_sig)) { + pr_err("Couldn't initiate module signature verification: %ld\n", + PTR_ERR(mod_sig)); + return PTR_ERR(mod_sig); + } + + mvdata->mod_sig = mod_sig; +#ifdef DEBUG + mvdata->xcsum = 0; +#endif + + /* Load data from each relevant section into the digest. Note that + * canonlist[] is a filtered list and only contains the sections we + * actually want. + */ + for (loop = 0; loop < mvdata->ncanon; loop++) { + int sect = mvdata->canonlist[loop]; + unsigned long sh_type = sechdrs[sect].sh_type; + unsigned long sh_info = sechdrs[sect].sh_info; + unsigned long sh_size = sechdrs[sect].sh_size; + const char *sh_name = secstrings + sechdrs[sect].sh_name; + const void *data = mvdata->buffer + sechdrs[sect].sh_offset; + +#ifdef DEBUG + mvdata->csum = 0; +#endif + + /* Digest the headers of any section we include. */ + crypto_digest_update_data(mvdata, sh_name, strlen(sh_name)); + crypto_digest_update_val(mvdata, sechdrs[sect].sh_type); + crypto_digest_update_val(mvdata, sechdrs[sect].sh_flags); + crypto_digest_update_val(mvdata, sechdrs[sect].sh_size); + crypto_digest_update_val(mvdata, sechdrs[sect].sh_addralign); + + /* Relocation record sections refer to the section to be + * relocated, but this needs to be canonicalised to survive + * stripping. + */ + if (is_elf_rel(sh_type) || is_elf_rela(sh_type)) + crypto_digest_update_val(mvdata, + mvdata->canonmap[sh_info]); + + /* Since relocation records give details of how we have to + * alter the allocatable sections, we need to digest these too. + * + * These, however, refer to metadata (symbols and sections) + * that may have been altered by the process of adding the + * signature section or the process of being stripped. + * + * To deal with this, we substitute the referenced metadata for + * the references to that metadata. So, for instance, the + * symbol ref from the relocation record is replaced with the + * contents of the symbol to which it refers, and the symbol's + * section ref is replaced with a canonicalised section number. + */ +#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.2 From 86969bc531b37c88b499311abae41e0116666dcc Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:14:03 +0100 Subject: [PATCH 26/28] MODSIGN: Suppress some redundant ELF checks Suppress some redundant ELF checks in module_verify_elf() that are also done by copy_and_check() in the core module loader code prior to calling module_verify(). Signed-off-by: David Howells --- kernel/module-verify.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/module-verify.c b/kernel/module-verify.c index e275759..bfd1286 100644 --- a/kernel/module-verify.c +++ b/kernel/module-verify.c @@ -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.2 From 4d5a1f0360ce04a24b847eee2da84d9618375ce8 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 24 Jul 2012 14:14:03 +0100 Subject: [PATCH 27/28] 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 Signed-off-by: David Howells --- kernel/module-verify.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/module-verify.c b/kernel/module-verify.c index bfd1286..b9c3955 100644 --- a/kernel/module-verify.c +++ b/kernel/module-verify.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #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.2 From dd6e65be6a8f225018259b16161decc26c09c300 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Thu, 2 Aug 2012 14:35:44 +0100 Subject: [PATCH 28/28] 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 Signed-off-by: David Howells --- kernel/module-verify.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernel/module-verify.c b/kernel/module-verify.c index b9c3955..22036d4 100644 --- a/kernel/module-verify.c +++ b/kernel/module-verify.c @@ -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.2 From bccd1bfe487e1f4df543f7a160cc9876d7d96fb7 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Thu, 2 Aug 2012 23:09:19 +0100 Subject: [PATCH] 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 Acked-by: Josh Boyer Signed-off-by: David Howells --- 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 --- a/Documentation/module-signing.txt +++ b/Documentation/module-signing.txt @@ -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.2