All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/29] Crypto keys and module signing [ver #4]
@ 2012-05-10 23:39 David Howells
  2012-05-10 23:39 ` [PATCH 01/29] MPILIB: Export some more symbols " David Howells
                   ` (32 more replies)
  0 siblings, 33 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:39 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings


Hi Rusty,

Here's my latest take on my module signing patch set.  I've retained my
strip-proof[*] signature-in-module concept, but I've shrunk the module
verification code by nearly half.  Its .text segment now stands at just over 2K
in size for an x86_64 kernel.

There were a lot of redundant tests, either redundant due to other tests, or
redundant due to the data being tested also being added to the digest before
being used.  Also rearranging things helped - and gcc's optimiser doesn't
always make good decisions, particularly when deciding whether to inline large
functions:-/.  I've tried to better comment/describe where I've found a
redundancy.

I think that a further reduction in size could probably be achieved by
combining the ELF checks with the layout code in kernel/module.c - but then it
is mandatory to perform the checks.

Note there is a patch in there (look for "Guard check in module loader against
integer overflow") that is a bugfix to kernel/module.c.

Can you please consider applying these patches?

David

[*] Not proof against strip or strip -s which totally break the module by
    removing the symbol table entirely, but proof against strip -g, strip -x
    and eu-strip.

---

Here are a set of patches that create a framework for using cryptographic keys
within the kernel.  The patches can also be found at:

	http://git.kernel.org/?p=linux/kernel/git/dhowells/linux-modsign.git;a=shortlog;h=refs/heads/modsign

The basic crypto key has no requirements as to how the key is implemented; it's
an anchor for any relevant data.  The crypto key uses registerable data parsers
that are used to extract information from key blobs and signature blobs and
from that construct keys and determine signature verification contexts, thus
connecting everything together.

I have provided an asymmetric public-key subtype with one public-key algorithm
(RSA).  This only provides signature verification facilities at this time.  The
public-key subtype defines a structure for storing key data that is useful to
this algorithm and to DSA, should it be added.

I have provided a PGP parser that can take PGP key blobs and PGP signatures,
set up public-key subtype keys and orchestrate signature verification using the
public key algorithms.

It would be possible to merely refer to keys held in a hardware keystore (such
as a TPM) and have the parser and subtype offload the actual work to that
keystore to be done in hardware.

With kernel module signing enabled, and a pair of keys (one RSA, one DSA)
compiled into the kernel, root can see these keys and the keyring that holds
them in /proc/keys:

195fa736 I-----     1 perm 3f010000     0     0 crypto    modsign.1: DSA 5acc2142 []
335ab517 I-----     1 perm 1f030000     0     0 keyring   .module_sign: 2/4
38d7d169 I-----     1 perm 3f010000     0     0 crypto    modsign.0: RSA 57532ca5 []

Module signing combinations that have been tested: RSA with all the SHA
algorithms.  I have a patch (not included here) that does DSA too, and that has
been tested with SHA-1.

The patches break down into a number of areas:

 (1) MPI library alterations.

 (2) Some small key-handling core code changes to make things easier.

 (3) Crypto key type: key handling and verification access functions.

 (4) Public key subtype and RSA algorithm.

 (5) PGP definitions and parsing utilities library.

 (6) PGP data parser for key blobs and signature blobs.

 (7) PGP key preloader for module signing to use.

 (8) A pair of macros for providing ELF note name wrappers.

 (9) A fix for the core module code to handle an integer overflow in a check.

(10) Module ELF verification and module signature verification.

The complete crypto type documentation can be found within the GIT tree here:

	http://git.kernel.org/?p=linux/kernel/git/dhowells/linux-modsign.git;a=blob;f=Documentation/security/keys-crypto.txt;h=ba2ab554fafc6db194448fd6791ba26874d60e53;hb=958049a9def253735019a5acf19b4c2aeec9f01c
	
and the module signature verification documentation can be found here:

	http://git.kernel.org/?p=linux/kernel/git/dhowells/linux-modsign.git;a=blob;f=Documentation/module-signing.txt;h=d75d47369406f7a6b78aa95926a310fe913e4b8b;hb=958049a9def253735019a5acf19b4c2aeec9f01c

---

Changes made 10/05/2012:

 (*) Overhauled the ELF checking code and module signing code.

     - Moved into one file.
     - Removed a lot of redundant ELF checks, relying a lot on the signature to
       catch stuff.
     - Rearranged the ELF checker function.
     - Commented thoroughly and documented things better in the commit messages.
     - Made it possible to exclude REL or RELA relocation handling.
     - Rearranged the modsign patch subset to be more logical.
     - Massively reduced the code size.

 (*) Applied a patch to handle short signatures.

 (*) Fixed a potential overflow in a check in the core module code.

Changes made 07/12/2011:

 (*) Dropped the DSA algorithm.

Changes made 02/12/2011:

 (*) Completely overhauled the architecture.

     - Introduced data parsers.
     - Reduced subtype to cryptographic data carrier.
     - Extracted out the common PGP bits of DSA and RSA algorithms.
     - Defined an asymmetric public-key subtype.
     - Reduced DSA and RSA algorithms to minimum.
     - Rolled verification initiation and key selection together into one.
     - Moved verification add_data/finish/cancel op pointers into verification
       context.

Changes made 29/11/2011:

 (*) Added RSA signature verification.

 (*) Stopped signature verification crashing on unsupported hash algorithm.

 (*) Fixed ENOMEM handling bug in MPI.

 (*) Worked around ccache problems with compilation of PGP public keyring into
     kernel (ccache hashes the preprocessor output, but the assembler includes
     the binary data, so ccache doesn't see that it changed).

 (*) Added a choice in kernel config for hash algorithm to use; forced the
     appropriate crypto module to be built directly into the kernel.

 (*) Cleaned out some debugging code.

 (*) Updated documentation.

---
David Howells (29):
      MODSIGN: Suppress some redundant ELF checks
      MODSIGN: Automatically generate module signing keys if missing
      MODSIGN: Create digest of module content and check signature
      MODSIGN: Produce a filtered and canonicalised section list
      MODSIGN: Check the ELF container
      MODSIGN: Provide module signing public keys to the kernel
      MODSIGN: Module signature verification stub
      MODSIGN: Sign modules during the build process
      MODSIGN: Provide Documentation and Kconfig options
      MODSIGN: Provide gitignore and make clean rules for extra files
      MODSIGN: Add indications of module ELF types
      Guard check in module loader against integer overflow
      Provide macros for forming the name of an ELF note and its section
      KEYS: Provide a function to load keys from a PGP keyring blob
      KEYS: PGP format signature parser
      KEYS: PGP-based public key signature verification
      KEYS: PGP data parser
      PGPLIB: Signature parser
      PGPLIB: Basic packet parser
      PGPLIB: PGP definitions (RFC 4880)
      Fix signature verification for shorter signatures
      KEYS: RSA signature verification algorithm
      KEYS: Asymmetric public-key algorithm crypto key subtype
      KEYS: Add signature verification facility
      KEYS: Create a key type that can be used for general cryptographic operations
      KEYS: Reorganise keys Makefile
      KEYS: Announce key type (un)registration
      KEYS: Move the key config into security/keys/Kconfig
      MPILIB: Export some more symbols


 .gitignore                             |   12 
 Documentation/module-signing.txt       |  194 +++++++
 Documentation/security/keys-crypto.txt |  302 +++++++++++
 Makefile                               |    1 
 arch/alpha/include/asm/module.h        |    3 
 arch/arm/include/asm/module.h          |    5 
 arch/cris/include/asm/module.h         |    5 
 arch/h8300/include/asm/module.h        |    5 
 arch/ia64/include/asm/module.h         |    5 
 arch/m32r/include/asm/module.h         |    5 
 arch/m68k/include/asm/module.h         |    5 
 arch/mips/include/asm/module.h         |   12 
 arch/parisc/include/asm/module.h       |    8 
 arch/powerpc/include/asm/module.h      |   10 
 arch/s390/include/asm/module.h         |    3 
 arch/x86/include/asm/module.h          |    6 
 include/asm-generic/module.h           |   10 
 include/keys/crypto-subtype.h          |   77 +++
 include/keys/crypto-type.h             |   37 +
 include/linux/elfnote.h                |    4 
 include/linux/modsign.h                |   27 +
 include/linux/module.h                 |    3 
 include/linux/pgp.h                    |  255 +++++++++
 init/Kconfig                           |   53 ++
 kernel/Makefile                        |   42 +
 kernel/modsign-pubkey.c                |   74 +++
 kernel/module-verify-defs.h            |   81 +++
 kernel/module-verify.c                 |  732 ++++++++++++++++++++++++++
 kernel/module-verify.h                 |   19 +
 kernel/module.c                        |   29 +
 lib/mpi/mpi-cmp.c                      |    2 
 lib/mpi/mpi-div.c                      |    1 
 lib/mpi/mpi-inv.c                      |    1 
 lib/mpi/mpi-mpow.c                     |    1 
 lib/mpi/mpi-mul.c                      |    1 
 net/dns_resolver/dns_key.c             |    5 
 scripts/Makefile.modpost               |   87 +++
 scripts/mod/.gitignore                 |    1 
 scripts/mod/Makefile                   |    2 
 scripts/mod/mod-extract.c              |  913 ++++++++++++++++++++++++++++++++
 scripts/mod/modsign-note.sh            |   16 +
 security/Kconfig                       |   68 --
 security/keys/Kconfig                  |   73 +++
 security/keys/Makefile                 |   13 
 security/keys/crypto/Kconfig           |   51 ++
 security/keys/crypto/Makefile          |   17 +
 security/keys/crypto/crypto_keys.h     |   28 +
 security/keys/crypto/crypto_rsa.c      |  290 ++++++++++
 security/keys/crypto/crypto_type.c     |  228 ++++++++
 security/keys/crypto/crypto_verify.c   |  111 ++++
 security/keys/crypto/pgp_key_parser.c  |  344 ++++++++++++
 security/keys/crypto/pgp_library.c     |  531 +++++++++++++++++++
 security/keys/crypto/pgp_parser.h      |   35 +
 security/keys/crypto/pgp_preload.c     |   90 +++
 security/keys/crypto/pgp_pubkey_sig.c  |  323 +++++++++++
 security/keys/crypto/pgp_sig_parser.c  |  104 ++++
 security/keys/crypto/public_key.c      |   55 ++
 security/keys/crypto/public_key.h      |  108 ++++
 security/keys/key.c                    |    3 
 59 files changed, 5441 insertions(+), 85 deletions(-)
 create mode 100644 Documentation/module-signing.txt
 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 include/linux/modsign.h
 create mode 100644 include/linux/pgp.h
 create mode 100644 kernel/modsign-pubkey.c
 create mode 100644 kernel/module-verify-defs.h
 create mode 100644 kernel/module-verify.c
 create mode 100644 kernel/module-verify.h
 create mode 100644 scripts/mod/mod-extract.c
 create mode 100644 scripts/mod/modsign-note.sh
 create mode 100644 security/keys/Kconfig
 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_rsa.c
 create mode 100644 security/keys/crypto/crypto_type.c
 create mode 100644 security/keys/crypto/crypto_verify.c
 create mode 100644 security/keys/crypto/pgp_key_parser.c
 create mode 100644 security/keys/crypto/pgp_library.c
 create mode 100644 security/keys/crypto/pgp_parser.h
 create mode 100644 security/keys/crypto/pgp_preload.c
 create mode 100644 security/keys/crypto/pgp_pubkey_sig.c
 create mode 100644 security/keys/crypto/pgp_sig_parser.c
 create mode 100644 security/keys/crypto/public_key.c
 create mode 100644 security/keys/crypto/public_key.h


^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH 01/29] MPILIB: Export some more symbols [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
@ 2012-05-10 23:39 ` David Howells
  2012-05-10 23:39 ` [PATCH 02/29] KEYS: Move the key config into security/keys/Kconfig " David Howells
                   ` (31 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:39 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

Export some more symbols for use by the DSA key subtype.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 lib/mpi/mpi-cmp.c  |    2 ++
 lib/mpi/mpi-div.c  |    1 +
 lib/mpi/mpi-inv.c  |    1 +
 lib/mpi/mpi-mpow.c |    1 +
 lib/mpi/mpi-mul.c  |    1 +
 5 files changed, 6 insertions(+), 0 deletions(-)


diff --git a/lib/mpi/mpi-cmp.c b/lib/mpi/mpi-cmp.c
index 914bc42..1871e7b 100644
--- a/lib/mpi/mpi-cmp.c
+++ b/lib/mpi/mpi-cmp.c
@@ -39,6 +39,7 @@ int mpi_cmp_ui(MPI u, unsigned long v)
 	else
 		return -1;
 }
+EXPORT_SYMBOL_GPL(mpi_cmp_ui);
 
 int mpi_cmp(MPI u, MPI v)
 {
@@ -66,3 +67,4 @@ int mpi_cmp(MPI u, MPI v)
 		return 1;
 	return -1;
 }
+EXPORT_SYMBOL_GPL(mpi_cmp);
diff --git a/lib/mpi/mpi-div.c b/lib/mpi/mpi-div.c
index f68cbbb..fed3405 100644
--- a/lib/mpi/mpi-div.c
+++ b/lib/mpi/mpi-div.c
@@ -59,6 +59,7 @@ nomem:
 		mpi_free(temp_divisor);
 	return rc;
 }
+EXPORT_SYMBOL_GPL(mpi_fdiv_r);
 
 /****************
  * Division rounding the quotient towards -infinity.
diff --git a/lib/mpi/mpi-inv.c b/lib/mpi/mpi-inv.c
index 0951f98..bfc5ca1 100644
--- a/lib/mpi/mpi-inv.c
+++ b/lib/mpi/mpi-inv.c
@@ -185,3 +185,4 @@ cleanup:
 	mpi_free(v);
 	return rc;
 }
+EXPORT_SYMBOL_GPL(mpi_invm);
diff --git a/lib/mpi/mpi-mpow.c b/lib/mpi/mpi-mpow.c
index 7328d0d..b8b22e5 100644
--- a/lib/mpi/mpi-mpow.c
+++ b/lib/mpi/mpi-mpow.c
@@ -132,3 +132,4 @@ nomem:
 err_out:
 	return rc;
 }
+EXPORT_SYMBOL_GPL(mpi_mulpowm);
diff --git a/lib/mpi/mpi-mul.c b/lib/mpi/mpi-mul.c
index 1f3219e..3d514b9 100644
--- a/lib/mpi/mpi-mul.c
+++ b/lib/mpi/mpi-mul.c
@@ -192,3 +192,4 @@ int mpi_mulm(MPI w, MPI u, MPI v, MPI m)
 		return -ENOMEM;
 	return mpi_fdiv_r(w, w, m);
 }
+EXPORT_SYMBOL_GPL(mpi_mulm);


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 02/29] KEYS: Move the key config into security/keys/Kconfig [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
  2012-05-10 23:39 ` [PATCH 01/29] MPILIB: Export some more symbols " David Howells
@ 2012-05-10 23:39 ` David Howells
  2012-05-10 23:39 ` [PATCH 03/29] KEYS: Announce key type (un)registration " David Howells
                   ` (30 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:39 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

Move the key config into security/keys/Kconfig as there are going to be a lot
of key-related options.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 security/Kconfig      |   68 +----------------------------------------------
 security/keys/Kconfig |   71 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 72 insertions(+), 67 deletions(-)
 create mode 100644 security/keys/Kconfig


diff --git a/security/Kconfig b/security/Kconfig
index ccc61f8..e9c6ac7 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -4,73 +4,7 @@
 
 menu "Security options"
 
-config KEYS
-	bool "Enable access key retention support"
-	help
-	  This option provides support for retaining authentication tokens and
-	  access keys in the kernel.
-
-	  It also includes provision of methods by which such keys might be
-	  associated with a process so that network filesystems, encryption
-	  support and the like can find them.
-
-	  Furthermore, a special type of key is available that acts as keyring:
-	  a searchable sequence of keys. Each process is equipped with access
-	  to five standard keyrings: UID-specific, GID-specific, session,
-	  process and thread.
-
-	  If you are unsure as to whether this is required, answer N.
-
-config TRUSTED_KEYS
-	tristate "TRUSTED KEYS"
-	depends on KEYS && TCG_TPM
-	select CRYPTO
-	select CRYPTO_HMAC
-	select CRYPTO_SHA1
-	help
-	  This option provides support for creating, sealing, and unsealing
-	  keys in the kernel. Trusted keys are random number symmetric keys,
-	  generated and RSA-sealed by the TPM. The TPM only unseals the keys,
-	  if the boot PCRs and other criteria match.  Userspace will only ever
-	  see encrypted blobs.
-
-	  If you are unsure as to whether this is required, answer N.
-
-config ENCRYPTED_KEYS
-	tristate "ENCRYPTED KEYS"
-	depends on KEYS
-	select CRYPTO
-	select CRYPTO_HMAC
-	select CRYPTO_AES
-	select CRYPTO_CBC
-	select CRYPTO_SHA256
-	select CRYPTO_RNG
-	help
-	  This option provides support for create/encrypting/decrypting keys
-	  in the kernel.  Encrypted keys are kernel generated random numbers,
-	  which are encrypted/decrypted with a 'master' symmetric key. The
-	  'master' key can be either a trusted-key or user-key type.
-	  Userspace only ever sees/stores encrypted blobs.
-
-	  If you are unsure as to whether this is required, answer N.
-
-config KEYS_DEBUG_PROC_KEYS
-	bool "Enable the /proc/keys file by which keys may be viewed"
-	depends on KEYS
-	help
-	  This option turns on support for the /proc/keys file - through which
-	  can be listed all the keys on the system that are viewable by the
-	  reading process.
-
-	  The only keys included in the list are those that grant View
-	  permission to the reading process whether or not it possesses them.
-	  Note that LSM security checks are still performed, and may further
-	  filter out keys that the current process is not authorised to view.
-
-	  Only key attributes are listed here; key payloads are not included in
-	  the resulting table.
-
-	  If you are unsure as to whether this is required, answer N.
+source security/keys/Kconfig
 
 config SECURITY_DMESG_RESTRICT
 	bool "Restrict unprivileged access to the kernel syslog"
diff --git a/security/keys/Kconfig b/security/keys/Kconfig
new file mode 100644
index 0000000..a90d6d3
--- /dev/null
+++ b/security/keys/Kconfig
@@ -0,0 +1,71 @@
+#
+# Key management configuration
+#
+
+config KEYS
+	bool "Enable access key retention support"
+	help
+	  This option provides support for retaining authentication tokens and
+	  access keys in the kernel.
+
+	  It also includes provision of methods by which such keys might be
+	  associated with a process so that network filesystems, encryption
+	  support and the like can find them.
+
+	  Furthermore, a special type of key is available that acts as keyring:
+	  a searchable sequence of keys. Each process is equipped with access
+	  to five standard keyrings: UID-specific, GID-specific, session,
+	  process and thread.
+
+	  If you are unsure as to whether this is required, answer N.
+
+config TRUSTED_KEYS
+	tristate "TRUSTED KEYS"
+	depends on KEYS && TCG_TPM
+	select CRYPTO
+	select CRYPTO_HMAC
+	select CRYPTO_SHA1
+	help
+	  This option provides support for creating, sealing, and unsealing
+	  keys in the kernel. Trusted keys are random number symmetric keys,
+	  generated and RSA-sealed by the TPM. The TPM only unseals the keys,
+	  if the boot PCRs and other criteria match.  Userspace will only ever
+	  see encrypted blobs.
+
+	  If you are unsure as to whether this is required, answer N.
+
+config ENCRYPTED_KEYS
+	tristate "ENCRYPTED KEYS"
+	depends on KEYS
+	select CRYPTO
+	select CRYPTO_HMAC
+	select CRYPTO_AES
+	select CRYPTO_CBC
+	select CRYPTO_SHA256
+	select CRYPTO_RNG
+	help
+	  This option provides support for create/encrypting/decrypting keys
+	  in the kernel.  Encrypted keys are kernel generated random numbers,
+	  which are encrypted/decrypted with a 'master' symmetric key. The
+	  'master' key can be either a trusted-key or user-key type.
+	  Userspace only ever sees/stores encrypted blobs.
+
+	  If you are unsure as to whether this is required, answer N.
+
+config KEYS_DEBUG_PROC_KEYS
+	bool "Enable the /proc/keys file by which keys may be viewed"
+	depends on KEYS
+	help
+	  This option turns on support for the /proc/keys file - through which
+	  can be listed all the keys on the system that are viewable by the
+	  reading process.
+
+	  The only keys included in the list are those that grant View
+	  permission to the reading process whether or not it possesses them.
+	  Note that LSM security checks are still performed, and may further
+	  filter out keys that the current process is not authorised to view.
+
+	  Only key attributes are listed here; key payloads are not included in
+	  the resulting table.
+
+	  If you are unsure as to whether this is required, answer N.


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 03/29] KEYS: Announce key type (un)registration [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
  2012-05-10 23:39 ` [PATCH 01/29] MPILIB: Export some more symbols " David Howells
  2012-05-10 23:39 ` [PATCH 02/29] KEYS: Move the key config into security/keys/Kconfig " David Howells
@ 2012-05-10 23:39 ` David Howells
  2012-05-10 23:39 ` [PATCH 04/29] KEYS: Reorganise keys Makefile " David Howells
                   ` (29 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:39 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

Announce the (un)registration of a key type in the core key code rather than
in the callers.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 net/dns_resolver/dns_key.c |    5 -----
 security/keys/key.c        |    3 +++
 2 files changed, 3 insertions(+), 5 deletions(-)


diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c
index c73bba3..14b2c3d 100644
--- a/net/dns_resolver/dns_key.c
+++ b/net/dns_resolver/dns_key.c
@@ -249,9 +249,6 @@ static int __init init_dns_resolver(void)
 	struct key *keyring;
 	int ret;
 
-	printk(KERN_NOTICE "Registering the %s key type\n",
-	       key_type_dns_resolver.name);
-
 	/* create an override credential set with a special thread keyring in
 	 * which DNS requests are cached
 	 *
@@ -301,8 +298,6 @@ static void __exit exit_dns_resolver(void)
 	key_revoke(dns_resolver_cache->thread_keyring);
 	unregister_key_type(&key_type_dns_resolver);
 	put_cred(dns_resolver_cache);
-	printk(KERN_NOTICE "Unregistered %s key type\n",
-	       key_type_dns_resolver.name);
 }
 
 module_init(init_dns_resolver)
diff --git a/security/keys/key.c b/security/keys/key.c
index 06783cf..dc62894 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -980,6 +980,8 @@ int register_key_type(struct key_type *ktype)
 
 	/* store the type */
 	list_add(&ktype->link, &key_types_list);
+
+	pr_notice("Key type %s registered\n", ktype->name);
 	ret = 0;
 
 out:
@@ -1002,6 +1004,7 @@ void unregister_key_type(struct key_type *ktype)
 	list_del_init(&ktype->link);
 	downgrade_write(&key_types_sem);
 	key_gc_keytype(ktype);
+	pr_notice("Key type %s unregistered\n", ktype->name);
 	up_read(&key_types_sem);
 }
 EXPORT_SYMBOL(unregister_key_type);


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 04/29] KEYS: Reorganise keys Makefile [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (2 preceding siblings ...)
  2012-05-10 23:39 ` [PATCH 03/29] KEYS: Announce key type (un)registration " David Howells
@ 2012-05-10 23:39 ` David Howells
  2012-05-10 23:39 ` [PATCH 05/29] KEYS: Create a key type that can be used for general cryptographic operations " David Howells
                   ` (28 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:39 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

Reorganise the keys directory Makefile to put all the core bits together and
the type-specific bits after.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 security/keys/Makefile |   12 +++++++++---
 1 files changed, 9 insertions(+), 3 deletions(-)


diff --git a/security/keys/Makefile b/security/keys/Makefile
index a56f1ff..504aaa0 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -2,6 +2,9 @@
 # Makefile for key management
 #
 
+#
+# Core
+#
 obj-y := \
 	gc.o \
 	key.o \
@@ -12,9 +15,12 @@ obj-y := \
 	request_key.o \
 	request_key_auth.o \
 	user_defined.o
-
-obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
-obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
 obj-$(CONFIG_KEYS_COMPAT) += compat.o
 obj-$(CONFIG_PROC_FS) += proc.o
 obj-$(CONFIG_SYSCTL) += sysctl.o
+
+#
+# Key types
+#
+obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
+obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 05/29] KEYS: Create a key type that can be used for general cryptographic operations [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (3 preceding siblings ...)
  2012-05-10 23:39 ` [PATCH 04/29] KEYS: Reorganise keys Makefile " David Howells
@ 2012-05-10 23:39 ` David Howells
  2012-05-10 23:40 ` [PATCH 06/29] KEYS: Add signature verification facility " David Howells
                   ` (27 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:39 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

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(+), 0 deletions(-)
 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);


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 06/29] KEYS: Add signature verification facility [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (4 preceding siblings ...)
  2012-05-10 23:39 ` [PATCH 05/29] KEYS: Create a key type that can be used for general cryptographic operations " David Howells
@ 2012-05-10 23:40 ` David Howells
  2012-05-10 23:40 ` [PATCH 07/29] KEYS: Asymmetric public-key algorithm crypto key subtype " David Howells
                   ` (26 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:40 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

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   |  111 ++++++++++++++++++++++++++++++++
 5 files changed, 243 insertions(+), 1 deletions(-)
 create mode 100644 security/keys/crypto/crypto_verify.c


diff --git a/Documentation/security/keys-crypto.txt b/Documentation/security/keys-crypto.txt
index 97dee80..a964717 100644
--- a/Documentation/security/keys-crypto.txt
+++ b/Documentation/security/keys-crypto.txt
@@ -7,6 +7,7 @@ Contents:
   - Overview.
   - Key identification.
   - Accessing crypto keys.
+    - Signature verification.
   - Implementing crypto parsers.
   - Implementing crypto subtypes.
 
@@ -89,6 +90,65 @@ This gives access to the key type:
 	struct key_type key_type_crypto;
 
 
+SIGNATURE VERIFICATION
+----------------------
+
+The four operations that can perform cryptographic signature verification,
+using one of a set of keys to provide the public key:
+
+ (1) Begin verification procedure.
+
+	struct crypto_key_verify_context *
+	verify_sig_begin(struct key *keyring, const void *sig, size_t siglen);
+
+     This function sets up a verification context from the information in the
+     signature and looks for a suitable key in the keyring.  The signature blob
+     must be presented again at the end of the procedure.  The keys will be
+     checked against parameters in the signature, and if the matching one is
+     not found then -ENOKEY will be returned.
+
+     The hashing algorithm, if such a thing applies, will be determined from
+     information in the signature and the appropriate crypto module will be
+     used.  -ENOPKG will be returned if the hash algorithm is unavailable.
+
+     The return value is an opaque pointer to be passed to the other functions,
+     or a negative error code.
+
+ (2) Indicate data to be verified.
+
+	int verify_sig_add_data(struct crypto_key_verify_context *ctx,
+				const void *data, size_t datalen);
+
+     This function is used to shovel data to the verification procedure so that
+     it can load it into the hash, pass it to hardware or whatever is
+     appropriate for the algorithm being employed.
+
+     The data is not canonicalised for the document type specified in the
+     signature.  The caller must do that.
+
+     It will return 0 if successful and a negative error code if not.
+
+ (3) Complete the verification process.
+
+	int verify_sig_end(struct crypto_key_verify_context *ctx,
+			   const void *sig, size_t siglen);
+
+     This function performs the actual signature verification step and cleans
+     up the resources allocated at the beginning.  The signature must be
+     presented again as some of the data therein may need to be added to the
+     internal hash.
+
+     It will return -EKEYREJECTED if the signature didn't match, 0 if
+     successful and may return other errors as appropriate.
+
+ (4) Cancel the verification process.
+
+	void verify_sig_cancel(struct crypto_key_verify_context *ctx);
+
+     This function cleans up the resources allocated at the beginning.  This is
+     not necessary if verify_sig_end() was called.
+
+
 ===========================
 IMPLEMENTING CRYPTO PARSERS
 ===========================
@@ -96,6 +156,7 @@ IMPLEMENTING CRYPTO PARSERS
 The crypto key type keeps a list of registered data parsers.  An example of
 such a parser is one that parses OpenPGP packet formatted data [RFC 4880].
 
+
 During key instantiation each parser in the list is tried until one doesn't
 return -EBADMSG.
 
@@ -107,6 +168,8 @@ The parser definition structure looks like the following:
 
 		int (*instantiate)(struct key *key,
 				   const void *data, size_t datalen);
+		struct crypto_key_verify_context *(*verify_sig_begin)(
+			struct key *keyring, const u8 *sig, size_t siglen);
 	};
 
 The owner and name fields should be set to the owning module and the name of
@@ -135,6 +198,44 @@ but it is expected that at least one will be defined.
      algorithm such as RSA and DSA this will likely be a printable hex version
      of the key's fingerprint.
 
+ (2) verify_sig_begin().
+
+     This is similar in concept to the instantiate() function, except that it
+     is given a signature blob to parse rather than a key data blob.
+
+     If the data format is not recognised, -EBADMSG should be returned.  If it
+     is recognised, but the signature verification process cannot for some
+     reason be set up, some other negative error code should be returned.
+     -ENOKEY should be used to indicate that no matching key is available and
+     -ENOPKG should be returned if the hash algorithm or the verification
+     algorithm are unavailable.
+
+     If successful, the parser should allocate a verification context and embed
+     the following struct in it:
+
+	struct crypto_key_verify_context {
+		struct key *key;
+		int (*add_data)(struct crypto_key_verify_context *ctx,
+				const void *data, size_t datalen);
+		int (*end)(struct crypto_key_verify_context *ctx,
+			   const u8 *sig, size_t siglen);
+		void (*cancel)(struct crypto_key_verify_context *ctx);
+	};
+
+     and return a pointer to this to the caller, who will then pass it to the
+     verification operation wrappers described in the "Signature Verification"
+     section.  The three operation pointers here correspond exactly to those
+     wrappers and are all mandatory.  container_of() should be used to retrieve
+     the actual context.
+
+     Note that the crypto key type retains a reference on the parser module for
+     the lifetime of this context, though the operation pointers need not point
+     into this module.
+
+     The parser should also record a pointer to the key selected and take a
+     reference on that key with key_get().
+
+
 Functions are provided to register and unregister parsers:
 
 	int register_crypto_key_parser(struct crypto_key_parser *parser);
diff --git a/include/keys/crypto-subtype.h b/include/keys/crypto-subtype.h
index fa87555..f2b927a 100644
--- a/include/keys/crypto-subtype.h
+++ b/include/keys/crypto-subtype.h
@@ -20,6 +20,20 @@
 extern struct key_type key_type_crypto;
 
 /*
+ * Context base for signature verification methods.  Allocated by the subtype
+ * and presumably embedded in something appropriate.
+ */
+struct crypto_key_verify_context {
+	struct key *key;
+	struct crypto_key_parser *parser;
+	int (*add_data)(struct crypto_key_verify_context *ctx,
+			const void *data, size_t datalen);
+	int (*end)(struct crypto_key_verify_context *ctx,
+		   const u8 *sig, size_t siglen);
+	void (*cancel)(struct crypto_key_verify_context *ctx);
+};
+
+/*
  * Keys of this type declare a subtype that indicates the handlers and
  * capabilities.
  */
@@ -48,6 +62,13 @@ struct crypto_key_parser {
 	 * Return EBADMSG if not recognised.
 	 */
 	int (*instantiate)(struct key *key, const void *data, size_t datalen);
+
+	/* Attempt to recognise a signature blob and find a matching key.
+	 *
+	 * Return EBADMSG if not recognised.
+	 */
+	struct crypto_key_verify_context *(*verify_sig_begin)(
+		struct key *keyring, const u8 *sig, size_t siglen);
 };
 
 extern int register_crypto_key_parser(struct crypto_key_parser *);
diff --git a/include/keys/crypto-type.h b/include/keys/crypto-type.h
index 47c00c7..6b93366 100644
--- a/include/keys/crypto-type.h
+++ b/include/keys/crypto-type.h
@@ -18,6 +18,15 @@
 
 extern struct key_type key_type_crypto;
 
+struct crypto_key_verify_context;
+extern struct crypto_key_verify_context *verify_sig_begin(
+	struct key *key, const void *sig, size_t siglen);
+extern int verify_sig_add_data(struct crypto_key_verify_context *ctx,
+			       const void *data, size_t datalen);
+extern int verify_sig_end(struct crypto_key_verify_context *ctx,
+			  const void *sig, size_t siglen);
+extern void verify_sig_cancel(struct crypto_key_verify_context *ctx);
+
 /*
  * The payload is at the discretion of the subtype.
  */
diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile
index 36db1d5..67001bc 100644
--- a/security/keys/crypto/Makefile
+++ b/security/keys/crypto/Makefile
@@ -4,4 +4,4 @@
 
 obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o
 
-crypto_keys-y := crypto_type.o
+crypto_keys-y := crypto_type.o crypto_verify.o
diff --git a/security/keys/crypto/crypto_verify.c b/security/keys/crypto/crypto_verify.c
new file mode 100644
index 0000000..65f734c
--- /dev/null
+++ b/security/keys/crypto/crypto_verify.c
@@ -0,0 +1,111 @@
+/* Signature verification with a crypto key
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ *
+ * See Documentation/security/keys-crypto.txt
+ */
+
+#include <keys/crypto-subtype.h>
+#include <linux/module.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);


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 07/29] KEYS: Asymmetric public-key algorithm crypto key subtype [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (5 preceding siblings ...)
  2012-05-10 23:40 ` [PATCH 06/29] KEYS: Add signature verification facility " David Howells
@ 2012-05-10 23:40 ` David Howells
  2012-05-10 23:40 ` [PATCH 08/29] KEYS: RSA signature verification algorithm " David Howells
                   ` (25 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:40 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

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 deletions(-)
 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 */


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 08/29] KEYS: RSA signature verification algorithm [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (6 preceding siblings ...)
  2012-05-10 23:40 ` [PATCH 07/29] KEYS: Asymmetric public-key algorithm crypto key subtype " David Howells
@ 2012-05-10 23:40 ` David Howells
  2012-05-10 23:40 ` [PATCH 09/29] Fix signature verification for shorter signatures " David Howells
                   ` (24 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:40 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

Implement the RSA algorithm (PKCS#1 / RFC3447).  At this time, only signature
verification is supported.  This uses the asymmetric public key subtype to hold
its key data.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 security/keys/crypto/Kconfig      |    7 +
 security/keys/crypto/Makefile     |    1 
 security/keys/crypto/crypto_rsa.c |  282 +++++++++++++++++++++++++++++++++++++
 security/keys/crypto/public_key.h |    2 
 4 files changed, 292 insertions(+), 0 deletions(-)
 create mode 100644 security/keys/crypto/crypto_rsa.c


diff --git a/security/keys/crypto/Kconfig b/security/keys/crypto/Kconfig
index 5f2b8ac..4e3777e 100644
--- a/security/keys/crypto/Kconfig
+++ b/security/keys/crypto/Kconfig
@@ -15,3 +15,10 @@ config CRYPTO_KEY_PUBLIC_KEY_SUBTYPE
 	  If signature generation and/or verification are to be used,
 	  appropriate hash algorithms (such as SHA-1) must be available.
 	  ENOPKG will be reported if the requisite algorithm is unavailable.
+
+config CRYPTO_KEY_PKEY_ALGO_RSA
+	tristate "RSA public-key algorithm"
+	depends on CRYPTO_KEY_PUBLIC_KEY_SUBTYPE
+	select MPILIB_EXTRA
+	help
+	  This option enables support for the RSA algorithm (PKCS#1, RFC3447).
diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile
index 6384306..b6b1a5a 100644
--- a/security/keys/crypto/Makefile
+++ b/security/keys/crypto/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o
 crypto_keys-y := crypto_type.o crypto_verify.o
 
 obj-$(CONFIG_CRYPTO_KEY_PUBLIC_KEY_SUBTYPE) += public_key.o
+obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) += crypto_rsa.o
diff --git a/security/keys/crypto/crypto_rsa.c b/security/keys/crypto/crypto_rsa.c
new file mode 100644
index 0000000..beb5181
--- /dev/null
+++ b/security/keys/crypto/crypto_rsa.c
@@ -0,0 +1,282 @@
+/* RSA asymmetric public-key algorithm [RFC3447]
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "RSA: "fmt
+#include <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 const *data;
+	size_t size;
+} RSA_ASN1_templates[PKEY_HASH__LAST] = {
+#define _(X) { RSA_digest_info_##X, sizeof(RSA_digest_info_##X) }
+	[PKEY_HASH_MD5]		= _(MD5),
+	[PKEY_HASH_SHA1]	= _(SHA1),
+	[PKEY_HASH_RIPE_MD_160]	= _(RIPE_MD_160),
+	[PKEY_HASH_SHA256]	= _(SHA256),
+	[PKEY_HASH_SHA384]	= _(SHA384),
+	[PKEY_HASH_SHA512]	= _(SHA512),
+	[PKEY_HASH_SHA224]	= _(SHA224),
+#undef _
+};
+
+/*
+ * RSAVP1() function [RFC3447 sec 5.2.2]
+ */
+static int RSAVP1(const struct public_key *key, MPI s, MPI *_m)
+{
+	MPI m;
+	int ret;
+
+	/* (1) Validate 0 <= s < n */
+	if (mpi_cmp_ui(s, 0) < 0) {
+		kleave(" = -EBADMSG [s < 0]");
+		return -EBADMSG;
+	}
+	if (mpi_cmp(s, key->rsa.n) >= 0) {
+		kleave(" = -EBADMSG [s >= n]");
+		return -EBADMSG;
+	}
+
+	m = mpi_alloc(0);
+	if (!m)
+		return -ENOMEM;
+
+	/* (2) m = s^e mod n */
+	ret = mpi_powm(m, s, key->rsa.e, key->rsa.n);
+	if (ret < 0) {
+		mpi_free(m);
+		return ret;
+	}
+
+	*_m = m;
+	return 0;
+}
+
+/*
+ * Integer to Octet String conversion [RFC3447 sec 4.1]
+ */
+static int RSA_I2OSP(MPI x, size_t xLen, u8 **_X)
+{
+	unsigned X_size, x_size;
+	int X_sign;
+	u8 *X;
+
+	/* Make sure the string is the right length.  The number should begin
+	 * with { 0x00, 0x01, ... } so we have to account for 15 leading zero
+	 * bits not being reported by MPI.
+	 */
+	x_size = mpi_get_nbits(x);
+	pr_devel("size(x)=%u xLen*8=%zu\n", x_size, xLen * 8);
+	if (x_size != xLen * 8 - 15)
+		return -ERANGE;
+
+	X = mpi_get_buffer(x, &X_size, &X_sign);
+	if (!X)
+		return -ENOMEM;
+	if (X_sign < 0) {
+		kfree(X);
+		return -EBADMSG;
+	}
+	if (X_size != xLen - 1) {
+		kfree(X);
+		return -EBADMSG;
+	}
+
+	*_X = X;
+	return 0;
+}
+
+/*
+ * Perform the RSA signature verification.
+ * @H: Value of hash of data and metadata
+ * @EM: The computed signature value
+ * @k: The size of EM (EM[0] is an invalid location but should hold 0x00)
+ * @hash_size: The size of H
+ * @asn1_template: The DigestInfo ASN.1 template
+ * @asn1_size: Size of asm1_template[]
+ */
+static int RSA_verify(const u8 *H, const u8 *EM, size_t k, size_t hash_size,
+		      const u8 *asn1_template, size_t asn1_size)
+{
+	unsigned PS_end, T_offset, i;
+
+	kenter(",,%zu,%zu,%zu", k, hash_size, asn1_size);
+
+	if (k < 2 + 1 + asn1_size + hash_size)
+		return -EBADMSG;
+
+	/* Decode the EMSA-PKCS1-v1_5 */
+	if (EM[1] != 0x01) {
+		kleave(" = -EBADMSG [EM[1] == %02u]", EM[1]);
+		return -EBADMSG;
+	}
+
+	T_offset = k - (asn1_size + hash_size);
+	PS_end = T_offset - 1;
+	if (EM[PS_end] != 0x00) {
+		kleave(" = -EBADMSG [EM[T-1] == %02u]", EM[PS_end]);
+		return -EBADMSG;
+	}
+
+	for (i = 2; i < PS_end; i++) {
+		if (EM[i] != 0xff) {
+			kleave(" = -EBADMSG [EM[PS%x] == %02u]", i - 2, EM[i]);
+			return -EBADMSG;
+		}
+	}
+
+	if (memcmp(asn1_template, EM + T_offset, asn1_size) != 0) {
+		kleave(" = -EBADMSG [EM[T] ASN.1 mismatch]");
+		return -EBADMSG;
+	}
+
+	if (memcmp(H, EM + T_offset + asn1_size, hash_size) != 0) {
+		kleave(" = -EKEYREJECTED [EM[T] hash mismatch]");
+		return -EKEYREJECTED;
+	}
+
+	kleave(" = 0");
+	return 0;
+}
+
+/*
+ * Perform the verification step [RFC3447 sec 8.2.2].
+ */
+static int RSA_verify_signature(const struct public_key *key,
+				const struct public_key_signature *sig)
+{
+	size_t tsize;
+	int ret;
+
+	/* Variables as per RFC3447 sec 8.2.2 */
+	const u8 *H = sig->digest;
+	u8 *EM = NULL;
+	MPI m = NULL;
+	size_t k;
+
+	kenter("");
+
+	/* (1) Check the signature size against the public key modulus size */
+	k = (mpi_get_nbits(key->rsa.n) + 7) / 8;
+
+	tsize = (mpi_get_nbits(sig->rsa.s) + 7) / 8;
+	pr_devel("step 1: k=%zu size(S)=%zu\n", k, tsize);
+	if (tsize != k) {
+		ret = -EBADMSG;
+		goto error;
+	}
+
+	/* (2b) Apply the RSAVP1 verification primitive to the public key */
+	ret = RSAVP1(key, sig->rsa.s, &m);
+	if (ret < 0)
+		goto error;
+
+	/* (2c) Convert the message representative (m) to an encoded message
+	 *      (EM) of length k octets.
+	 *
+	 *      NOTE!  The leading zero byte is suppressed by MPI, so we pass a
+	 *      pointer to the _preceding_ byte to RSA_verify()!
+	 */
+	ret = RSA_I2OSP(m, k, &EM);
+	if (ret < 0)
+		goto error;
+
+#if 0
+	{
+		int i;
+		printk("H:  ");
+		for (i = 0; i < sig->digest_size; i++)
+			printk("%02x", H[i]);
+		printk("\n");
+	}
+
+	{
+		int i;
+		printk("EM: 00");
+		for (i = 0; i < k - 1; i++)
+			printk("%02x", EM[i]);
+		printk("\n");
+	}
+#endif
+
+	ret = RSA_verify(H, EM - 1, k, sig->digest_size,
+			 RSA_ASN1_templates[sig->pkey_hash_algo].data,
+			 RSA_ASN1_templates[sig->pkey_hash_algo].size);
+
+error:
+	kfree(EM);
+	mpi_free(m);
+	kleave(" = %d", ret);
+	return ret;
+}
+
+const struct public_key_algorithm RSA_public_key_algorithm = {
+	.name		= "RSA",
+	.n_pub_mpi	= 2,
+	.n_sec_mpi	= 3,
+	.n_sig_mpi	= 1,
+	.verify		= RSA_verify_signature,
+};
+EXPORT_SYMBOL_GPL(RSA_public_key_algorithm);
diff --git a/security/keys/crypto/public_key.h b/security/keys/crypto/public_key.h
index 81ed603..7913615 100644
--- a/security/keys/crypto/public_key.h
+++ b/security/keys/crypto/public_key.h
@@ -42,6 +42,8 @@ struct public_key_algorithm {
 		      const struct public_key_signature *sig);
 };
 
+extern const struct public_key_algorithm RSA_public_key_algorithm;
+
 /*
  * Asymmetric public key data
  */


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 09/29] Fix signature verification for shorter signatures [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (7 preceding siblings ...)
  2012-05-10 23:40 ` [PATCH 08/29] KEYS: RSA signature verification algorithm " David Howells
@ 2012-05-10 23:40 ` David Howells
  2012-05-10 23:40 ` [PATCH 10/29] PGPLIB: PGP definitions (RFC 4880) " David Howells
                   ` (23 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:40 UTC (permalink / raw)
  To: rusty
  Cc: kyle, linux-kernel, linux-security-module, keyrings, Milan Broz,
	David Howells

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 files changed, 11 insertions(+), 3 deletions(-)


diff --git a/security/keys/crypto/crypto_rsa.c b/security/keys/crypto/crypto_rsa.c
index beb5181..cc5cd95 100644
--- a/security/keys/crypto/crypto_rsa.c
+++ b/security/keys/crypto/crypto_rsa.c
@@ -219,15 +219,23 @@ static int RSA_verify_signature(const struct public_key *key,
 	kenter("");
 
 	/* (1) Check the signature size against the public key modulus size */
-	k = (mpi_get_nbits(key->rsa.n) + 7) / 8;
+	k = mpi_get_nbits(key->rsa.n);
+	tsize = mpi_get_nbits(sig->rsa.s);
 
-	tsize = (mpi_get_nbits(sig->rsa.s) + 7) / 8;
+	/* According to RFC 4880 sec 3.2, length of MPI is computed starting
+	 * from most significant bit.  So the RFC 3447 sec 8.2.2 size check
+	 * must be relaxed to conform with shorter signatures - so we fail here
+	 * only if signature length is longer than modulus size.
+	 */
 	pr_devel("step 1: k=%zu size(S)=%zu\n", k, tsize);
-	if (tsize != k) {
+	if (k < tsize) {
 		ret = -EBADMSG;
 		goto error;
 	}
 
+	/* Round up and convert to octets */
+	k = (k + 7) / 8;
+
 	/* (2b) Apply the RSAVP1 verification primitive to the public key */
 	ret = RSAVP1(key, sig->rsa.s, &m);
 	if (ret < 0)


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 10/29] PGPLIB: PGP definitions (RFC 4880) [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (8 preceding siblings ...)
  2012-05-10 23:40 ` [PATCH 09/29] Fix signature verification for shorter signatures " David Howells
@ 2012-05-10 23:40 ` David Howells
  2012-05-10 23:41 ` [PATCH 11/29] PGPLIB: Basic packet parser " David Howells
                   ` (22 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:40 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

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 files changed, 206 insertions(+), 0 deletions(-)
 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 */


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 11/29] PGPLIB: Basic packet parser [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (9 preceding siblings ...)
  2012-05-10 23:40 ` [PATCH 10/29] PGPLIB: PGP definitions (RFC 4880) " David Howells
@ 2012-05-10 23:41 ` David Howells
  2012-05-10 23:41 ` [PATCH 12/29] PGPLIB: Signature " David Howells
                   ` (21 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:41 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

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/pgp.h                |   25 ++++
 security/keys/crypto/Kconfig       |    6 +
 security/keys/crypto/Makefile      |    1 
 security/keys/crypto/pgp_library.c |  254 ++++++++++++++++++++++++++++++++++++
 4 files changed, 286 insertions(+), 0 deletions(-)
 create mode 100644 security/keys/crypto/pgp_library.c


diff --git a/include/linux/pgp.h b/include/linux/pgp.h
index 1359f64..235270a 100644
--- a/include/linux/pgp.h
+++ b/include/linux/pgp.h
@@ -203,4 +203,29 @@ struct pgp_key_v4_packet {
 	u8 key_material[0];
 } __packed;
 
+/*
+ * PGP library packet parser
+ */
+struct pgp_parse_context {
+	u64 types_of_interest;
+	int (*process_packet)(struct pgp_parse_context *context,
+			      enum pgp_packet_tag type,
+			      u8 headerlen,
+			      const u8 *data,
+			      size_t datalen);
+};
+
+extern int pgp_parse_packets(const u8 *data, size_t datalen,
+			     struct pgp_parse_context *ctx);
+
+struct pgp_parse_pubkey {
+	enum pgp_key_version version : 8;
+	enum pgp_pubkey_algo pubkey_algo : 8;
+	time_t		creation_time;
+	time_t		expires_at;
+};
+
+extern int pgp_parse_public_key(const u8 **_data, size_t *_datalen,
+				struct pgp_parse_pubkey *pk);
+
 #endif /* _LINUX_PGP_H */
diff --git a/security/keys/crypto/Kconfig b/security/keys/crypto/Kconfig
index 4e3777e..88ce0e2 100644
--- a/security/keys/crypto/Kconfig
+++ b/security/keys/crypto/Kconfig
@@ -22,3 +22,9 @@ config CRYPTO_KEY_PKEY_ALGO_RSA
 	select MPILIB_EXTRA
 	help
 	  This option enables support for the RSA algorithm (PKCS#1, RFC3447).
+
+config PGP_LIBRARY
+	tristate "PGP parsing library"
+	help
+	  This option enables a library that provides a number of simple
+	  utility functions for parsing PGP (RFC 4880) packet-based messages.
diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile
index b6b1a5a..5fbe54e 100644
--- a/security/keys/crypto/Makefile
+++ b/security/keys/crypto/Makefile
@@ -7,3 +7,4 @@ crypto_keys-y := crypto_type.o crypto_verify.o
 
 obj-$(CONFIG_CRYPTO_KEY_PUBLIC_KEY_SUBTYPE) += public_key.o
 obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) += crypto_rsa.o
+obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o
diff --git a/security/keys/crypto/pgp_library.c b/security/keys/crypto/pgp_library.c
new file mode 100644
index 0000000..685660f
--- /dev/null
+++ b/security/keys/crypto/pgp_library.c
@@ -0,0 +1,254 @@
+/* PGP packet parser (RFC 4880)
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#define pr_fmt(fmt) "PGP: "fmt
+#include <linux/pgp.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.
+ */
+ssize_t pgp_parse_packet_header(const u8 **_data, size_t *_datalen,
+				enum pgp_packet_tag *_type,
+				u8 *_headerlen)
+{
+	enum pgp_packet_tag type;
+	const u8 *data = *_data;
+	size_t size, datalen = *_datalen;
+
+	pr_devel("-->pgp_parse_packet_header(,%zu,,)", datalen);
+
+	if (datalen < 2)
+		goto short_packet;
+
+	pr_devel("pkthdr %02x, %02x\n", data[0], data[1]);
+
+	type = *data++;
+	datalen--;
+	if (!(type & 0x80)) {
+		pr_debug("Packet type does not have MSB set\n");
+		return -EBADMSG;
+	}
+	type &= ~0x80;
+
+	if (type & 0x40) {
+		/* New packet length format */
+		type &= ~0x40;
+		pr_devel("new format: t=%u\n", type);
+		switch (data[0]) {
+		case 0x00 ... 0xbf:
+			/* One-byte length */
+			size = data[0];
+			data++;
+			datalen--;
+			*_headerlen = 2;
+			break;
+		case 0xc0 ... 0xdf:
+			/* Two-byte length */
+			if (datalen < 2)
+				goto short_packet;
+			size = (data[0] - 192) * 256;
+			size += data[1] + 192;
+			data += 2;
+			datalen -= 2;
+			*_headerlen = 3;
+			break;
+		case 0xff:
+			pr_debug("Five-byte packet length not supported\n");
+			return -EBADMSG;
+		default:
+			pr_debug("Error parsing packet length\n");
+			return -EBADMSG;
+		}
+	} else {
+		/* Old packet length format */
+		u8 length_type = type & 0x03;
+		type >>= 2;
+		pr_devel("old format: t=%u lt=%u\n", type, length_type);
+
+		switch (length_type) {
+		case 0:
+			/* One-byte length */
+			size = data[0];
+			data++;
+			datalen--;
+			*_headerlen = 2;
+			break;
+		case 1:
+			/* Two-byte length */
+			if (datalen < 2)
+				goto short_packet;
+			size  = data[0] << 8;
+			size |= data[1];
+			data += 2;
+			datalen -= 2;
+			*_headerlen = 3;
+			break;
+		case 2:
+			/* Four-byte length */
+			if (datalen < 4)
+				goto short_packet;
+			size  = data[0] << 24;
+			size |= data[1] << 16;
+			size |= data[2] << 8;
+			size |= data[3];
+			data += 4;
+			datalen -= 4;
+			*_headerlen = 5;
+			break;
+		default:
+			pr_debug("Indefinite length packet not supported\n");
+			return -EBADMSG;
+		}
+	}
+
+	pr_devel("datalen=%zu size=%zu", datalen, size);
+	if (datalen < size)
+		goto short_packet;
+
+	*_data = data;
+	*_datalen = datalen;
+	*_type = type;
+	pr_devel("Found packet type=%u size=%zd\n", type, size);
+	return size;
+
+short_packet:
+	pr_debug("Attempt to parse short packet\n");
+	return -EBADMSG;
+}
+
+/**
+ * pgp_parse_packets - Parse a set of PGP packets
+ * @_data: Data to be parsed (updated)
+ * @_datalen: Amount of data (updated)
+ * @ctx: Parsing context
+ *
+ * Parse a set of PGP packets [RFC 4880: 4].
+ */
+int pgp_parse_packets(const u8 *data, size_t datalen,
+		      struct pgp_parse_context *ctx)
+{
+	enum pgp_packet_tag type;
+	ssize_t pktlen;
+	u8 headerlen;
+	int ret;
+
+	while (datalen > 2) {
+		pktlen = pgp_parse_packet_header(&data, &datalen, &type,
+						 &headerlen);
+		if (pktlen < 0)
+			return pktlen;
+
+		if ((ctx->types_of_interest >> type) & 1) {
+			ret = ctx->process_packet(ctx, type, headerlen,
+						  data, pktlen);
+			if (ret < 0)
+				return ret;
+		}
+		data += pktlen;
+		datalen -= pktlen;
+	}
+
+	if (datalen != 0) {
+		pr_debug("Excess octets in packet stream\n");
+		return -EBADMSG;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pgp_parse_packets);
+
+/**
+ * pgp_parse_public_key - Parse the common part of a PGP pubkey packet
+ * @_data: Content of packet (updated)
+ * @_datalen: Length of packet remaining (updated)
+ * @pk: Public key data
+ *
+ * Parse the common data struct for a PGP pubkey packet [RFC 4880: 5.5.2].
+ */
+int pgp_parse_public_key(const u8 **_data, size_t *_datalen,
+			 struct pgp_parse_pubkey *pk)
+{
+	const u8 *data = *_data;
+	size_t datalen = *_datalen;
+	__be32 tmp;
+
+	if (datalen < 12) {
+		pr_debug("Public key packet too short\n");
+		return -EBADMSG;
+	}
+
+	pk->version = *data++;
+	switch (pk->version) {
+	case PGP_KEY_VERSION_2:
+	case PGP_KEY_VERSION_3:
+	case PGP_KEY_VERSION_4:
+		break;
+	default:
+		pr_debug("Public key packet with unhandled version %d\n",
+			   pk->version);
+		return -EBADMSG;
+	}
+
+	tmp  = *data++ << 24;
+	tmp |= *data++ << 16;
+	tmp |= *data++ << 8;
+	tmp |= *data++;
+	pk->creation_time = tmp;
+	if (pk->version == PGP_KEY_VERSION_4) {
+		pk->expires_at = 0; /* Have to get it from the selfsignature */
+	} else {
+		unsigned short ndays;
+		ndays  = *data++ << 8;
+		ndays |= *data++;
+		if (ndays)
+			pk->expires_at = pk->creation_time + ndays * 86400UL;
+		else
+			pk->expires_at = 0;
+		datalen -= 2;
+	}
+
+	pk->pubkey_algo = *data++;
+	datalen -= 6;
+
+	pr_devel("%x,%x,%lx,%lx",
+		 pk->version, pk->pubkey_algo, pk->creation_time,
+		 pk->expires_at);
+
+	*_data = data;
+	*_datalen = datalen;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pgp_parse_public_key);


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 12/29] PGPLIB: Signature parser [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (10 preceding siblings ...)
  2012-05-10 23:41 ` [PATCH 11/29] PGPLIB: Basic packet parser " David Howells
@ 2012-05-10 23:41 ` David Howells
  2012-05-10 23:41 ` [PATCH 13/29] KEYS: PGP data " David Howells
                   ` (20 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:41 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

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/pgp.h                |   24 +++
 security/keys/crypto/pgp_library.c |  277 ++++++++++++++++++++++++++++++++++++
 2 files changed, 301 insertions(+), 0 deletions(-)


diff --git a/include/linux/pgp.h b/include/linux/pgp.h
index 235270a..ab1a8fa 100644
--- a/include/linux/pgp.h
+++ b/include/linux/pgp.h
@@ -228,4 +228,28 @@ struct pgp_parse_pubkey {
 extern int pgp_parse_public_key(const u8 **_data, size_t *_datalen,
 				struct pgp_parse_pubkey *pk);
 
+struct pgp_parse_sig_context {
+	unsigned long types_of_interest[128 / BITS_PER_LONG];
+	int (*process_packet)(struct pgp_parse_sig_context *context,
+			      enum pgp_sig_subpkt_type type,
+			      const u8 *data,
+			      size_t datalen);
+};
+
+extern int pgp_parse_sig_packets(const u8 *data, size_t datalen,
+				 struct pgp_parse_sig_context *ctx);
+
+struct pgp_sig_parameters {
+	enum pgp_signature_type signature_type : 8;
+	union {
+		struct pgp_key_ID issuer;
+		__be32 issuer32[2];
+	};
+	enum pgp_pubkey_algo pubkey_algo : 8;
+	enum pgp_hash_algo hash_algo : 8;
+};
+
+extern int pgp_parse_sig_params(const u8 **_data, size_t *_datalen,
+				struct pgp_sig_parameters *p);
+
 #endif /* _LINUX_PGP_H */
diff --git a/security/keys/crypto/pgp_library.c b/security/keys/crypto/pgp_library.c
index 685660f..f6b831f 100644
--- a/security/keys/crypto/pgp_library.c
+++ b/security/keys/crypto/pgp_library.c
@@ -252,3 +252,280 @@ int pgp_parse_public_key(const u8 **_data, size_t *_datalen,
 	return 0;
 }
 EXPORT_SYMBOL_GPL(pgp_parse_public_key);
+
+/**
+ * pgp_parse_sig_subpkt_header - Parse a PGP V4 signature subpacket header
+ * @_data: Start of the subpacket (updated to subpacket data)
+ * @_datalen: Amount of data remaining in buffer (decreased)
+ * @_type: Where the subpacket type will be returned
+ *
+ * Parse a PGP V4 signature subpacket header [RFC 4880: 5.2.3.1].
+ *
+ * Returns packet data size on success; non-zero on error.  If successful,
+ * *_data and *_datalen will have been updated and *_headerlen will be set to
+ * hold the length of the packet header.
+ */
+ssize_t pgp_parse_sig_subpkt_header(const u8 **_data, size_t *_datalen,
+				    enum pgp_sig_subpkt_type *_type)
+{
+	enum pgp_sig_subpkt_type type;
+	const u8 *data = *_data;
+	size_t size, datalen = *_datalen;
+
+	pr_devel("-->pgp_parse_sig_subpkt_header(,%zu,,)", datalen);
+
+	if (datalen < 2)
+		goto short_subpacket;
+
+	pr_devel("subpkt hdr %02x, %02x\n", data[0], data[1]);
+
+	switch (data[0]) {
+	case 0x00 ... 0xbf:
+		/* One-byte length */
+		size = data[0];
+		data++;
+		datalen--;
+		break;
+	case 0xc0 ... 0xfe:
+		/* Two-byte length */
+		if (datalen < 3)
+			goto short_subpacket;
+		size = (data[0] - 192) * 256;
+		size += data[1] + 192;
+		data += 2;
+		datalen -= 2;
+		break;
+	case 0xff:
+		if (datalen < 6)
+			goto short_subpacket;
+		size  = data[1] << 24;
+		size |= data[2] << 16;
+		size |= data[3] << 8;
+		size |= data[4];
+		data += 5;
+		datalen -= 5;
+		break;
+	}
+
+	/* The type octet is included in the size */
+	if (size == 0) {
+		pr_debug("Signature subpacket size can't be zero\n");
+		return -EBADMSG;
+	}
+
+	type = *data++ & ~PGP_SIG_SUBPKT_TYPE_CRITICAL_MASK;
+	datalen--;
+	size--;
+
+	pr_devel("datalen=%zu size=%zu", datalen, size);
+	if (datalen < size)
+		goto short_subpacket;
+
+	*_data = data;
+	*_datalen = datalen;
+	*_type = type;
+	pr_devel("Found subpkt type=%u size=%zd\n", type, size);
+	return size;
+
+short_subpacket:
+	pr_debug("Attempt to parse short signature subpacket\n");
+	return -EBADMSG;
+}
+
+/**
+ * pgp_parse_sig_subpkts - Parse a set of PGP V4 signatute subpackets
+ * @_data: Data to be parsed (updated)
+ * @_datalen: Amount of data (updated)
+ * @ctx: Parsing context
+ *
+ * Parse a set of PGP signature subpackets [RFC 4880: 5.2.3].
+ */
+int pgp_parse_sig_subpkts(const u8 *data, size_t datalen,
+			  struct pgp_parse_sig_context *ctx)
+{
+	enum pgp_sig_subpkt_type type;
+	ssize_t pktlen;
+	int ret;
+
+	pr_devel("-->pgp_parse_sig_subpkts(,%zu,,)", datalen);
+
+	while (datalen > 2) {
+		pktlen = pgp_parse_sig_subpkt_header(&data, &datalen, &type);
+		if (pktlen < 0)
+			return pktlen;
+		if (test_bit(type, ctx->types_of_interest)) {
+			ret = ctx->process_packet(ctx, type, data, pktlen);
+			if (ret < 0)
+				return ret;
+		}
+		data += pktlen;
+		datalen -= pktlen;
+	}
+
+	if (datalen != 0) {
+		pr_debug("Excess octets in signature subpacket stream\n");
+		return -EBADMSG;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pgp_parse_sig_subpkts);
+
+struct pgp_parse_sig_params_ctx {
+	struct pgp_parse_sig_context base;
+	struct pgp_sig_parameters *params;
+	bool got_the_issuer;
+};
+
+/*
+ * Process a V4 signature subpacket.
+ */
+static int pgp_process_sig_params_subpkt(struct pgp_parse_sig_context *context,
+					 enum pgp_sig_subpkt_type type,
+					 const u8 *data,
+					 size_t datalen)
+{
+	struct pgp_parse_sig_params_ctx *ctx =
+		container_of(context, struct pgp_parse_sig_params_ctx, base);
+
+	if (ctx->got_the_issuer) {
+		pr_debug("V4 signature packet has multiple issuers\n");
+		return -EBADMSG;
+	}
+
+	if (datalen != 8) {
+		pr_debug("V4 signature issuer subpkt not 8 long (%zu)\n",
+			   datalen);
+		return -EBADMSG;
+	}
+
+	memcpy(&ctx->params->issuer, data, 8);
+	ctx->got_the_issuer = true;
+	return 0;
+}
+
+/**
+ * pgp_parse_sig_params - Parse basic parameters from a PGP signature packet
+ * @_data: Content of packet (updated)
+ * @_datalen: Length of packet remaining (updated)
+ * @p: The basic parameters
+ *
+ * Parse the basic parameters from a PGP signature packet [RFC 4880: 5.2] that
+ * are needed to start off a signature verification operation.  The only ones
+ * actually necessary are the signature type (which affects how the data is
+ * transformed) and the has algorithm.
+ *
+ * We also extract the public key algorithm and the issuer's key ID as we'll
+ * need those to determine if we actually have the public key available.  If
+ * not, then we can't verify the signature anyway.
+ *
+ * Returns 0 if successful or a negative error code.  *_data and *_datalen are
+ * updated to point to the 16-bit subset of the hash value and the set of MPIs.
+ */
+int pgp_parse_sig_params(const u8 **_data, size_t *_datalen,
+			 struct pgp_sig_parameters *p)
+{
+	enum pgp_signature_version version;
+	const u8 *data = *_data;
+	size_t datalen = *_datalen;
+	int ret;
+
+	pr_devel("-->pgp_parse_sig_params(,%zu,,)", datalen);
+
+	if (datalen < 1)
+		return -EBADMSG;
+	version = *data;
+
+	if (version == PGP_SIG_VERSION_3) {
+		const struct pgp_signature_v3_packet *v3 = (const void *)data;
+
+		if (datalen < sizeof(*v3)) {
+			pr_debug("Short V3 signature packet\n");
+			return -EBADMSG;
+		}
+		datalen -= sizeof(*v3);
+		data += sizeof(*v3);
+
+		/* V3 has everything we need in the header */
+		p->signature_type = v3->hashed.signature_type;
+		p->issuer = v3->issuer;
+		p->pubkey_algo = v3->pubkey_algo;
+		p->hash_algo = v3->hash_algo;
+
+	} else if (version == PGP_SIG_VERSION_4) {
+		const struct pgp_signature_v4_packet *v4 = (const void *)data;
+		struct pgp_parse_sig_params_ctx ctx = {
+			.base.process_packet = pgp_process_sig_params_subpkt,
+			.params = p,
+			.got_the_issuer = false,
+		};
+		size_t subdatalen;
+
+		if (datalen < sizeof(*v4) + 2 + 2 + 2) {
+			pr_debug("Short V4 signature packet\n");
+			return -EBADMSG;
+		}
+		datalen -= sizeof(*v4);
+		data += sizeof(*v4);
+
+		/* V4 has most things in the header... */
+		p->signature_type = v4->signature_type;
+		p->pubkey_algo = v4->pubkey_algo;
+		p->hash_algo = v4->hash_algo;
+
+		/* ... but we have to get the key ID from the subpackets, of
+		 * which there are two sets. */
+		__set_bit(PGP_SIG_ISSUER, ctx.base.types_of_interest);
+
+		subdatalen  = *data++ << 8;
+		subdatalen |= *data++;
+		datalen -= 2;
+		if (subdatalen) {
+			/* Hashed subpackets */
+			pr_devel("hashed data: %zu (after %zu)\n",
+				 subdatalen, sizeof(*v4));
+			if (subdatalen > datalen + 2 + 2) {
+				pr_debug("Short V4 signature packet [hdata]\n");
+				return -EBADMSG;
+			}
+			ret = pgp_parse_sig_subpkts(data, subdatalen,
+						    &ctx.base);
+			if (ret < 0)
+				return ret;
+			data += subdatalen;
+			datalen += subdatalen;
+		}
+
+		subdatalen  = *data++ << 8;
+		subdatalen |= *data++;
+		datalen -= 2;
+		if (subdatalen) {
+			/* Unhashed subpackets */
+			pr_devel("unhashed data: %zu\n", subdatalen);
+			if (subdatalen > datalen + 2) {
+				pr_debug("Short V4 signature packet [udata]\n");
+				return -EBADMSG;
+			}
+			ret = pgp_parse_sig_subpkts(data, subdatalen,
+						    &ctx.base);
+			if (ret < 0)
+				return ret;
+			data += subdatalen;
+			datalen += subdatalen;
+		}
+
+		if (!ctx.got_the_issuer) {
+			pr_debug("V4 signature packet lacks issuer\n");
+			return -EBADMSG;
+		}
+	} else {
+		pr_debug("Signature packet with unhandled version %d\n",
+			 version);
+		return -EBADMSG;
+	}
+
+	*_data = data;
+	*_datalen = datalen;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pgp_parse_sig_params);


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 13/29] KEYS: PGP data parser [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (11 preceding siblings ...)
  2012-05-10 23:41 ` [PATCH 12/29] PGPLIB: Signature " David Howells
@ 2012-05-10 23:41 ` David Howells
  2012-05-10 23:41 ` [PATCH 14/29] KEYS: PGP-based public key signature verification " David Howells
                   ` (19 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:41 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

Implement a PGP data parser for the crypto key type to use when instantiating a
key.

This parser attempts to parse the instantiation data as a PGP packet sequence
(RFC 4880) and if it parses okay, attempts to extract a public-key algorithm
key or subkey from it.

If it finds such a key, it will set up a public_key subtype payload with
appropriate handler routines (DSA or RSA) and attach it to the key.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 security/keys/crypto/Kconfig          |   12 +
 security/keys/crypto/Makefile         |    4 
 security/keys/crypto/pgp_key_parser.c |  343 +++++++++++++++++++++++++++++++++
 security/keys/crypto/pgp_parser.h     |   23 ++
 4 files changed, 382 insertions(+), 0 deletions(-)
 create mode 100644 security/keys/crypto/pgp_key_parser.c
 create mode 100644 security/keys/crypto/pgp_parser.h


diff --git a/security/keys/crypto/Kconfig b/security/keys/crypto/Kconfig
index 88ce0e2..1c2ae55 100644
--- a/security/keys/crypto/Kconfig
+++ b/security/keys/crypto/Kconfig
@@ -28,3 +28,15 @@ config PGP_LIBRARY
 	help
 	  This option enables a library that provides a number of simple
 	  utility functions for parsing PGP (RFC 4880) packet-based messages.
+
+config CRYPTO_KEY_PGP_PARSER
+	tristate "PGP key blob parser"
+	depends on CRYPTO_KEY_TYPE
+	select CRYPTO_KEY_PUBLIC_KEY_SUBTYPE
+	select PGP_LIBRARY
+	select MD5 # V3 fingerprint generation
+	select SHA1 # V4 fingerprint generation
+	help
+	  This option provides support for parsing PGP (RFC 4880) format blobs
+	  for key data and provides the ability to instantiate a crypto key
+	  from a public key packet found inside the blob.
diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile
index 5fbe54e..3bb2e61 100644
--- a/security/keys/crypto/Makefile
+++ b/security/keys/crypto/Makefile
@@ -8,3 +8,7 @@ crypto_keys-y := crypto_type.o crypto_verify.o
 obj-$(CONFIG_CRYPTO_KEY_PUBLIC_KEY_SUBTYPE) += public_key.o
 obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) += crypto_rsa.o
 obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o
+
+obj-$(CONFIG_CRYPTO_KEY_PGP_PARSER) += pgp_parser.o
+pgp_parser-y := \
+	pgp_key_parser.o
diff --git a/security/keys/crypto/pgp_key_parser.c b/security/keys/crypto/pgp_key_parser.c
new file mode 100644
index 0000000..4efc4de
--- /dev/null
+++ b/security/keys/crypto/pgp_key_parser.c
@@ -0,0 +1,343 @@
+/* Parser for PGP format key data [RFC 4880]
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "PGP: "fmt
+#include <keys/crypto-subtype.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mpi.h>
+#include <linux/pgp.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("");
+
+	n = (pgp->version < PGP_KEY_VERSION_4) ? 8 : 6;
+	for (i = 0; i < npkey; i++) {
+		nb[i] = mpi_get_nbits(key->mpi[i]);
+		pp[i] = mpi_get_buffer(key->mpi[i], nn + i, NULL);
+		if (!pp[i])
+			goto error;
+		n += 2 + nn[i];
+	}
+
+	digest_putc(digest, 0x99);     /* ctb */
+	digest_putc(digest, n >> 8);   /* 16-bit header length */
+	digest_putc(digest, n);
+	digest_putc(digest, pgp->version);
+
+	a32 = pgp->creation_time;
+	digest_putc(digest, a32 >> 24);
+	digest_putc(digest, a32 >> 16);
+	digest_putc(digest, a32 >>  8);
+	digest_putc(digest, a32 >>  0);
+
+	if (pgp->version < PGP_KEY_VERSION_4) {
+		u16 a16;
+
+		if( pgp->expires_at)
+			a16 = (pgp->expires_at - pgp->creation_time) / 86400UL;
+		else
+			a16 = 0;
+		digest_putc(digest, a16 >> 8);
+		digest_putc(digest, a16 >> 0);
+	}
+
+	digest_putc(digest, pgp->pubkey_algo);
+
+	for (i = 0; i < npkey; i++) {
+		digest_putc(digest, nb[i] >> 8);
+		digest_putc(digest, nb[i]);
+		crypto_shash_update(digest, pp[i], nn[i]);
+	}
+	ret = 0;
+
+error:
+	for (i = 0; i < npkey; i++)
+		kfree(pp[i]);
+	kleave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * Calculate the public key ID fingerprint
+ */
+static int pgp_generate_fingerprint(struct pgp_key_data_parse_context *ctx,
+				    struct pgp_parse_pubkey *pgp,
+				    struct public_key *key)
+{
+	struct crypto_shash *tfm;
+	struct shash_desc *digest;
+	char *fingerprint;
+	u8 *raw_fingerprint;
+	int digest_size, offset;
+	int ret, i;
+
+	ret = -ENOMEM;
+	tfm = crypto_alloc_shash(pgp->version < PGP_KEY_VERSION_4 ?
+				 "md5" : "sha1", 0, 0);
+	if (!tfm)
+		goto cleanup;
+
+	digest = kmalloc(sizeof(*digest) + crypto_shash_descsize(tfm),
+			 GFP_KERNEL);
+	if (!digest)
+		goto cleanup_tfm;
+
+	digest->tfm = tfm;
+	digest->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+	ret = crypto_shash_init(digest);
+	if (ret < 0)
+		goto cleanup_hash;
+
+	ret = pgp_calc_pkey_keyid(digest, pgp, key);
+	if (ret < 0)
+		goto cleanup_hash;
+
+	digest_size = crypto_shash_digestsize(tfm);
+
+	raw_fingerprint = kmalloc(digest_size, GFP_KERNEL);
+	if (!raw_fingerprint)
+		goto cleanup_hash;
+
+	ret = crypto_shash_final(digest, raw_fingerprint);
+	if (ret < 0)
+		goto cleanup_raw_fingerprint;
+
+	fingerprint = kmalloc(digest_size * 2 + 1, GFP_KERNEL);
+	if (!fingerprint)
+		goto cleanup_raw_fingerprint;
+
+	offset = digest_size - 8;
+	pr_debug("offset %u/%u\n", offset, digest_size);
+
+	for (i = 0; i < digest_size; i++)
+		sprintf(fingerprint + i * 2, "%02x", raw_fingerprint[i]);
+	pr_debug("fingerprint %s\n", fingerprint);
+
+	memcpy(&key->key_id, raw_fingerprint + offset, 8);
+	key->key_id_size = 8;
+
+	ctx->fingerprint = fingerprint;
+	ret = 0;
+cleanup_raw_fingerprint:
+	kfree(raw_fingerprint);
+cleanup_hash:
+	kfree(digest);
+cleanup_tfm:
+	crypto_free_shash(tfm);
+cleanup:
+	kleave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * Extract a public key or public subkey from the PGP stream.
+ */
+static int pgp_process_public_key(struct pgp_parse_context *context,
+				  enum pgp_packet_tag type,
+				  u8 headerlen,
+				  const u8 *data,
+				  size_t datalen)
+{
+	const struct public_key_algorithm *algo;
+	struct pgp_key_data_parse_context *ctx =
+		container_of(context, struct pgp_key_data_parse_context, pgp);
+	struct pgp_parse_pubkey pgp;
+	struct public_key *key;
+	int i, ret;
+
+	kenter(",%u,%u,,%zu", type, headerlen, datalen);
+
+	if (ctx->subtype) {
+		kleave(" = -ENOKEY [already]");
+		return -EBADMSG;
+	}
+
+	key = kzalloc(sizeof(struct public_key), GFP_KERNEL);
+	if (!key)
+		return -ENOMEM;
+
+	ret = pgp_parse_public_key(&data, &datalen, &pgp);
+	if (ret < 0)
+		goto cleanup;
+
+	if (pgp.pubkey_algo >= PGP_PUBKEY__LAST ||
+	    !pgp_public_key_algorithms[pgp.pubkey_algo]) {
+		pr_debug("Unsupported public key algorithm %u\n",
+			 pgp.pubkey_algo);
+		ret = -ENOPKG;
+		goto cleanup;
+	}
+
+	algo = key->algo = pgp_public_key_algorithms[pgp.pubkey_algo];
+
+	/* It's a public key, so that only gives us encrypt and verify
+	 * capabilities.
+	 */
+	key->capabilities = pgp_public_key_capabilities[pgp.pubkey_algo] &
+		(PKEY_CAN_ENCRYPT | PKEY_CAN_VERIFY);
+
+	ret = -ENOMEM;
+	for (i = 0; i < algo->n_pub_mpi; i++) {
+		unsigned int remaining = datalen;
+		ret = -EBADMSG;
+		if (remaining == 0) {
+			pr_debug("short %zu mpi %d\n", datalen, i);
+			goto cleanup;
+		}
+		key->mpi[i] = mpi_read_from_buffer(data, &remaining);
+		if (!key->mpi[i])
+			goto cleanup;
+		data += remaining;
+		datalen -= remaining;
+	}
+
+	ret = -EBADMSG;
+	if (datalen != 0) {
+		pr_debug("excess %zu\n", datalen);
+		goto cleanup;
+	}
+
+	ret = pgp_generate_fingerprint(ctx, &pgp, key);
+	if (ret < 0)
+		goto cleanup;
+
+	/* We're pinning the module by being linked against it */
+	__module_get(public_key_crypto_key_subtype.owner);
+	ctx->subtype = &public_key_crypto_key_subtype;
+	ctx->payload = key;
+	kleave(" = 0 [use]");
+	return 0;
+
+cleanup:
+	pr_devel("cleanup");
+	if (key) {
+		for (i = 0; i < ARRAY_SIZE(key->mpi); i++)
+			mpi_free(key->mpi[i]);
+		kfree(key);
+	}
+	kleave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * Attempt to parse the instantiation data blob for a key as a PGP packet
+ * message holding a key.
+ */
+static int pgp_key_instantiate(struct key *key,
+			       const void *data, size_t datalen)
+{
+	struct pgp_key_data_parse_context ctx;
+	int ret;
+
+	kenter("");
+
+	ret = key_payload_reserve(key, datalen);
+	if (ret < 0)
+		return ret;
+
+	ctx.pgp.types_of_interest =
+		(1 << PGP_PKT_PUBLIC_KEY) | (1 << PGP_PKT_PUBLIC_SUBKEY);
+	ctx.pgp.process_packet = pgp_process_public_key;
+	ctx.subtype = NULL;
+	ctx.fingerprint = NULL;
+	ctx.payload = NULL;
+
+	ret = pgp_parse_packets(data, datalen, &ctx.pgp);
+	if (ret < 0) {
+		if (ctx.payload)
+			ctx.subtype->destroy(ctx.payload);
+		if (ctx.subtype)
+			module_put(ctx.subtype->owner);
+		kfree(ctx.fingerprint);
+		key_payload_reserve(key, 0);
+		return ret;
+	}
+
+	key->type_data.p[0] = ctx.subtype;
+	key->type_data.p[1] = ctx.fingerprint;
+	key->payload.data = ctx.payload;
+	return 0;
+}
+
+static struct crypto_key_parser pgp_key_parser = {
+	.owner		= THIS_MODULE,
+	.name		= "pgp",
+	.instantiate	= pgp_key_instantiate,
+};
+
+/*
+ * Module stuff
+ */
+static int __init pgp_key_init(void)
+{
+	return register_crypto_key_parser(&pgp_key_parser);
+}
+
+static void __exit pgp_key_exit(void)
+{
+	unregister_crypto_key_parser(&pgp_key_parser);
+}
+
+module_init(pgp_key_init);
+module_exit(pgp_key_exit);
diff --git a/security/keys/crypto/pgp_parser.h b/security/keys/crypto/pgp_parser.h
new file mode 100644
index 0000000..1cda231
--- /dev/null
+++ b/security/keys/crypto/pgp_parser.h
@@ -0,0 +1,23 @@
+/* PGP crypto data parser internal definitions
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <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];


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 14/29] KEYS: PGP-based public key signature verification [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (12 preceding siblings ...)
  2012-05-10 23:41 ` [PATCH 13/29] KEYS: PGP data " David Howells
@ 2012-05-10 23:41 ` David Howells
  2012-05-10 23:41 ` [PATCH 15/29] KEYS: PGP format signature parser " David Howells
                   ` (18 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:41 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

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_pubkey_sig.c |  323 +++++++++++++++++++++++++++++++++
 3 files changed, 331 insertions(+), 1 deletions(-)
 create mode 100644 security/keys/crypto/pgp_pubkey_sig.c


diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile
index 3bb2e61..d4ac8ac 100644
--- a/security/keys/crypto/Makefile
+++ b/security/keys/crypto/Makefile
@@ -11,4 +11,5 @@ obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o
 
 obj-$(CONFIG_CRYPTO_KEY_PGP_PARSER) += pgp_parser.o
 pgp_parser-y := \
-	pgp_key_parser.o
+	pgp_key_parser.o \
+	pgp_pubkey_sig.o
diff --git a/security/keys/crypto/pgp_parser.h b/security/keys/crypto/pgp_parser.h
index 1cda231..a6192ce 100644
--- a/security/keys/crypto/pgp_parser.h
+++ b/security/keys/crypto/pgp_parser.h
@@ -21,3 +21,9 @@
  */
 extern const
 struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST];
+
+/*
+ * pgp_pubkey_sig.c
+ */
+extern struct crypto_key_verify_context *pgp_pkey_verify_sig_begin(
+	struct key *crypto_key, const u8 *sigdata, size_t siglen);
diff --git a/security/keys/crypto/pgp_pubkey_sig.c b/security/keys/crypto/pgp_pubkey_sig.c
new file mode 100644
index 0000000..b4b7cb0
--- /dev/null
+++ b/security/keys/crypto/pgp_pubkey_sig.c
@@ -0,0 +1,323 @@
+/* Handling for PGP public key signature data [RFC 4880]
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "PGPSIG: "fmt
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/pgp.h>
+#include "public_key.h"
+#include "pgp_parser.h"
+
+const struct {
+	enum pkey_hash_algo algo : 8;
+} pgp_pubkey_hash[PGP_HASH__LAST] = {
+	[PGP_HASH_MD5].algo		= PKEY_HASH_MD5,
+	[PGP_HASH_SHA1].algo		= PKEY_HASH_SHA1,
+	[PGP_HASH_RIPE_MD_160].algo	= PKEY_HASH_RIPE_MD_160,
+	[PGP_HASH_SHA256].algo		= PKEY_HASH_SHA256,
+	[PGP_HASH_SHA384].algo		= PKEY_HASH_SHA384,
+	[PGP_HASH_SHA512].algo		= PKEY_HASH_SHA512,
+	[PGP_HASH_SHA224].algo		= PKEY_HASH_SHA224,
+};
+
+static int pgp_pkey_verify_sig_add_data(struct crypto_key_verify_context *ctx,
+					const void *data, size_t datalen);
+static int pgp_pkey_verify_sig_end(struct crypto_key_verify_context *ctx,
+				   const u8 *sig, size_t siglen);
+static void pgp_pkey_verify_sig_cancel(struct crypto_key_verify_context *ctx);
+
+struct pgp_pkey_sig_parse_context {
+	struct pgp_parse_context pgp;
+	struct pgp_sig_parameters params;
+};
+
+static int pgp_pkey_parse_signature(struct pgp_parse_context *context,
+				    enum pgp_packet_tag type,
+				    u8 headerlen,
+				    const u8 *data,
+				    size_t datalen)
+{
+	struct pgp_pkey_sig_parse_context *ctx =
+		container_of(context, struct pgp_pkey_sig_parse_context, pgp);
+
+	return pgp_parse_sig_params(&data, &datalen, &ctx->params);
+}
+
+/*
+ * Begin the process of verifying a DSA signature.
+ *
+ * This involves allocating the hash into which first the data and then the
+ * metadata will be put, and parsing the signature to check that it matches the
+ * key.
+ */
+struct crypto_key_verify_context *pgp_pkey_verify_sig_begin(
+	struct key *crypto_key, const u8 *sigdata, size_t siglen)
+{
+	struct pgp_pkey_sig_parse_context p;
+	struct public_key_signature *sig;
+	struct crypto_shash *tfm;
+	const struct public_key *key = crypto_key->payload.data;
+	size_t digest_size, desc_size;
+	int ret;
+
+	kenter("{%d},,%zu", key_serial(crypto_key), siglen);
+
+	if (!key) {
+		kleave(" = -ENOKEY [no public key]");
+		return ERR_PTR(-ENOKEY);
+	}
+
+	p.pgp.types_of_interest = (1 << PGP_PKT_SIGNATURE);
+	p.pgp.process_packet = pgp_pkey_parse_signature;
+	ret = pgp_parse_packets(sigdata, siglen, &p.pgp);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	if (p.params.pubkey_algo >= PGP_PUBKEY__LAST ||
+	    !pgp_public_key_algorithms[p.params.pubkey_algo]) {
+		pr_debug("Unsupported public key algorithm %u\n",
+			 p.params.pubkey_algo);
+		return ERR_PTR(-ENOKEY);
+	}
+
+	if (pgp_public_key_algorithms[p.params.pubkey_algo] != key->algo) {
+		kleave(" = -ENOKEY [wrong pk algo]");
+		return ERR_PTR(-ENOKEY);
+	}
+
+	if (!(key->capabilities & PKEY_CAN_VERIFY)) {
+		kleave(" = -EKEYREJECTED [key can't verify]");
+		return ERR_PTR(-EKEYREJECTED);
+	}
+
+	if (p.params.hash_algo >= PGP_HASH__LAST ||
+	    !pgp_hash_algorithms[p.params.hash_algo]) {
+		kleave(" = -ENOPKG [hash]");
+		return ERR_PTR(-ENOPKG);
+	}
+
+	pr_debug("Signature generated with %s hash\n",
+		 pgp_hash_algorithms[p.params.hash_algo]);
+
+	if (memcmp(&p.params.issuer, key->key_id, 8) != 0) {
+		kleave(" = -ENOKEY [wrong key ID]");
+		return ERR_PTR(-ENOKEY);
+	}
+
+	if (p.params.signature_type != PGP_SIG_BINARY_DOCUMENT_SIG &&
+	    p.params.signature_type != PGP_SIG_STANDALONE_SIG) {
+		/* We don't want to canonicalise */
+		kleave(" = -EOPNOTSUPP [canon]");
+		return ERR_PTR(-EOPNOTSUPP);
+	}
+
+	/* Allocate the hashing algorithm we're going to need and find out how
+	 * big the hash operational data will be.
+	 */
+	tfm = crypto_alloc_shash(pgp_hash_algorithms[p.params.hash_algo], 0, 0);
+	if (IS_ERR(tfm))
+		return PTR_ERR(tfm) == -ENOENT ?
+			ERR_PTR(-ENOPKG) : ERR_CAST(tfm);
+
+	desc_size = crypto_shash_descsize(tfm);
+	digest_size = crypto_shash_digestsize(tfm);
+
+	/* We allocate the hash operational data storage on the end of our
+	 * context data.
+	 */
+	sig = kzalloc(sizeof(*sig) + desc_size + digest_size, GFP_KERNEL);
+	if (!sig) {
+		crypto_free_shash(tfm);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	sig->base.key		= crypto_key;
+	sig->base.add_data	= pgp_pkey_verify_sig_add_data;
+	sig->base.end		= pgp_pkey_verify_sig_end;
+	sig->base.cancel	= pgp_pkey_verify_sig_cancel;
+	sig->pkey_hash_algo	= pgp_pubkey_hash[p.params.hash_algo].algo;
+	sig->digest		= (u8 *)sig + sizeof(*sig) + desc_size;
+	sig->digest_size	= digest_size;
+	sig->hash.tfm		= tfm;
+	sig->hash.flags		= CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	ret = crypto_shash_init(&sig->hash);
+	if (ret < 0) {
+		crypto_free_shash(sig->hash.tfm);
+		kfree(sig);
+		return ERR_PTR(ret);
+	}
+
+	key_get(sig->base.key);
+	kleave(" = %p", sig);
+	return &sig->base;
+}
+
+/*
+ * Load data into the hash
+ */
+static int pgp_pkey_verify_sig_add_data(struct crypto_key_verify_context *ctx,
+					const void *data, size_t datalen)
+{
+	struct public_key_signature *sig =
+		container_of(ctx, struct public_key_signature, base);
+
+	return crypto_shash_update(&sig->hash, data, datalen);
+}
+
+struct pgp_pkey_sig_digest_context {
+	struct pgp_parse_context pgp;
+	const struct public_key *key;
+	struct public_key_signature *sig;
+};
+
+/*
+ * Extract required metadata from the signature packet and add what we need to
+ * to the hash.
+ */
+static int pgp_pkey_digest_signature(struct pgp_parse_context *context,
+				     enum pgp_packet_tag type,
+				     u8 headerlen,
+				     const u8 *data,
+				     size_t datalen)
+{
+	struct pgp_pkey_sig_digest_context *ctx =
+		container_of(context, struct pgp_pkey_sig_digest_context, pgp);
+	enum pgp_signature_version version;
+	int i;
+
+	kenter(",%u,%u,,%zu", type, headerlen, datalen);
+
+	version = *data;
+	if (version == PGP_SIG_VERSION_3) {
+		/* We just include an excerpt of the metadata from a V3
+		 * signature.
+		 */
+		crypto_shash_update(&ctx->sig->hash, data + 1, 5);
+		data += sizeof(struct pgp_signature_v3_packet);
+		datalen -= sizeof(struct pgp_signature_v3_packet);
+	} else if (version == PGP_SIG_VERSION_4) {
+		/* We add the whole metadata header and some of the hashed data
+		 * for a V4 signature, plus a trailer.
+		 */
+		size_t hashedsz, unhashedsz;
+		u8 trailer[6];
+
+		hashedsz = 4 + 2 + (data[4] << 8) + data[5];
+		crypto_shash_update(&ctx->sig->hash, data, hashedsz);
+
+		trailer[0] = version;
+		trailer[1] = 0xffU;
+		trailer[2] = hashedsz >> 24;
+		trailer[3] = hashedsz >> 16;
+		trailer[4] = hashedsz >> 8;
+		trailer[5] = hashedsz;
+
+		crypto_shash_update(&ctx->sig->hash, trailer, 6);
+		data += hashedsz;
+		datalen -= hashedsz;
+
+		unhashedsz = 2 + (data[0] << 8) + data[1];
+		data += unhashedsz;
+		datalen -= unhashedsz;
+	}
+
+	if (datalen <= 2) {
+		kleave(" = -EBADMSG");
+		return -EBADMSG;
+	}
+
+	/* There's a quick check on the hash available. */
+	ctx->sig->signed_hash_msw[0] = *data++;
+	ctx->sig->signed_hash_msw[1] = *data++;
+	datalen -= 2;
+
+	/* And then the cryptographic data, which we'll need for the
+	 * algorithm.
+	 */
+	for (i = 0; i < ctx->key->algo->n_sig_mpi; i++) {
+		unsigned int remaining = datalen;
+		if (remaining == 0) {
+			pr_debug("short %zu mpi %d\n", datalen, i);
+			return -EBADMSG;
+		}
+		ctx->sig->mpi[i] = mpi_read_from_buffer(data, &remaining);
+		if (!ctx->sig->mpi[i])
+			return -ENOMEM;
+		data += remaining;
+		datalen -= remaining;
+	}
+
+	if (datalen != 0) {
+		kleave(" = -EBADMSG [trailer %zu]", datalen);
+		return -EBADMSG;
+	}
+
+	kleave(" = 0");
+	return 0;
+}
+
+/*
+ * The data is now all loaded into the hash; load the metadata, finalise the
+ * hash and perform the verification step.
+ */
+static int pgp_pkey_verify_sig_end(struct crypto_key_verify_context *ctx,
+				   const u8 *sigdata, size_t siglen)
+{
+	struct public_key_signature *sig =
+		container_of(ctx, struct public_key_signature, base);
+	const struct public_key *key = sig->base.key->payload.data;
+	struct pgp_pkey_sig_digest_context p;
+	int ret;
+
+	kenter("");
+
+	/* Firstly we add metadata, starting with some of the data from the
+	 * signature packet */
+	p.pgp.types_of_interest = (1 << PGP_PKT_SIGNATURE);
+	p.pgp.process_packet = pgp_pkey_digest_signature;
+	p.key = key;
+	p.sig = sig;
+	ret = pgp_parse_packets(sigdata, siglen, &p.pgp);
+	if (ret < 0)
+		goto error_free_ctx;
+
+	crypto_shash_final(&sig->hash, sig->digest);
+
+	ret = key->algo->verify(key, sig);
+
+error_free_ctx:
+	pgp_pkey_verify_sig_cancel(ctx);
+	kleave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * Cancel an in-progress data loading
+ */
+static void pgp_pkey_verify_sig_cancel(struct crypto_key_verify_context *ctx)
+{
+	struct public_key_signature *sig =
+		container_of(ctx, struct public_key_signature, base);
+	int i;
+
+	kenter("");
+
+	/* !!! Do we need to tell the crypto layer to cancel too? */
+	crypto_free_shash(sig->hash.tfm);
+	key_put(sig->base.key);
+	for (i = 0; i < ARRAY_SIZE(sig->mpi); i++)
+		mpi_free(sig->mpi[i]);
+	kfree(sig);
+
+	kleave("");
+}


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 15/29] KEYS: PGP format signature parser [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (13 preceding siblings ...)
  2012-05-10 23:41 ` [PATCH 14/29] KEYS: PGP-based public key signature verification " David Howells
@ 2012-05-10 23:41 ` David Howells
  2012-05-10 23:41 ` [PATCH 16/29] KEYS: Provide a function to load keys from a PGP keyring blob " David Howells
                   ` (17 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:41 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

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_key_parser.c |    1 
 security/keys/crypto/pgp_parser.h     |    6 ++
 security/keys/crypto/pgp_sig_parser.c |  104 +++++++++++++++++++++++++++++++++
 4 files changed, 112 insertions(+), 0 deletions(-)
 create mode 100644 security/keys/crypto/pgp_sig_parser.c


diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile
index d4ac8ac..fa7746d 100644
--- a/security/keys/crypto/Makefile
+++ b/security/keys/crypto/Makefile
@@ -12,4 +12,5 @@ obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o
 obj-$(CONFIG_CRYPTO_KEY_PGP_PARSER) += pgp_parser.o
 pgp_parser-y := \
 	pgp_key_parser.o \
+	pgp_sig_parser.o \
 	pgp_pubkey_sig.o
diff --git a/security/keys/crypto/pgp_key_parser.c b/security/keys/crypto/pgp_key_parser.c
index 4efc4de..1407e2e 100644
--- a/security/keys/crypto/pgp_key_parser.c
+++ b/security/keys/crypto/pgp_key_parser.c
@@ -324,6 +324,7 @@ static struct crypto_key_parser pgp_key_parser = {
 	.owner		= THIS_MODULE,
 	.name		= "pgp",
 	.instantiate	= pgp_key_instantiate,
+	.verify_sig_begin = pgp_verify_sig_begin,
 };
 
 /*
diff --git a/security/keys/crypto/pgp_parser.h b/security/keys/crypto/pgp_parser.h
index a6192ce..73c900e 100644
--- a/security/keys/crypto/pgp_parser.h
+++ b/security/keys/crypto/pgp_parser.h
@@ -23,6 +23,12 @@ extern const
 struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST];
 
 /*
+ * pgp_sig_parser.c
+ */
+extern struct crypto_key_verify_context *pgp_verify_sig_begin(
+	struct key *keyring, const u8 *sig, size_t siglen);
+
+/*
  * pgp_pubkey_sig.c
  */
 extern struct crypto_key_verify_context *pgp_pkey_verify_sig_begin(
diff --git a/security/keys/crypto/pgp_sig_parser.c b/security/keys/crypto/pgp_sig_parser.c
new file mode 100644
index 0000000..b72c505
--- /dev/null
+++ b/security/keys/crypto/pgp_sig_parser.c
@@ -0,0 +1,104 @@
+/* Handling for PGP public key signature data [RFC 4880]
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "PGPSIG: "fmt
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pgp.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(-EINVAL);
+
+	sprintf(criterion, "id:%08x%08x",
+		be32_to_cpu(p.params.issuer32[0]),
+		be32_to_cpu(p.params.issuer32[1]));
+
+	pr_debug("Look up: %s\n", criterion);
+
+	key = keyring_search(make_key_ref(keyring, 1),
+			     &key_type_crypto, criterion);
+	if (IS_ERR(key))
+		return ERR_CAST(key);
+
+	pr_debug("Found key %x\n", key_serial(key_ref_to_ptr(key)));
+	return key_ref_to_ptr(key);
+}
+
+/*
+ * Attempt to parse a signature as a PGP packet format blob and find a
+ * matching key.
+ */
+struct crypto_key_verify_context *pgp_verify_sig_begin(
+	struct key *keyring, const u8 *sig, size_t siglen)
+{
+	struct crypto_key_verify_context *ctx;
+	struct key *key;
+
+	key = find_key_for_pgp_sig(keyring, sig, siglen);
+	if (IS_ERR(key))
+		return ERR_CAST(key);
+
+	/* We only handle in-kernel public key signatures for the moment */
+	ctx = pgp_pkey_verify_sig_begin(key, sig, siglen);
+	key_put(key);
+	return ctx;
+}


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 16/29] KEYS: Provide a function to load keys from a PGP keyring blob [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (14 preceding siblings ...)
  2012-05-10 23:41 ` [PATCH 15/29] KEYS: PGP format signature parser " David Howells
@ 2012-05-10 23:41 ` David Howells
  2012-05-10 23:42 ` [PATCH 17/29] Provide macros for forming the name of an ELF note and its section " David Howells
                   ` (16 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:41 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

Provide a function to load keys from a PGP keyring blob for use in initialising
the module signing key keyring:

	int load_PGP_keys(const u8 *pgpdata, size_t pgpdatalen,
			  struct key *keyring, const char *descprefix);

The keys are labelled with descprefix plus a number to uniquify them.  The keys
will actually be identified by the ID calculated from the PGP data rather than
by the description, so this shouldn't be a problem.

The keys are attached to the keyring supplied.

Looking as root in /proc/keys after the module signing keyring has been loaded:

24460d1c I-----     1 perm 3f010000     0     0 crypto    modsign.0: dsa 5acc2142 []
3ca85723 I-----     1 perm 1f010000     0     0 keyring   .module_sign: 1/4

Signed-off-by: David Howells <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     |   90 ++++++++++++++++++++++++++++++++
 5 files changed, 123 insertions(+), 0 deletions(-)
 create mode 100644 security/keys/crypto/pgp_preload.c


diff --git a/Documentation/security/keys-crypto.txt b/Documentation/security/keys-crypto.txt
index a964717..ba2ab55 100644
--- a/Documentation/security/keys-crypto.txt
+++ b/Documentation/security/keys-crypto.txt
@@ -10,6 +10,7 @@ Contents:
     - Signature verification.
   - Implementing crypto parsers.
   - Implementing crypto subtypes.
+  - Initial PGP key preloading.
 
 
 ========
@@ -280,3 +281,22 @@ There are a number of operations defined by the subtype:
      Mandatory.  This should free the memory associated with the key.  The
      crypto key will look after freeing the fingerprint and releasing the
      reference on the subtype module.
+
+
+=======================
+INITIAL PGP KEY LOADING
+=======================
+
+A function is provided to perform an initial load of a set of public keys bound
+into a PGP packet format blob:
+
+	int preload_pgp_keys(const u8 *pgpdata, size_t pgpdatalen,
+			     struct key *keyring, const char *descprefix);
+
+This takes the blob of data defined by pgpdata and pgpdatalen, extracts keys
+from them and adds them to the specified keyring.  The keys are labelled with
+descprefix plus a simple uniquifier - it is not expected that the description
+will be used to identify the key.  The description is required to prevent all
+but the last key being discarded when the keys are linked into the keyring.
+
+This function is only available during initial kernel set up.
diff --git a/include/keys/crypto-type.h b/include/keys/crypto-type.h
index 6b93366..710e77f 100644
--- a/include/keys/crypto-type.h
+++ b/include/keys/crypto-type.h
@@ -31,4 +31,7 @@ extern void verify_sig_cancel(struct crypto_key_verify_context *ctx);
  * The payload is at the discretion of the subtype.
  */
 
+extern __init int preload_pgp_keys(const u8 *pgpdata, size_t pgpdatalen,
+				   struct key *keyring, const char *descprefix);
+
 #endif /* _KEYS_CRYPTO_TYPE_H */
diff --git a/security/keys/crypto/Kconfig b/security/keys/crypto/Kconfig
index 1c2ae55..8af0155 100644
--- a/security/keys/crypto/Kconfig
+++ b/security/keys/crypto/Kconfig
@@ -40,3 +40,12 @@ config CRYPTO_KEY_PGP_PARSER
 	  This option provides support for parsing PGP (RFC 4880) format blobs
 	  for key data and provides the ability to instantiate a crypto key
 	  from a public key packet found inside the blob.
+
+config PGP_PRELOAD
+	bool "PGP public key preloading facility"
+	select PGP_LIBRARY
+	select CRYPTO_KEY_PGP_PARSER
+	help
+	  This option provides a facility for the kernel to preload PGP-wrapped
+	  bundles of keys during boot.  It is used by module signing to load
+	  the module signing keys for example.
diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile
index fa7746d..4162ecb 100644
--- a/security/keys/crypto/Makefile
+++ b/security/keys/crypto/Makefile
@@ -8,6 +8,7 @@ crypto_keys-y := crypto_type.o crypto_verify.o
 obj-$(CONFIG_CRYPTO_KEY_PUBLIC_KEY_SUBTYPE) += public_key.o
 obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) += crypto_rsa.o
 obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o
+obj-$(CONFIG_PGP_PRELOAD) += pgp_preload.o
 
 obj-$(CONFIG_CRYPTO_KEY_PGP_PARSER) += pgp_parser.o
 pgp_parser-y := \
diff --git a/security/keys/crypto/pgp_preload.c b/security/keys/crypto/pgp_preload.c
new file mode 100644
index 0000000..25154e3
--- /dev/null
+++ b/security/keys/crypto/pgp_preload.c
@@ -0,0 +1,90 @@
+/* Cryptographic key request handling
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ *
+ * See Documentation/security/keys-crypto.txt
+ */
+
+#include <linux/module.h>
+#include <linux/key.h>
+#include <linux/pgp.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;
+
+	sprintf(ctx->descbuf + ctx->dsize, "%d", ctx->key_n++);
+
+	key = key_create_or_update(ctx->keyring, "crypto", ctx->descbuf,
+				   data - headerlen, datalen + headerlen,
+				   KEY_POS_ALL | KEY_USR_VIEW,
+				   KEY_ALLOC_NOT_IN_QUOTA);
+
+	if (IS_ERR(key))
+		return PTR_ERR(key);
+
+	pr_notice("Loaded %s key: %s\n",
+		  key_ref_to_ptr(key)->description,
+		  crypto_key_id(key_ref_to_ptr(key)));
+
+	key_ref_put(key);
+	return 0;
+}
+
+/**
+ * preload_pgp_keys - Load keys from a PGP keyring blob
+ * @pgpdata: The PGP keyring blob containing the keys.
+ * @pgpdatalen: The size of the @pgpdata blob.
+ * @keyring: The keyring to add the new keys to.
+ * @descprefix: The key description prefix.
+ *
+ * Preload a pack of keys from a PGP keyring blob.
+ *
+ * The keys are given description of @descprefix + the number of the key in the
+ * list.  Since keys can be matched on their key IDs independently of the key
+ * description, the description is mostly irrelevant apart from the fact that
+ * keys of the same description displace one another from a keyring.
+ *
+ * The caller should override the current creds if they want the keys to be
+ * owned by someone other than the current process's owner.  Keys will not be
+ * accounted towards the owner's quota.
+ *
+ * This function may only be called whilst the kernel is booting.
+ */
+int __init preload_pgp_keys(const u8 *pgpdata, size_t pgpdatalen,
+			    struct key *keyring, const char *descprefix)
+{
+	struct preload_pgp_keys_context ctx;
+
+	ctx.pgp.types_of_interest =
+		(1 << PGP_PKT_PUBLIC_KEY) | (1 << PGP_PKT_PUBLIC_SUBKEY);
+	ctx.pgp.process_packet = found_pgp_key;
+	ctx.keyring = make_key_ref(keyring, 1);
+	ctx.key_n = 0;
+	ctx.dsize = strlen(descprefix);
+	strcpy(ctx.descbuf, descprefix);
+
+	return pgp_parse_packets(pgpdata, pgpdatalen, &ctx.pgp);
+}


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 17/29] Provide macros for forming the name of an ELF note and its section [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (15 preceding siblings ...)
  2012-05-10 23:41 ` [PATCH 16/29] KEYS: Provide a function to load keys from a PGP keyring blob " David Howells
@ 2012-05-10 23:42 ` David Howells
  2012-05-10 23:42 ` [PATCH 18/29] Guard check in module loader against integer overflow " David Howells
                   ` (15 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:42 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

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 files changed, 4 insertions(+), 0 deletions(-)


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 */


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 18/29] Guard check in module loader against integer overflow [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (16 preceding siblings ...)
  2012-05-10 23:42 ` [PATCH 17/29] Provide macros for forming the name of an ELF note and its section " David Howells
@ 2012-05-10 23:42 ` David Howells
  2012-05-10 23:42 ` [PATCH 19/29] MODSIGN: Add indications of module ELF types " David Howells
                   ` (14 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:42 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

The check:

	if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr))

may not work if there's an overflow in the right-hand side of the condition.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 kernel/module.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)


diff --git a/kernel/module.c b/kernel/module.c
index 78ac6ec..377cb06 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2429,7 +2429,8 @@ static int copy_and_check(struct load_info *info,
 		goto free_hdr;
 	}
 
-	if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) {
+	if (hdr->e_shoff >= len ||
+	    hdr->e_shnum * sizeof(Elf_Shdr) > len - hdr->e_shoff) {
 		err = -ENOEXEC;
 		goto free_hdr;
 	}


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 19/29] MODSIGN: Add indications of module ELF types [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (17 preceding siblings ...)
  2012-05-10 23:42 ` [PATCH 18/29] Guard check in module loader against integer overflow " David Howells
@ 2012-05-10 23:42 ` David Howells
  2012-05-10 23:42 ` [PATCH 20/29] MODSIGN: Provide gitignore and make clean rules for extra files " David Howells
                   ` (13 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:42 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

Add per-arch indications of module ELF types and relocation table entry types.

Signed-Off-By: David Howells <dhowells@redhat.com>
---

 arch/alpha/include/asm/module.h   |    3 +++
 arch/arm/include/asm/module.h     |    5 +++++
 arch/cris/include/asm/module.h    |    5 +++++
 arch/h8300/include/asm/module.h   |    5 +++++
 arch/ia64/include/asm/module.h    |    5 +++++
 arch/m32r/include/asm/module.h    |    5 +++++
 arch/m68k/include/asm/module.h    |    5 +++++
 arch/mips/include/asm/module.h    |   12 ++++++++++--
 arch/parisc/include/asm/module.h  |    8 ++++++++
 arch/powerpc/include/asm/module.h |   10 ++++++++++
 arch/s390/include/asm/module.h    |    3 +++
 arch/x86/include/asm/module.h     |    6 ++++++
 include/asm-generic/module.h      |   10 ++++++++++
 13 files changed, 80 insertions(+), 2 deletions(-)


diff --git a/arch/alpha/include/asm/module.h b/arch/alpha/include/asm/module.h
index 7b63743..3d5a3ea 100644
--- a/arch/alpha/include/asm/module.h
+++ b/arch/alpha/include/asm/module.h
@@ -6,6 +6,7 @@ struct mod_arch_specific
 	unsigned int gotsecindex;
 };
 
+#define MODULES_ARE_ELF64
 #define Elf_Sym Elf64_Sym
 #define Elf_Shdr Elf64_Shdr
 #define Elf_Ehdr Elf64_Ehdr
@@ -13,6 +14,8 @@ struct mod_arch_specific
 #define Elf_Dyn Elf64_Dyn
 #define Elf_Rel Elf64_Rel
 #define Elf_Rela Elf64_Rela
+#define ELF_R_TYPE(X)	ELF64_R_TYPE(X)
+#define ELF_R_SYM(X)	ELF64_R_SYM(X)
 
 #define ARCH_SHF_SMALL SHF_ALPHA_GPREL
 
diff --git a/arch/arm/include/asm/module.h b/arch/arm/include/asm/module.h
index 6c6809f..f47d9cd 100644
--- a/arch/arm/include/asm/module.h
+++ b/arch/arm/include/asm/module.h
@@ -1,9 +1,14 @@
 #ifndef _ASM_ARM_MODULE_H
 #define _ASM_ARM_MODULE_H
 
+#define MODULES_ARE_ELF32
 #define Elf_Shdr	Elf32_Shdr
 #define Elf_Sym		Elf32_Sym
 #define Elf_Ehdr	Elf32_Ehdr
+#define Elf_Rel		Elf32_Rel
+#define Elf_Rela	Elf32_Rela
+#define ELF_R_TYPE(X)	ELF32_R_TYPE(X)
+#define ELF_R_SYM(X)	ELF32_R_SYM(X)
 
 struct unwind_table;
 
diff --git a/arch/cris/include/asm/module.h b/arch/cris/include/asm/module.h
index 7ee7231..03f7b2e 100644
--- a/arch/cris/include/asm/module.h
+++ b/arch/cris/include/asm/module.h
@@ -3,7 +3,12 @@
 /* cris is simple */
 struct mod_arch_specific { };
 
+#define MODULES_ARE_ELF32
 #define Elf_Shdr Elf32_Shdr
 #define Elf_Sym Elf32_Sym
 #define Elf_Ehdr Elf32_Ehdr
+#define Elf_Rel Elf32_Rel
+#define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X)	ELF32_R_TYPE(X)
+#define ELF_R_SYM(X)	ELF32_R_SYM(X)
 #endif /* _ASM_CRIS_MODULE_H */
diff --git a/arch/h8300/include/asm/module.h b/arch/h8300/include/asm/module.h
index 8e46724..5140128 100644
--- a/arch/h8300/include/asm/module.h
+++ b/arch/h8300/include/asm/module.h
@@ -4,8 +4,13 @@
  * This file contains the H8/300 architecture specific module code.
  */
 struct mod_arch_specific { };
+#define MODULES_ARE_ELF32
 #define Elf_Shdr Elf32_Shdr
 #define Elf_Sym Elf32_Sym
 #define Elf_Ehdr Elf32_Ehdr
+#define Elf_Rel Elf32_Rel
+#define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X)	ELF32_R_TYPE(X)
+#define ELF_R_SYM(X)	ELF32_R_SYM(X)
 
 #endif /* _ASM_H8/300_MODULE_H */
diff --git a/arch/ia64/include/asm/module.h b/arch/ia64/include/asm/module.h
index 908eaef..3c4cd94 100644
--- a/arch/ia64/include/asm/module.h
+++ b/arch/ia64/include/asm/module.h
@@ -29,9 +29,14 @@ struct mod_arch_specific {
 	unsigned int next_got_entry;	/* index of next available got entry */
 };
 
+#define MODULES_ARE_ELF64
 #define Elf_Shdr	Elf64_Shdr
 #define Elf_Sym		Elf64_Sym
 #define Elf_Ehdr	Elf64_Ehdr
+#define Elf_Rel		Elf64_Rel
+#define Elf_Rela	Elf64_Rela
+#define ELF_R_TYPE(X)	ELF64_R_TYPE(X)
+#define ELF_R_SYM(X)	ELF64_R_SYM(X)
 
 #define MODULE_PROC_FAMILY	"ia64"
 #define MODULE_ARCH_VERMAGIC	MODULE_PROC_FAMILY \
diff --git a/arch/m32r/include/asm/module.h b/arch/m32r/include/asm/module.h
index eb73ee0..7146455 100644
--- a/arch/m32r/include/asm/module.h
+++ b/arch/m32r/include/asm/module.h
@@ -3,8 +3,13 @@
 
 struct mod_arch_specific { };
 
+#define MODULES_ARE_ELF32
 #define Elf_Shdr	Elf32_Shdr
 #define Elf_Sym		Elf32_Sym
 #define Elf_Ehdr	Elf32_Ehdr
+#define Elf_Rel		Elf32_Rel
+#define Elf_Rela	Elf32_Rela
+#define ELF_R_TYPE(X)	ELF32_R_TYPE(X)
+#define ELF_R_SYM(X)	ELF32_R_SYM(X)
 
 #endif /* _ASM_M32R_MODULE_H */
diff --git a/arch/m68k/include/asm/module.h b/arch/m68k/include/asm/module.h
index edffe66..9e2cd74 100644
--- a/arch/m68k/include/asm/module.h
+++ b/arch/m68k/include/asm/module.h
@@ -36,8 +36,13 @@ struct module;
 extern void module_fixup(struct module *mod, struct m68k_fixup_info *start,
 			 struct m68k_fixup_info *end);
 
+#define MODULES_ARE_ELF32
 #define Elf_Shdr Elf32_Shdr
 #define Elf_Sym Elf32_Sym
 #define Elf_Ehdr Elf32_Ehdr
+#define Elf_Rel		Elf32_Rel
+#define Elf_Rela	Elf32_Rela
+#define ELF_R_TYPE(X)	ELF32_R_TYPE(X)
+#define ELF_R_SYM(X)	ELF32_R_SYM(X)
 
 #endif /* _ASM_M68K_MODULE_H */
diff --git a/arch/mips/include/asm/module.h b/arch/mips/include/asm/module.h
index 7467d1d..4404cca 100644
--- a/arch/mips/include/asm/module.h
+++ b/arch/mips/include/asm/module.h
@@ -33,11 +33,15 @@ typedef struct {
 } Elf64_Mips_Rela;
 
 #ifdef CONFIG_32BIT
-
+#define MODULES_ARE_ELF32
 #define Elf_Shdr	Elf32_Shdr
 #define Elf_Sym		Elf32_Sym
 #define Elf_Ehdr	Elf32_Ehdr
 #define Elf_Addr	Elf32_Addr
+#define Elf_Rel		Elf32_Rel
+#define Elf_Rela	Elf32_Rela
+#define ELF_R_TYPE(X)	ELF32_R_TYPE(X)
+#define ELF_R_SYM(X)	ELF32_R_SYM(X)
 
 #define Elf_Mips_Rel	Elf32_Rel
 #define Elf_Mips_Rela	Elf32_Rela
@@ -48,11 +52,15 @@ typedef struct {
 #endif
 
 #ifdef CONFIG_64BIT
-
+#define MODULES_ARE_ELF64
 #define Elf_Shdr	Elf64_Shdr
 #define Elf_Sym		Elf64_Sym
 #define Elf_Ehdr	Elf64_Ehdr
 #define Elf_Addr	Elf64_Addr
+#define Elf_Rel		Elf64_Rel
+#define Elf_Rela	Elf64_Rela
+#define ELF_R_TYPE(X)	ELF64_R_TYPE(X)
+#define ELF_R_SYM(X)	ELF64_R_SYM(X)
 
 #define Elf_Mips_Rel	Elf64_Mips_Rel
 #define Elf_Mips_Rela	Elf64_Mips_Rela
diff --git a/arch/parisc/include/asm/module.h b/arch/parisc/include/asm/module.h
index 1f41234..3e13f69 100644
--- a/arch/parisc/include/asm/module.h
+++ b/arch/parisc/include/asm/module.h
@@ -4,17 +4,25 @@
  * This file contains the parisc architecture specific module code.
  */
 #ifdef CONFIG_64BIT
+#define MODULES_ARE_ELF64
 #define Elf_Shdr Elf64_Shdr
 #define Elf_Sym Elf64_Sym
 #define Elf_Ehdr Elf64_Ehdr
 #define Elf_Addr Elf64_Addr
+#define Elf_Rel	 Elf64_Rel
 #define Elf_Rela Elf64_Rela
+#define ELF_R_TYPE(X)	ELF64_R_TYPE(X)
+#define ELF_R_SYM(X)	ELF64_R_SYM(X)
 #else
+#define MODULES_ARE_ELF32
 #define Elf_Shdr Elf32_Shdr
 #define Elf_Sym Elf32_Sym
 #define Elf_Ehdr Elf32_Ehdr
 #define Elf_Addr Elf32_Addr
+#define Elf_Rel  Elf32_Rel
 #define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X)	ELF32_R_TYPE(X)
+#define ELF_R_SYM(X)	ELF32_R_SYM(X)
 #endif
 
 struct unwind_table;
diff --git a/arch/powerpc/include/asm/module.h b/arch/powerpc/include/asm/module.h
index 0192a4e..e949704 100644
--- a/arch/powerpc/include/asm/module.h
+++ b/arch/powerpc/include/asm/module.h
@@ -60,16 +60,26 @@ struct mod_arch_specific {
  */
 
 #ifdef __powerpc64__
+#    define MODULES_ARE_ELF64
 #    define Elf_Shdr	Elf64_Shdr
 #    define Elf_Sym	Elf64_Sym
 #    define Elf_Ehdr	Elf64_Ehdr
+#    define Elf_Rel Elf64_Rel
+#    define Elf_Rela Elf64_Rela
+#    define ELF_R_TYPE(X)	ELF64_R_TYPE(X)
+#    define ELF_R_SYM(X)	ELF64_R_SYM(X)
 #    ifdef MODULE
 	asm(".section .stubs,\"ax\",@nobits; .align 3; .previous");
 #    endif
 #else
+#    define MODULES_ARE_ELF32
 #    define Elf_Shdr	Elf32_Shdr
 #    define Elf_Sym	Elf32_Sym
 #    define Elf_Ehdr	Elf32_Ehdr
+#    define Elf_Rel Elf32_Rel
+#    define Elf_Rela Elf32_Rela
+#    define ELF_R_TYPE(X)	ELF32_R_TYPE(X)
+#    define ELF_R_SYM(X)	ELF32_R_SYM(X)
 #    ifdef MODULE
 	asm(".section .plt,\"ax\",@nobits; .align 3; .previous");
 	asm(".section .init.plt,\"ax\",@nobits; .align 3; .previous");
diff --git a/arch/s390/include/asm/module.h b/arch/s390/include/asm/module.h
index 1cc1c5a..b64dab0 100644
--- a/arch/s390/include/asm/module.h
+++ b/arch/s390/include/asm/module.h
@@ -29,14 +29,17 @@ struct mod_arch_specific
 };
 
 #ifdef __s390x__
+#define MODULES_ARE_ELF64
 #define ElfW(x) Elf64_ ## x
 #define ELFW(x) ELF64_ ## x
 #else
+#define MODULES_ARE_ELF32
 #define ElfW(x) Elf32_ ## x
 #define ELFW(x) ELF32_ ## x
 #endif
 
 #define Elf_Addr ElfW(Addr)
+#define Elf_Rel ElfW(Rel)
 #define Elf_Rela ElfW(Rela)
 #define Elf_Shdr ElfW(Shdr)
 #define Elf_Sym ElfW(Sym)
diff --git a/arch/x86/include/asm/module.h b/arch/x86/include/asm/module.h
index 9eae775..724f173 100644
--- a/arch/x86/include/asm/module.h
+++ b/arch/x86/include/asm/module.h
@@ -63,4 +63,10 @@
 # define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY
 #endif
 
+#ifdef CONFIG_X86_32
+#define MODULE_HAS_ELF_REL_ONLY
+#else
+#define MODULE_HAS_ELF_RELA_ONLY
+#endif
+
 #endif /* _ASM_X86_MODULE_H */
diff --git a/include/asm-generic/module.h b/include/asm-generic/module.h
index ed5b44d..e053617b 100644
--- a/include/asm-generic/module.h
+++ b/include/asm-generic/module.h
@@ -10,13 +10,23 @@ struct mod_arch_specific
 };
 
 #ifdef CONFIG_64BIT
+#define MODULES_ARE_ELF64
 #define Elf_Shdr Elf64_Shdr
 #define Elf_Sym Elf64_Sym
 #define Elf_Ehdr Elf64_Ehdr
+#define Elf_Rel		Elf64_Rel
+#define Elf_Rela	Elf64_Rela
+#define ELF_R_TYPE(X)	ELF64_R_TYPE(X)
+#define ELF_R_SYM(X)	ELF64_R_SYM(X)
 #else
+#define MODULES_ARE_ELF32
 #define Elf_Shdr Elf32_Shdr
 #define Elf_Sym Elf32_Sym
 #define Elf_Ehdr Elf32_Ehdr
+#define Elf_Rel		Elf32_Rel
+#define Elf_Rela	Elf32_Rela
+#define ELF_R_TYPE(X)	ELF32_R_TYPE(X)
+#define ELF_R_SYM(X)	ELF32_R_SYM(X)
 #endif
 
 #endif /* __ASM_GENERIC_MODULE_H */


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 20/29] MODSIGN: Provide gitignore and make clean rules for extra files [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (18 preceding siblings ...)
  2012-05-10 23:42 ` [PATCH 19/29] MODSIGN: Add indications of module ELF types " David Howells
@ 2012-05-10 23:42 ` David Howells
  2012-05-10 23:42 ` [PATCH 21/29] MODSIGN: Provide Documentation and Kconfig options " David Howells
                   ` (12 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:42 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

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(+), 0 deletions(-)


diff --git a/.gitignore b/.gitignore
index 57af07c..7948eeb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,9 @@
 *.o.*
 *.a
 *.s
+*.ko.unsigned
+*.ko.digest
+*.ko.digest.sig
 *.ko
 *.so
 *.so.dbg
@@ -84,3 +87,12 @@ GTAGS
 *.orig
 *~
 \#*#
+
+#
+# GPG leavings from module signing
+#
+genkey
+modsign.pub
+modsign.sec
+random_seed
+trustdb.gpg
diff --git a/Makefile b/Makefile
index a06ee9f..1df8b14 100644
--- a/Makefile
+++ b/Makefile
@@ -1407,6 +1407,7 @@ clean: $(clean-dirs)
 	$(call cmd,rmfiles)
 	@find $(if $(KBUILD_EXTMOD), $(KBUILD_EXTMOD), .) $(RCS_FIND_IGNORE) \
 		\( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \
+		-o -name '*.ko.*' \
 		-o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \
 		-o -name '*.symtypes' -o -name 'modules.order' \
 		-o -name modules.builtin -o -name '.tmp_*.o.*' \
diff --git a/scripts/mod/.gitignore b/scripts/mod/.gitignore
index e9b7abe..223dfd6 100644
--- a/scripts/mod/.gitignore
+++ b/scripts/mod/.gitignore
@@ -1,4 +1,5 @@
 elfconfig.h
 mk_elfconfig
 modpost
+mod-extract
 


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 21/29] MODSIGN: Provide Documentation and Kconfig options [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (19 preceding siblings ...)
  2012-05-10 23:42 ` [PATCH 20/29] MODSIGN: Provide gitignore and make clean rules for extra files " David Howells
@ 2012-05-10 23:42 ` David Howells
  2012-05-10 23:43 ` [PATCH 22/29] MODSIGN: Sign modules during the build process " David Howells
                   ` (11 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:42 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

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                     |   53 ++++++++++
 3 files changed, 274 insertions(+), 0 deletions(-)
 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 6cfd71d..7cda3e6 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1409,6 +1409,59 @@ config MODULE_SRCVERSION_ALL
 	  the version).  With this option, such a "srcversion" field
 	  will be created for all modules.  If unsure, say N.
 
+config MODULE_SIG
+	bool "Module signature verification"
+	depends on MODULES
+	select CRYPTO_KEY_TYPE
+	select CRYPTO_KEY_PKEY_ALGO_DSA
+	select CRYPTO_KEY_PKEY_ALGO_RSA
+	select PGP_PARSER
+	select PGP_PRELOAD
+	help
+	  Check modules for valid signatures upon load.  For more information
+	  see:
+
+	  Documentation/module-signing.txt
+
+choice
+	prompt "Which hash algorithm should modules be signed with?"
+	depends on MODULE_SIG
+	help
+	  This determines which sort of hashing algorithm will be used during
+	  signature generation.  This algorithm _must_ be built into the kernel
+	  directly so that signature verification can take place.  It is not
+	  possible to load a signed module containing the algorithm to check
+	  the signature on that module.
+
+config MODULE_SIG_SHA1
+	bool "Sign modules with SHA-1"
+	select CRYPTO_SHA1
+
+config MODULE_SIG_SHA224
+	bool "Sign modules with SHA-224"
+	select CRYPTO_SHA224
+
+config MODULE_SIG_SHA256
+	bool "Sign modules with SHA-256"
+	select CRYPTO_SHA256
+
+config MODULE_SIG_SHA384
+	bool "Sign modules with SHA-384"
+	select CRYPTO_SHA384
+
+config MODULE_SIG_SHA512
+	bool "Sign modules with SHA-512"
+	select CRYPTO_SHA512
+
+endchoice
+
+config MODULE_SIG_FORCE
+	bool "Required modules to be validly signed (EXPERIMENTAL)"
+	depends on MODULE_SIG
+	help
+	  Reject unsigned modules or signed modules for which we don't have a
+	  key.
+
 endif # MODULES
 
 config INIT_ALL_POSSIBLE


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 22/29] MODSIGN: Sign modules during the build process [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (20 preceding siblings ...)
  2012-05-10 23:42 ` [PATCH 21/29] MODSIGN: Provide Documentation and Kconfig options " David Howells
@ 2012-05-10 23:43 ` David Howells
  2012-05-10 23:43 ` [PATCH 23/29] MODSIGN: Module signature verification stub " David Howells
                   ` (10 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:43 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

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(&sections[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(&sections[loop].sh_name);
+		Elf64_Word  sh_type	= get32(&sections[loop].sh_type);
+		Elf64_Xword sh_size	= get64(&sections[loop].sh_size);
+		Elf64_Xword sh_flags	= get64(&sections[loop].sh_flags);
+		Elf64_Word  sh_info	= get32(&sections[loop].sh_info);
+		Elf64_Off   sh_offset	= get64(&sections[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(&sections[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(&sections[canonlist[loop + 0]].sh_name);
+			const char *y = secstrings + get32(&sections[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(&sections[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(&sections[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(&sections[sect].sh_name);
+		Elf64_Word  sh_type	= get32(&sections[sect].sh_type);
+		Elf64_Xword sh_size	= get64(&sections[sect].sh_size);
+		Elf64_Xword sh_flags	= get64(&sections[sect].sh_flags);
+		Elf64_Word  sh_info	= get32(&sections[sect].sh_info);
+		Elf64_Off   sh_offset	= get64(&sections[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(&sections[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(&sections[loop].sh_name);
+		Elf32_Word  sh_type	= get32(&sections[loop].sh_type);
+		Elf32_Xword sh_size	= get32(&sections[loop].sh_size);
+		Elf32_Xword sh_flags	= get32(&sections[loop].sh_flags);
+		Elf64_Word  sh_info	= get32(&sections[loop].sh_info);
+		Elf32_Off   sh_offset	= get32(&sections[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(&sections[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(&sections[canonlist[loop + 0]].sh_name);
+			const char *y = secstrings + get32(&sections[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(&sections[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(&sections[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(&sections[sect].sh_name);
+		Elf32_Word  sh_type	= get32(&sections[sect].sh_type);
+		Elf32_Xword sh_size	= get32(&sections[sect].sh_size);
+		Elf32_Xword sh_flags	= get32(&sections[sect].sh_flags);
+		Elf32_Word  sh_info	= get32(&sections[sect].sh_info);
+		Elf32_Off   sh_offset	= get32(&sections[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


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 23/29] MODSIGN: Module signature verification stub [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (21 preceding siblings ...)
  2012-05-10 23:43 ` [PATCH 22/29] MODSIGN: Sign modules during the build process " David Howells
@ 2012-05-10 23:43 ` David Howells
  2012-05-10 23:43 ` [PATCH 24/29] MODSIGN: Provide module signing public keys to the kernel " David Howells
                   ` (9 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:43 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

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      |  109 +++++++++++++++++++++++++++++++++++++++++++
 kernel/module-verify.h      |   19 +++++++
 kernel/module.c             |   26 ++++++++--
 6 files changed, 230 insertions(+), 5 deletions(-)
 create mode 100644 kernel/module-verify-defs.h
 create mode 100644 kernel/module-verify.c
 create mode 100644 kernel/module-verify.h


diff --git a/include/linux/module.h b/include/linux/module.h
index fbcafe2..7391833 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -227,6 +227,9 @@ struct module
 	/* Unique handle for this module */
 	char name[MODULE_NAME_LEN];
 
+	/* Is this module GPG signed */
+	bool gpgsig_ok;
+
 	/* Sysfs stuff. */
 	struct module_kobject mkobj;
 	struct module_attribute *modinfo_attrs;
diff --git a/kernel/Makefile b/kernel/Makefile
index cb41b95..7608053 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o
 obj-$(CONFIG_PROVE_LOCKING) += spinlock.o
 obj-$(CONFIG_UID16) += uid16.o
 obj-$(CONFIG_MODULES) += module.o
+obj-$(CONFIG_MODULE_SIG) += module-verify.o
 obj-$(CONFIG_KALLSYMS) += kallsyms.o
 obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
 obj-$(CONFIG_KEXEC) += kexec.o
diff --git a/kernel/module-verify-defs.h b/kernel/module-verify-defs.h
new file mode 100644
index 0000000..292d2ba
--- /dev/null
+++ b/kernel/module-verify-defs.h
@@ -0,0 +1,77 @@
+/* Module verification internal definitions
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifdef CONFIG_MODULE_SIG
+
+/*
+ * Internal state
+ */
+struct module_verify_data {
+	struct crypto_key_verify_context *mod_sig; /* Module signing context */
+	union {
+		const void	*buffer;	/* module buffer */
+		const Elf_Ehdr	*hdr;		/* ELF header */
+	};
+	const Elf_Shdr		*sections;	/* ELF section table */
+	const char		*secstrings;	/* ELF section string table */
+	const void		*sig;		/* Signature note content */
+	size_t			size;		/* module object size */
+	size_t			nsects;		/* number of sections */
+	size_t			sig_size;	/* Size of signature */
+	size_t			signed_size;	/* count of bytes contributed to digest */
+	unsigned		*canonlist;	/* list of canonicalised sections */
+	unsigned		*canonmap;	/* section canonicalisation map */
+	unsigned		ncanon;		/* number of canonicalised sections */
+	unsigned		sig_index;	/* module signature section index */
+	uint8_t			xcsum;		/* checksum of bytes contributed to digest */
+	uint8_t			csum;		/* checksum of bytes representing a section */
+};
+
+/*
+ * Whether or not we support various types of ELF relocation record
+ */
+#if defined(MODULE_HAS_ELF_REL_ONLY)
+#define is_elf_rel(sh_type)	((sh_type) == SHT_REL)
+#define is_elf_rela(sh_type)	(0)
+#elif defined(MODULE_HAS_ELF_RELA_ONLY)
+#define is_elf_rel(sh_type)	(0)
+#define is_elf_rela(sh_type)	((sh_type) == SHT_RELA)
+#else
+#define is_elf_rel(sh_type)	((sh_type) == SHT_REL)
+#define is_elf_rela(sh_type)	((sh_type) == SHT_RELA)
+#endif
+
+/*
+ * Debugging.  Define DEBUG to enable.
+ */
+#define _debug(FMT, ...)			      \
+	do {					      \
+		if (unlikely(modsign_debug))	      \
+			pr_debug(FMT, ##__VA_ARGS__); \
+	} while(0)
+
+#ifdef DEBUG
+#define count_and_csum(C, __p, __n)			\
+do {							\
+	int __loop;					\
+	for (__loop = 0; __loop < __n; __loop++) {	\
+		(C)->csum += __p[__loop];		\
+		(C)->xcsum += __p[__loop];		\
+	}						\
+	(C)->signed_size += __n;			\
+} while (0)
+#else
+#define count_and_csum(C, __p, __n)		\
+do {						\
+} while (0)
+#endif
+
+#endif /* CONFIG_MODULE_SIG */
diff --git a/kernel/module-verify.c b/kernel/module-verify.c
new file mode 100644
index 0000000..0a3eb4b
--- /dev/null
+++ b/kernel/module-verify.c
@@ -0,0 +1,109 @@
+/* Module signature verification
+ *
+ * The code in this file examines a signed kernel module and attempts to
+ * determine if the PGP signature inside the module matches a digest of the
+ * allocatable sections and the canonicalised relocation tables for those
+ * allocatable sections.
+ *
+ * The module signature is included in an ELF note within the ELF structure of
+ * the module blob.  This, combined with the minimal canonicalisation performed
+ * here, permits the module to pass through "strip -x", "strip -g" and
+ * "eu-strip" without becoming corrupt.  "strip" and "strip -s" will render a
+ * module unusable by removing the symbol table.
+ *
+ * Copyright (C) 2004, 2011, 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from GregKH's RSA module signer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#undef DEBUG
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.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..6bb6b56
--- /dev/null
+++ b/kernel/module-verify.h
@@ -0,0 +1,19 @@
+/* Module verification definitions
+ *
+ * Copyright (C) 2004, 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifdef CONFIG_MODULE_SIG
+extern int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok);
+#else
+static inline int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok)
+{
+	return 0;
+}
+#endif
diff --git a/kernel/module.c b/kernel/module.c
index 377cb06..c3797f7 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -58,6 +58,7 @@
 #include <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>
@@ -2402,7 +2403,8 @@ static inline void kmemleak_load_module(const struct module *mod,
 /* Sets info->hdr and info->len. */
 static int copy_and_check(struct load_info *info,
 			  const void __user *umod, unsigned long len,
-			  const char __user *uargs)
+			  const char __user *uargs,
+			  bool *_gpgsig_ok)
 {
 	int err;
 	Elf_Ehdr *hdr;
@@ -2435,6 +2437,12 @@ static int copy_and_check(struct load_info *info,
 		goto free_hdr;
 	}
 
+	/* Verify the module's contents */
+	*_gpgsig_ok = false;
+	err = module_verify(hdr, len, _gpgsig_ok);
+	if (err < 0)
+		goto free_hdr;
+
 	info->hdr = hdr;
 	info->len = len;
 	return 0;
@@ -2777,7 +2785,8 @@ int __weak module_frob_arch_sections(Elf_Ehdr *hdr,
 	return 0;
 }
 
-static struct module *layout_and_allocate(struct load_info *info)
+static struct module *layout_and_allocate(struct load_info *info,
+					  bool gpgsig_ok)
 {
 	/* Module within temporary copy. */
 	struct module *mod;
@@ -2787,6 +2796,7 @@ static struct module *layout_and_allocate(struct load_info *info)
 	mod = setup_load_info(info);
 	if (IS_ERR(mod))
 		return mod;
+	mod->gpgsig_ok = gpgsig_ok;
 
 	err = check_modinfo(mod, info);
 	if (err)
@@ -2870,17 +2880,18 @@ static struct module *load_module(void __user *umod,
 	struct load_info info = { NULL, };
 	struct module *mod;
 	long err;
+	bool gpgsig_ok;
 
 	pr_debug("load_module: umod=%p, len=%lu, uargs=%p\n",
 	       umod, len, uargs);
 
 	/* Copy in the blobs from userspace, check they are vaguely sane. */
-	err = copy_and_check(&info, umod, len, uargs);
+	err = copy_and_check(&info, umod, len, uargs, &gpgsig_ok);
 	if (err)
 		return ERR_PTR(err);
 
 	/* Figure out module layout, and allocate all the memory. */
-	mod = layout_and_allocate(&info);
+	mod = layout_and_allocate(&info, gpgsig_ok);
 	if (IS_ERR(mod)) {
 		err = PTR_ERR(mod);
 		goto free_copy;
@@ -3517,8 +3528,13 @@ void print_modules(void)
 	printk(KERN_DEFAULT "Modules linked in:");
 	/* Most callers should already have preempt disabled, but make sure */
 	preempt_disable();
-	list_for_each_entry_rcu(mod, &modules, list)
+	list_for_each_entry_rcu(mod, &modules, list) {
 		printk(" %s%s", mod->name, module_flags(mod, buf));
+#ifdef CONFIG_MODULE_SIG
+		if (!mod->gpgsig_ok)
+			printk("(U)");
+#endif
+	}
 	preempt_enable();
 	if (last_unloaded_module[0])
 		printk(" [last unloaded: %s]", last_unloaded_module);


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 24/29] MODSIGN: Provide module signing public keys to the kernel [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (22 preceding siblings ...)
  2012-05-10 23:43 ` [PATCH 23/29] MODSIGN: Module signature verification stub " David Howells
@ 2012-05-10 23:43 ` David Howells
  2012-05-10 23:43 ` [PATCH 25/29] MODSIGN: Check the ELF container " David Howells
                   ` (8 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:43 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

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             |    3 +-
 kernel/modsign-pubkey.c     |   74 +++++++++++++++++++++++++++++++++++++++++++
 kernel/module-verify-defs.h |    4 ++
 kernel/module-verify.c      |    2 -
 4 files changed, 80 insertions(+), 3 deletions(-)
 create mode 100644 kernel/modsign-pubkey.c


diff --git a/kernel/Makefile b/kernel/Makefile
index 7608053..986ed7f 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -51,7 +51,8 @@ obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o
 obj-$(CONFIG_PROVE_LOCKING) += spinlock.o
 obj-$(CONFIG_UID16) += uid16.o
 obj-$(CONFIG_MODULES) += module.o
-obj-$(CONFIG_MODULE_SIG) += module-verify.o
+obj-$(CONFIG_MODULE_SIG) += module-verify.o modsign-pubkey.o
+kernel/modsign-pubkey.o: modsign.pub
 obj-$(CONFIG_KALLSYMS) += kallsyms.o
 obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
 obj-$(CONFIG_KEXEC) += kexec.o
diff --git a/kernel/modsign-pubkey.c b/kernel/modsign-pubkey.c
new file mode 100644
index 0000000..2ada460
--- /dev/null
+++ b/kernel/modsign-pubkey.c
@@ -0,0 +1,74 @@
+/* Public keys for module signature verification
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/cred.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 292d2ba..45bea45 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 0a3eb4b..b1c1d4c 100644
--- a/kernel/module-verify.c
+++ b/kernel/module-verify.c
@@ -27,8 +27,6 @@
 #include <linux/slab.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>


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 25/29] MODSIGN: Check the ELF container [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (23 preceding siblings ...)
  2012-05-10 23:43 ` [PATCH 24/29] MODSIGN: Provide module signing public keys to the kernel " David Howells
@ 2012-05-10 23:43 ` David Howells
  2012-05-10 23:43 ` [PATCH 26/29] MODSIGN: Produce a filtered and canonicalised section list " David Howells
                   ` (7 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:43 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

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 |  226 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 226 insertions(+), 0 deletions(-)


diff --git a/kernel/module-verify.c b/kernel/module-verify.c
index b1c1d4c..5711aeb 100644
--- a/kernel/module-verify.c
+++ b/kernel/module-verify.c
@@ -50,6 +50,224 @@ static const char modsign_note_name[] = ELFNOTE_NAME(MODSIGN_NOTE_NAME);
 static const char modsign_note_section[] = ELFNOTE_SECTION(MODSIGN_NOTE_NAME);
 
 /*
+ * Verify the minimum amount of ELF structure of a module needed to check the
+ * module's signature without bad ELF crashing the kernel.
+ */
+static noinline int module_verify_elf(struct module_verify_data *mvdata)
+{
+	const struct elf_note *note;
+	const Elf_Ehdr *hdr = mvdata->hdr;
+	const Elf_Shdr *section, *secstop;
+	const Elf_Sym *symbols, *symbol, *symstop;
+	const char *strtab;
+	size_t size, secsize, secstrsize, strsize, notesize, notemetasize;
+	unsigned line;
+
+	size = mvdata->size;
+
+#define elfcheck(X)							\
+do { if (unlikely(!(X))) { line = __LINE__; goto elfcheck_error; } } while(0)
+
+#define seccheck(X)							\
+do { if (unlikely(!(X))) { line = __LINE__; goto seccheck_error; } } while(0)
+
+#define symcheck(X)							\
+do { if (unlikely(!(X))) { line = __LINE__; goto symcheck_error; } } while(0)
+
+	/* Validate the ELF header */
+	elfcheck(size > sizeof(Elf_Ehdr));
+	elfcheck(hdr->e_ehsize < size);
+
+	elfcheck(hdr->e_shnum < SHN_LORESERVE);
+	elfcheck(hdr->e_shstrndx < hdr->e_shnum);
+	elfcheck(hdr->e_shentsize == sizeof(Elf_Shdr));
+	elfcheck(hdr->e_shoff < size);
+	elfcheck(hdr->e_shoff >= hdr->e_ehsize);
+	elfcheck(hdr->e_shoff % sizeof(long) == 0);
+	elfcheck(hdr->e_shnum * sizeof(Elf_Shdr) <= size - hdr->e_shoff);
+
+	/* Validate the section table contents */
+	mvdata->nsects = hdr->e_shnum;
+	mvdata->sections = mvdata->buffer + hdr->e_shoff;
+	secstop = mvdata->sections + mvdata->nsects;
+
+	/* Section 0 is special, usually indicating an undefined symbol */
+	seccheck(mvdata->sections[SHN_UNDEF].sh_type == SHT_NULL);
+
+	/* We also want access to the section name table */
+	seccheck(mvdata->sections[hdr->e_shstrndx].sh_type == SHT_STRTAB);
+	secstrsize = mvdata->sections[hdr->e_shstrndx].sh_size;
+
+	for (section = mvdata->sections + 1; section < secstop; section++) {
+		seccheck(section->sh_name < secstrsize);
+		seccheck(section->sh_link < hdr->e_shnum);
+
+		/* Section file offsets must reside within the file, though
+		 * they don't have to actually consume file space (.bss for
+		 * example).
+		 */
+		seccheck(section->sh_offset >= hdr->e_ehsize);
+		seccheck((section->sh_offset & (section->sh_addralign - 1)) == 0);
+		seccheck(section->sh_offset <= size);
+		if (section->sh_type != SHT_NOBITS)
+			seccheck(section->sh_size <= size - section->sh_offset);
+
+		/* Some types of section should contain arrays of fixed-length
+		 * records of a predetermined size and mustn't contain partial
+		 * records.  Also, records we're going to access directly must
+		 * have appropriate alignment that we don't get a misalignment
+		 * exception.
+		 */
+		if (section->sh_entsize > 1)
+			seccheck(section->sh_size % section->sh_entsize == 0);
+
+		switch (section->sh_type) {
+		case SHT_SYMTAB:
+			seccheck(section->sh_entsize == sizeof(Elf_Sym));
+			seccheck(section->sh_addralign % sizeof(long) == 0);
+			break;
+		case SHT_REL:
+#ifndef MODULE_HAS_ELF_RELA_ONLY
+			seccheck(section->sh_entsize == sizeof(Elf_Rel));
+			seccheck(section->sh_addralign % sizeof(long) == 0);
+			break;
+#else
+			seccheck(false);
+			break;
+#endif
+		case SHT_RELA:
+#ifndef MODULE_HAS_ELF_REL_ONLY
+			seccheck(section->sh_entsize == sizeof(Elf_Rela));
+			seccheck(section->sh_addralign % sizeof(long) == 0);
+			break;
+#else
+			seccheck(false);
+			break;
+#endif
+		case SHT_NOTE:
+			seccheck(section->sh_addralign % 4 == 0);
+			break;
+		case SHT_STRTAB:
+			/* We require all string tables to be non-empty.  If
+			 * not empty, a string table must end in a NUL (it
+			 * should also begin with a NUL, but it's not a problem
+			 * for us if it doesn't).
+			 */
+			seccheck(section->sh_size >= 2);
+			strtab = mvdata->buffer + section->sh_offset;
+			seccheck(strtab[section->sh_size - 1] == '\0');
+			break;
+		}
+	}
+
+	/* Check features specific to the type of each section.
+	 *
+	 * Note that having a separate loop here allows the compiler to discard
+	 * some local variables used in the above loop thus making the code
+	 * smaller.
+	 */
+	for (section = mvdata->sections + 1; section < secstop; section++) {
+		switch (section->sh_type) {
+		case SHT_SYMTAB:
+			/* Symbol tables nominate a string table. */
+			seccheck(mvdata->sections[section->sh_link].sh_type ==
+				 SHT_STRTAB);
+
+			/* Validate the symbols in the table.  The first symbol
+			 * (STN_UNDEF) is special.
+			 */
+			symbol = symbols = mvdata->buffer + section->sh_offset;
+			symstop = mvdata->buffer +
+				(section->sh_offset + section->sh_size);
+
+			symcheck(ELF_ST_TYPE(symbols[0].st_info) == STT_NOTYPE);
+			symcheck(symbol[0].st_shndx == SHN_UNDEF);
+
+			strsize = mvdata->sections[section->sh_link].sh_size;
+			for (symbol++; symbol < symstop; symbol++) {
+				symcheck(symbol->st_name < strsize);
+				symcheck(symbol->st_shndx < hdr->e_shnum ||
+					 symbol->st_shndx >= SHN_LORESERVE);
+			}
+			break;
+
+#ifndef MODULE_HAS_ELF_RELA_ONLY
+		case SHT_REL:
+#endif
+#ifndef MODULE_HAS_ELF_REL_ONLY
+		case SHT_RELA:
+#endif
+			/* Relocation tables nominate a symbol table and a
+			 * target section to which the relocations will be
+			 * applied.
+			 */
+			seccheck(mvdata->sections[section->sh_link].sh_type ==
+				 SHT_SYMTAB);
+			seccheck(section->sh_info > 0);
+			seccheck(section->sh_info < hdr->e_shnum);
+			break;
+		}
+	}
+
+	/* We can now use section name string table section as we checked its
+	 * bounds in the loop above.
+	 *
+	 * Each name is NUL-terminated, and the table as a whole should have a
+	 * NUL at either end as there to be at least one named section for the
+	 * module information.
+	 */
+	section = &mvdata->sections[hdr->e_shstrndx];
+	mvdata->secstrings = mvdata->buffer + section->sh_offset;
+
+	for (section = mvdata->sections + 1; section < secstop; section++) {
+		const char *name = mvdata->secstrings + section->sh_name;
+
+		switch (section->sh_type) {
+		case SHT_NOTE:
+			if (strcmp(name, modsign_note_section) != 0)
+				continue;
+
+			/* We've found a note purporting to contain a signature
+			 * so we should check the structure of that.
+			 */
+			notemetasize = sizeof(struct elf_note) +
+				roundup(sizeof(modsign_note_name), 4);
+
+			seccheck(mvdata->sig_index == 0);
+			seccheck(section->sh_size > notemetasize);
+			note = mvdata->buffer + section->sh_offset;
+			seccheck(note->n_type == MODSIGN_NOTE_TYPE);
+			seccheck(note->n_namesz == sizeof(modsign_note_name));
+
+			notesize = section->sh_size - notemetasize;
+			seccheck(note->n_descsz <= notesize);
+
+			seccheck(memcmp(note + 1, modsign_note_name,
+					note->n_namesz) == 0);
+
+			mvdata->sig_size = note->n_descsz;
+			mvdata->sig = (void *)note + notemetasize;
+			mvdata->sig_index = section - mvdata->sections;
+			break;
+		}
+	}
+
+	return 0;
+
+elfcheck_error:
+	_debug("Verify ELF error (check %u)\n", line);
+	return -ELIBBAD;
+seccheck_error:
+	_debug("Verify ELF error [sec %ld] (check %u)\n",
+	       (long)(section - mvdata->sections), line);
+	return -ELIBBAD;
+symcheck_error:
+	_debug("Verify ELF error [sym %ld] (check %u)\n",
+	       (long)(symbol - symbols), line);
+	return -ELIBBAD;
+}
+
+/*
  * Verify a module's integrity
  */
 int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok)
@@ -61,6 +279,14 @@ int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok)
 	mvdata.buffer = hdr;
 	mvdata.size = size;
 
+	/* Minimally check the ELF to make sure building the signature digest
+	 * won't crash the kernel.
+	 */
+	ret = module_verify_elf(&mvdata);
+	if (ret < 0)
+		goto out;
+
+	/* The ELF checker found the sig for us if it exists */
 	if (mvdata.sig_index <= 0) {
 		/* Deal with an unsigned module */
 		if (modsign_signedonly) {


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 26/29] MODSIGN: Produce a filtered and canonicalised section list [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (24 preceding siblings ...)
  2012-05-10 23:43 ` [PATCH 25/29] MODSIGN: Check the ELF container " David Howells
@ 2012-05-10 23:43 ` David Howells
  2012-05-10 23:43 ` [PATCH 27/29] MODSIGN: Create digest of module content and check signature " David Howells
                   ` (6 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:43 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

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 files changed, 80 insertions(+), 0 deletions(-)


diff --git a/kernel/module-verify.c b/kernel/module-verify.c
index 5711aeb..13c60c2 100644
--- a/kernel/module-verify.c
+++ b/kernel/module-verify.c
@@ -268,6 +268,80 @@ symcheck_error:
 }
 
 /*
+ * Canonicalise the section table index numbers.
+ *
+ * We build a list of the sections we want to add to the digest and sort it by
+ * name.  We're only interested in adding two types of section:
+ *
+ *  (1) Allocatable sections.  These should have no references to other
+ *      sections.
+ *
+ *  (2) Relocation tables for allocatable sections.  The section table entry
+ *      has a reference to the target section to which the relocations will be
+ *      applied.  The relocation entries have references to symbols in
+ *      non-allocatable sections.  Symbols can be replaced by their contents,
+ *      but do include a further reference to a section - which must be
+ *      canonicalised.
+ *
+ * We also build a map of raw section index to canonical section index.
+ */
+static int module_verify_canonicalise(struct module_verify_data *mvdata)
+{
+	const Elf_Shdr *sechdrs = mvdata->sections;
+	unsigned *canonlist, canon, loop, tmp;
+	bool changed;
+
+	canonlist = kmalloc(sizeof(unsigned) * mvdata->nsects * 2, GFP_KERNEL);
+	if (!canonlist)
+		return -ENOMEM;
+
+	mvdata->canonlist = canonlist;
+	mvdata->canonmap = canonlist + mvdata->nsects;
+	canon = 0;
+
+	for (loop = 1; loop < mvdata->nsects; loop++) {
+		const Elf_Shdr *section = mvdata->sections + loop;
+
+		if (loop == mvdata->sig_index)
+			continue;
+
+		/* We only want allocatable sections and relocation tables */
+		if (section->sh_flags & SHF_ALLOC)
+			canonlist[canon++] = loop;
+		else if ((is_elf_rel(section->sh_type) ||
+			  is_elf_rela(section->sh_type)) &&
+			 mvdata->sections[section->sh_info].sh_flags & SHF_ALLOC)
+			canonlist[canon++] = loop;
+	}
+
+	/* Sort the canonicalisation list */
+	do {
+		changed = false;
+
+		for (loop = 0; loop < canon - 1; loop++) {
+			const char *x, *y;
+
+			x = mvdata->secstrings + sechdrs[canonlist[loop + 0]].sh_name;
+			y = mvdata->secstrings + sechdrs[canonlist[loop + 1]].sh_name;
+
+			if (strcmp(x, y) > 0) {
+				tmp = canonlist[loop + 0];
+				canonlist[loop + 0] = canonlist[loop + 1];
+				canonlist[loop + 1] = tmp;
+				changed = true;
+			}
+		}
+	} while (changed);
+
+	/* What we really want is a raw-to-canon lookup table */
+	memset(mvdata->canonmap, 0xff, mvdata->nsects * sizeof(unsigned));
+	for (loop = 0; loop < canon; loop++)
+		mvdata->canonmap[mvdata->canonlist[loop]] = loop + 1;
+	mvdata->ncanon = canon;
+	return 0;
+}
+
+/*
  * Verify a module's integrity
  */
 int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok)
@@ -298,7 +372,13 @@ int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok)
 		goto out;
 	}
 
+	/* Produce a canonicalisation map for the sections */
+	ret = module_verify_canonicalise(&mvdata);
+	if (ret < 0)
+		goto out;
+
 	ret = 0;
+	kfree(mvdata.canonlist);
 
 out:
 	switch (ret) {


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 27/29] MODSIGN: Create digest of module content and check signature [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (25 preceding siblings ...)
  2012-05-10 23:43 ` [PATCH 26/29] MODSIGN: Produce a filtered and canonicalised section list " David Howells
@ 2012-05-10 23:43 ` David Howells
  2012-05-10 23:44 ` [PATCH 28/29] MODSIGN: Automatically generate module signing keys if missing " David Howells
                   ` (5 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:43 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

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.c |  321 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 320 insertions(+), 1 deletions(-)


diff --git a/kernel/module-verify.c b/kernel/module-verify.c
index 13c60c2..a31b39c 100644
--- a/kernel/module-verify.c
+++ b/kernel/module-verify.c
@@ -49,6 +49,22 @@ static bool modsign_signedonly;
 static const char modsign_note_name[] = ELFNOTE_NAME(MODSIGN_NOTE_NAME);
 static const char modsign_note_section[] = ELFNOTE_SECTION(MODSIGN_NOTE_NAME);
 
+#define crypto_digest_update_data(C, PTR, N)			\
+do {								\
+	uint8_t *__p = (uint8_t *)(PTR);			\
+	size_t __n = (N);					\
+	count_and_csum((C), __p, __n);				\
+	verify_sig_add_data((C)->mod_sig, __p, __n);		\
+} while (0)
+
+#define crypto_digest_update_val(C, VAL)			\
+do {								\
+	uint8_t *__p = (uint8_t *)&(VAL);			\
+	size_t __n = sizeof(VAL);				\
+	count_and_csum((C), __p, __n);				\
+	verify_sig_add_data((C)->mod_sig, __p, __n);		\
+} while (0)
+
 /*
  * Verify the minimum amount of ELF structure of a module needed to check the
  * module's signature without bad ELF crashing the kernel.
@@ -342,6 +358,309 @@ static int module_verify_canonicalise(struct module_verify_data *mvdata)
 }
 
 /*
+ * Extract an ELF REL table
+ *
+ * We need to canonicalise the entries in case section/symbol addition/removal
+ * has rearranged the symbol table and the section table.
+ */
+static int extract_elf_rel(struct module_verify_data *mvdata,
+			   unsigned secix,
+			   const Elf_Rel *reltab, size_t nrels,
+			   const char *sh_name)
+{
+	struct {
+#if defined(MODULES_ARE_ELF32)
+		uint32_t	r_offset;
+		uint32_t	st_value;
+		uint32_t	st_size;
+		uint16_t	st_shndx;
+		uint8_t		r_type;
+		uint8_t		st_info;
+		uint8_t		st_other;
+#elif defined(MODULES_ARE_ELF64)
+		uint64_t	r_offset;
+		uint64_t	st_value;
+		uint64_t	st_size;
+		uint32_t	r_type;
+		uint16_t	st_shndx;
+		uint8_t		st_info;
+		uint8_t		st_other;
+#else
+#error unsupported module type
+#endif
+	} __attribute__((packed)) relocation;
+
+	const Elf_Rel *reloc;
+	const Elf_Sym *symbols, *symbol;
+	const char *strings;
+	unsigned long r_sym;
+	size_t nsyms, loop;
+
+	nsyms = mvdata->sections[secix].sh_size / sizeof(Elf_Sym);
+	symbols = mvdata->buffer + mvdata->sections[secix].sh_offset;
+	strings = mvdata->buffer +
+		mvdata->sections[mvdata->sections[secix].sh_link].sh_offset;
+
+	/* Contribute the relevant bits from a join of { REL, SYMBOL, SECTION } */
+	for (loop = 0; loop < nrels; loop++) {
+		unsigned st_shndx;
+
+		reloc = &reltab[loop];
+
+		/* Decode the relocation */
+		relocation.r_offset = reloc->r_offset;
+		relocation.r_type = ELF_R_TYPE(reloc->r_info);
+
+		/* Decode the symbol referenced by the relocation */
+		r_sym = ELF_R_SYM(reloc->r_info);
+		if (r_sym >= nsyms)
+			return -ELIBBAD;
+		symbol = &symbols[r_sym];
+		relocation.st_info = symbol->st_info;
+		relocation.st_other = symbol->st_other;
+		relocation.st_value = symbol->st_value;
+		relocation.st_size = symbol->st_size;
+		relocation.st_shndx = symbol->st_shndx;
+		st_shndx = symbol->st_shndx;
+
+		/* Canonicalise the section used by the symbol */
+		if (st_shndx > SHN_UNDEF && st_shndx < mvdata->nsects) {
+			if (!(mvdata->sections[st_shndx].sh_flags & SHF_ALLOC))
+				return -ELIBBAD;
+			relocation.st_shndx = mvdata->canonmap[st_shndx];
+		}
+
+		crypto_digest_update_val(mvdata, relocation);
+
+		/* Undefined symbols must be named if referenced */
+		if (st_shndx == SHN_UNDEF) {
+			const char *name = strings + symbol->st_name;
+			crypto_digest_update_data(mvdata,
+						  name, strlen(name) + 1);
+		}
+	}
+
+	_debug("%08zx %02x digested the %s section, nrels %zu\n",
+	       mvdata->signed_size, mvdata->csum, sh_name, nrels);
+
+	return 0;
+}
+
+/*
+ * Extract an ELF RELA table
+ *
+ * We need to canonicalise the entries in case section/symbol addition/removal
+ * has rearranged the symbol table and the section table.
+ */
+static int extract_elf_rela(struct module_verify_data *mvdata,
+			    unsigned secix,
+			    const Elf_Rela *relatab, size_t nrels,
+			    const char *sh_name)
+{
+	struct {
+#if defined(MODULES_ARE_ELF32)
+		uint32_t	r_offset;
+		uint32_t	r_addend;
+		uint32_t	st_value;
+		uint32_t	st_size;
+		uint16_t	st_shndx;
+		uint8_t		r_type;
+		uint8_t		st_info;
+		uint8_t		st_other;
+#elif defined(MODULES_ARE_ELF64)
+		uint64_t	r_offset;
+		uint64_t	r_addend;
+		uint64_t	st_value;
+		uint64_t	st_size;
+		uint32_t	r_type;
+		uint16_t	st_shndx;
+		uint8_t		st_info;
+		uint8_t		st_other;
+#else
+#error unsupported module type
+#endif
+	} __attribute__((packed)) relocation;
+
+	const Elf_Shdr *relsec, *symsec, *strsec;
+	const Elf_Rela *reloc;
+	const Elf_Sym *symbols, *symbol;
+	unsigned long r_sym;
+	const char *strings;
+	size_t nsyms, loop;
+
+	relsec = &mvdata->sections[secix];
+	symsec = &mvdata->sections[relsec->sh_link];
+	strsec = &mvdata->sections[symsec->sh_link];
+	nsyms = symsec->sh_size / sizeof(Elf_Sym);
+	symbols = mvdata->buffer + symsec->sh_offset;
+	strings = mvdata->buffer + strsec->sh_offset;
+
+	/* Contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
+	for (loop = 0; loop < nrels; loop++) {
+		unsigned st_shndx;
+
+		reloc = &relatab[loop];
+
+		/* Decode the relocation */
+		relocation.r_offset = reloc->r_offset;
+		relocation.r_addend = reloc->r_addend;
+		relocation.r_type = ELF_R_TYPE(reloc->r_info);
+
+		/* Decode the symbol referenced by the relocation */
+		r_sym = ELF_R_SYM(reloc->r_info);
+		if (r_sym >= nsyms)
+			return -ELIBBAD;
+		symbol = &symbols[r_sym];
+		relocation.st_info = symbol->st_info;
+		relocation.st_other = symbol->st_other;
+		relocation.st_value = symbol->st_value;
+		relocation.st_size = symbol->st_size;
+		relocation.st_shndx = 0;
+		st_shndx = symbol->st_shndx;
+
+		/* Canonicalise the section used by the symbol */
+		if (st_shndx > SHN_UNDEF && st_shndx < mvdata->nsects) {
+			if (!(mvdata->sections[st_shndx].sh_flags & SHF_ALLOC))
+				return -ELIBBAD;
+			relocation.st_shndx = mvdata->canonmap[st_shndx];
+		}
+
+		crypto_digest_update_val(mvdata, relocation);
+
+		/* Undefined symbols must be named if referenced */
+		if (st_shndx == SHN_UNDEF) {
+			const char *name = strings + symbol->st_name;
+			crypto_digest_update_data(mvdata,
+						  name, strlen(name) + 1);
+		}
+	}
+
+	_debug("%08zx %02x digested the %s section, nrels %zu\n",
+	       mvdata->signed_size, mvdata->csum, sh_name, nrels);
+
+	return 0;
+}
+
+/*
+ * Verify a module's signature
+ */
+static noinline int module_verify_signature(struct module_verify_data *mvdata)
+{
+	struct crypto_key_verify_context *mod_sig;
+	const Elf_Shdr *sechdrs = mvdata->sections;
+	const char *secstrings = mvdata->secstrings;
+	const u8 *sig = mvdata->sig;
+	size_t sig_size = mvdata->sig_size;
+	int loop, ret;
+
+	_debug("sig in section %u (size %zu)\n",
+	       mvdata->sig_index, mvdata->sig_size);
+	_debug("%02x%02x%02x%02x%02x%02x%02x%02x\n",
+	       sig[0], sig[1], sig[2], sig[3],
+	       sig[4], sig[5], sig[6], sig[7]);
+
+	/* Find the crypto key for the module signature
+	 * - !!! if this tries to load the required hash algorithm module,
+	 *       we will deadlock!!!
+	 */
+	mod_sig = verify_sig_begin(modsign_keyring, sig, sig_size);
+	if (IS_ERR(mod_sig)) {
+		pr_err("Couldn't initiate module signature verification: %ld\n",
+		       PTR_ERR(mod_sig));
+		return PTR_ERR(mod_sig);
+	}
+
+	mvdata->mod_sig = mod_sig;
+#ifdef DEBUG
+	mvdata->xcsum = 0;
+#endif
+
+	/* Load data from each relevant section into the digest.  Note that
+	 * canonlist[] is a filtered list and only contains the sections we
+	 * actually want.
+	 */
+	for (loop = 0; loop < mvdata->ncanon; loop++) {
+		int sect = mvdata->canonlist[loop];
+		unsigned long sh_type = sechdrs[sect].sh_type;
+		unsigned long sh_info = sechdrs[sect].sh_info;
+		unsigned long sh_size = sechdrs[sect].sh_size;
+		const char *sh_name = secstrings + sechdrs[sect].sh_name;
+		const void *data = mvdata->buffer + sechdrs[sect].sh_offset;
+
+#ifdef DEBUG
+		mvdata->csum = 0;
+#endif
+
+		/* Digest the headers of any section we include. */
+		crypto_digest_update_data(mvdata, sh_name, strlen(sh_name));
+		crypto_digest_update_val(mvdata, sechdrs[sect].sh_type);
+		crypto_digest_update_val(mvdata, sechdrs[sect].sh_flags);
+		crypto_digest_update_val(mvdata, sechdrs[sect].sh_size);
+		crypto_digest_update_val(mvdata, sechdrs[sect].sh_addralign);
+
+		/* Relocation record sections refer to the section to be
+		 * relocated, but this needs to be canonicalised to survive
+		 * stripping.
+		 */
+		if (is_elf_rel(sh_type) || is_elf_rela(sh_type))
+			crypto_digest_update_val(mvdata,
+						 mvdata->canonmap[sh_info]);
+
+		/* Since relocation records give details of how we have to
+		 * alter the allocatable sections, we need to digest these too.
+		 *
+		 * These, however, refer to metadata (symbols and sections)
+		 * that may have been altered by the process of adding the
+		 * signature section or the process of being stripped.
+		 *
+		 * To deal with this, we substitute the referenced metadata for
+		 * the references to that metadata.  So, for instance, the
+		 * symbol ref from the relocation record is replaced with the
+		 * contents of the symbol to which it refers, and the symbol's
+		 * section ref is replaced with a canonicalised section number.
+		 */
+		if (is_elf_rel(sh_type)) {
+			ret = extract_elf_rel(mvdata, sect,
+					      data,
+					      sh_size / sizeof(Elf_Rel),
+					      sh_name);
+			if (ret < 0)
+				goto format_error;
+			continue;
+		}
+
+		if (is_elf_rela(sh_type)) {
+			ret = extract_elf_rela(mvdata, sect,
+					       data,
+					       sh_size / sizeof(Elf_Rela),
+					       sh_name);
+			if (ret < 0)
+				goto format_error;
+			continue;
+		}
+
+		/* Include allocatable loadable sections */
+		if (sh_type != SHT_NOBITS)
+			crypto_digest_update_data(mvdata, data, sh_size);
+
+		_debug("%08zx %02x digested the %s section, size %ld\n",
+		       mvdata->signed_size, mvdata->csum, sh_name, sh_size);
+	}
+
+	_debug("Contributed %zu bytes to the digest (csum 0x%02x)\n",
+	       mvdata->signed_size, mvdata->xcsum);
+
+	/* Do the actual signature verification */
+	ret = verify_sig_end(mvdata->mod_sig, sig, sig_size);
+	_debug("verify-sig : %d\n", ret);
+	return ret;
+
+format_error:
+	verify_sig_cancel(mvdata->mod_sig);
+	return -ELIBBAD;
+}
+
+/*
  * Verify a module's integrity
  */
 int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok)
@@ -377,7 +696,7 @@ int module_verify(const Elf_Ehdr *hdr, size_t size, bool *_gpgsig_ok)
 	if (ret < 0)
 		goto out;
 
-	ret = 0;
+	ret = module_verify_signature(&mvdata);
 	kfree(mvdata.canonlist);
 
 out:


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 28/29] MODSIGN: Automatically generate module signing keys if missing [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (26 preceding siblings ...)
  2012-05-10 23:43 ` [PATCH 27/29] MODSIGN: Create digest of module content and check signature " David Howells
@ 2012-05-10 23:44 ` David Howells
  2012-05-10 23:44 ` [PATCH 29/29] MODSIGN: Suppress some redundant ELF checks " David Howells
                   ` (4 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:44 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

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 |   42 +++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 41 insertions(+), 1 deletions(-)


diff --git a/kernel/Makefile b/kernel/Makefile
index 986ed7f..d8139bb 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -52,7 +52,6 @@ obj-$(CONFIG_PROVE_LOCKING) += spinlock.o
 obj-$(CONFIG_UID16) += uid16.o
 obj-$(CONFIG_MODULES) += module.o
 obj-$(CONFIG_MODULE_SIG) += module-verify.o modsign-pubkey.o
-kernel/modsign-pubkey.o: modsign.pub
 obj-$(CONFIG_KALLSYMS) += kallsyms.o
 obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
 obj-$(CONFIG_KEXEC) += kexec.o
@@ -129,3 +128,44 @@ quiet_cmd_timeconst  = TIMEC   $@
 targets += timeconst.h
 $(obj)/timeconst.h: $(src)/timeconst.pl FORCE
 	$(call if_changed,timeconst)
+
+###############################################################################
+#
+# If module signing is requested, say by allyesconfig, but a key has not been
+# supplied, then one will need to be generated to make sure the build does not
+# fail and that the kernel may be used afterwards.
+#
+###############################################################################
+ifeq ($(CONFIG_MODULE_SIG),y)
+kernel/modsign-pubkey.o: modsign.pub
+
+modsign.pub modsign.sec: genkey
+	@echo "###"
+	@echo "### Now generating a PGP key pair to be used for signing modules."
+	@echo "###"
+	@echo "### If this takes a long time, you might wish to run rngd in the"
+	@echo "### background to keep the supply of entropy topped up.  It"
+	@echo "### needs to be run as root, and should use a hardware random"
+	@echo "### number generator if one is available, eg:"
+	@echo "###"
+	@echo "###     rngd -r /dev/hwrandom"
+	@echo "###"
+	gpg --homedir . --batch --gen-key genkey
+	@echo "###"
+	@echo "### Key pair generated."
+	@echo "###"
+	rm -f pubring.gpg secring.gpg trustdb.gpg
+
+genkey:
+	echo "%pubring modsign.pub" >genkey
+	echo "%secring modsign.sec" >>genkey
+	echo "%no-protection: yes" >> genkey
+	echo "%transient-key: yes" >>genkey
+	echo "Key-Type: RSA" >>genkey
+	echo "Key-Length: 4096" >>genkey
+	echo "Name-Real: Sample kernel key" >>genkey
+	echo "Name-Comment: Sample kernel module signing key" >>genkey
+	echo "%commit" >>genkey
+
+endif
+CLEAN_FILES += modsign.pub modsign.sec genkey random_seed


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 29/29] MODSIGN: Suppress some redundant ELF checks [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (27 preceding siblings ...)
  2012-05-10 23:44 ` [PATCH 28/29] MODSIGN: Automatically generate module signing keys if missing " David Howells
@ 2012-05-10 23:44 ` David Howells
  2012-05-11 13:30 ` [PATCH 00/29] Crypto keys and module signing " Tetsuo Handa
                   ` (3 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-10 23:44 UTC (permalink / raw)
  To: rusty; +Cc: kyle, linux-kernel, linux-security-module, keyrings, David Howells

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 |    8 ++++----
 1 files changed, 4 insertions(+), 4 deletions(-)


diff --git a/kernel/module-verify.c b/kernel/module-verify.c
index a31b39c..e1bfd28 100644
--- a/kernel/module-verify.c
+++ b/kernel/module-verify.c
@@ -76,7 +76,7 @@ static noinline int module_verify_elf(struct module_verify_data *mvdata)
 	const Elf_Shdr *section, *secstop;
 	const Elf_Sym *symbols, *symbol, *symstop;
 	const char *strtab;
-	size_t size, secsize, secstrsize, strsize, notesize, notemetasize;
+	size_t size, secstrsize, strsize, notesize, notemetasize;
 	unsigned line;
 
 	size = mvdata->size;
@@ -96,11 +96,11 @@ do { if (unlikely(!(X))) { line = __LINE__; goto symcheck_error; } } while(0)
 
 	elfcheck(hdr->e_shnum < SHN_LORESERVE);
 	elfcheck(hdr->e_shstrndx < hdr->e_shnum);
-	elfcheck(hdr->e_shentsize == sizeof(Elf_Shdr));
-	elfcheck(hdr->e_shoff < size);
+	/* elfcheck(hdr->e_shentsize == sizeof(Elf_Shdr)); */
+	/* elfcheck(hdr->e_shoff < size); */
 	elfcheck(hdr->e_shoff >= hdr->e_ehsize);
 	elfcheck(hdr->e_shoff % sizeof(long) == 0);
-	elfcheck(hdr->e_shnum * sizeof(Elf_Shdr) <= size - hdr->e_shoff);
+	/* elfcheck(hdr->e_shnum * sizeof(Elf_Shdr) <= size - hdr->e_shoff); */
 
 	/* Validate the section table contents */
 	mvdata->nsects = hdr->e_shnum;


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [PATCH 00/29] Crypto keys and module signing [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (28 preceding siblings ...)
  2012-05-10 23:44 ` [PATCH 29/29] MODSIGN: Suppress some redundant ELF checks " David Howells
@ 2012-05-11 13:30 ` Tetsuo Handa
  2012-05-11 14:32 ` David Howells
                   ` (2 subsequent siblings)
  32 siblings, 0 replies; 34+ messages in thread
From: Tetsuo Handa @ 2012-05-11 13:30 UTC (permalink / raw)
  To: dhowells; +Cc: rusty, kyle, linux-kernel, linux-security-module, keyrings

+static int pgp_calc_pkey_keyid(struct shash_desc *digest,
+			       struct pgp_parse_pubkey *pgp,
+			       struct public_key *key)
+{
+	unsigned nb[ARRAY_SIZE(key->mpi)];
+	unsigned nn[ARRAY_SIZE(key->mpi)];
+	unsigned n;
+	u8 *pp[ARRAY_SIZE(key->mpi)];
+	u32 a32;
+	int npkey = key->algo->n_pub_mpi;
+	int i, ret = -ENOMEM;
+
+	kenter("");
+
+	n = (pgp->version < PGP_KEY_VERSION_4) ? 8 : 6;
+	for (i = 0; i < npkey; i++) {
+		nb[i] = mpi_get_nbits(key->mpi[i]);

Is key->algo->n_pub_mpi < ARRAY_SIZE(key->mpi) guaranteed?

+		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)

checkpatch.pl

+			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]);

Stack memory may not be initialized.

+	kleave(" = %d", ret);
+	return ret;
+}



+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];

Given the (datalen <= 2) check below, can we trust data[4,5] here?

+		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;
+	}



+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;

Can mvdata->nsects == (UINT_MAX + 1) / (sizeof(unsigned) * 2) due to size_t?
I think we want kmalloc() variant that does not return ZERO_SIZE_PTR.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 00/29] Crypto keys and module signing [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (29 preceding siblings ...)
  2012-05-11 13:30 ` [PATCH 00/29] Crypto keys and module signing " Tetsuo Handa
@ 2012-05-11 14:32 ` David Howells
  2012-05-11 16:17 ` David Howells
  2012-05-19  0:53 ` Rusty Russell
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-11 14:32 UTC (permalink / raw)
  To: Tetsuo Handa
  Cc: dhowells, rusty, kyle, linux-kernel, linux-security-module, keyrings

Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> wrote:

> +	int npkey = key->algo->n_pub_mpi;
> +	int i, ret = -ENOMEM;
> +
> +	kenter("");
> +
> +	n = (pgp->version < PGP_KEY_VERSION_4) ? 8 : 6;
> +	for (i = 0; i < npkey; i++) {
> +		nb[i] = mpi_get_nbits(key->mpi[i]);
> 
> Is key->algo->n_pub_mpi < ARRAY_SIZE(key->mpi) guaranteed?

Yes.  It's hard-coded in the public_key_algorithm struct - for instance the
one to be found at the bottom of security/keys/crypto_rsa.c.

We also check for an excessive quantity of MPIs in pgp_process_public_key().

> +		if( pgp->expires_at)
> 
> checkpatch.pl

Fixed some of those.

> +error:
> +	for (i = 0; i < npkey; i++)
> +		kfree(pp[i]);
> 
> Stack memory may not be initialized.

Fair point.

> +		hashedsz = 4 + 2 + (data[4] << 8) + data[5];
> 
> Given the (datalen <= 2) check below, can we trust data[4,5] here?

We've already done some length and content checking on the signature data.  In
the module verification case, this happens:

 (1) module_verify_sig() has already passed the signature data to
     verify_sig_begin(),

 (2) which passed it to pgp_pkey_verify_sig_begin(),

 (3) which invoked pgp_parse_packets(),

 (4) which called back to pgp_pkey_parse_signature(),

 (5) which then invoked pgp_parse_sig_params() which did sufficient length
     checking to make sure we're okay here.

by the time we get to pgp_pkey_digest_signature() we're at the end of the
process (in verify_sig_end()).

The trailer, however, is not checked at that point - though I suppose it
probably should be.  There isn't currently an MPI function to do just a check
rather than an extraction.

I can add comments to this effect if you think it would help reduce confusion.

> +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;
> 
> Can mvdata->nsects == (UINT_MAX + 1) / (sizeof(unsigned) * 2) due to size_t?
> I think we want kmalloc() variant that does not return ZERO_SIZE_PTR.

This line should prevent that:

	elfcheck(hdr->e_shnum < SHN_LORESERVE);

given:

	#define SHN_LORESERVE	0xff00

Thanks for the thorough review!

David

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 00/29] Crypto keys and module signing [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (30 preceding siblings ...)
  2012-05-11 14:32 ` David Howells
@ 2012-05-11 16:17 ` David Howells
  2012-05-19  0:53 ` Rusty Russell
  32 siblings, 0 replies; 34+ messages in thread
From: David Howells @ 2012-05-11 16:17 UTC (permalink / raw)
  Cc: dhowells, Tetsuo Handa, rusty, kyle, linux-kernel,
	linux-security-module, keyrings


I've pushed another couple of patches to clear those up.  I may merge them
into the other patches at some point.

David

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 00/29] Crypto keys and module signing [ver #4]
  2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
                   ` (31 preceding siblings ...)
  2012-05-11 16:17 ` David Howells
@ 2012-05-19  0:53 ` Rusty Russell
  32 siblings, 0 replies; 34+ messages in thread
From: Rusty Russell @ 2012-05-19  0:53 UTC (permalink / raw)
  To: David Howells; +Cc: kyle, linux-kernel, linux-security-module, keyrings

On Fri, 11 May 2012 00:39:01 +0100, David Howells <dhowells@redhat.com> wrote:
> 
> Hi Rusty,
> 
> Here's my latest take on my module signing patch set.  I've retained my
> strip-proof[*] signature-in-module concept, but I've shrunk the module
> verification code by nearly half.  Its .text segment now stands at just over 2K
> in size for an x86_64 kernel.

Hi David!

        I get it.  Some management bigwig at RH has told you to get this
patch in, right?  And you told them it'd had been Nacked, that the
maintainer had said it was never going in, and of course, that it was a
stupid idea and to give up on the idea of stripping modules after
signing, and just append a magic marker and the signature.

        But they just wouldn't listen, would they?  So you had to waste
your time polishing this turd, until you annoy me enough to get the kind
of flaming rejection which is visible from space and chars the eyeballs
of your manager so they understand.

        Well, here it is.  I even put it in caps for you!

NAK.  THIS PATCH WILL NEVER, EVER GO IN.  I AM NOT PUTTING CRAP IN THE
KERNEL BECAUSE RH CAN'T FIGURE OUT HOW TO PRODUCE STRIPPED VERSIONS OF
MODULES DURING BUILD.  DON'T BE TOO PROUD OF THIS TECHNOLOGICAL TERROR
YOU'VE CONSTRUCTED.

I look forward to you updated patch series!
Rusty.

^ permalink raw reply	[flat|nested] 34+ messages in thread

end of thread, other threads:[~2012-05-19  0:53 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-05-10 23:39 [PATCH 00/29] Crypto keys and module signing [ver #4] David Howells
2012-05-10 23:39 ` [PATCH 01/29] MPILIB: Export some more symbols " David Howells
2012-05-10 23:39 ` [PATCH 02/29] KEYS: Move the key config into security/keys/Kconfig " David Howells
2012-05-10 23:39 ` [PATCH 03/29] KEYS: Announce key type (un)registration " David Howells
2012-05-10 23:39 ` [PATCH 04/29] KEYS: Reorganise keys Makefile " David Howells
2012-05-10 23:39 ` [PATCH 05/29] KEYS: Create a key type that can be used for general cryptographic operations " David Howells
2012-05-10 23:40 ` [PATCH 06/29] KEYS: Add signature verification facility " David Howells
2012-05-10 23:40 ` [PATCH 07/29] KEYS: Asymmetric public-key algorithm crypto key subtype " David Howells
2012-05-10 23:40 ` [PATCH 08/29] KEYS: RSA signature verification algorithm " David Howells
2012-05-10 23:40 ` [PATCH 09/29] Fix signature verification for shorter signatures " David Howells
2012-05-10 23:40 ` [PATCH 10/29] PGPLIB: PGP definitions (RFC 4880) " David Howells
2012-05-10 23:41 ` [PATCH 11/29] PGPLIB: Basic packet parser " David Howells
2012-05-10 23:41 ` [PATCH 12/29] PGPLIB: Signature " David Howells
2012-05-10 23:41 ` [PATCH 13/29] KEYS: PGP data " David Howells
2012-05-10 23:41 ` [PATCH 14/29] KEYS: PGP-based public key signature verification " David Howells
2012-05-10 23:41 ` [PATCH 15/29] KEYS: PGP format signature parser " David Howells
2012-05-10 23:41 ` [PATCH 16/29] KEYS: Provide a function to load keys from a PGP keyring blob " David Howells
2012-05-10 23:42 ` [PATCH 17/29] Provide macros for forming the name of an ELF note and its section " David Howells
2012-05-10 23:42 ` [PATCH 18/29] Guard check in module loader against integer overflow " David Howells
2012-05-10 23:42 ` [PATCH 19/29] MODSIGN: Add indications of module ELF types " David Howells
2012-05-10 23:42 ` [PATCH 20/29] MODSIGN: Provide gitignore and make clean rules for extra files " David Howells
2012-05-10 23:42 ` [PATCH 21/29] MODSIGN: Provide Documentation and Kconfig options " David Howells
2012-05-10 23:43 ` [PATCH 22/29] MODSIGN: Sign modules during the build process " David Howells
2012-05-10 23:43 ` [PATCH 23/29] MODSIGN: Module signature verification stub " David Howells
2012-05-10 23:43 ` [PATCH 24/29] MODSIGN: Provide module signing public keys to the kernel " David Howells
2012-05-10 23:43 ` [PATCH 25/29] MODSIGN: Check the ELF container " David Howells
2012-05-10 23:43 ` [PATCH 26/29] MODSIGN: Produce a filtered and canonicalised section list " David Howells
2012-05-10 23:43 ` [PATCH 27/29] MODSIGN: Create digest of module content and check signature " David Howells
2012-05-10 23:44 ` [PATCH 28/29] MODSIGN: Automatically generate module signing keys if missing " David Howells
2012-05-10 23:44 ` [PATCH 29/29] MODSIGN: Suppress some redundant ELF checks " David Howells
2012-05-11 13:30 ` [PATCH 00/29] Crypto keys and module signing " Tetsuo Handa
2012-05-11 14:32 ` David Howells
2012-05-11 16:17 ` David Howells
2012-05-19  0:53 ` Rusty Russell

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.