8747 lines
260 KiB
Diff
8747 lines
260 KiB
Diff
From 711fd460b3d44d666fbddd80a91ae5f825c7ebb6 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
Cc: David S. Miller <davem@davemloft.net>
|
|
Cc: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
|
|
Cc: Arnd Bergmann <arnd@arndb.com>
|
|
---
|
|
include/asm-generic/bitops/count_zeros.h | 57 +++++++++++++
|
|
lib/mpi/longlong.h | 138 +------------------------------
|
|
lib/mpi/mpi-bit.c | 2 +-
|
|
lib/mpi/mpi-pow.c | 4 +-
|
|
4 files changed, 62 insertions(+), 139 deletions(-)
|
|
create mode 100644 include/asm-generic/bitops/count_zeros.h
|
|
|
|
diff --git a/include/asm-generic/bitops/count_zeros.h b/include/asm-generic/bitops/count_zeros.h
|
|
new file mode 100644
|
|
index 0000000..97520d2
|
|
--- /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 <asm/bitops.h>
|
|
+
|
|
+/**
|
|
+ * count_leading_zeros - Count the number of zeros from the MSB back
|
|
+ * @x: The value
|
|
+ *
|
|
+ * Count the number of leading zeros from the MSB going towards the LSB in @x.
|
|
+ *
|
|
+ * If the MSB of @x is set, the result is 0.
|
|
+ * If only the LSB of @x is set, then the result is BITS_PER_LONG-1.
|
|
+ * If @x is 0 then the result is COUNT_LEADING_ZEROS_0.
|
|
+ */
|
|
+static inline int count_leading_zeros(unsigned long x)
|
|
+{
|
|
+ if (sizeof(x) == 4)
|
|
+ return BITS_PER_LONG - fls(x);
|
|
+ else
|
|
+ return BITS_PER_LONG - fls64(x);
|
|
+}
|
|
+
|
|
+#define COUNT_LEADING_ZEROS_0 BITS_PER_LONG
|
|
+
|
|
+/**
|
|
+ * count_trailing_zeros - Count the number of zeros from the LSB forwards
|
|
+ * @x: The value
|
|
+ *
|
|
+ * Count the number of trailing zeros from the LSB going towards the MSB in @x.
|
|
+ *
|
|
+ * If the LSB of @x is set, the result is 0.
|
|
+ * If only the MSB of @x is set, then the result is BITS_PER_LONG-1.
|
|
+ * If @x is 0 then the result is COUNT_TRAILING_ZEROS_0.
|
|
+ */
|
|
+static inline int count_trailing_zeros(unsigned long x)
|
|
+{
|
|
+#define COUNT_TRAILING_ZEROS_0 (-1)
|
|
+
|
|
+ if (sizeof(x) == 4)
|
|
+ return ffs(x);
|
|
+ else
|
|
+ return (x != 0) ? __ffs(x) : COUNT_TRAILING_ZEROS_0;
|
|
+}
|
|
+
|
|
+#endif /* _ASM_GENERIC_BITOPS_COUNT_ZEROS_H_ */
|
|
diff --git a/lib/mpi/longlong.h b/lib/mpi/longlong.h
|
|
index 29f9862..678ce4f 100644
|
|
--- 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 <asm-generic/bitops/count_zeros.h>
|
|
+
|
|
/* You have to define the following before including this file:
|
|
*
|
|
* UWtype -- An unsigned type, default type for operations (typically a "word")
|
|
@@ -146,12 +148,6 @@ do { \
|
|
: "1" ((USItype)(n1)), \
|
|
"r" ((USItype)(n0)), \
|
|
"r" ((USItype)(d)))
|
|
-
|
|
-#define count_leading_zeros(count, x) \
|
|
- __asm__ ("clz %0,%1" \
|
|
- : "=r" ((USItype)(count)) \
|
|
- : "r" ((USItype)(x)))
|
|
-#define COUNT_LEADING_ZEROS_0 32
|
|
#endif /* __a29k__ */
|
|
|
|
#if defined(__alpha) && W_TYPE_SIZE == 64
|
|
@@ -298,11 +294,6 @@ extern UDItype __udiv_qrnnd();
|
|
: "1" ((USItype)(nh)), \
|
|
"0" ((USItype)(nl)), \
|
|
"g" ((USItype)(d)))
|
|
-#define count_leading_zeros(count, x) \
|
|
- __asm__ ("bsch/1 %1,%0" \
|
|
- : "=g" (count) \
|
|
- : "g" ((USItype)(x)), \
|
|
- "0" ((USItype)0))
|
|
#endif
|
|
|
|
/***************************************
|
|
@@ -354,27 +345,6 @@ do { USItype __r; \
|
|
} while (0)
|
|
extern USItype __udiv_qrnnd();
|
|
#endif /* LONGLONG_STANDALONE */
|
|
-#define count_leading_zeros(count, x) \
|
|
-do { \
|
|
- USItype __tmp; \
|
|
- __asm__ ( \
|
|
- "ldi 1,%0\n" \
|
|
- "extru,= %1,15,16,%%r0 ; Bits 31..16 zero?\n" \
|
|
- "extru,tr %1,15,16,%1 ; No. Shift down, skip add.\n" \
|
|
- "ldo 16(%0),%0 ; Yes. Perform add.\n" \
|
|
- "extru,= %1,23,8,%%r0 ; Bits 15..8 zero?\n" \
|
|
- "extru,tr %1,23,8,%1 ; No. Shift down, skip add.\n" \
|
|
- "ldo 8(%0),%0 ; Yes. Perform add.\n" \
|
|
- "extru,= %1,27,4,%%r0 ; Bits 7..4 zero?\n" \
|
|
- "extru,tr %1,27,4,%1 ; No. Shift down, skip add.\n" \
|
|
- "ldo 4(%0),%0 ; Yes. Perform add.\n" \
|
|
- "extru,= %1,29,2,%%r0 ; Bits 3..2 zero?\n" \
|
|
- "extru,tr %1,29,2,%1 ; No. Shift down, skip add.\n" \
|
|
- "ldo 2(%0),%0 ; Yes. Perform add.\n" \
|
|
- "extru %1,30,1,%1 ; Extract bit 1.\n" \
|
|
- "sub %0,%1,%0 ; Subtract it. " \
|
|
- : "=r" (count), "=r" (__tmp) : "1" (x)); \
|
|
-} while (0)
|
|
#endif /* hppa */
|
|
|
|
/***************************************
|
|
@@ -457,15 +427,6 @@ do { \
|
|
: "0" ((USItype)(n0)), \
|
|
"1" ((USItype)(n1)), \
|
|
"rm" ((USItype)(d)))
|
|
-#define count_leading_zeros(count, x) \
|
|
-do { \
|
|
- USItype __cbtmp; \
|
|
- __asm__ ("bsrl %1,%0" \
|
|
- : "=r" (__cbtmp) : "rm" ((USItype)(x))); \
|
|
- (count) = __cbtmp ^ 31; \
|
|
-} while (0)
|
|
-#define count_trailing_zeros(count, x) \
|
|
- __asm__ ("bsfl %1,%0" : "=r" (count) : "rm" ((USItype)(x)))
|
|
#ifndef UMUL_TIME
|
|
#define UMUL_TIME 40
|
|
#endif
|
|
@@ -536,15 +497,6 @@ do { \
|
|
"dI" ((USItype)(d))); \
|
|
(r) = __rq.__i.__l; (q) = __rq.__i.__h; \
|
|
} while (0)
|
|
-#define count_leading_zeros(count, x) \
|
|
-do { \
|
|
- USItype __cbtmp; \
|
|
- __asm__ ("scanbit %1,%0" \
|
|
- : "=r" (__cbtmp) \
|
|
- : "r" ((USItype)(x))); \
|
|
- (count) = __cbtmp ^ 31; \
|
|
-} while (0)
|
|
-#define COUNT_LEADING_ZEROS_0 (-32) /* sic */
|
|
#if defined(__i960mx) /* what is the proper symbol to test??? */
|
|
#define rshift_rhlc(r, h, l, c) \
|
|
do { \
|
|
@@ -603,11 +555,6 @@ do { \
|
|
: "0" ((USItype)(n0)), \
|
|
"1" ((USItype)(n1)), \
|
|
"dmi" ((USItype)(d)))
|
|
-#define count_leading_zeros(count, x) \
|
|
- __asm__ ("bfffo %1{%b2:%b2},%0" \
|
|
- : "=d" ((USItype)(count)) \
|
|
- : "od" ((USItype)(x)), "n" (0))
|
|
-#define COUNT_LEADING_ZEROS_0 32
|
|
#else /* not mc68020 */
|
|
#define umul_ppmm(xh, xl, a, b) \
|
|
do { USItype __umul_tmp1, __umul_tmp2; \
|
|
@@ -664,15 +611,6 @@ do { USItype __umul_tmp1, __umul_tmp2; \
|
|
"rJ" ((USItype)(bh)), \
|
|
"rJ" ((USItype)(al)), \
|
|
"rJ" ((USItype)(bl)))
|
|
-#define count_leading_zeros(count, x) \
|
|
-do { \
|
|
- USItype __cbtmp; \
|
|
- __asm__ ("ff1 %0,%1" \
|
|
- : "=r" (__cbtmp) \
|
|
- : "r" ((USItype)(x))); \
|
|
- (count) = __cbtmp ^ 31; \
|
|
-} while (0)
|
|
-#define COUNT_LEADING_ZEROS_0 63 /* sic */
|
|
#if defined(__m88110__)
|
|
#define umul_ppmm(wh, wl, u, v) \
|
|
do { \
|
|
@@ -779,12 +717,6 @@ do { \
|
|
: "0" (__xx.__ll), \
|
|
"g" ((USItype)(d))); \
|
|
(r) = __xx.__i.__l; (q) = __xx.__i.__h; })
|
|
-#define count_trailing_zeros(count, x) \
|
|
-do { \
|
|
- __asm__("ffsd %2,%0" \
|
|
- : "=r"((USItype) (count)) \
|
|
- : "0"((USItype) 0), "r"((USItype) (x))); \
|
|
- } while (0)
|
|
#endif /* __ns32000__ */
|
|
|
|
/***************************************
|
|
@@ -855,11 +787,6 @@ do { \
|
|
"rI" ((USItype)(al)), \
|
|
"r" ((USItype)(bl))); \
|
|
} while (0)
|
|
-#define count_leading_zeros(count, x) \
|
|
- __asm__ ("{cntlz|cntlzw} %0,%1" \
|
|
- : "=r" ((USItype)(count)) \
|
|
- : "r" ((USItype)(x)))
|
|
-#define COUNT_LEADING_ZEROS_0 32
|
|
#if defined(_ARCH_PPC)
|
|
#define umul_ppmm(ph, pl, m0, m1) \
|
|
do { \
|
|
@@ -1001,19 +928,6 @@ do { \
|
|
} while (0)
|
|
#define UMUL_TIME 20
|
|
#define UDIV_TIME 200
|
|
-#define count_leading_zeros(count, x) \
|
|
-do { \
|
|
- if ((x) >= 0x10000) \
|
|
- __asm__ ("clz %0,%1" \
|
|
- : "=r" ((USItype)(count)) \
|
|
- : "r" ((USItype)(x) >> 16)); \
|
|
- else { \
|
|
- __asm__ ("clz %0,%1" \
|
|
- : "=r" ((USItype)(count)) \
|
|
- : "r" ((USItype)(x))); \
|
|
- (count) += 16; \
|
|
- } \
|
|
-} while (0)
|
|
#endif /* RT/ROMP */
|
|
|
|
/***************************************
|
|
@@ -1142,13 +1056,6 @@ do { \
|
|
"rI" ((USItype)(d)) \
|
|
: "%g1" __AND_CLOBBER_CC)
|
|
#define UDIV_TIME 37
|
|
-#define count_leading_zeros(count, x) \
|
|
- __asm__ ("scan %1,0,%0" \
|
|
- : "=r" ((USItype)(x)) \
|
|
- : "r" ((USItype)(count)))
|
|
-/* Early sparclites return 63 for an argument of 0, but they warn that future
|
|
- implementations might change this. Therefore, leave COUNT_LEADING_ZEROS_0
|
|
- undefined. */
|
|
#endif /* __sparclite__ */
|
|
#endif /* __sparc_v8__ */
|
|
/* Default to sparc v7 versions of umul_ppmm and udiv_qrnnd. */
|
|
@@ -1454,47 +1361,6 @@ do { \
|
|
#define udiv_qrnnd __udiv_qrnnd_c
|
|
#endif
|
|
|
|
-#undef count_leading_zeros
|
|
-#if !defined(count_leading_zeros)
|
|
- extern
|
|
-#ifdef __STDC__
|
|
- const
|
|
-#endif
|
|
- unsigned char __clz_tab[];
|
|
-#define count_leading_zeros(count, x) \
|
|
-do { \
|
|
- UWtype __xr = (x); \
|
|
- UWtype __a; \
|
|
- \
|
|
- if (W_TYPE_SIZE <= 32) { \
|
|
- __a = __xr < ((UWtype) 1 << 2*__BITS4) \
|
|
- ? (__xr < ((UWtype) 1 << __BITS4) ? 0 : __BITS4) \
|
|
- : (__xr < ((UWtype) 1 << 3*__BITS4) ? 2*__BITS4 : 3*__BITS4); \
|
|
- } \
|
|
- else { \
|
|
- for (__a = W_TYPE_SIZE - 8; __a > 0; __a -= 8) \
|
|
- if (((__xr >> __a) & 0xff) != 0) \
|
|
- break; \
|
|
- } \
|
|
- \
|
|
- (count) = W_TYPE_SIZE - (__clz_tab[__xr >> __a] + __a); \
|
|
-} while (0)
|
|
- /* This version gives a well-defined value for zero. */
|
|
-#define COUNT_LEADING_ZEROS_0 W_TYPE_SIZE
|
|
-#endif
|
|
-
|
|
-#if !defined(count_trailing_zeros)
|
|
-/* Define count_trailing_zeros using count_leading_zeros. The latter might be
|
|
- defined in asm, but if it is not, the C version above is good enough. */
|
|
-#define count_trailing_zeros(count, x) \
|
|
-do { \
|
|
- UWtype __ctz_x = (x); \
|
|
- UWtype __ctz_c; \
|
|
- count_leading_zeros(__ctz_c, __ctz_x & -__ctz_x); \
|
|
- (count) = W_TYPE_SIZE - 1 - __ctz_c; \
|
|
-} while (0)
|
|
-#endif
|
|
-
|
|
#ifndef UDIV_NEEDS_NORMALIZATION
|
|
#define UDIV_NEEDS_NORMALIZATION 0
|
|
#endif
|
|
diff --git a/lib/mpi/mpi-bit.c b/lib/mpi/mpi-bit.c
|
|
index 5687248..503537e 100644
|
|
--- 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 <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
---
|
|
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:<hexdigits>" then the match
|
|
+ function will examine a key's fingerprint to see if the hex digits given
|
|
+ after the "id:" match the tail. For instance:
|
|
+
|
|
+ keyctl search @s crypto id:5acc2142
|
|
+
|
|
+ will match a key with fingerprint:
|
|
+
|
|
+ 1A00 2040 7601 7889 DE11 882C 3823 04AD 5ACC 2142
|
|
+
|
|
+ (2) If the criterion string is of the form "<subtype>:<hexdigits>" then the
|
|
+ match will match the ID as in (1), but with the added restriction that
|
|
+ only keys of the specified subtype (e.g. dsa or rsa) will be matched. For
|
|
+ instance:
|
|
+
|
|
+ keyctl search @s crypto dsa:5acc2142
|
|
+
|
|
+Looking in /proc/keys, the last 8 hex digits of the key fingerprint are
|
|
+displayed, along with the subtype:
|
|
+
|
|
+ 1a39e171 I----- 1 perm 3f010000 0 0 crypto modsign.0: DSA 5acc2142 []
|
|
+
|
|
+
|
|
+=====================
|
|
+ACCESSING CRYPTO KEYS
|
|
+=====================
|
|
+
|
|
+To access crypto keys from within the kernel, the following inclusion is
|
|
+required:
|
|
+
|
|
+ #include <keys/crypto-type.h>
|
|
+
|
|
+This gives access to the key type:
|
|
+
|
|
+ struct key_type key_type_crypto;
|
|
+
|
|
+
|
|
+===========================
|
|
+IMPLEMENTING CRYPTO PARSERS
|
|
+===========================
|
|
+
|
|
+The crypto key type keeps a list of registered data parsers. An example of
|
|
+such a parser is one that parses OpenPGP packet formatted data [RFC 4880].
|
|
+
|
|
+During key instantiation each parser in the list is tried until one doesn't
|
|
+return -EBADMSG.
|
|
+
|
|
+The parser definition structure looks like the following:
|
|
+
|
|
+ struct crypto_key_parser {
|
|
+ struct module *owner;
|
|
+ const char *name;
|
|
+
|
|
+ int (*instantiate)(struct key *key,
|
|
+ const void *data, size_t datalen);
|
|
+ };
|
|
+
|
|
+The owner and name fields should be set to the owning module and the name of
|
|
+the parser.
|
|
+
|
|
+There are a number of operations defined by the parser. They are all optional,
|
|
+but it is expected that at least one will be defined.
|
|
+
|
|
+ (1) instantiate().
|
|
+
|
|
+ The arguments are the same as for the instantiate function in the key
|
|
+ type. 'key' is the crypto key being instantiated; data and datalen are
|
|
+ the instantiation data, presumably containing cryptographic key data, and
|
|
+ the length of that data.
|
|
+
|
|
+ If the data format is not recognised, -EBADMSG should be returned. If it
|
|
+ is recognised, but the key cannot for some reason be set up, some other
|
|
+ negative error code should be returned.
|
|
+
|
|
+ If the key can be successfully set up, then key->payload should be set to
|
|
+ point to the retained data, key->type_data.p[0] should be set to point to
|
|
+ the subtype chosen and key->type_data.p[1] should be set to point to a
|
|
+ copy of the key's identity string and 0 should be returned.
|
|
+
|
|
+ The key's identity string may be partially matched upon. For a public-key
|
|
+ algorithm such as RSA and DSA this will likely be a printable hex version
|
|
+ of the key's fingerprint.
|
|
+
|
|
+Functions are provided to register and unregister parsers:
|
|
+
|
|
+ int register_crypto_key_parser(struct crypto_key_parser *parser);
|
|
+ void unregister_crypto_key_parser(struct crypto_key_parser *subtype);
|
|
+
|
|
+Parsers may not have the same name. The names are only used for displaying in
|
|
+debugging messages.
|
|
+
|
|
+
|
|
+============================
|
|
+IMPLEMENTING CRYPTO SUBTYPES
|
|
+============================
|
|
+
|
|
+The parser selects the appropriate subtype directly and sets it on the key; the
|
|
+crypto key then retains a reference on the subtype module (which means the
|
|
+parser can be removed thereafter).
|
|
+
|
|
+The subtype definition structure looks like the following:
|
|
+
|
|
+ struct crypto_key_subtype {
|
|
+ struct module *owner;
|
|
+ const char *name;
|
|
+
|
|
+ void (*describe)(const struct key *key, struct seq_file *m);
|
|
+ void (*destroy)(void *payload);
|
|
+ };
|
|
+
|
|
+The owner and name fields should be set to the owning module and the name of
|
|
+the subtype.
|
|
+
|
|
+There are a number of operations defined by the subtype:
|
|
+
|
|
+ (1) describe().
|
|
+
|
|
+ Mandatory. This allows the subtype to display something in /proc/keys
|
|
+ against the key. For instance the name of the public key algorithm type
|
|
+ could be displayed. The key type will display the tail of the key
|
|
+ identity string after this.
|
|
+
|
|
+ (2) destroy().
|
|
+
|
|
+ Mandatory. This should free the memory associated with the key. The
|
|
+ crypto key will look after freeing the fingerprint and releasing the
|
|
+ reference on the subtype module.
|
|
diff --git a/include/keys/crypto-subtype.h b/include/keys/crypto-subtype.h
|
|
new file mode 100644
|
|
index 0000000..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 <linux/seq_file.h>
|
|
+#include <keys/crypto-type.h>
|
|
+
|
|
+extern struct key_type key_type_crypto;
|
|
+
|
|
+/*
|
|
+ * Keys of this type declare a subtype that indicates the handlers and
|
|
+ * capabilities.
|
|
+ */
|
|
+struct crypto_key_subtype {
|
|
+ struct module *owner;
|
|
+ const char *name;
|
|
+ unsigned short name_len; /* length of name */
|
|
+
|
|
+ void (*describe)(const struct key *key, struct seq_file *m);
|
|
+
|
|
+ void (*destroy)(void *payload);
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Data parser. Called during instantiation and signature verification
|
|
+ * initiation.
|
|
+ */
|
|
+struct crypto_key_parser {
|
|
+ struct list_head link;
|
|
+ struct module *owner;
|
|
+ const char *name;
|
|
+
|
|
+ /* Attempt to 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 <linux/key-type.h>
|
|
+
|
|
+extern struct key_type key_type_crypto;
|
|
+
|
|
+/*
|
|
+ * The payload is at the discretion of the subtype.
|
|
+ */
|
|
+
|
|
+#endif /* _KEYS_CRYPTO_TYPE_H */
|
|
diff --git a/security/keys/Kconfig b/security/keys/Kconfig
|
|
index a90d6d3..992fe52 100644
|
|
--- 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 <keys/crypto-subtype.h>
|
|
+#include <linux/seq_file.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/slab.h>
|
|
+#include "crypto_keys.h"
|
|
+
|
|
+MODULE_LICENSE("GPL");
|
|
+
|
|
+LIST_HEAD(crypto_key_parsers);
|
|
+DECLARE_RWSEM(crypto_key_parsers_sem);
|
|
+
|
|
+/*
|
|
+ * Match crypto_keys on (part of) their name
|
|
+ * We have some shorthand methods for matching keys. We allow:
|
|
+ *
|
|
+ * "<desc>" - request a key by description
|
|
+ * "id:<id>" - request a key matching the ID
|
|
+ * "<subtype>:<id>" - request a key of a subtype
|
|
+ */
|
|
+static int crypto_key_match(const struct key *key, const void *description)
|
|
+{
|
|
+ const struct crypto_key_subtype *subtype = crypto_key_subtype(key);
|
|
+ const char *spec = description;
|
|
+ const char *id, *kid;
|
|
+ ptrdiff_t speclen;
|
|
+ size_t idlen, kidlen;
|
|
+
|
|
+ if (!subtype || !spec || !*spec)
|
|
+ return 0;
|
|
+
|
|
+ /* See if the full key description matches as is */
|
|
+ if (key->description && strcmp(key->description, description) == 0)
|
|
+ return 1;
|
|
+
|
|
+ /* All tests from here on break the criterion description into a
|
|
+ * specifier, a colon and then an identifier.
|
|
+ */
|
|
+ id = strchr(spec, ':');
|
|
+ if (!id)
|
|
+ return 0;
|
|
+
|
|
+ speclen = id - spec;
|
|
+ id++;
|
|
+
|
|
+ /* Anything after here requires a partial match on the ID string */
|
|
+ kid = crypto_key_id(key);
|
|
+ if (!kid)
|
|
+ return 0;
|
|
+
|
|
+ idlen = strlen(id);
|
|
+ kidlen = strlen(kid);
|
|
+ if (idlen > kidlen)
|
|
+ return 0;
|
|
+
|
|
+ kid += kidlen - idlen;
|
|
+ if (strcasecmp(id, kid) != 0)
|
|
+ return 0;
|
|
+
|
|
+ if (speclen == 2 &&
|
|
+ memcmp(spec, "id", 2) == 0)
|
|
+ return 1;
|
|
+
|
|
+ if (speclen == subtype->name_len &&
|
|
+ memcmp(spec, subtype->name, speclen) == 0)
|
|
+ return 1;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Describe the crypto key
|
|
+ */
|
|
+static void crypto_key_describe(const struct key *key, struct seq_file *m)
|
|
+{
|
|
+ const struct crypto_key_subtype *subtype = crypto_key_subtype(key);
|
|
+ const char *kid = crypto_key_id(key);
|
|
+ size_t n;
|
|
+
|
|
+ seq_puts(m, key->description);
|
|
+
|
|
+ if (subtype) {
|
|
+ seq_puts(m, ": ");
|
|
+ subtype->describe(key, m);
|
|
+
|
|
+ if (kid) {
|
|
+ seq_putc(m, ' ');
|
|
+ n = strlen(kid);
|
|
+ if (n <= 8)
|
|
+ seq_puts(m, kid);
|
|
+ else
|
|
+ seq_puts(m, kid + n - 8);
|
|
+ }
|
|
+
|
|
+ seq_puts(m, " [");
|
|
+ /* put something here to indicate the key's capabilities */
|
|
+ seq_putc(m, ']');
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * 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 <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
---
|
|
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 <keys/crypto-subtype.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/err.h>
|
|
+#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 <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
---
|
|
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 <linux/module.h>
|
|
+#include <linux/kernel.h>
|
|
+#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 <linux/mpi.h>
|
|
+#include <crypto/hash.h>
|
|
+#include <keys/crypto-subtype.h>
|
|
+
|
|
+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 <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
---
|
|
lib/mpi/Makefile | 1 +
|
|
lib/mpi/mpi-cmp.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
2 files changed, 71 insertions(+)
|
|
create mode 100644 lib/mpi/mpi-cmp.c
|
|
|
|
diff --git a/lib/mpi/Makefile b/lib/mpi/Makefile
|
|
index 45ca90a..019a68c 100644
|
|
--- 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 <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
---
|
|
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 <linux/module.h>
|
|
+#include <linux/kernel.h>
|
|
+#include "public_key.h"
|
|
+
|
|
+MODULE_LICENSE("GPL");
|
|
+
|
|
+#define kenter(FMT, ...) \
|
|
+ pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
|
|
+#define kleave(FMT, ...) \
|
|
+ pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
|
|
+
|
|
+/*
|
|
+ * Hash algorithm OIDs plus ASN.1 DER wrappings [RFC4880 sec 5.2.2].
|
|
+ */
|
|
+static const u8 RSA_digest_info_MD5[] = {
|
|
+ 0x30, 0x20, 0x30, 0x0C, 0x06, 0x08,
|
|
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, /* OID */
|
|
+ 0x05, 0x00, 0x04, 0x10
|
|
+};
|
|
+
|
|
+static const u8 RSA_digest_info_SHA1[] = {
|
|
+ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05,
|
|
+ 0x2B, 0x0E, 0x03, 0x02, 0x1A,
|
|
+ 0x05, 0x00, 0x04, 0x14
|
|
+};
|
|
+
|
|
+static const u8 RSA_digest_info_RIPE_MD_160[] = {
|
|
+ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05,
|
|
+ 0x2B, 0x24, 0x03, 0x02, 0x01,
|
|
+ 0x05, 0x00, 0x04, 0x14
|
|
+};
|
|
+
|
|
+static const u8 RSA_digest_info_SHA224[] = {
|
|
+ 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09,
|
|
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04,
|
|
+ 0x05, 0x00, 0x04, 0x1C
|
|
+};
|
|
+
|
|
+static const u8 RSA_digest_info_SHA256[] = {
|
|
+ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09,
|
|
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
|
|
+ 0x05, 0x00, 0x04, 0x20
|
|
+};
|
|
+
|
|
+static const u8 RSA_digest_info_SHA384[] = {
|
|
+ 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09,
|
|
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02,
|
|
+ 0x05, 0x00, 0x04, 0x30
|
|
+};
|
|
+
|
|
+static const u8 RSA_digest_info_SHA512[] = {
|
|
+ 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09,
|
|
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
|
|
+ 0x05, 0x00, 0x04, 0x40
|
|
+};
|
|
+
|
|
+static const struct {
|
|
+ const u8 *data;
|
|
+ size_t size;
|
|
+} RSA_ASN1_templates[PKEY_HASH__LAST] = {
|
|
+#define _(X) { RSA_digest_info_##X, sizeof(RSA_digest_info_##X) }
|
|
+ [PKEY_HASH_MD5] = _(MD5),
|
|
+ [PKEY_HASH_SHA1] = _(SHA1),
|
|
+ [PKEY_HASH_RIPE_MD_160] = _(RIPE_MD_160),
|
|
+ [PKEY_HASH_SHA256] = _(SHA256),
|
|
+ [PKEY_HASH_SHA384] = _(SHA384),
|
|
+ [PKEY_HASH_SHA512] = _(SHA512),
|
|
+ [PKEY_HASH_SHA224] = _(SHA224),
|
|
+#undef _
|
|
+};
|
|
+
|
|
+/*
|
|
+ * RSAVP1() function [RFC3447 sec 5.2.2]
|
|
+ */
|
|
+static int RSAVP1(const struct public_key *key, MPI s, MPI *_m)
|
|
+{
|
|
+ MPI m;
|
|
+ int ret;
|
|
+
|
|
+ /* (1) Validate 0 <= s < n */
|
|
+ if (mpi_cmp_ui(s, 0) < 0) {
|
|
+ kleave(" = -EBADMSG [s < 0]");
|
|
+ return -EBADMSG;
|
|
+ }
|
|
+ if (mpi_cmp(s, key->rsa.n) >= 0) {
|
|
+ kleave(" = -EBADMSG [s >= n]");
|
|
+ return -EBADMSG;
|
|
+ }
|
|
+
|
|
+ m = mpi_alloc(0);
|
|
+ if (!m)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* (2) m = s^e mod n */
|
|
+ ret = mpi_powm(m, s, key->rsa.e, key->rsa.n);
|
|
+ if (ret < 0) {
|
|
+ mpi_free(m);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ *_m = m;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Integer to Octet String conversion [RFC3447 sec 4.1]
|
|
+ */
|
|
+static int RSA_I2OSP(MPI x, size_t xLen, u8 **_X)
|
|
+{
|
|
+ unsigned X_size, x_size;
|
|
+ int X_sign;
|
|
+ u8 *X;
|
|
+
|
|
+ /* Make sure the string is the right length. The number should begin
|
|
+ * with { 0x00, 0x01, ... } so we have to account for 15 leading zero
|
|
+ * bits not being reported by MPI.
|
|
+ */
|
|
+ x_size = mpi_get_nbits(x);
|
|
+ pr_devel("size(x)=%u xLen*8=%zu\n", x_size, xLen * 8);
|
|
+ if (x_size != xLen * 8 - 15)
|
|
+ return -ERANGE;
|
|
+
|
|
+ X = mpi_get_buffer(x, &X_size, &X_sign);
|
|
+ if (!X)
|
|
+ return -ENOMEM;
|
|
+ if (X_sign < 0) {
|
|
+ kfree(X);
|
|
+ return -EBADMSG;
|
|
+ }
|
|
+ if (X_size != xLen - 1) {
|
|
+ kfree(X);
|
|
+ return -EBADMSG;
|
|
+ }
|
|
+
|
|
+ *_X = X;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Perform the RSA signature verification.
|
|
+ * @H: Value of hash of data and metadata
|
|
+ * @EM: The computed signature value
|
|
+ * @k: The size of EM (EM[0] is an invalid location but should hold 0x00)
|
|
+ * @hash_size: The size of H
|
|
+ * @asn1_template: The DigestInfo ASN.1 template
|
|
+ * @asn1_size: Size of asm1_template[]
|
|
+ */
|
|
+static int RSA_verify(const u8 *H, const u8 *EM, size_t k, size_t hash_size,
|
|
+ const u8 *asn1_template, size_t asn1_size)
|
|
+{
|
|
+ unsigned PS_end, T_offset, i;
|
|
+
|
|
+ kenter(",,%zu,%zu,%zu", k, hash_size, asn1_size);
|
|
+
|
|
+ if (k < 2 + 1 + asn1_size + hash_size)
|
|
+ return -EBADMSG;
|
|
+
|
|
+ /* Decode the EMSA-PKCS1-v1_5 */
|
|
+ if (EM[1] != 0x01) {
|
|
+ kleave(" = -EBADMSG [EM[1] == %02u]", EM[1]);
|
|
+ return -EBADMSG;
|
|
+ }
|
|
+
|
|
+ T_offset = k - (asn1_size + hash_size);
|
|
+ PS_end = T_offset - 1;
|
|
+ if (EM[PS_end] != 0x00) {
|
|
+ kleave(" = -EBADMSG [EM[T-1] == %02u]", EM[PS_end]);
|
|
+ return -EBADMSG;
|
|
+ }
|
|
+
|
|
+ for (i = 2; i < PS_end; i++) {
|
|
+ if (EM[i] != 0xff) {
|
|
+ kleave(" = -EBADMSG [EM[PS%x] == %02u]", i - 2, EM[i]);
|
|
+ return -EBADMSG;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (memcmp(asn1_template, EM + T_offset, asn1_size) != 0) {
|
|
+ kleave(" = -EBADMSG [EM[T] ASN.1 mismatch]");
|
|
+ return -EBADMSG;
|
|
+ }
|
|
+
|
|
+ if (memcmp(H, EM + T_offset + asn1_size, hash_size) != 0) {
|
|
+ kleave(" = -EKEYREJECTED [EM[T] hash mismatch]");
|
|
+ return -EKEYREJECTED;
|
|
+ }
|
|
+
|
|
+ kleave(" = 0");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Perform the verification step [RFC3447 sec 8.2.2].
|
|
+ */
|
|
+static int RSA_verify_signature(const struct public_key *key,
|
|
+ const struct public_key_signature *sig)
|
|
+{
|
|
+ size_t tsize;
|
|
+ int ret;
|
|
+
|
|
+ /* Variables as per RFC3447 sec 8.2.2 */
|
|
+ const u8 *H = sig->digest;
|
|
+ u8 *EM = NULL;
|
|
+ MPI m = NULL;
|
|
+ size_t k;
|
|
+
|
|
+ kenter("");
|
|
+
|
|
+ /* (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 <dhowells@redhat.com>
|
|
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 <mbroz@redhat.com>
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
security/keys/crypto/crypto_rsa.c | 14 +++++++++++---
|
|
1 file changed, 11 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/security/keys/crypto/crypto_rsa.c b/security/keys/crypto/crypto_rsa.c
|
|
index 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 <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
---
|
|
include/linux/pgp.h | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
1 file changed, 206 insertions(+)
|
|
create mode 100644 include/linux/pgp.h
|
|
|
|
diff --git a/include/linux/pgp.h b/include/linux/pgp.h
|
|
new file mode 100644
|
|
index 0000000..1359f64
|
|
--- /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 <linux/types.h>
|
|
+
|
|
+struct pgp_key_ID {
|
|
+ u8 id[8];
|
|
+};
|
|
+
|
|
+struct pgp_time {
|
|
+ u8 time[4];
|
|
+};
|
|
+
|
|
+/*
|
|
+ * PGP public-key algorithm identifiers [RFC4880: 9.1]
|
|
+ */
|
|
+enum pgp_pubkey_algo {
|
|
+ PGP_PUBKEY_RSA_ENC_OR_SIG = 1,
|
|
+ PGP_PUBKEY_RSA_ENC_ONLY = 2,
|
|
+ PGP_PUBKEY_RSA_SIG_ONLY = 3,
|
|
+ PGP_PUBKEY_ELGAMAL = 16,
|
|
+ PGP_PUBKEY_DSA = 17,
|
|
+ PGP_PUBKEY__LAST
|
|
+};
|
|
+
|
|
+/*
|
|
+ * PGP symmetric-key algorithm identifiers [RFC4880: 9.2]
|
|
+ */
|
|
+enum pgp_symkey_algo {
|
|
+ PGP_SYMKEY_PLAINTEXT = 0,
|
|
+ PGP_SYMKEY_IDEA = 1,
|
|
+ PGP_SYMKEY_3DES = 2,
|
|
+ PGP_SYMKEY_CAST5 = 3,
|
|
+ PGP_SYMKEY_BLOWFISH = 4,
|
|
+ PGP_SYMKEY_AES_128KEY = 7,
|
|
+ PGP_SYMKEY_AES_192KEY = 8,
|
|
+ PGP_SYMKEY_AES_256KEY = 9,
|
|
+ PGP_SYMKEY_TWOFISH_256KEY = 10,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * PGP compression algorithm identifiers [RFC4880: 9.3]
|
|
+ */
|
|
+enum pgp_compr_algo {
|
|
+ PGP_COMPR_UNCOMPRESSED = 0,
|
|
+ PGP_COMPR_ZIP = 1,
|
|
+ PGP_COMPR_ZLIB = 2,
|
|
+ PGP_COMPR_BZIP2 = 3,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * PGP hash algorithm identifiers [RFC4880: 9.4]
|
|
+ */
|
|
+enum pgp_hash_algo {
|
|
+ PGP_HASH_MD5 = 1,
|
|
+ PGP_HASH_SHA1 = 2,
|
|
+ PGP_HASH_RIPE_MD_160 = 3,
|
|
+ PGP_HASH_SHA256 = 8,
|
|
+ PGP_HASH_SHA384 = 9,
|
|
+ PGP_HASH_SHA512 = 10,
|
|
+ PGP_HASH_SHA224 = 11,
|
|
+ PGP_HASH__LAST
|
|
+};
|
|
+
|
|
+extern const char *const pgp_hash_algorithms[PGP_HASH__LAST];
|
|
+
|
|
+/*
|
|
+ * PGP packet type tags [RFC4880: 4.3].
|
|
+ */
|
|
+enum pgp_packet_tag {
|
|
+ PGP_PKT_RESERVED = 0,
|
|
+ PGP_PKT_PUBKEY_ENC_SESSION_KEY = 1,
|
|
+ PGP_PKT_SIGNATURE = 2,
|
|
+ PGP_PKT_SYMKEY_ENC_SESSION_KEY = 3,
|
|
+ PGP_PKT_ONEPASS_SIGNATURE = 4,
|
|
+ PGP_PKT_SECRET_KEY = 5,
|
|
+ PGP_PKT_PUBLIC_KEY = 6,
|
|
+ PGP_PKT_SECRET_SUBKEY = 7,
|
|
+ PGP_PKT_COMPRESSED_DATA = 8,
|
|
+ PGP_PKT_SYM_ENC_DATA = 9,
|
|
+ PGP_PKT_MARKER = 10,
|
|
+ PGP_PKT_LITERAL_DATA = 11,
|
|
+ PGP_PKT_TRUST = 12,
|
|
+ PGP_PKT_USER_ID = 13,
|
|
+ PGP_PKT_PUBLIC_SUBKEY = 14,
|
|
+ PGP_PKT_USER_ATTRIBUTE = 17,
|
|
+ PGP_PKT_SYM_ENC_AND_INTEG_DATA = 18,
|
|
+ PGP_PKT_MODIFY_DETECT_CODE = 19,
|
|
+ PGP_PKT_PRIVATE_0 = 60,
|
|
+ PGP_PKT_PRIVATE_3 = 63,
|
|
+ PGP_PKT__HIGHEST = 63
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Signature (tag 2) packet [RFC4880: 5.2].
|
|
+ */
|
|
+enum pgp_signature_version {
|
|
+ PGP_SIG_VERSION_3 = 3,
|
|
+ PGP_SIG_VERSION_4 = 4,
|
|
+};
|
|
+
|
|
+enum pgp_signature_type {
|
|
+ PGP_SIG_BINARY_DOCUMENT_SIG = 0x00,
|
|
+ PGP_SIG_CANONICAL_TEXT_DOCUMENT_SIG = 0x01,
|
|
+ PGP_SIG_STANDALONE_SIG = 0x02,
|
|
+ PGP_SIG_GENERAL_CERT_OF_UID_PUBKEY = 0x10,
|
|
+ PGP_SIG_PERSONAL_CERT_OF_UID_PUBKEY = 0x11,
|
|
+ PGP_SIG_CASUAL_CERT_OF_UID_PUBKEY = 0x12,
|
|
+ PGP_SIG_POSTITIVE_CERT_OF_UID_PUBKEY = 0x13,
|
|
+ PGP_SIG_SUBKEY_BINDING_SIG = 0x18,
|
|
+ PGP_SIG_PRIMARY_KEY_BINDING_SIG = 0x19,
|
|
+ PGP_SIG_DIRECTLY_ON_KEY = 0x1F,
|
|
+ PGP_SIG_KEY_REVOCATION_SIG = 0x20,
|
|
+ PGP_SIG_SUBKEY_REVOCATION_SIG = 0x28,
|
|
+ PGP_SIG_CERT_REVOCATION_SIG = 0x30,
|
|
+ PGP_SIG_TIMESTAMP_SIG = 0x40,
|
|
+ PGP_SIG_THIRD_PARTY_CONFIRM_SIG = 0x50,
|
|
+};
|
|
+
|
|
+struct pgp_signature_v3_packet {
|
|
+ enum pgp_signature_version version : 8; /* == PGP_SIG_VERSION_3 */
|
|
+ u8 length_of_hashed; /* == 5 */
|
|
+ struct {
|
|
+ enum pgp_signature_type signature_type : 8;
|
|
+ struct pgp_time creation_time;
|
|
+ } hashed;
|
|
+ struct pgp_key_ID issuer;
|
|
+ enum pgp_pubkey_algo pubkey_algo : 8;
|
|
+ enum pgp_hash_algo hash_algo : 8;
|
|
+} __packed;
|
|
+
|
|
+struct pgp_signature_v4_packet {
|
|
+ enum pgp_signature_version version : 8; /* == PGP_SIG_VERSION_4 */
|
|
+ enum pgp_signature_type signature_type : 8;
|
|
+ enum pgp_pubkey_algo pubkey_algo : 8;
|
|
+ enum pgp_hash_algo hash_algo : 8;
|
|
+} __packed;
|
|
+
|
|
+/*
|
|
+ * V4 signature subpacket types [RFC4880: 5.2.3.1].
|
|
+ */
|
|
+enum pgp_sig_subpkt_type {
|
|
+ PGP_SIG_CREATION_TIME = 2,
|
|
+ PGP_SIG_EXPIRATION_TIME = 3,
|
|
+ PGP_SIG_EXPORTABLE_CERT = 4,
|
|
+ PGP_SIG_TRUST_SIG = 5,
|
|
+ PGP_SIG_REGEXP = 6,
|
|
+ PGP_SIG_REVOCABLE = 7,
|
|
+ PGP_SIG_KEY_EXPIRATION_TIME = 9,
|
|
+ PGP_SIG_PREF_SYM_ALGO = 11,
|
|
+ PGP_SIG_REVOCATION_KEY = 12,
|
|
+ PGP_SIG_ISSUER = 16,
|
|
+ PGP_SIG_NOTATION_DATA = 20,
|
|
+ PGP_SIG_PREF_HASH_ALGO = 21,
|
|
+ PGP_SIG_PREF_COMPR_ALGO = 22,
|
|
+ PGP_SIG_KEY_SERVER_PREFS = 23,
|
|
+ PGP_SIG_PREF_KEY_SERVER = 24,
|
|
+ PGP_SIG_PRIMARY_USER_ID = 25,
|
|
+ PGP_SIG_POLICY_URI = 26,
|
|
+ PGP_SIG_KEY_FLAGS = 27,
|
|
+ PGP_SIG_SIGNERS_USER_ID = 28,
|
|
+ PGP_SIG_REASON_FOR_REVOCATION = 29,
|
|
+ PGP_SIG_FEATURES = 30,
|
|
+ PGP_SIG_TARGET = 31,
|
|
+ PGP_SIG_EMBEDDED_SIG = 32,
|
|
+ PGP_SIG__LAST
|
|
+};
|
|
+
|
|
+#define PGP_SIG_SUBPKT_TYPE_CRITICAL_MASK 0x80
|
|
+
|
|
+/*
|
|
+ * Key (tag 5, 6, 7 and 14) packet
|
|
+ */
|
|
+enum pgp_key_version {
|
|
+ PGP_KEY_VERSION_2 = 2,
|
|
+ PGP_KEY_VERSION_3 = 3,
|
|
+ PGP_KEY_VERSION_4 = 4,
|
|
+};
|
|
+
|
|
+struct pgp_key_v3_packet {
|
|
+ enum pgp_key_version version : 8;
|
|
+ struct pgp_time creation_time;
|
|
+ u8 expiry[2]; /* 0 or time in days till expiry */
|
|
+ enum pgp_pubkey_algo pubkey_algo : 8;
|
|
+ u8 key_material[0];
|
|
+} __packed;
|
|
+
|
|
+struct pgp_key_v4_packet {
|
|
+ enum pgp_key_version version : 8;
|
|
+ struct pgp_time creation_time;
|
|
+ enum pgp_pubkey_algo pubkey_algo : 8;
|
|
+ u8 key_material[0];
|
|
+} __packed;
|
|
+
|
|
+#endif /* _LINUX_PGP_H */
|
|
--
|
|
1.7.11.2
|
|
|
|
|
|
From 0b8ec95fe7220288c143a820b8d8996c356129f1 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
---
|
|
include/linux/pgplib.h | 47 +++++++
|
|
security/keys/crypto/Kconfig | 6 +
|
|
security/keys/crypto/Makefile | 1 +
|
|
security/keys/crypto/pgp_library.c | 268 +++++++++++++++++++++++++++++++++++++
|
|
4 files changed, 322 insertions(+)
|
|
create mode 100644 include/linux/pgplib.h
|
|
create mode 100644 security/keys/crypto/pgp_library.c
|
|
|
|
diff --git a/include/linux/pgplib.h b/include/linux/pgplib.h
|
|
new file mode 100644
|
|
index 0000000..a045b3a
|
|
--- /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 <linux/pgp.h>
|
|
+
|
|
+/*
|
|
+ * PGP library packet parser
|
|
+ */
|
|
+struct pgp_parse_context {
|
|
+ u64 types_of_interest;
|
|
+ int (*process_packet)(struct pgp_parse_context *context,
|
|
+ enum pgp_packet_tag type,
|
|
+ u8 headerlen,
|
|
+ const u8 *data,
|
|
+ size_t datalen);
|
|
+};
|
|
+
|
|
+extern int pgp_parse_packets(const u8 *data, size_t datalen,
|
|
+ struct pgp_parse_context *ctx);
|
|
+
|
|
+struct pgp_parse_pubkey {
|
|
+ enum pgp_key_version version : 8;
|
|
+ enum pgp_pubkey_algo pubkey_algo : 8;
|
|
+ time_t creation_time;
|
|
+ time_t expires_at;
|
|
+};
|
|
+
|
|
+extern int pgp_parse_public_key(const u8 **_data, size_t *_datalen,
|
|
+ struct pgp_parse_pubkey *pk);
|
|
+
|
|
+
|
|
+#endif /* CONFIG_PGP_LIBRARY */
|
|
+
|
|
+#endif /* _LINUX_PGPLIB_H */
|
|
diff --git a/security/keys/crypto/Kconfig b/security/keys/crypto/Kconfig
|
|
index 4e3777e..88ce0e2 100644
|
|
--- 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 <linux/pgplib.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+
|
|
+MODULE_LICENSE("GPL");
|
|
+
|
|
+const char *const pgp_hash_algorithms[PGP_HASH__LAST] = {
|
|
+ [PGP_HASH_MD5] = "md5",
|
|
+ [PGP_HASH_SHA1] = "sha1",
|
|
+ [PGP_HASH_RIPE_MD_160] = "rmd160",
|
|
+ [PGP_HASH_SHA256] = "sha256",
|
|
+ [PGP_HASH_SHA384] = "sha384",
|
|
+ [PGP_HASH_SHA512] = "sha512",
|
|
+ [PGP_HASH_SHA224] = "sha224",
|
|
+};
|
|
+EXPORT_SYMBOL_GPL(pgp_hash_algorithms);
|
|
+
|
|
+/**
|
|
+ * pgp_parse_packet_header - Parse a PGP packet header
|
|
+ * @_data: Start of the PGP packet (updated to PGP packet data)
|
|
+ * @_datalen: Amount of data remaining in buffer (decreased)
|
|
+ * @_type: Where the packet type will be returned
|
|
+ * @_headerlen: Where the header length will be returned
|
|
+ *
|
|
+ * Parse a set of PGP packet header [RFC 4880: 4.2].
|
|
+ *
|
|
+ * Returns packet data size on success; non-zero on error. If successful,
|
|
+ * *_data and *_datalen will have been updated and *_headerlen will be set to
|
|
+ * hold the length of the packet header.
|
|
+ */
|
|
+static ssize_t pgp_parse_packet_header(const u8 **_data, size_t *_datalen,
|
|
+ enum pgp_packet_tag *_type,
|
|
+ u8 *_headerlen)
|
|
+{
|
|
+ enum pgp_packet_tag type;
|
|
+ const u8 *data = *_data;
|
|
+ size_t size, datalen = *_datalen;
|
|
+
|
|
+ pr_devel("-->pgp_parse_packet_header(,%zu,,)", 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 <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
---
|
|
include/linux/pgplib.h | 25 ++++
|
|
security/keys/crypto/pgp_library.c | 280 +++++++++++++++++++++++++++++++++++++
|
|
2 files changed, 305 insertions(+)
|
|
|
|
diff --git a/include/linux/pgplib.h b/include/linux/pgplib.h
|
|
index a045b3a..34594a9 100644
|
|
--- 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 <dhowells@redhat.com>
|
|
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 <penguin-kernel@I-love.SAKURA.ne.jp> for pointing out
|
|
some errors.
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
security/keys/crypto/Kconfig | 12 ++
|
|
security/keys/crypto/Makefile | 4 +
|
|
security/keys/crypto/pgp_parser.h | 23 +++
|
|
security/keys/crypto/pgp_public_key.c | 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 <linux/pgp.h>
|
|
+
|
|
+#define kenter(FMT, ...) \
|
|
+ pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
|
|
+#define kleave(FMT, ...) \
|
|
+ pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
|
|
+
|
|
+/*
|
|
+ * pgp_key_parser.c
|
|
+ */
|
|
+extern const
|
|
+struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST];
|
|
diff --git a/security/keys/crypto/pgp_public_key.c b/security/keys/crypto/pgp_public_key.c
|
|
new file mode 100644
|
|
index 0000000..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 <keys/crypto-subtype.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/mpi.h>
|
|
+#include <linux/pgplib.h>
|
|
+#include <crypto/hash.h>
|
|
+#include "public_key.h"
|
|
+#include "pgp_parser.h"
|
|
+
|
|
+MODULE_LICENSE("GPL");
|
|
+
|
|
+const
|
|
+struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST] = {
|
|
+#if defined(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) || \
|
|
+ defined(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA_MODULE)
|
|
+ [PGP_PUBKEY_RSA_ENC_OR_SIG] = &RSA_public_key_algorithm,
|
|
+ [PGP_PUBKEY_RSA_ENC_ONLY] = &RSA_public_key_algorithm,
|
|
+ [PGP_PUBKEY_RSA_SIG_ONLY] = &RSA_public_key_algorithm,
|
|
+#endif
|
|
+ [PGP_PUBKEY_ELGAMAL] = NULL,
|
|
+ [PGP_PUBKEY_DSA] = NULL,
|
|
+};
|
|
+
|
|
+static const u8 pgp_public_key_capabilities[PGP_PUBKEY__LAST] = {
|
|
+ [PGP_PUBKEY_RSA_ENC_OR_SIG] = PKEY_CAN_ENCDEC | PKEY_CAN_SIGVER,
|
|
+ [PGP_PUBKEY_RSA_ENC_ONLY] = PKEY_CAN_ENCDEC,
|
|
+ [PGP_PUBKEY_RSA_SIG_ONLY] = PKEY_CAN_SIGVER,
|
|
+ [PGP_PUBKEY_ELGAMAL] = 0,
|
|
+ [PGP_PUBKEY_DSA] = 0,
|
|
+};
|
|
+
|
|
+static inline void digest_putc(struct shash_desc *digest, uint8_t ch)
|
|
+{
|
|
+ crypto_shash_update(digest, &ch, 1);
|
|
+}
|
|
+
|
|
+struct pgp_key_data_parse_context {
|
|
+ struct pgp_parse_context pgp;
|
|
+ struct crypto_key_subtype *subtype;
|
|
+ char *fingerprint;
|
|
+ void *payload;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Calculate the public key ID (RFC4880 12.2)
|
|
+ */
|
|
+static int pgp_calc_pkey_keyid(struct shash_desc *digest,
|
|
+ struct pgp_parse_pubkey *pgp,
|
|
+ struct public_key *key)
|
|
+{
|
|
+ unsigned nb[ARRAY_SIZE(key->mpi)];
|
|
+ unsigned nn[ARRAY_SIZE(key->mpi)];
|
|
+ unsigned n;
|
|
+ u8 *pp[ARRAY_SIZE(key->mpi)];
|
|
+ u32 a32;
|
|
+ int npkey = key->algo->n_pub_mpi;
|
|
+ int i, ret = -ENOMEM;
|
|
+
|
|
+ kenter("");
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(pp); i++)
|
|
+ pp[i] = NULL;
|
|
+
|
|
+ n = (pgp->version < PGP_KEY_VERSION_4) ? 8 : 6;
|
|
+ for (i = 0; i < npkey; i++) {
|
|
+ nb[i] = mpi_get_nbits(key->mpi[i]);
|
|
+ pp[i] = mpi_get_buffer(key->mpi[i], nn + i, NULL);
|
|
+ if (!pp[i])
|
|
+ goto error;
|
|
+ n += 2 + nn[i];
|
|
+ }
|
|
+
|
|
+ digest_putc(digest, 0x99); /* ctb */
|
|
+ digest_putc(digest, n >> 8); /* 16-bit header length */
|
|
+ digest_putc(digest, n);
|
|
+ digest_putc(digest, pgp->version);
|
|
+
|
|
+ a32 = pgp->creation_time;
|
|
+ digest_putc(digest, a32 >> 24);
|
|
+ digest_putc(digest, a32 >> 16);
|
|
+ digest_putc(digest, a32 >> 8);
|
|
+ digest_putc(digest, a32 >> 0);
|
|
+
|
|
+ if (pgp->version < PGP_KEY_VERSION_4) {
|
|
+ u16 a16;
|
|
+
|
|
+ if (pgp->expires_at)
|
|
+ a16 = (pgp->expires_at - pgp->creation_time) / 86400UL;
|
|
+ else
|
|
+ a16 = 0;
|
|
+ digest_putc(digest, a16 >> 8);
|
|
+ digest_putc(digest, a16 >> 0);
|
|
+ }
|
|
+
|
|
+ digest_putc(digest, pgp->pubkey_algo);
|
|
+
|
|
+ for (i = 0; i < npkey; i++) {
|
|
+ digest_putc(digest, nb[i] >> 8);
|
|
+ digest_putc(digest, nb[i]);
|
|
+ crypto_shash_update(digest, pp[i], nn[i]);
|
|
+ }
|
|
+ ret = 0;
|
|
+
|
|
+error:
|
|
+ for (i = 0; i < npkey; i++)
|
|
+ kfree(pp[i]);
|
|
+ kleave(" = %d", ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Calculate the public key ID fingerprint
|
|
+ */
|
|
+static int pgp_generate_fingerprint(struct pgp_key_data_parse_context *ctx,
|
|
+ struct pgp_parse_pubkey *pgp,
|
|
+ struct public_key *key)
|
|
+{
|
|
+ struct crypto_shash *tfm;
|
|
+ struct shash_desc *digest;
|
|
+ char *fingerprint;
|
|
+ u8 *raw_fingerprint;
|
|
+ int digest_size, offset;
|
|
+ int ret, i;
|
|
+
|
|
+ ret = -ENOMEM;
|
|
+ tfm = crypto_alloc_shash(pgp->version < PGP_KEY_VERSION_4 ?
|
|
+ "md5" : "sha1", 0, 0);
|
|
+ if (!tfm)
|
|
+ goto cleanup;
|
|
+
|
|
+ digest = kmalloc(sizeof(*digest) + crypto_shash_descsize(tfm),
|
|
+ GFP_KERNEL);
|
|
+ if (!digest)
|
|
+ goto cleanup_tfm;
|
|
+
|
|
+ digest->tfm = tfm;
|
|
+ digest->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
|
+ ret = crypto_shash_init(digest);
|
|
+ if (ret < 0)
|
|
+ goto cleanup_hash;
|
|
+
|
|
+ ret = pgp_calc_pkey_keyid(digest, pgp, key);
|
|
+ if (ret < 0)
|
|
+ goto cleanup_hash;
|
|
+
|
|
+ digest_size = crypto_shash_digestsize(tfm);
|
|
+
|
|
+ raw_fingerprint = kmalloc(digest_size, GFP_KERNEL);
|
|
+ if (!raw_fingerprint)
|
|
+ goto cleanup_hash;
|
|
+
|
|
+ ret = crypto_shash_final(digest, raw_fingerprint);
|
|
+ if (ret < 0)
|
|
+ goto cleanup_raw_fingerprint;
|
|
+
|
|
+ fingerprint = kmalloc(digest_size * 2 + 1, GFP_KERNEL);
|
|
+ if (!fingerprint)
|
|
+ goto cleanup_raw_fingerprint;
|
|
+
|
|
+ offset = digest_size - 8;
|
|
+ pr_debug("offset %u/%u\n", offset, digest_size);
|
|
+
|
|
+ for (i = 0; i < digest_size; i++)
|
|
+ sprintf(fingerprint + i * 2, "%02x", raw_fingerprint[i]);
|
|
+ pr_debug("fingerprint %s\n", fingerprint);
|
|
+
|
|
+ memcpy(&key->key_id, raw_fingerprint + offset, 8);
|
|
+ key->key_id_size = 8;
|
|
+
|
|
+ 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 <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
---
|
|
security/keys/crypto/Makefile | 3 +-
|
|
security/keys/crypto/pgp_parser.h | 6 +
|
|
security/keys/crypto/pgp_sig_verify.c | 325 ++++++++++++++++++++++++++++++++++
|
|
3 files changed, 333 insertions(+), 1 deletion(-)
|
|
create mode 100644 security/keys/crypto/pgp_sig_verify.c
|
|
|
|
diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile
|
|
index 35733fc..0c8b8a1 100644
|
|
--- 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 <linux/module.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/pgplib.h>
|
|
+#include <linux/err.h>
|
|
+#include "public_key.h"
|
|
+#include "pgp_parser.h"
|
|
+
|
|
+static const struct {
|
|
+ enum pkey_hash_algo algo : 8;
|
|
+} pgp_pubkey_hash[PGP_HASH__LAST] = {
|
|
+ [PGP_HASH_MD5].algo = PKEY_HASH_MD5,
|
|
+ [PGP_HASH_SHA1].algo = PKEY_HASH_SHA1,
|
|
+ [PGP_HASH_RIPE_MD_160].algo = PKEY_HASH_RIPE_MD_160,
|
|
+ [PGP_HASH_SHA256].algo = PKEY_HASH_SHA256,
|
|
+ [PGP_HASH_SHA384].algo = PKEY_HASH_SHA384,
|
|
+ [PGP_HASH_SHA512].algo = PKEY_HASH_SHA512,
|
|
+ [PGP_HASH_SHA224].algo = PKEY_HASH_SHA224,
|
|
+};
|
|
+
|
|
+static int pgp_pkey_verify_sig_add_data(struct crypto_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 <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
---
|
|
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 <linux/module.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/pgplib.h>
|
|
+#include <linux/err.h>
|
|
+#include "public_key.h"
|
|
+#include "pgp_parser.h"
|
|
+
|
|
+struct PGP_sig_parse_context {
|
|
+ struct pgp_parse_context pgp;
|
|
+ struct pgp_sig_parameters params;
|
|
+ bool found_sig;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Look inside signature sections for a key ID
|
|
+ */
|
|
+static int pgp_process_signature(struct pgp_parse_context *context,
|
|
+ enum pgp_packet_tag type,
|
|
+ u8 headerlen,
|
|
+ const u8 *data,
|
|
+ size_t datalen)
|
|
+{
|
|
+ struct PGP_sig_parse_context *ctx =
|
|
+ container_of(context, struct PGP_sig_parse_context, pgp);
|
|
+
|
|
+ ctx->found_sig = true;
|
|
+ return pgp_parse_sig_params(&data, &datalen, &ctx->params);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Attempt to find a key to use for PGP signature verification, starting off by
|
|
+ * looking in the supplied keyring.
|
|
+ *
|
|
+ * The function may also look for other key sources such as a TPM. If an
|
|
+ * alternative key is found it can be added to the keyring for future
|
|
+ * reference.
|
|
+ */
|
|
+static struct key *find_key_for_pgp_sig(struct key *keyring,
|
|
+ const u8 *sig, size_t siglen)
|
|
+{
|
|
+ struct PGP_sig_parse_context p;
|
|
+ key_ref_t key;
|
|
+ char criterion[3 + 8 * 2 + 1];
|
|
+ int ret;
|
|
+
|
|
+ if (!keyring)
|
|
+ return ERR_PTR(-ENOKEY);
|
|
+
|
|
+ /* Need to find the key ID */
|
|
+ p.pgp.types_of_interest = (1 << PGP_PKT_SIGNATURE);
|
|
+ p.pgp.process_packet = pgp_process_signature;
|
|
+ p.found_sig = false;
|
|
+ ret = pgp_parse_packets(sig, siglen, &p.pgp);
|
|
+ if (ret < 0)
|
|
+ return ERR_PTR(ret);
|
|
+
|
|
+ if (!p.found_sig)
|
|
+ return ERR_PTR(-ENOMSG);
|
|
+
|
|
+ sprintf(criterion, "id:%08x%08x",
|
|
+ be32_to_cpu(p.params.issuer32[0]),
|
|
+ be32_to_cpu(p.params.issuer32[1]));
|
|
+
|
|
+ pr_debug("Look up: %s\n", criterion);
|
|
+
|
|
+ key = keyring_search(make_key_ref(keyring, 1),
|
|
+ &key_type_crypto, criterion);
|
|
+ if (IS_ERR(key)) {
|
|
+ switch (PTR_ERR(key)) {
|
|
+ /* Hide some search errors */
|
|
+ case -EACCES:
|
|
+ case -ENOTDIR:
|
|
+ case -EAGAIN:
|
|
+ return ERR_PTR(-ENOKEY);
|
|
+ default:
|
|
+ return ERR_CAST(key);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pr_debug("Found key %x\n", key_serial(key_ref_to_ptr(key)));
|
|
+ return key_ref_to_ptr(key);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Attempt to parse a signature as a PGP packet format blob and find a
|
|
+ * matching key.
|
|
+ */
|
|
+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 <dhowells@redhat.com>
|
|
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 <penguin-kernel@I-love.SAKURA.ne.jp> for some pointing
|
|
out some errors.
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
Documentation/security/keys-crypto.txt | 20 +++++++
|
|
include/keys/crypto-type.h | 3 ++
|
|
security/keys/crypto/Kconfig | 9 ++++
|
|
security/keys/crypto/Makefile | 1 +
|
|
security/keys/crypto/pgp_preload.c | 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 <linux/module.h>
|
|
+#include <linux/key.h>
|
|
+#include <linux/pgplib.h>
|
|
+#include <linux/err.h>
|
|
+#include <keys/crypto-type.h>
|
|
+#include "crypto_keys.h"
|
|
+
|
|
+struct preload_pgp_keys_context {
|
|
+ struct pgp_parse_context pgp;
|
|
+ key_ref_t keyring;
|
|
+ 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 <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
---
|
|
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 <asm-generic/module.h>
|
|
+
|
|
struct mod_arch_specific
|
|
{
|
|
unsigned int gotsecindex;
|
|
};
|
|
|
|
-#define Elf_Sym Elf64_Sym
|
|
-#define Elf_Shdr Elf64_Shdr
|
|
-#define Elf_Ehdr Elf64_Ehdr
|
|
-#define Elf_Phdr Elf64_Phdr
|
|
-#define Elf_Dyn Elf64_Dyn
|
|
-#define Elf_Rel Elf64_Rel
|
|
-#define Elf_Rela Elf64_Rela
|
|
-
|
|
#define ARCH_SHF_SMALL SHF_ALPHA_GPREL
|
|
|
|
#ifdef MODULE
|
|
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
|
|
index 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 <asm-generic/module.h>
|
|
|
|
struct unwind_table;
|
|
|
|
@@ -16,13 +14,11 @@ enum {
|
|
ARM_SEC_DEVEXIT,
|
|
ARM_SEC_MAX,
|
|
};
|
|
-#endif
|
|
|
|
struct mod_arch_specific {
|
|
-#ifdef CONFIG_ARM_UNWIND
|
|
struct unwind_table *unwind[ARM_SEC_MAX];
|
|
-#endif
|
|
};
|
|
+#endif
|
|
|
|
/*
|
|
* Add the ARM architecture version to the version magic string
|
|
diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig
|
|
index 5ade51c..06e73bf 100644
|
|
--- 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 <asm-generic/module.h>
|
|
+
|
|
struct mod_arch_syminfo {
|
|
unsigned long got_offset;
|
|
int got_initialized;
|
|
@@ -17,10 +19,6 @@ struct mod_arch_specific {
|
|
struct mod_arch_syminfo *syminfo;
|
|
};
|
|
|
|
-#define Elf_Shdr Elf32_Shdr
|
|
-#define Elf_Sym Elf32_Sym
|
|
-#define Elf_Ehdr Elf32_Ehdr
|
|
-
|
|
#define MODULE_PROC_FAMILY "AVR32v1"
|
|
|
|
#define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY
|
|
diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig
|
|
index f348619..a48d8be 100644
|
|
--- 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 <asm-generic/module.h>
|
|
|
|
struct mod_arch_specific {
|
|
Elf_Shdr *text_l1;
|
|
diff --git a/arch/c6x/Kconfig b/arch/c6x/Kconfig
|
|
index 052f81a..8f3a304 100644
|
|
--- 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 <asm-generic/module.h>
|
|
|
|
struct loaded_sections {
|
|
unsigned int new_vaddr;
|
|
diff --git a/arch/cris/Kconfig b/arch/cris/Kconfig
|
|
index e922154..7bb8cf9 100644
|
|
--- 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 <asm-generic/module.h>
|
|
|
|
/*
|
|
* 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 <asm-generic/module.h>
|
|
+
|
|
/*
|
|
* IA-64-specific support for kernel module loader.
|
|
*
|
|
@@ -29,10 +31,6 @@ struct mod_arch_specific {
|
|
unsigned int next_got_entry; /* index of next available got entry */
|
|
};
|
|
|
|
-#define Elf_Shdr Elf64_Shdr
|
|
-#define Elf_Sym Elf64_Sym
|
|
-#define Elf_Ehdr Elf64_Ehdr
|
|
-
|
|
#define MODULE_PROC_FAMILY "ia64"
|
|
#define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY \
|
|
"gcc-" __stringify(__GNUC__) "." __stringify(__GNUC_MINOR__)
|
|
diff --git a/arch/m32r/Kconfig b/arch/m32r/Kconfig
|
|
index 49498bb..fc61533 100644
|
|
--- 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 <asm-generic/module.h>
|
|
+
|
|
enum m68k_fixup_type {
|
|
m68k_fixup_memoffset,
|
|
m68k_fixup_vnode_shift,
|
|
@@ -36,8 +38,4 @@ struct module;
|
|
extern void module_fixup(struct module *mod, struct m68k_fixup_info *start,
|
|
struct m68k_fixup_info *end);
|
|
|
|
-#define Elf_Shdr Elf32_Shdr
|
|
-#define Elf_Sym Elf32_Sym
|
|
-#define Elf_Ehdr Elf32_Ehdr
|
|
-
|
|
#endif /* _ASM_M68K_MODULE_H */
|
|
diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig
|
|
index ab9afca..b4f409f 100644
|
|
--- 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 <asm-generic/module.h>
|
|
|
|
/*
|
|
* 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 <asm-generic/module.h>
|
|
+
|
|
/*
|
|
* This file contains the parisc architecture specific module code.
|
|
*/
|
|
-#ifdef CONFIG_64BIT
|
|
-#define Elf_Shdr Elf64_Shdr
|
|
-#define Elf_Sym Elf64_Sym
|
|
-#define Elf_Ehdr Elf64_Ehdr
|
|
-#define Elf_Addr Elf64_Addr
|
|
-#define Elf_Rela Elf64_Rela
|
|
-#else
|
|
-#define Elf_Shdr Elf32_Shdr
|
|
-#define Elf_Sym Elf32_Sym
|
|
-#define Elf_Ehdr Elf32_Ehdr
|
|
-#define Elf_Addr Elf32_Addr
|
|
-#define Elf_Rela Elf32_Rela
|
|
-#endif
|
|
|
|
struct unwind_table;
|
|
|
|
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
|
|
index 352f416..74f8478 100644
|
|
--- 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 <linux/list.h>
|
|
#include <asm/bug.h>
|
|
+#include <asm-generic/module.h>
|
|
|
|
|
|
#ifndef __powerpc64__
|
|
@@ -60,16 +61,10 @@ struct mod_arch_specific {
|
|
*/
|
|
|
|
#ifdef __powerpc64__
|
|
-# define Elf_Shdr Elf64_Shdr
|
|
-# define Elf_Sym Elf64_Sym
|
|
-# define Elf_Ehdr Elf64_Ehdr
|
|
# ifdef MODULE
|
|
asm(".section .stubs,\"ax\",@nobits; .align 3; .previous");
|
|
# endif
|
|
#else
|
|
-# define Elf_Shdr Elf32_Shdr
|
|
-# define Elf_Sym Elf32_Sym
|
|
-# define Elf_Ehdr Elf32_Ehdr
|
|
# ifdef MODULE
|
|
asm(".section .plt,\"ax\",@nobits; .align 3; .previous");
|
|
asm(".section .init.plt,\"ax\",@nobits; .align 3; .previous");
|
|
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
|
|
index 107610e..c76a052 100644
|
|
--- 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 <asm-generic/module.h>
|
|
+
|
|
/*
|
|
* This file contains the s390 architecture specific module code.
|
|
*/
|
|
@@ -28,19 +31,4 @@ struct mod_arch_specific
|
|
struct mod_arch_syminfo *syminfo;
|
|
};
|
|
|
|
-#ifdef CONFIG_64BIT
|
|
-#define ElfW(x) Elf64_ ## x
|
|
-#define ELFW(x) ELF64_ ## x
|
|
-#else
|
|
-#define ElfW(x) Elf32_ ## x
|
|
-#define ELFW(x) ELF32_ ## x
|
|
-#endif
|
|
-
|
|
-#define Elf_Addr ElfW(Addr)
|
|
-#define Elf_Rela ElfW(Rela)
|
|
-#define Elf_Shdr ElfW(Shdr)
|
|
-#define Elf_Sym ElfW(Sym)
|
|
-#define Elf_Ehdr ElfW(Ehdr)
|
|
-#define ELF_R_SYM ELFW(R_SYM)
|
|
-#define ELF_R_TYPE ELFW(R_TYPE)
|
|
#endif /* _ASM_S390_MODULE_H */
|
|
diff --git a/arch/score/Kconfig b/arch/score/Kconfig
|
|
index ba0f412..e2c8db4 100644
|
|
--- 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 <linux/list.h>
|
|
#include <asm/uaccess.h>
|
|
+#include <asm-generic/module.h>
|
|
|
|
struct mod_arch_specific {
|
|
/* Data Bus Error exception tables */
|
|
@@ -13,11 +14,6 @@ struct mod_arch_specific {
|
|
|
|
typedef uint8_t Elf64_Byte; /* Type for a 8-bit quantity. */
|
|
|
|
-#define Elf_Shdr Elf32_Shdr
|
|
-#define Elf_Sym Elf32_Sym
|
|
-#define Elf_Ehdr Elf32_Ehdr
|
|
-#define Elf_Addr Elf32_Addr
|
|
-
|
|
/* Given an address, look for it in the exception tables. */
|
|
#ifdef CONFIG_MODULES
|
|
const struct exception_table_entry *search_module_dbetables(unsigned long addr);
|
|
diff --git a/arch/score/kernel/module.c b/arch/score/kernel/module.c
|
|
index 469e3b6..1378d99 100644
|
|
--- 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 <asm-generic/module.h>
|
|
+
|
|
#ifdef CONFIG_DWARF_UNWINDER
|
|
+struct mod_arch_specific {
|
|
struct list_head fde_list;
|
|
struct list_head cie_list;
|
|
-#endif
|
|
};
|
|
-
|
|
-#ifdef CONFIG_64BIT
|
|
-#define Elf_Shdr Elf64_Shdr
|
|
-#define Elf_Sym Elf64_Sym
|
|
-#define Elf_Ehdr Elf64_Ehdr
|
|
-#else
|
|
-#define Elf_Shdr Elf32_Shdr
|
|
-#define Elf_Sym Elf32_Sym
|
|
-#define Elf_Ehdr Elf32_Ehdr
|
|
#endif
|
|
|
|
#ifdef CONFIG_CPU_LITTLE_ENDIAN
|
|
diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
|
|
index 67f1f6f..a244e70 100644
|
|
--- 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 <asm-generic/module.h>
|
|
|
|
#endif /* _XTENSA_MODULE_H */
|
|
diff --git a/include/asm-generic/module.h b/include/asm-generic/module.h
|
|
index ed5b44d..14dc41d 100644
|
|
--- 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 <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
---
|
|
include/linux/elfnote.h | 4 ++++
|
|
1 file changed, 4 insertions(+)
|
|
|
|
diff --git a/include/linux/elfnote.h b/include/linux/elfnote.h
|
|
index 278e3ef..949d494 100644
|
|
--- a/include/linux/elfnote.h
|
|
+++ b/include/linux/elfnote.h
|
|
@@ -58,6 +58,7 @@
|
|
ELFNOTE_END
|
|
|
|
#else /* !__ASSEMBLER__ */
|
|
+#include <linux/stringify.h>
|
|
#include <linux/elf.h>
|
|
/*
|
|
* Use an anonymous structure which matches the shape of
|
|
@@ -93,6 +94,9 @@
|
|
|
|
#define ELFNOTE32(name, type, desc) ELFNOTE(32, name, type, desc)
|
|
#define ELFNOTE64(name, type, desc) ELFNOTE(64, name, type, desc)
|
|
+
|
|
+#define ELFNOTE_NAME(name) __stringify(name)
|
|
+#define ELFNOTE_SECTION(name) ".note."ELFNOTE_NAME(name)
|
|
#endif /* __ASSEMBLER__ */
|
|
|
|
#endif /* _LINUX_ELFNOTE_H */
|
|
--
|
|
1.7.11.2
|
|
|
|
|
|
From 1d83fa4cf20b3b6f7ffd471459dcad47d6e2ac64 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
---
|
|
.gitignore | 12 ++++++++++++
|
|
Makefile | 1 +
|
|
scripts/mod/.gitignore | 1 +
|
|
3 files changed, 14 insertions(+)
|
|
|
|
diff --git a/.gitignore b/.gitignore
|
|
index 57af07c..7948eeb 100644
|
|
--- 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 <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
---
|
|
Documentation/module-signing.txt | 194 +++++++++++++++++++++++++++++++++++++++
|
|
include/linux/modsign.h | 27 ++++++
|
|
init/Kconfig | 54 +++++++++++
|
|
3 files changed, 275 insertions(+)
|
|
create mode 100644 Documentation/module-signing.txt
|
|
create mode 100644 include/linux/modsign.h
|
|
|
|
diff --git a/Documentation/module-signing.txt b/Documentation/module-signing.txt
|
|
new file mode 100644
|
|
index 0000000..d75d473
|
|
--- /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 <<EOF
|
|
+ %pubring modsign.pub
|
|
+ %secring modsign.sec
|
|
+ Key-Type: RSA
|
|
+ Key-Length: 4096
|
|
+ Name-Real: A. N. Other
|
|
+ Name-Comment: Kernel Module GPG key
|
|
+ %commit
|
|
+ EOF
|
|
+ gpg --homedir . --batch --gen-key genkey
|
|
+
|
|
+The above generates fresh keys using /dev/random. If there's insufficient data
|
|
+in /dev/random, more can be provided using the rngd program if there's a
|
|
+hardware random number generator available.
|
|
+
|
|
+Note that no GPG password is used in the above scriptlet.
|
|
+
|
|
+The modsign.pub file is compiled into the kernel directly by the assembler by
|
|
+means of an ".incbin" directive in kernel/modsign-pubkey.c.
|
|
+
|
|
+Once the kernel is running, the keys are visible to root as kernel crypto keys
|
|
+in /proc/keys in a keyring called .module_sign:
|
|
+
|
|
+335ab517 I----- 1 perm 1f030000 0 0 keyring .module_sign: 2/4
|
|
+38d7d169 I----- 1 perm 3f010000 0 0 crypto modsign.0: rsa 57532ca5 []
|
|
+195fa736 I----- 1 perm 3f010000 0 0 crypto modsign.1: dsa 5acc2142 []
|
|
+
|
|
+This keyring can be listed with the keyctl program. See:
|
|
+
|
|
+ Documentation/security/keys-crypto.txt
|
|
+
|
|
+for more information of crypto keys.
|
|
+
|
|
+
|
|
+============================
|
|
+SELECTING THE HASH ALGORITHM
|
|
+============================
|
|
+
|
|
+The hash algorithm to be used is selected by a multiple choice configuration
|
|
+item that enables one of the following variables:
|
|
+
|
|
+ CONFIG_SIG_SHA1
|
|
+ CONFIG_SIG_SHA224
|
|
+ CONFIG_SIG_SHA256
|
|
+ CONFIG_SIG_SHA384
|
|
+ CONFIG_SIG_SHA512
|
|
+
|
|
+These cause an appropriate "--digest-algo=" parameter to be passed to gpg when
|
|
+signing a module and force the appropriate hash algorithm to be compiled
|
|
+directly into the kernel rather than being built as a module.
|
|
+
|
|
+
|
|
+==============
|
|
+MODULE SIGNING
|
|
+==============
|
|
+
|
|
+Modules will then be signed automatically. The kernel make command line can
|
|
+include the following options:
|
|
+
|
|
+ (*) MODSECKEY=<secret-key-ring-path>
|
|
+
|
|
+ This indicates the whereabouts of the GPG keyring that is the source of
|
|
+ the secret key to be used. The default is "./modsign.sec".
|
|
+
|
|
+ (*) MODPUBKEY=<public-key-ring-path>
|
|
+
|
|
+ This indicates the whereabouts of the GPG keyring that is the source of
|
|
+ the public key to be used. The default is "./modsign.pub".
|
|
+
|
|
+ (*) MODKEYNAME=<key-name>
|
|
+
|
|
+ The name of the key pair to be used from the aforementioned keyrings.
|
|
+ This defaults to being unset, thus leaving the choice of default key to
|
|
+ gpg.
|
|
+
|
|
+ (*) KEYFLAGS="gpg-options"
|
|
+
|
|
+ Override the complete gpg command line, including the preceding three
|
|
+ options. The default options supplied to gpg are:
|
|
+
|
|
+ --no-default-keyring
|
|
+ --secret-keyring $(MODSECKEY)
|
|
+ --keyring $(MODPUBKEY)
|
|
+ --no-default-keyring
|
|
+ --homedir .
|
|
+ --no-options
|
|
+ --no-auto-check-trustdb
|
|
+ --no-permission-warning
|
|
+ --digest-algo=<hash-algorithm>
|
|
+
|
|
+ with:
|
|
+
|
|
+ --default-key $(MODKEYNAME)
|
|
+
|
|
+ being added if requested.
|
|
+
|
|
+The resulting module.ko file will be the signed module.
|
|
+
|
|
+
|
|
+========================
|
|
+STRIPPING SIGNED MODULES
|
|
+========================
|
|
+
|
|
+Signed modules may be safely stripped with any of the following:
|
|
+
|
|
+ strip -x
|
|
+ strip -g
|
|
+ eu-strip
|
|
+
|
|
+as the signature only covers those parts of the module the kernel actually uses
|
|
+and any ELF metadata required to deal with them. Any necessary ELF metadata
|
|
+that is affected by stripping is canonicalised by the sig generator and the sig
|
|
+checker to hide strip effects.
|
|
+
|
|
+This permits the debuginfo to be detached from the module and placed in another
|
|
+spot so that gdb can find it when referring to that module without the need for
|
|
+multiple signed versions of the module. Such is done by rpmbuild when
|
|
+producing RPMs.
|
|
+
|
|
+It also permits the module to be stripped as far as possible for when modules
|
|
+are being reduced prior to being included in an initial ramdisk composition.
|
|
+
|
|
+Note that "strip" and "strip -s" may not be used on a module, signed or
|
|
+otherwise, as they remove the symbol table and render the relocation tables
|
|
+unusable.
|
|
+
|
|
+
|
|
+======================
|
|
+LOADING SIGNED MODULES
|
|
+======================
|
|
+
|
|
+Modules are loaded with insmod, exactly as for unsigned modules. The signature
|
|
+is inserted into the module object file during the build process as an ELF note
|
|
+called "module.sig" in an ELF section called ".note.module.sig". The signature
|
|
+checker will detect it and apply signature checking.
|
|
+
|
|
+
|
|
+=========================================
|
|
+NON-VALID SIGNATURES AND UNSIGNED MODULES
|
|
+=========================================
|
|
+
|
|
+If CONFIG_MODULE_SIG_FORCE is enabled or "enforcemodulesig=1" is supplied on
|
|
+the kernel command line, the kernel will _only_ load validly signed modules
|
|
+for which it has a public key. Otherwise, it will also load modules that are
|
|
+unsigned. Any module for which the kernel has a key, but which proves to have
|
|
+a signature mismatch will not be permitted to load (returning EKEYREJECTED).
|
|
+
|
|
+This table indicates the behaviours of the various situations:
|
|
+
|
|
+ MODULE STATE PERMISSIVE MODE ENFORCING MODE
|
|
+ ======================================= =============== ===============
|
|
+ Unsigned Ok EKEYREJECTED
|
|
+ Signed, no public key ENOKEY ENOKEY
|
|
+ Validly signed, public key Ok Ok
|
|
+ Invalidly signed, public key EKEYREJECTED EKEYREJECTED
|
|
+ Validly signed, expired key EKEYEXPIRED EKEYEXPIRED
|
|
+ Signed, hash algorithm unavailable ENOPKG ENOPKG
|
|
+ Corrupt signature EBADMSG EBADMSG
|
|
+ Corrupt ELF ELIBBAD ELIBBAD
|
|
diff --git a/include/linux/modsign.h b/include/linux/modsign.h
|
|
new file mode 100644
|
|
index 0000000..c5ac87a
|
|
--- /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 <linux/elfnote.h>
|
|
+
|
|
+/*
|
|
+ * The parameters of the ELF note used to carry the signature
|
|
+ */
|
|
+#define MODSIGN_NOTE_NAME module.sig
|
|
+#define MODSIGN_NOTE_TYPE 100
|
|
+
|
|
+#endif
|
|
+
|
|
+#endif /* _LINUX_MODSIGN_H */
|
|
diff --git a/init/Kconfig b/init/Kconfig
|
|
index af6c7f8..e23ed83 100644
|
|
--- 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 <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
---
|
|
scripts/Makefile.modpost | 87 ++++-
|
|
scripts/mod/Makefile | 2 +-
|
|
scripts/mod/mod-extract.c | 913 ++++++++++++++++++++++++++++++++++++++++++++
|
|
scripts/mod/modsign-note.sh | 16 +
|
|
4 files changed, 1016 insertions(+), 2 deletions(-)
|
|
create mode 100644 scripts/mod/mod-extract.c
|
|
create mode 100644 scripts/mod/modsign-note.sh
|
|
|
|
diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost
|
|
index 08dce14..17465d8 100644
|
|
--- a/scripts/Makefile.modpost
|
|
+++ b/scripts/Makefile.modpost
|
|
@@ -14,7 +14,8 @@
|
|
# 3) create one <module>.mod.c file pr. module
|
|
# 4) create one Module.symvers file with CRC for all exported symbols
|
|
# 5) compile all <module>.mod.c files
|
|
-# 6) final link of the module to a <module.ko> file
|
|
+# 6) final link of the module to a <module.ko> (or <module.unsigned>) file
|
|
+# 7) signs the modules to a <module.ko> file
|
|
|
|
# Step 3 is used to place certain information in the module's ELF
|
|
# section, including information such as:
|
|
@@ -32,6 +33,8 @@
|
|
# Step 4 is solely used to allow module versioning in external modules,
|
|
# where the CRC of each module is retrieved from the Module.symvers file.
|
|
|
|
+# Step 7 is dependent on CONFIG_MODULE_SIG being enabled.
|
|
+
|
|
# KBUILD_MODPOST_WARN can be set to avoid error out in case of undefined
|
|
# symbols in the final module linking stage
|
|
# KBUILD_MODPOST_NOFINAL can be set to skip the final link of modules.
|
|
@@ -116,6 +119,7 @@ $(modules:.ko=.mod.o): %.mod.o: %.mod.c FORCE
|
|
targets += $(modules:.ko=.mod.o)
|
|
|
|
# Step 6), final link of the modules
|
|
+ifneq ($(CONFIG_MODULE_SIG),y)
|
|
quiet_cmd_ld_ko_o = LD [M] $@
|
|
cmd_ld_ko_o = $(LD) -r $(LDFLAGS) \
|
|
$(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \
|
|
@@ -125,7 +129,88 @@ $(modules): %.ko :%.o %.mod.o FORCE
|
|
$(call if_changed,ld_ko_o)
|
|
|
|
targets += $(modules)
|
|
+else
|
|
+quiet_cmd_ld_ko_unsigned_o = LD [M] $@
|
|
+ cmd_ld_ko_unsigned_o = \
|
|
+ $(LD) -r $(LDFLAGS) \
|
|
+ $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \
|
|
+ -o $@ $(filter-out FORCE,$^) \
|
|
+ $(if $(AFTER_LINK),; $(AFTER_LINK))
|
|
+
|
|
+$(modules:.ko=.ko.unsigned): %.ko.unsigned :%.o %.mod.o FORCE
|
|
+ $(call if_changed,ld_ko_unsigned_o)
|
|
+
|
|
+targets += $(modules:.ko=.ko.unsigned)
|
|
+
|
|
+# Step 7), sign the modules
|
|
+MODSECKEY = ./modsign.sec
|
|
+MODPUBKEY = ./modsign.pub
|
|
+KEYFLAGS = --no-default-keyring --secret-keyring $(MODSECKEY) --keyring $(MODPUBKEY) --no-default-keyring --homedir . --no-options --no-auto-check-trustdb --no-permission-warning
|
|
+
|
|
+ifdef CONFIG_MODULE_SIG_SHA1
|
|
+KEYFLAGS += --digest-algo=SHA1
|
|
+else
|
|
+ifdef CONFIG_MODULE_SIG_SHA224
|
|
+KEYFLAGS += --digest-algo=SHA224
|
|
+else
|
|
+ifdef CONFIG_MODULE_SIG_SHA256
|
|
+KEYFLAGS += --digest-algo=SHA256
|
|
+else
|
|
+ifdef CONFIG_MODULE_SIG_SHA384
|
|
+KEYFLAGS += --digest-algo=SHA384
|
|
+else
|
|
+ifdef CONFIG_MODULE_SIG_SHA512
|
|
+KEYFLAGS += --digest-algo=SHA512
|
|
+else
|
|
+endif
|
|
+endif
|
|
+endif
|
|
+endif
|
|
+endif
|
|
+
|
|
+ifdef MODKEYNAME
|
|
+KEYFLAGS += --default-key $(MODKEYNAME)
|
|
+endif
|
|
|
|
+ifeq ($(wildcard $(MODSECKEY))+$(wildcard $(MODPUBKEY)),$(MODSECKEY)+$(MODPUBKEY))
|
|
+ifeq ($(KBUILD_SRC),)
|
|
+ # no O= is being used
|
|
+ SCRIPTS_DIR := scripts
|
|
+else
|
|
+ SCRIPTS_DIR := $(KBUILD_SRC)/scripts
|
|
+endif
|
|
+SIGN_MODULES := 1
|
|
+else
|
|
+SIGN_MODULES := 0
|
|
+endif
|
|
+
|
|
+# only sign if it's an in-tree module
|
|
+ifneq ($(KBUILD_EXTMOD),)
|
|
+SIGN_MODULES := 0
|
|
+endif
|
|
+
|
|
+ifeq ($(SIGN_MODULES),1)
|
|
+KEYRING_DEP := modsign.sec modsign.pub
|
|
+quiet_cmd_sign_ko_ko_unsigned = SIGN [M] $@
|
|
+ cmd_sign_ko_ko_unsigned = \
|
|
+ scripts/mod/mod-extract $< $@.digest && \
|
|
+ rm -f $@.digest.sig && \
|
|
+ gpg --batch --no-greeting $(KEYFLAGS) -b $@.digest && \
|
|
+ sh $(SCRIPTS_DIR)/mod/modsign-note.sh $@.digest.sig | \
|
|
+ $(CC) -x assembler-with-cpp $(c_flags) $(CFLAGS_MODULE) -c -o $@.note.o - && \
|
|
+ $(LD) -r $(LDFLAGS) -o $@ $< $@.note.o
|
|
+else
|
|
+KEYRING_DEP :=
|
|
+quiet_cmd_sign_ko_ko_unsigned = NO SIGN [M] $@
|
|
+ cmd_sign_ko_ko_unsigned = \
|
|
+ cp $< $@
|
|
+endif
|
|
+
|
|
+$(modules): %.ko :%.ko.unsigned $(KEYRING_DEP) FORCE
|
|
+ $(call if_changed,sign_ko_ko_unsigned)
|
|
+
|
|
+targets += $(modules)
|
|
+endif
|
|
|
|
# Add FORCE to the prequisites of a target to force it to be always rebuilt.
|
|
# ---------------------------------------------------------------------------
|
|
diff --git a/scripts/mod/Makefile b/scripts/mod/Makefile
|
|
index ff954f8..4654e3b 100644
|
|
--- 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 <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <stdint.h>
|
|
+#include <stdarg.h>
|
|
+#include <string.h>
|
|
+#include <unistd.h>
|
|
+#include <fcntl.h>
|
|
+#include <sys/mman.h>
|
|
+#include <sys/stat.h>
|
|
+#include <elf.h>
|
|
+#include <asm/byteorder.h>
|
|
+
|
|
+static void extract_elf64(void *buffer, size_t size, Elf64_Ehdr *hdr);
|
|
+static void extract_elf32(void *buffer, size_t size, Elf32_Ehdr *hdr);
|
|
+
|
|
+struct byteorder {
|
|
+ uint16_t (*get16)(const uint16_t *);
|
|
+ uint32_t (*get32)(const uint32_t *);
|
|
+ uint64_t (*get64)(const uint64_t *);
|
|
+ void (*set16)(uint16_t *, uint16_t);
|
|
+ void (*set32)(uint32_t *, uint32_t);
|
|
+ void (*set64)(uint64_t *, uint64_t);
|
|
+};
|
|
+
|
|
+static uint16_t get16_le(const uint16_t *p) { return __le16_to_cpu(*p); }
|
|
+static uint32_t get32_le(const uint32_t *p) { return __le32_to_cpu(*p); }
|
|
+static uint64_t get64_le(const uint64_t *p) { return __le64_to_cpu(*p); }
|
|
+static uint16_t get16_be(const uint16_t *p) { return __be16_to_cpu(*p); }
|
|
+static uint32_t get32_be(const uint32_t *p) { return __be32_to_cpu(*p); }
|
|
+static uint64_t get64_be(const uint64_t *p) { return __be64_to_cpu(*p); }
|
|
+
|
|
+static void set16_le(uint16_t *p, uint16_t n) { *p = __cpu_to_le16(n); }
|
|
+static void set32_le(uint32_t *p, uint32_t n) { *p = __cpu_to_le32(n); }
|
|
+static void set64_le(uint64_t *p, uint64_t n) { *p = __cpu_to_le64(n); }
|
|
+static void set16_be(uint16_t *p, uint16_t n) { *p = __cpu_to_be16(n); }
|
|
+static void set32_be(uint32_t *p, uint32_t n) { *p = __cpu_to_be32(n); }
|
|
+static void set64_be(uint64_t *p, uint64_t n) { *p = __cpu_to_be64(n); }
|
|
+
|
|
+static const struct byteorder byteorder_le = {
|
|
+ get16_le, get32_le, get64_le,
|
|
+ set16_le, set32_le, set64_le
|
|
+};
|
|
+static const struct byteorder byteorder_be = {
|
|
+ get16_be, get32_be, get64_be,
|
|
+ set16_be, set32_be, set64_be
|
|
+};
|
|
+static const struct byteorder *order;
|
|
+
|
|
+static inline uint16_t get16(const uint16_t *p) { return order->get16(p); }
|
|
+static inline uint32_t get32(const uint32_t *p) { return order->get32(p); }
|
|
+static inline uint64_t get64(const uint64_t *p) { return order->get64(p); }
|
|
+static inline void set16(uint16_t *p, uint16_t n) { order->set16(p, n); }
|
|
+static inline void set32(uint32_t *p, uint32_t n) { order->set32(p, n); }
|
|
+static inline void set64(uint64_t *p, uint64_t n) { order->set64(p, n); }
|
|
+
|
|
+static FILE *outfd;
|
|
+static uint8_t csum, xcsum;
|
|
+
|
|
+static void write_out(const void *data, size_t size)
|
|
+{
|
|
+ const uint8_t *p = data;
|
|
+ size_t loop;
|
|
+
|
|
+ for (loop = 0; loop < size; loop++) {
|
|
+ csum += p[loop];
|
|
+ xcsum += p[loop];
|
|
+ }
|
|
+
|
|
+ if (fwrite(data, 1, size, outfd) != size) {
|
|
+ perror("write");
|
|
+ exit(1);
|
|
+ }
|
|
+}
|
|
+
|
|
+#define write_out_val(VAL) write_out(&(VAL), sizeof(VAL))
|
|
+
|
|
+static int is_verbose;
|
|
+
|
|
+static __attribute__((format(printf, 1, 2)))
|
|
+void verbose(const char *fmt, ...)
|
|
+{
|
|
+ va_list va;
|
|
+
|
|
+ if (is_verbose) {
|
|
+ va_start(va, fmt);
|
|
+ vprintf(fmt, va);
|
|
+ va_end(va);
|
|
+ }
|
|
+}
|
|
+
|
|
+static __attribute__((noreturn))
|
|
+void usage(void)
|
|
+{
|
|
+ fprintf(stderr, "Usage: mod-extract [-v] <modulefile> <extractfile>\n");
|
|
+ exit(2);
|
|
+}
|
|
+
|
|
+/*
|
|
+ *
|
|
+ */
|
|
+int main(int argc, char **argv)
|
|
+{
|
|
+ struct stat st;
|
|
+ Elf32_Ehdr *hdr32;
|
|
+ Elf64_Ehdr *hdr64;
|
|
+ size_t len;
|
|
+ void *buffer;
|
|
+ int fd, be, b64;
|
|
+
|
|
+ while (argc > 1 && strcmp("-v", argv[1]) == 0) {
|
|
+ argv++;
|
|
+ argc--;
|
|
+ is_verbose++;
|
|
+ }
|
|
+
|
|
+ if (argc != 3)
|
|
+ usage();
|
|
+
|
|
+ /* map the module into memory */
|
|
+ fd = open(argv[1], O_RDONLY);
|
|
+ if (fd < 0) {
|
|
+ perror("open input");
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ if (fstat(fd, &st) < 0) {
|
|
+ perror("fstat");
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ len = st.st_size;
|
|
+
|
|
+ buffer = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
|
|
+ if (buffer == MAP_FAILED) {
|
|
+ perror("mmap");
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ if (close(fd) < 0) {
|
|
+ perror("close input");
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ /* check it's an ELF object */
|
|
+ hdr32 = buffer;
|
|
+ hdr64 = buffer;
|
|
+
|
|
+ if (hdr32->e_ident[EI_MAG0] != ELFMAG0 ||
|
|
+ hdr32->e_ident[EI_MAG1] != ELFMAG1 ||
|
|
+ hdr32->e_ident[EI_MAG2] != ELFMAG2 ||
|
|
+ hdr32->e_ident[EI_MAG3] != ELFMAG3
|
|
+ ) {
|
|
+ fprintf(stderr, "Module does not appear to be ELF\n");
|
|
+ exit(3);
|
|
+ }
|
|
+
|
|
+ /* determine endianness and word size */
|
|
+ b64 = (hdr32->e_ident[EI_CLASS] == ELFCLASS64);
|
|
+ be = (hdr32->e_ident[EI_DATA] == ELFDATA2MSB);
|
|
+ order = be ? &byteorder_be : &byteorder_le;
|
|
+
|
|
+ verbose("Module is %s-bit %s-endian\n",
|
|
+ b64 ? "64" : "32",
|
|
+ be ? "big" : "little");
|
|
+
|
|
+ /* open the output file */
|
|
+ outfd = fopen(argv[2], "w");
|
|
+ if (!outfd) {
|
|
+ perror("open output");
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ /* perform the extraction */
|
|
+ if (b64)
|
|
+ extract_elf64(buffer, len, hdr64);
|
|
+ else
|
|
+ extract_elf32(buffer, len, hdr32);
|
|
+
|
|
+ /* done */
|
|
+ if (fclose(outfd) == EOF) {
|
|
+ perror("close output");
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * extract a RELA table
|
|
+ * - need to canonicalise the entries in case section addition/removal has
|
|
+ * rearranged the symbol table and the section table
|
|
+ */
|
|
+static void extract_elf64_rela(const void *buffer, int secix, int targetix,
|
|
+ const Elf64_Rela *relatab, size_t nrels,
|
|
+ const Elf64_Sym *symbols, size_t nsyms,
|
|
+ const Elf64_Shdr *sections, size_t nsects, int *canonmap,
|
|
+ const char *strings, size_t nstrings,
|
|
+ const char *sh_name)
|
|
+{
|
|
+ struct {
|
|
+ uint64_t r_offset;
|
|
+ uint64_t r_addend;
|
|
+ uint64_t st_value;
|
|
+ uint64_t st_size;
|
|
+ uint32_t r_type;
|
|
+ uint16_t st_shndx;
|
|
+ uint8_t st_info;
|
|
+ uint8_t st_other;
|
|
+
|
|
+ } __attribute__((packed)) relocation;
|
|
+
|
|
+ const Elf64_Sym *symbol;
|
|
+ size_t loop;
|
|
+
|
|
+ /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
|
|
+ for (loop = 0; loop < nrels; loop++) {
|
|
+ Elf64_Section st_shndx;
|
|
+ Elf64_Xword r_info;
|
|
+
|
|
+ /* decode the relocation */
|
|
+ r_info = get64(&relatab[loop].r_info);
|
|
+ relocation.r_offset = relatab[loop].r_offset;
|
|
+ relocation.r_addend = relatab[loop].r_addend;
|
|
+ set32(&relocation.r_type, ELF64_R_TYPE(r_info));
|
|
+
|
|
+ if (ELF64_R_SYM(r_info) >= nsyms) {
|
|
+ fprintf(stderr, "Invalid symbol ID %zx in relocation %zu\n",
|
|
+ (size_t)ELF64_R_SYM(r_info), loop);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ /* decode the symbol referenced by the relocation */
|
|
+ symbol = &symbols[ELF64_R_SYM(r_info)];
|
|
+ relocation.st_info = symbol->st_info;
|
|
+ relocation.st_other = symbol->st_other;
|
|
+ relocation.st_value = symbol->st_value;
|
|
+ relocation.st_size = symbol->st_size;
|
|
+ relocation.st_shndx = symbol->st_shndx;
|
|
+ st_shndx = get16(&symbol->st_shndx);
|
|
+
|
|
+ /* canonicalise the section used by the symbol */
|
|
+ if (st_shndx > SHN_UNDEF && st_shndx < nsects)
|
|
+ set16(&relocation.st_shndx, canonmap[st_shndx]);
|
|
+
|
|
+ write_out_val(relocation);
|
|
+
|
|
+ /* undefined symbols must be named if referenced */
|
|
+ if (st_shndx == SHN_UNDEF) {
|
|
+ const char *name = strings + get32(&symbol->st_name);
|
|
+ write_out(name, strlen(name) + 1);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ verbose("%02x %4d %s [canon]\n", csum, secix, sh_name);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * extract a REL table
|
|
+ * - need to canonicalise the entries in case section addition/removal has
|
|
+ * rearranged the symbol table and the section table
|
|
+ */
|
|
+static void extract_elf64_rel(const void *buffer, int secix, int targetix,
|
|
+ const Elf64_Rel *relatab, size_t nrels,
|
|
+ const Elf64_Sym *symbols, size_t nsyms,
|
|
+ const Elf64_Shdr *sections, size_t nsects, int *canonmap,
|
|
+ const char *strings, size_t nstrings,
|
|
+ const char *sh_name)
|
|
+{
|
|
+ struct {
|
|
+ uint64_t r_offset;
|
|
+ uint64_t st_value;
|
|
+ uint64_t st_size;
|
|
+ uint32_t r_type;
|
|
+ uint16_t st_shndx;
|
|
+ uint8_t st_info;
|
|
+ uint8_t st_other;
|
|
+
|
|
+ } __attribute__((packed)) relocation;
|
|
+
|
|
+ const Elf64_Sym *symbol;
|
|
+ size_t loop;
|
|
+
|
|
+ /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
|
|
+ for (loop = 0; loop < nrels; loop++) {
|
|
+ Elf64_Section st_shndx;
|
|
+ Elf64_Xword r_info;
|
|
+
|
|
+ /* decode the relocation */
|
|
+ r_info = get64(&relatab[loop].r_info);
|
|
+ relocation.r_offset = relatab[loop].r_offset;
|
|
+ set32(&relocation.r_type, ELF64_R_TYPE(r_info));
|
|
+
|
|
+ if (ELF64_R_SYM(r_info) >= nsyms) {
|
|
+ fprintf(stderr, "Invalid symbol ID %zx in relocation %zu\n",
|
|
+ (size_t)ELF64_R_SYM(r_info), loop);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ /* decode the symbol referenced by the relocation */
|
|
+ symbol = &symbols[ELF64_R_SYM(r_info)];
|
|
+ relocation.st_info = symbol->st_info;
|
|
+ relocation.st_other = symbol->st_other;
|
|
+ relocation.st_value = symbol->st_value;
|
|
+ relocation.st_size = symbol->st_size;
|
|
+ relocation.st_shndx = symbol->st_shndx;
|
|
+ st_shndx = get16(&symbol->st_shndx);
|
|
+
|
|
+ /* canonicalise the section used by the symbol */
|
|
+ if (st_shndx > SHN_UNDEF && st_shndx < nsects)
|
|
+ set16(&relocation.st_shndx, canonmap[st_shndx]);
|
|
+
|
|
+ write_out_val(relocation);
|
|
+
|
|
+ /* undefined symbols must be named if referenced */
|
|
+ if (st_shndx == SHN_UNDEF) {
|
|
+ const char *name = strings + get32(&symbol->st_name);
|
|
+ write_out(name, strlen(name) + 1);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ verbose("%02x %4d %s [canon]\n", csum, secix, sh_name);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * extract the data from a 64-bit module
|
|
+ */
|
|
+static void extract_elf64(void *buffer, size_t len, Elf64_Ehdr *hdr)
|
|
+{
|
|
+ const Elf64_Sym *symbols;
|
|
+ Elf64_Shdr *sections;
|
|
+ const char *secstrings, *strings;
|
|
+ size_t nsyms, nstrings;
|
|
+ int loop, shnum, *canonlist, *canonmap, canon, changed, tmp;
|
|
+
|
|
+ sections = buffer + get64(&hdr->e_shoff);
|
|
+ secstrings = buffer + get64(§ions[get16(&hdr->e_shstrndx)].sh_offset);
|
|
+ shnum = get16(&hdr->e_shnum);
|
|
+
|
|
+ /* find the symbol table and the string table and produce a list of
|
|
+ * index numbers of sections that contribute to the kernel's module
|
|
+ * image
|
|
+ */
|
|
+ canonlist = calloc(sizeof(int), shnum * 2);
|
|
+ if (!canonlist) {
|
|
+ perror("calloc");
|
|
+ exit(1);
|
|
+ }
|
|
+ canonmap = canonlist + shnum;
|
|
+ canon = 0;
|
|
+
|
|
+ symbols = NULL;
|
|
+ strings = NULL;
|
|
+ nstrings = 0;
|
|
+ nsyms = 0;
|
|
+
|
|
+ for (loop = 1; loop < shnum; loop++) {
|
|
+ const char *sh_name = secstrings + get32(§ions[loop].sh_name);
|
|
+ Elf64_Word sh_type = get32(§ions[loop].sh_type);
|
|
+ Elf64_Xword sh_size = get64(§ions[loop].sh_size);
|
|
+ Elf64_Xword sh_flags = get64(§ions[loop].sh_flags);
|
|
+ Elf64_Word sh_info = get32(§ions[loop].sh_info);
|
|
+ Elf64_Off sh_offset = get64(§ions[loop].sh_offset);
|
|
+ void *data = buffer + sh_offset;
|
|
+
|
|
+ /* quick sanity check */
|
|
+ if (sh_type != SHT_NOBITS && len < sh_offset + sh_size) {
|
|
+ fprintf(stderr, "Section goes beyond EOF\n");
|
|
+ exit(3);
|
|
+ }
|
|
+
|
|
+ /* we only need to canonicalise allocatable sections */
|
|
+ if (sh_flags & SHF_ALLOC)
|
|
+ canonlist[canon++] = loop;
|
|
+ else if ((sh_type == SHT_REL || sh_type == SHT_RELA) &&
|
|
+ get64(§ions[sh_info].sh_flags) & SHF_ALLOC)
|
|
+ canonlist[canon++] = loop;
|
|
+
|
|
+ /* keep track of certain special sections */
|
|
+ switch (sh_type) {
|
|
+ case SHT_SYMTAB:
|
|
+ if (strcmp(sh_name, ".symtab") == 0) {
|
|
+ symbols = data;
|
|
+ nsyms = sh_size / sizeof(Elf64_Sym);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case SHT_STRTAB:
|
|
+ if (strcmp(sh_name, ".strtab") == 0) {
|
|
+ strings = data;
|
|
+ nstrings = sh_size;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!symbols) {
|
|
+ fprintf(stderr, "Couldn't locate symbol table\n");
|
|
+ exit(3);
|
|
+ }
|
|
+
|
|
+ if (!strings) {
|
|
+ fprintf(stderr, "Couldn't locate strings table\n");
|
|
+ exit(3);
|
|
+ }
|
|
+
|
|
+ /* canonicalise the index numbers of the contributing section */
|
|
+ do {
|
|
+ changed = 0;
|
|
+
|
|
+ for (loop = 0; loop < canon - 1; loop++) {
|
|
+ const char *x = secstrings + get32(§ions[canonlist[loop + 0]].sh_name);
|
|
+ const char *y = secstrings + get32(§ions[canonlist[loop + 1]].sh_name);
|
|
+ if (strcmp(x, y) > 0) {
|
|
+ tmp = canonlist[loop + 0];
|
|
+ canonlist[loop + 0] = canonlist[loop + 1];
|
|
+ canonlist[loop + 1] = tmp;
|
|
+ changed = 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ } while (changed);
|
|
+
|
|
+ for (loop = 0; loop < canon; loop++)
|
|
+ canonmap[canonlist[loop]] = loop + 1;
|
|
+
|
|
+ if (is_verbose > 1) {
|
|
+ printf("\nSection canonicalisation map:\n");
|
|
+ for (loop = 1; loop < shnum; loop++) {
|
|
+ const char *x = secstrings + get32(§ions[loop].sh_name);
|
|
+ printf("%4d %s\n", canonmap[loop], x);
|
|
+ }
|
|
+
|
|
+ printf("\nAllocated section list in canonical order:\n");
|
|
+ for (loop = 0; loop < canon; loop++) {
|
|
+ const char *x = secstrings + get32(§ions[canonlist[loop]].sh_name);
|
|
+ printf("%4d %s\n", canonlist[loop], x);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* iterate through the section table looking for sections we want to
|
|
+ * contribute to the signature */
|
|
+ verbose("\n");
|
|
+ verbose("CAN FILE POS CS SECT NAME\n");
|
|
+ verbose("=== ======== == ==== ==============================\n");
|
|
+
|
|
+ for (loop = 0; loop < canon; loop++) {
|
|
+ int sect = canonlist[loop];
|
|
+ const char *sh_name = secstrings + get32(§ions[sect].sh_name);
|
|
+ Elf64_Word sh_type = get32(§ions[sect].sh_type);
|
|
+ Elf64_Xword sh_size = get64(§ions[sect].sh_size);
|
|
+ Elf64_Xword sh_flags = get64(§ions[sect].sh_flags);
|
|
+ Elf64_Word sh_info = get32(§ions[sect].sh_info);
|
|
+ Elf64_Off sh_offset = get64(§ions[sect].sh_offset);
|
|
+ void *data = buffer + sh_offset;
|
|
+
|
|
+ csum = 0;
|
|
+
|
|
+ /* include canonicalised relocation sections */
|
|
+ if (sh_type == SHT_REL || sh_type == SHT_RELA) {
|
|
+ Elf32_Word canon_sh_info;
|
|
+
|
|
+ if (sh_info <= 0 && sh_info >= hdr->e_shnum) {
|
|
+ fprintf(stderr,
|
|
+ "Invalid ELF - REL/RELA sh_info does"
|
|
+ " not refer to a valid section\n");
|
|
+ exit(3);
|
|
+ }
|
|
+
|
|
+ verbose("%3u %08lx ", loop, ftell(outfd));
|
|
+
|
|
+ set32(&canon_sh_info, canonmap[sh_info]);
|
|
+
|
|
+ /* write out selected portions of the section header */
|
|
+ write_out(sh_name, strlen(sh_name));
|
|
+ write_out_val(sections[sect].sh_type);
|
|
+ write_out_val(sections[sect].sh_flags);
|
|
+ write_out_val(sections[sect].sh_size);
|
|
+ write_out_val(sections[sect].sh_addralign);
|
|
+ write_out_val(canon_sh_info);
|
|
+
|
|
+ if (sh_type == SHT_RELA)
|
|
+ extract_elf64_rela(buffer, sect, sh_info,
|
|
+ data, sh_size / sizeof(Elf64_Rela),
|
|
+ symbols, nsyms,
|
|
+ sections, shnum, canonmap,
|
|
+ strings, nstrings,
|
|
+ sh_name);
|
|
+ else
|
|
+ extract_elf64_rel(buffer, sect, sh_info,
|
|
+ data, sh_size / sizeof(Elf64_Rel),
|
|
+ symbols, nsyms,
|
|
+ sections, shnum, canonmap,
|
|
+ strings, nstrings,
|
|
+ sh_name);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /* include the headers of BSS sections */
|
|
+ if (sh_type == SHT_NOBITS && sh_flags & SHF_ALLOC) {
|
|
+ verbose("%3u %08lx ", loop, ftell(outfd));
|
|
+
|
|
+ /* write out selected portions of the section header */
|
|
+ write_out(sh_name, strlen(sh_name));
|
|
+ write_out_val(sections[sect].sh_type);
|
|
+ write_out_val(sections[sect].sh_flags);
|
|
+ write_out_val(sections[sect].sh_size);
|
|
+ write_out_val(sections[sect].sh_addralign);
|
|
+
|
|
+ verbose("%02x %4d %s\n", csum, sect, sh_name);
|
|
+ }
|
|
+
|
|
+ /* include allocatable loadable sections */
|
|
+ if (sh_type != SHT_NOBITS && sh_flags & SHF_ALLOC)
|
|
+ goto include_section;
|
|
+
|
|
+ /* not this section */
|
|
+ continue;
|
|
+
|
|
+ include_section:
|
|
+ verbose("%3u %08lx ", loop, ftell(outfd));
|
|
+
|
|
+ /* write out selected portions of the section header */
|
|
+ write_out(sh_name, strlen(sh_name));
|
|
+ write_out_val(sections[sect].sh_type);
|
|
+ write_out_val(sections[sect].sh_flags);
|
|
+ write_out_val(sections[sect].sh_size);
|
|
+ write_out_val(sections[sect].sh_addralign);
|
|
+
|
|
+ /* write out the section data */
|
|
+ write_out(data, sh_size);
|
|
+
|
|
+ verbose("%02x %4d %s\n", csum, sect, sh_name);
|
|
+ }
|
|
+
|
|
+ verbose("%08lx (%lu bytes csum 0x%02x)\n",
|
|
+ ftell(outfd), ftell(outfd), xcsum);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * extract a RELA table
|
|
+ * - need to canonicalise the entries in case section addition/removal has
|
|
+ * rearranged the symbol table and the section table
|
|
+ */
|
|
+static void extract_elf32_rela(const void *buffer, int secix, int targetix,
|
|
+ const Elf32_Rela *relatab, size_t nrels,
|
|
+ const Elf32_Sym *symbols, size_t nsyms,
|
|
+ const Elf32_Shdr *sections, size_t nsects,
|
|
+ int *canonmap,
|
|
+ const char *strings, size_t nstrings,
|
|
+ const char *sh_name)
|
|
+{
|
|
+ struct {
|
|
+ uint32_t r_offset;
|
|
+ uint32_t r_addend;
|
|
+ uint32_t st_value;
|
|
+ uint32_t st_size;
|
|
+ uint16_t st_shndx;
|
|
+ uint8_t r_type;
|
|
+ uint8_t st_info;
|
|
+ uint8_t st_other;
|
|
+
|
|
+ } __attribute__((packed)) relocation;
|
|
+
|
|
+ const Elf32_Sym *symbol;
|
|
+ size_t loop;
|
|
+
|
|
+ /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
|
|
+ for (loop = 0; loop < nrels; loop++) {
|
|
+ Elf32_Section st_shndx;
|
|
+ Elf32_Word r_info;
|
|
+
|
|
+ /* decode the relocation */
|
|
+ r_info = get32(&relatab[loop].r_info);
|
|
+ relocation.r_offset = relatab[loop].r_offset;
|
|
+ relocation.r_addend = relatab[loop].r_addend;
|
|
+ relocation.r_type = ELF32_R_TYPE(r_info);
|
|
+
|
|
+ if (ELF32_R_SYM(r_info) >= nsyms) {
|
|
+ fprintf(stderr, "Invalid symbol ID %x in relocation %zu\n",
|
|
+ ELF32_R_SYM(r_info), loop);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ /* decode the symbol referenced by the relocation */
|
|
+ symbol = &symbols[ELF32_R_SYM(r_info)];
|
|
+ relocation.st_info = symbol->st_info;
|
|
+ relocation.st_other = symbol->st_other;
|
|
+ relocation.st_value = symbol->st_value;
|
|
+ relocation.st_size = symbol->st_size;
|
|
+ relocation.st_shndx = symbol->st_shndx;
|
|
+ st_shndx = get16(&symbol->st_shndx);
|
|
+
|
|
+ /* canonicalise the section used by the symbol */
|
|
+ if (st_shndx > SHN_UNDEF && st_shndx < nsects)
|
|
+ set16(&relocation.st_shndx, canonmap[st_shndx]);
|
|
+
|
|
+ write_out_val(relocation);
|
|
+
|
|
+ /* undefined symbols must be named if referenced */
|
|
+ if (st_shndx == SHN_UNDEF) {
|
|
+ const char *name = strings + get32(&symbol->st_name);
|
|
+ write_out(name, strlen(name) + 1);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ verbose("%02x %4d %s [canon]\n", csum, secix, sh_name);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * extract a REL table
|
|
+ * - need to canonicalise the entries in case section addition/removal has
|
|
+ * rearranged the symbol table and the section table
|
|
+ */
|
|
+static void extract_elf32_rel(const void *buffer, int secix, int targetix,
|
|
+ const Elf32_Rel *relatab, size_t nrels,
|
|
+ const Elf32_Sym *symbols, size_t nsyms,
|
|
+ const Elf32_Shdr *sections, size_t nsects,
|
|
+ int *canonmap,
|
|
+ const char *strings, size_t nstrings,
|
|
+ const char *sh_name)
|
|
+{
|
|
+ struct {
|
|
+ uint32_t r_offset;
|
|
+ uint32_t st_value;
|
|
+ uint32_t st_size;
|
|
+ uint16_t st_shndx;
|
|
+ uint8_t r_type;
|
|
+ uint8_t st_info;
|
|
+ uint8_t st_other;
|
|
+
|
|
+ } __attribute__((packed)) relocation;
|
|
+
|
|
+ const Elf32_Sym *symbol;
|
|
+ size_t loop;
|
|
+
|
|
+ /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
|
|
+ for (loop = 0; loop < nrels; loop++) {
|
|
+ Elf32_Section st_shndx;
|
|
+ Elf32_Word r_info;
|
|
+
|
|
+ /* decode the relocation */
|
|
+ r_info = get32(&relatab[loop].r_info);
|
|
+ relocation.r_offset = relatab[loop].r_offset;
|
|
+ relocation.r_type = ELF32_R_TYPE(r_info);
|
|
+
|
|
+ if (ELF32_R_SYM(r_info) >= nsyms) {
|
|
+ fprintf(stderr, "Invalid symbol ID %x in relocation %zu\n",
|
|
+ ELF32_R_SYM(r_info), loop);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ /* decode the symbol referenced by the relocation */
|
|
+ symbol = &symbols[ELF32_R_SYM(r_info)];
|
|
+ relocation.st_info = symbol->st_info;
|
|
+ relocation.st_other = symbol->st_other;
|
|
+ relocation.st_value = symbol->st_value;
|
|
+ relocation.st_size = symbol->st_size;
|
|
+ relocation.st_shndx = symbol->st_shndx;
|
|
+ st_shndx = get16(&symbol->st_shndx);
|
|
+
|
|
+ /* canonicalise the section used by the symbol */
|
|
+ if (st_shndx > SHN_UNDEF && st_shndx < nsects)
|
|
+ set16(&relocation.st_shndx, canonmap[st_shndx]);
|
|
+
|
|
+ write_out_val(relocation);
|
|
+
|
|
+ /* undefined symbols must be named if referenced */
|
|
+ if (st_shndx == SHN_UNDEF) {
|
|
+ const char *name = strings + get32(&symbol->st_name);
|
|
+ write_out(name, strlen(name) + 1);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ verbose("%02x %4d %s [canon]\n", csum, secix, sh_name);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * extract the data from a 32-bit module
|
|
+ */
|
|
+static void extract_elf32(void *buffer, size_t len, Elf32_Ehdr *hdr)
|
|
+{
|
|
+ const Elf32_Sym *symbols;
|
|
+ Elf32_Shdr *sections;
|
|
+ const char *secstrings, *strings;
|
|
+ size_t nsyms, nstrings;
|
|
+ int loop, shnum, *canonlist, *canonmap, canon, changed, tmp;
|
|
+
|
|
+ sections = buffer + get32(&hdr->e_shoff);
|
|
+ secstrings = buffer + get32(§ions[get16(&hdr->e_shstrndx)].sh_offset);
|
|
+ shnum = get16(&hdr->e_shnum);
|
|
+
|
|
+ /* find the symbol table and the string table and produce a list of
|
|
+ * index numbers of sections that contribute to the kernel's module
|
|
+ * image
|
|
+ */
|
|
+ canonlist = calloc(sizeof(int), shnum * 2);
|
|
+ if (!canonlist) {
|
|
+ perror("calloc");
|
|
+ exit(1);
|
|
+ }
|
|
+ canonmap = canonlist + shnum;
|
|
+ canon = 0;
|
|
+
|
|
+ symbols = NULL;
|
|
+ strings = NULL;
|
|
+ nstrings = 0;
|
|
+ nsyms = 0;
|
|
+
|
|
+ for (loop = 1; loop < shnum; loop++) {
|
|
+ const char *sh_name = secstrings + get32(§ions[loop].sh_name);
|
|
+ Elf32_Word sh_type = get32(§ions[loop].sh_type);
|
|
+ Elf32_Xword sh_size = get32(§ions[loop].sh_size);
|
|
+ Elf32_Xword sh_flags = get32(§ions[loop].sh_flags);
|
|
+ Elf64_Word sh_info = get32(§ions[loop].sh_info);
|
|
+ Elf32_Off sh_offset = get32(§ions[loop].sh_offset);
|
|
+ void *data = buffer + sh_offset;
|
|
+
|
|
+ /* quick sanity check */
|
|
+ if (sh_type != SHT_NOBITS && len < sh_offset + sh_size) {
|
|
+ fprintf(stderr, "Section goes beyond EOF\n");
|
|
+ exit(3);
|
|
+ }
|
|
+
|
|
+ /* we only need to canonicalise allocatable sections */
|
|
+ if (sh_flags & SHF_ALLOC)
|
|
+ canonlist[canon++] = loop;
|
|
+ else if ((sh_type == SHT_REL || sh_type == SHT_RELA) &&
|
|
+ get32(§ions[sh_info].sh_flags) & SHF_ALLOC)
|
|
+ canonlist[canon++] = loop;
|
|
+
|
|
+ /* keep track of certain special sections */
|
|
+ switch (sh_type) {
|
|
+ case SHT_SYMTAB:
|
|
+ if (strcmp(sh_name, ".symtab") == 0) {
|
|
+ symbols = data;
|
|
+ nsyms = sh_size / sizeof(Elf32_Sym);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case SHT_STRTAB:
|
|
+ if (strcmp(sh_name, ".strtab") == 0) {
|
|
+ strings = data;
|
|
+ nstrings = sh_size;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!symbols) {
|
|
+ fprintf(stderr, "Couldn't locate symbol table\n");
|
|
+ exit(3);
|
|
+ }
|
|
+
|
|
+ if (!strings) {
|
|
+ fprintf(stderr, "Couldn't locate strings table\n");
|
|
+ exit(3);
|
|
+ }
|
|
+
|
|
+ /* canonicalise the index numbers of the contributing section */
|
|
+ do {
|
|
+ changed = 0;
|
|
+
|
|
+ for (loop = 0; loop < canon - 1; loop++) {
|
|
+ const char *x = secstrings + get32(§ions[canonlist[loop + 0]].sh_name);
|
|
+ const char *y = secstrings + get32(§ions[canonlist[loop + 1]].sh_name);
|
|
+ if (strcmp(x, y) > 0) {
|
|
+ tmp = canonlist[loop + 0];
|
|
+ canonlist[loop + 0] = canonlist[loop + 1];
|
|
+ canonlist[loop + 1] = tmp;
|
|
+ changed = 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ } while (changed);
|
|
+
|
|
+ for (loop = 0; loop < canon; loop++)
|
|
+ canonmap[canonlist[loop]] = loop + 1;
|
|
+
|
|
+ if (is_verbose > 1) {
|
|
+ printf("\nSection canonicalisation map:\n");
|
|
+ for (loop = 1; loop < shnum; loop++) {
|
|
+ const char *x = secstrings + get32(§ions[loop].sh_name);
|
|
+ printf("%4d %s\n", canonmap[loop], x);
|
|
+ }
|
|
+
|
|
+ printf("\nAllocated section list in canonical order:\n");
|
|
+ for (loop = 0; loop < canon; loop++) {
|
|
+ const char *x = secstrings + get32(§ions[canonlist[loop]].sh_name);
|
|
+ printf("%4d %s\n", canonlist[loop], x);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* iterate through the section table looking for sections we want to
|
|
+ * contribute to the signature */
|
|
+ verbose("\n");
|
|
+ verbose("CAN FILE POS CS SECT NAME\n");
|
|
+ verbose("=== ======== == ==== ==============================\n");
|
|
+
|
|
+ for (loop = 0; loop < canon; loop++) {
|
|
+ int sect = canonlist[loop];
|
|
+ const char *sh_name = secstrings + get32(§ions[sect].sh_name);
|
|
+ Elf32_Word sh_type = get32(§ions[sect].sh_type);
|
|
+ Elf32_Xword sh_size = get32(§ions[sect].sh_size);
|
|
+ Elf32_Xword sh_flags = get32(§ions[sect].sh_flags);
|
|
+ Elf32_Word sh_info = get32(§ions[sect].sh_info);
|
|
+ Elf32_Off sh_offset = get32(§ions[sect].sh_offset);
|
|
+ void *data = buffer + sh_offset;
|
|
+
|
|
+ csum = 0;
|
|
+
|
|
+ /* quick sanity check */
|
|
+ if (sh_type != SHT_NOBITS && len < sh_offset + sh_size) {
|
|
+ fprintf(stderr, "section goes beyond EOF\n");
|
|
+ exit(3);
|
|
+ }
|
|
+
|
|
+ /* include canonicalised relocation sections */
|
|
+ if (sh_type == SHT_REL || sh_type == SHT_RELA) {
|
|
+ Elf32_Word canon_sh_info;
|
|
+
|
|
+ if (sh_info <= 0 && sh_info >= hdr->e_shnum) {
|
|
+ fprintf(stderr,
|
|
+ "Invalid ELF - REL/RELA sh_info does"
|
|
+ " not refer to a valid section\n");
|
|
+ exit(3);
|
|
+ }
|
|
+
|
|
+ verbose("%3u %08lx ", loop, ftell(outfd));
|
|
+
|
|
+ set32(&canon_sh_info, canonmap[sh_info]);
|
|
+
|
|
+ /* write out selected portions of the section header */
|
|
+ write_out(sh_name, strlen(sh_name));
|
|
+ write_out_val(sections[sect].sh_type);
|
|
+ write_out_val(sections[sect].sh_flags);
|
|
+ write_out_val(sections[sect].sh_size);
|
|
+ write_out_val(sections[sect].sh_addralign);
|
|
+ write_out_val(canon_sh_info);
|
|
+
|
|
+ if (sh_type == SHT_RELA)
|
|
+ extract_elf32_rela(buffer, sect, sh_info,
|
|
+ data, sh_size / sizeof(Elf32_Rela),
|
|
+ symbols, nsyms,
|
|
+ sections, shnum, canonmap,
|
|
+ strings, nstrings,
|
|
+ sh_name);
|
|
+ else
|
|
+ extract_elf32_rel(buffer, sect, sh_info,
|
|
+ data, sh_size / sizeof(Elf32_Rel),
|
|
+ symbols, nsyms,
|
|
+ sections, shnum, canonmap,
|
|
+ strings, nstrings,
|
|
+ sh_name);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /* include the headers of BSS sections */
|
|
+ if (sh_type == SHT_NOBITS && sh_flags & SHF_ALLOC) {
|
|
+ verbose("%3u %08lx ", loop, ftell(outfd));
|
|
+
|
|
+ /* write out selected portions of the section header */
|
|
+ write_out(sh_name, strlen(sh_name));
|
|
+ write_out_val(sections[sect].sh_type);
|
|
+ write_out_val(sections[sect].sh_flags);
|
|
+ write_out_val(sections[sect].sh_size);
|
|
+ write_out_val(sections[sect].sh_addralign);
|
|
+
|
|
+ verbose("%02x %4d %s\n", csum, sect, sh_name);
|
|
+ }
|
|
+
|
|
+ /* include allocatable loadable sections */
|
|
+ if (sh_type != SHT_NOBITS && sh_flags & SHF_ALLOC)
|
|
+ goto include_section;
|
|
+
|
|
+ /* not this section */
|
|
+ continue;
|
|
+
|
|
+ include_section:
|
|
+ verbose("%3u %08lx ", loop, ftell(outfd));
|
|
+
|
|
+ /* write out selected portions of the section header */
|
|
+ write_out(sh_name, strlen(sh_name));
|
|
+ write_out_val(sections[sect].sh_type);
|
|
+ write_out_val(sections[sect].sh_flags);
|
|
+ write_out_val(sections[sect].sh_size);
|
|
+ write_out_val(sections[sect].sh_addralign);
|
|
+
|
|
+ /* write out the section data */
|
|
+ write_out(data, sh_size);
|
|
+
|
|
+ verbose("%02x %4d %s\n", csum, sect, sh_name);
|
|
+ }
|
|
+
|
|
+ verbose("%08lx (%lu bytes csum 0x%02x)\n",
|
|
+ ftell(outfd), ftell(outfd), xcsum);
|
|
+}
|
|
diff --git a/scripts/mod/modsign-note.sh b/scripts/mod/modsign-note.sh
|
|
new file mode 100644
|
|
index 0000000..bca67c0
|
|
--- /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-file> ><note-src-file>
|
|
+#
|
|
+
|
|
+SIG=$1
|
|
+
|
|
+cat <<EOF
|
|
+#include <linux/modsign.h>
|
|
+
|
|
+ELFNOTE(MODSIGN_NOTE_NAME, MODSIGN_NOTE_TYPE, .incbin "$SIG")
|
|
+EOF
|
|
+
|
|
+exit 0
|
|
--
|
|
1.7.11.2
|
|
|
|
|
|
From 6a2e8f0245dadda42c355eda278110f496e3a6d5 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
---
|
|
include/linux/module.h | 3 ++
|
|
kernel/Makefile | 1 +
|
|
kernel/module-verify-defs.h | 77 +++++++++++++++++++++++++++++++
|
|
kernel/module-verify.c | 110 ++++++++++++++++++++++++++++++++++++++++++++
|
|
kernel/module-verify.h | 20 ++++++++
|
|
kernel/module.c | 26 +++++++++--
|
|
6 files changed, 232 insertions(+), 5 deletions(-)
|
|
create mode 100644 kernel/module-verify-defs.h
|
|
create mode 100644 kernel/module-verify.c
|
|
create mode 100644 kernel/module-verify.h
|
|
|
|
diff --git a/include/linux/module.h b/include/linux/module.h
|
|
index fbcafe2..7391833 100644
|
|
--- 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 <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/err.h>
|
|
+#include <linux/elf.h>
|
|
+#include <linux/elfnote.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/cred.h>
|
|
+#include <linux/modsign.h>
|
|
+#include <linux/moduleparam.h>
|
|
+#include <keys/crypto-type.h>
|
|
+#include "module-verify.h"
|
|
+#include "module-verify-defs.h"
|
|
+
|
|
+#ifdef DEBUG
|
|
+static int modsign_debug;
|
|
+core_param(modsign_debug, modsign_debug, int, 0644);
|
|
+#else
|
|
+#define modsign_debug false
|
|
+#endif
|
|
+
|
|
+#ifdef CONFIG_MODULE_SIG_FORCE
|
|
+#define modsign_signedonly true
|
|
+#else
|
|
+static bool modsign_signedonly;
|
|
+#endif
|
|
+
|
|
+static const char modsign_note_name[] = ELFNOTE_NAME(MODSIGN_NOTE_NAME);
|
|
+static const char modsign_note_section[] = ELFNOTE_SECTION(MODSIGN_NOTE_NAME);
|
|
+
|
|
+/*
|
|
+ * Verify a module's integrity
|
|
+ */
|
|
+int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok)
|
|
+{
|
|
+ struct module_verify_data mvdata;
|
|
+ int ret;
|
|
+
|
|
+ memset(&mvdata, 0, sizeof(mvdata));
|
|
+ mvdata.buffer = hdr;
|
|
+ mvdata.size = size;
|
|
+
|
|
+ if (mvdata.sig_index <= 0) {
|
|
+ /* Deal with an unsigned module */
|
|
+ if (modsign_signedonly) {
|
|
+ pr_err("An attempt to load unsigned module was rejected\n");
|
|
+ return -EKEYREJECTED;
|
|
+ } else {
|
|
+ return 0;
|
|
+ }
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ ret = 0;
|
|
+
|
|
+out:
|
|
+ switch (ret) {
|
|
+ case 0: /* Good signature */
|
|
+ *_gpgsig_ok = true;
|
|
+ break;
|
|
+ case -ELIBBAD:
|
|
+ pr_err("Module format error encountered\n");
|
|
+ break;
|
|
+ case -EBADMSG:
|
|
+ pr_err("Module signature error encountered\n");
|
|
+ break;
|
|
+ case -EKEYREJECTED: /* Signature mismatch or number format error */
|
|
+ pr_err("Module signature verification failed\n");
|
|
+ break;
|
|
+ case -ENOKEY: /* Signed, but we don't have the public key */
|
|
+ pr_err("Module signed with unknown public key\n");
|
|
+ break;
|
|
+ default: /* Other error (probably ENOMEM) */
|
|
+ break;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int __init sign_setup(char *str)
|
|
+{
|
|
+#ifndef CONFIG_MODULE_SIG_FORCE
|
|
+ modsign_signedonly = true;
|
|
+#endif
|
|
+ return 0;
|
|
+}
|
|
+__setup("enforcemodulesig", sign_setup);
|
|
diff --git a/kernel/module-verify.h b/kernel/module-verify.h
|
|
new file mode 100644
|
|
index 0000000..c640634
|
|
--- /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 <linux/jump_label.h>
|
|
#include <linux/pfn.h>
|
|
#include <linux/bsearch.h>
|
|
+#include "module-verify.h"
|
|
|
|
#define CREATE_TRACE_POINTS
|
|
#include <trace/events/module.h>
|
|
@@ -2382,7 +2383,8 @@ static inline void kmemleak_load_module(const struct module *mod,
|
|
/* Sets info->hdr and info->len. */
|
|
static int copy_and_check(struct load_info *info,
|
|
const void __user *umod, unsigned long len,
|
|
- const char __user *uargs)
|
|
+ const char __user *uargs,
|
|
+ bool *_gpgsig_ok)
|
|
{
|
|
int err;
|
|
Elf_Ehdr *hdr;
|
|
@@ -2415,6 +2417,12 @@ static int copy_and_check(struct load_info *info,
|
|
goto free_hdr;
|
|
}
|
|
|
|
+ /* Verify the module's contents */
|
|
+ *_gpgsig_ok = false;
|
|
+ err = module_verify(hdr, len, _gpgsig_ok);
|
|
+ if (err < 0)
|
|
+ goto free_hdr;
|
|
+
|
|
info->hdr = hdr;
|
|
info->len = len;
|
|
return 0;
|
|
@@ -2757,7 +2765,8 @@ int __weak module_frob_arch_sections(Elf_Ehdr *hdr,
|
|
return 0;
|
|
}
|
|
|
|
-static struct module *layout_and_allocate(struct load_info *info)
|
|
+static struct module *layout_and_allocate(struct load_info *info,
|
|
+ bool gpgsig_ok)
|
|
{
|
|
/* Module within temporary copy. */
|
|
struct module *mod;
|
|
@@ -2767,6 +2776,7 @@ static struct module *layout_and_allocate(struct load_info *info)
|
|
mod = setup_load_info(info);
|
|
if (IS_ERR(mod))
|
|
return mod;
|
|
+ mod->gpgsig_ok = gpgsig_ok;
|
|
|
|
err = check_modinfo(mod, info);
|
|
if (err)
|
|
@@ -2850,17 +2860,18 @@ static struct module *load_module(void __user *umod,
|
|
struct load_info info = { NULL, };
|
|
struct module *mod;
|
|
long err;
|
|
+ bool gpgsig_ok;
|
|
|
|
pr_debug("load_module: umod=%p, len=%lu, uargs=%p\n",
|
|
umod, len, uargs);
|
|
|
|
/* Copy in the blobs from userspace, check they are vaguely sane. */
|
|
- err = copy_and_check(&info, umod, len, uargs);
|
|
+ err = copy_and_check(&info, umod, len, uargs, &gpgsig_ok);
|
|
if (err)
|
|
return ERR_PTR(err);
|
|
|
|
/* Figure out module layout, and allocate all the memory. */
|
|
- mod = layout_and_allocate(&info);
|
|
+ mod = layout_and_allocate(&info, gpgsig_ok);
|
|
if (IS_ERR(mod)) {
|
|
err = PTR_ERR(mod);
|
|
goto free_copy;
|
|
@@ -3497,8 +3508,13 @@ void print_modules(void)
|
|
printk(KERN_DEFAULT "Modules linked in:");
|
|
/* Most callers should already have preempt disabled, but make sure */
|
|
preempt_disable();
|
|
- list_for_each_entry_rcu(mod, &modules, list)
|
|
+ list_for_each_entry_rcu(mod, &modules, list) {
|
|
printk(" %s%s", mod->name, module_flags(mod, buf));
|
|
+#ifdef CONFIG_MODULE_SIG
|
|
+ if (!mod->gpgsig_ok)
|
|
+ printk("(U)");
|
|
+#endif
|
|
+ }
|
|
preempt_enable();
|
|
if (last_unloaded_module[0])
|
|
printk(" [last unloaded: %s]", last_unloaded_module);
|
|
--
|
|
1.7.11.2
|
|
|
|
|
|
From 62c90369e58486688303c4803e39d7df44a932f9 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
---
|
|
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 <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
---
|
|
kernel/Makefile | 25 ++++++++-------
|
|
kernel/modsign-pubkey.c | 75 +++++++++++++++++++++++++++++++++++++++++++++
|
|
kernel/module-verify-defs.h | 4 +++
|
|
kernel/module-verify.c | 2 --
|
|
4 files changed, 93 insertions(+), 13 deletions(-)
|
|
create mode 100644 kernel/modsign-pubkey.c
|
|
|
|
diff --git a/kernel/Makefile b/kernel/Makefile
|
|
index 28cd248..1d20704 100644
|
|
--- 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 <linux/kernel.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/cred.h>
|
|
+#include <linux/err.h>
|
|
+#include <keys/crypto-type.h>
|
|
+#include "module-verify-defs.h"
|
|
+
|
|
+struct key *modsign_keyring;
|
|
+
|
|
+extern __initdata const u8 modsign_public_keys[];
|
|
+extern __initdata const u8 modsign_public_keys_end[];
|
|
+asm(".section .init.data,\"aw\"\n"
|
|
+ "modsign_public_keys:\n"
|
|
+ ".incbin \"modsign.pub\"\n"
|
|
+ "modsign_public_keys_end:"
|
|
+ );
|
|
+
|
|
+/*
|
|
+ * We need to make sure ccache doesn't cache the .o file as it doesn't notice
|
|
+ * if modsign.pub changes.
|
|
+ */
|
|
+static __initdata const char annoy_ccache[] = __TIME__ "foo";
|
|
+
|
|
+/*
|
|
+ * Load the compiled-in keys
|
|
+ */
|
|
+static __init int module_verify_init(void)
|
|
+{
|
|
+ pr_notice("Initialise module verification\n");
|
|
+
|
|
+ modsign_keyring = key_alloc(&key_type_keyring, ".module_sign",
|
|
+ 0, 0, current_cred(),
|
|
+ (KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
|
+ KEY_USR_VIEW | KEY_USR_READ,
|
|
+ KEY_ALLOC_NOT_IN_QUOTA);
|
|
+ if (IS_ERR(modsign_keyring))
|
|
+ panic("Can't allocate module signing keyring\n");
|
|
+
|
|
+ if (key_instantiate_and_link(modsign_keyring, NULL, 0, NULL, NULL) < 0)
|
|
+ panic("Can't instantiate module signing keyring\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Must be initialised before we try and load the keys into the keyring.
|
|
+ */
|
|
+device_initcall(module_verify_init);
|
|
+
|
|
+/*
|
|
+ * Load the compiled-in keys
|
|
+ */
|
|
+static __init int modsign_pubkey_init(void)
|
|
+{
|
|
+ pr_notice("Load module verification keys\n");
|
|
+
|
|
+ if (preload_pgp_keys(modsign_public_keys,
|
|
+ modsign_public_keys_end - modsign_public_keys,
|
|
+ modsign_keyring, "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 <linux/module.h>
|
|
+
|
|
+extern struct key *modsign_keyring;
|
|
+
|
|
/*
|
|
* Internal state
|
|
*/
|
|
diff --git a/kernel/module-verify.c b/kernel/module-verify.c
|
|
index 4bf857e..05473e6 100644
|
|
--- a/kernel/module-verify.c
|
|
+++ b/kernel/module-verify.c
|
|
@@ -28,8 +28,6 @@
|
|
#include <linux/err.h>
|
|
#include <linux/elf.h>
|
|
#include <linux/elfnote.h>
|
|
-#include <linux/sched.h>
|
|
-#include <linux/cred.h>
|
|
#include <linux/modsign.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <keys/crypto-type.h>
|
|
--
|
|
1.7.11.2
|
|
|
|
|
|
From 9b94f77eea94d028df6a041e6772f9f142eb89e7 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
---
|
|
kernel/module-verify.c | 230 +++++++++++++++++++++++++++++++++++++++++++++++++
|
|
1 file changed, 230 insertions(+)
|
|
|
|
diff --git a/kernel/module-verify.c b/kernel/module-verify.c
|
|
index 05473e6..2161d11 100644
|
|
--- 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 <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
---
|
|
kernel/module-verify.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
1 file changed, 80 insertions(+)
|
|
|
|
diff --git a/kernel/module-verify.c b/kernel/module-verify.c
|
|
index 2161d11..646b104 100644
|
|
--- 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 <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
---
|
|
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 <dhowells@redhat.com>
|
|
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 <dhowells@redhat.com>
|
|
---
|
|
kernel/module-verify.c | 6 +++---
|
|
1 file changed, 3 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/kernel/module-verify.c b/kernel/module-verify.c
|
|
index 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 <dhowells@redhat.com>
|
|
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 <stephan.mueller@atsec.com>
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
kernel/module-verify.c | 5 +++++
|
|
1 file changed, 5 insertions(+)
|
|
|
|
diff --git a/kernel/module-verify.c b/kernel/module-verify.c
|
|
index bfd1286..b9c3955 100644
|
|
--- a/kernel/module-verify.c
|
|
+++ b/kernel/module-verify.c
|
|
@@ -30,6 +30,7 @@
|
|
#include <linux/elfnote.h>
|
|
#include <linux/modsign.h>
|
|
#include <linux/moduleparam.h>
|
|
+#include <linux/fips.h>
|
|
#include <keys/crypto-type.h>
|
|
#include "module-verify.h"
|
|
#include "module-verify-defs.h"
|
|
@@ -716,6 +717,10 @@ int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok)
|
|
kfree(mvdata.canonlist);
|
|
|
|
out:
|
|
+ if (ret < 0 && fips_enabled)
|
|
+ panic("Module verification failed with error %d in FIPS mode\n",
|
|
+ ret);
|
|
+
|
|
switch (ret) {
|
|
case 0: /* Good signature */
|
|
*_gpgsig_ok = true;
|
|
--
|
|
1.7.11.2
|
|
|
|
|
|
From dd6e65be6a8f225018259b16161decc26c09c300 Mon Sep 17 00:00:00 2001
|
|
From: Josh Boyer <jwboyer@redhat.com>
|
|
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 <jwboyer@redhat.com>
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
kernel/module-verify.c | 7 +++++++
|
|
1 file changed, 7 insertions(+)
|
|
|
|
diff --git a/kernel/module-verify.c b/kernel/module-verify.c
|
|
index 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 <pjones@redhat.com>
|
|
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 <pjones@redhat.com>
|
|
Acked-by: Josh Boyer <jwboyer@redhat.com>
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
Documentation/module-signing.txt | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/Documentation/module-signing.txt b/Documentation/module-signing.txt
|
|
index d75d473..8c4bef9 100644
|
|
--- 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
|
|
|