2012-05-22 23:02:35

by David Howells

[permalink] [raw]
Subject: [PATCH 00/23] Crypto keys and module signing


Okay Rusty,

Here's a set of patches that does module signing attaching the signature in the
way you wanted. I do not agree with doing it this way. However, as you
insisted rather forcefully...


===============
<<< WARNING >>>
===============

Anyone wanting to use the module signing facility contained in these
patches MUST FIRST make sure that modules are not stripped after being
signed. This includes by packaging systems (such as rpmbuild) extracting
debug information and due to size reduction for initramfs composition.

This is likely to require alterations to userspace packages, both on the
build machine and the installed machine.

If the module is passed through strip, but the signature is _not_
discarded, then the module load will be rejected, potentially resulting in
an unbootable machine. If you are in FIPS mode the kernel will panic.

To make the modules more usable from an initramfs (which may have limited
capacity), the module files are maximally stripped (by both strip -x -g and
eu-strip) before being signed. This appears to result in the smallest,
still-viable module possible.

However, all debug information is discarded, along with chunks of the symbol
and string tables that might otherwise be useful. An unstripped, unsigned copy
of the module is left in the build tree (called foo.ko.unsigned) that should be
usable for debugging purposes.

The extended module file format is as follows:

The module signature is appended to the end of the module file, followed by
"@mod_size@\n@sig_size@\nThis Is A Crypto Signed Module".

The EOF must follow the magic string directly, so nothing further must be
appended to the file.

mod_size and sig_size are left-aligned decimal numbers, space-padded to 8
characters. mod_size is the size of the module payload part of the file and
sig_size is the size of the signature blob.


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

The patches can also be found here:

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

David
---

Changes made 22/05/2012:

(*) Fixes for the PGP/keys handling parts:

- Fix some checkpatch noise [Thanks to Tetsuo Handa].
- Preclear array on stack [Thanks to Tetsuo Handa].
- Check (sub)packet length [Thanks to Stephan Mueller].
- Decrease (not increase) remnant length in signature parsing.
- Handle new-format 5-octet length encoding.
- Better report encounter of Partial Body Length spec.
- Adjust the error handling.

(*) Completely redo how signature is attached to the module file to comply
with Rusty's specified method. Also pre-strip modules.

(*) Drop the MPILIB extra-exports patch as it's only required for the DSA
algorithm.

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 (23):
MODSIGN: Panic the kernel if FIPS is enabled upon module signing failure
MODSIGN: Automatically generate module signing keys if missing
MODSIGN: Module signature verification
MODSIGN: Provide module signing public keys to the kernel
MODSIGN: Sign modules during the build process
MODSIGN: Provide Documentation and Kconfig options
MODSIGN: Provide gitignore and make clean rules for extra files
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
Guard check in module loader against integer overflow


.gitignore | 13 +
Documentation/module-signing.txt | 183 +++++++++++
Documentation/security/keys-crypto.txt | 302 ++++++++++++++++++
Makefile | 1
include/keys/crypto-subtype.h | 77 ++++
include/keys/crypto-type.h | 37 ++
include/linux/modsign.h | 27 ++
include/linux/module.h | 3
include/linux/pgp.h | 255 +++++++++++++++
init/Kconfig | 62 ++++
kernel/Makefile | 42 ++
kernel/modsign-pubkey.c | 74 ++++
kernel/module-verify.c | 153 +++++++++
kernel/module-verify.h | 20 +
kernel/module.c | 29 +-
net/dns_resolver/dns_key.c | 5
scripts/Makefile.modpost | 98 ++++++
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 | 347 ++++++++++++++++++++
security/keys/crypto/pgp_library.c | 550 ++++++++++++++++++++++++++++++++
security/keys/crypto/pgp_parser.h | 35 ++
security/keys/crypto/pgp_preload.c | 90 +++++
security/keys/crypto/pgp_pubkey_sig.c | 324 +++++++++++++++++++
security/keys/crypto/pgp_sig_parser.c | 113 +++++++
security/keys/crypto/public_key.c | 55 +++
security/keys/crypto/public_key.h | 108 ++++++
security/keys/key.c | 3
35 files changed, 3803 insertions(+), 82 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.c
create mode 100644 kernel/module-verify.h
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


2012-05-22 23:02:37

by David Howells

[permalink] [raw]
Subject: [PATCH 01/23] Guard check in module loader against integer overflow

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 <[email protected]>
---

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

2012-05-22 23:02:46

by David Howells

[permalink] [raw]
Subject: [PATCH 02/23] KEYS: Move the key config into security/keys/Kconfig

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 <[email protected]>
---

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.

2012-05-22 23:02:52

by David Howells

[permalink] [raw]
Subject: [PATCH 03/23] KEYS: Announce key type (un)registration

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

Signed-off-by: David Howells <[email protected]>
---

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

2012-05-22 23:03:00

by David Howells

[permalink] [raw]
Subject: [PATCH 04/23] KEYS: Reorganise keys Makefile

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

Signed-off-by: David Howells <[email protected]>
---

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/

2012-05-22 23:03:12

by David Howells

[permalink] [raw]
Subject: [PATCH 05/23] KEYS: Create a key type that can be used for general cryptographic operations

Create a key type that can be used for general cryptographic operations, such
as encryption, decryption, signature generation and signature verification.

The key type is "crypto" and can provide access to a variety of cryptographic
algorithms.

Signed-off-by: David Howells <[email protected]>
---

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 ([email protected])
+ *
+ * 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 ([email protected])
+ *
+ * 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 ([email protected])
+ *
+ * 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 ([email protected])
+ *
+ * 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);

2012-05-22 23:03:22

by David Howells

[permalink] [raw]
Subject: [PATCH 06/23] KEYS: Add signature verification facility

Add a facility whereby a key subtype may be asked to verify a signature against
the data it is purported to have signed.

This adds four routines:

(1) struct crypto_key_verify_context *
verify_sig_begin(struct key *keyring, const void *sig, size_t siglen);

This sets up a verification context for the given signature using
information in that signature to select a key from the specified keyring
and to request a hash algorithm from the crypto layer.

(2) int verify_sig_add_data(struct crypto_key_verify_context *ctx,
const void *data, size_t datalen);

Incrementally supply data to be signed. May be called multiple times.

(3) int verify_sig_end(struct crypto_key_verify_context *ctx,
const void *sig, size_t siglen);

Complete the verification process and return the result. -EKEYREJECTED
will indicate that the verification failed and 0 will indicate success.
Other errors are also possible.

(4) void verify_sig_cancel(struct crypto_key_verify_context *ctx);

Cancel the verification process.

Signed-off-by: David Howells <[email protected]>
---

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 ([email protected])
+ *
+ * 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);

2012-05-22 23:03:29

by David Howells

[permalink] [raw]
Subject: [PATCH 07/23] KEYS: Asymmetric public-key algorithm crypto key subtype

Add a subtype for supporting asymmetric public-key encryption algorithms such
as DSA (FIPS-186) and RSA (PKCS#1 / RFC1337).

Signed-off-by: David Howells <[email protected]>
---

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 ([email protected])
+ *
+ * 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 ([email protected])
+ *
+ * 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 */

2012-05-22 23:03:35

by David Howells

[permalink] [raw]
Subject: [PATCH 08/23] KEYS: RSA signature verification algorithm

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

Signed-off-by: David Howells <[email protected]>
---

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 ([email protected])
+ *
+ * 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
*/

2012-05-22 23:03:41

by David Howells

[permalink] [raw]
Subject: [PATCH 09/23] Fix signature verification for shorter signatures

gpg can produce a signature file where length of signature is less than the
modulus size because the amount of space an MPI takes up is kept as low as
possible by discarding leading zeros. This regularly happens for several
modules during the build.

Fix it by relaxing check in RSA verification code.

Thanks to Tomas Mraz and Miloslav Trmac for help.

Signed-off-by: Milan Broz <[email protected]>
Signed-off-by: David Howells <[email protected]>
---

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)

2012-05-22 23:03:48

by David Howells

[permalink] [raw]
Subject: [PATCH 10/23] PGPLIB: PGP definitions (RFC 4880)

Provide some useful PGP definitions from RFC 4880. These describe details of
public key crypto as used by crypto keys for things like signature
verification.

Signed-off-by: David Howells <[email protected]>
---

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 ([email protected])
+ *
+ * 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 */

2012-05-22 23:03:57

by David Howells

[permalink] [raw]
Subject: [PATCH 11/23] PGPLIB: Basic packet parser

Provide a simple parser that extracts the packets from a PGP packet blob and
passes the desirous ones to the given processor function:

struct pgp_parse_context {
u64 types_of_interest;
int (*process_packet)(struct pgp_parse_context *context,
enum pgp_packet_tag type,
u8 headerlen,
const u8 *data,
size_t datalen);
};

int pgp_parse_packets(const u8 *data, size_t datalen,
struct pgp_parse_context *ctx);

This is configured on with CONFIG_PGP_LIBRARY.

Signed-off-by: David Howells <[email protected]>
---

include/linux/pgp.h | 25 +++
security/keys/crypto/Kconfig | 6 +
security/keys/crypto/Makefile | 1
security/keys/crypto/pgp_library.c | 268 ++++++++++++++++++++++++++++++++++++
4 files changed, 300 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..b6cf860
--- /dev/null
+++ b/security/keys/crypto/pgp_library.c
@@ -0,0 +1,268 @@
+/* PGP packet parser (RFC 4880)
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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:
+ /* Five-byte length */
+ if (datalen < 5)
+ goto short_packet;
+ size = data[1] << 24;
+ size |= data[2] << 16;
+ size |= data[3] << 8;
+ size |= data[4];
+ data += 5;
+ datalen -= 5;
+ *_headerlen = 6;
+ break;
+ default:
+ pr_debug("Partial body length packet not supported\n");
+ return -EBADMSG;
+ }
+ } else {
+ /* Old packet length format */
+ u8 length_type = type & 0x03;
+ type >>= 2;
+ pr_devel("old format: t=%u lt=%u\n", type, length_type);
+
+ switch (length_type) {
+ case 0:
+ /* One-byte length */
+ size = data[0];
+ data++;
+ datalen--;
+ *_headerlen = 2;
+ break;
+ case 1:
+ /* Two-byte length */
+ if (datalen < 2)
+ goto short_packet;
+ size = data[0] << 8;
+ size |= data[1];
+ data += 2;
+ datalen -= 2;
+ *_headerlen = 3;
+ break;
+ case 2:
+ /* Four-byte length */
+ if (datalen < 4)
+ goto short_packet;
+ size = data[0] << 24;
+ size |= data[1] << 16;
+ size |= data[2] << 8;
+ size |= data[3];
+ data += 4;
+ datalen -= 4;
+ *_headerlen = 5;
+ break;
+ default:
+ pr_debug("Indefinite length packet not supported\n");
+ return -EBADMSG;
+ }
+ }
+
+ pr_devel("datalen=%zu size=%zu", datalen, size);
+ if (datalen < size)
+ goto short_packet;
+ if ((int)size < 0)
+ goto too_big;
+
+ *_data = data;
+ *_datalen = datalen;
+ *_type = type;
+ pr_devel("Found packet type=%u size=%zd\n", type, size);
+ return size;
+
+short_packet:
+ pr_debug("Attempt to parse short packet\n");
+ return -EBADMSG;
+too_big:
+ pr_debug("Signature subpacket size >2G\n");
+ return -EMSGSIZE;
+}
+
+/**
+ * pgp_parse_packets - Parse a set of PGP packets
+ * @_data: Data to be parsed (updated)
+ * @_datalen: Amount of data (updated)
+ * @ctx: Parsing context
+ *
+ * Parse a set of PGP packets [RFC 4880: 4].
+ */
+int pgp_parse_packets(const u8 *data, size_t datalen,
+ struct pgp_parse_context *ctx)
+{
+ enum pgp_packet_tag type;
+ ssize_t pktlen;
+ u8 headerlen;
+ int ret;
+
+ while (datalen > 2) {
+ pktlen = pgp_parse_packet_header(&data, &datalen, &type,
+ &headerlen);
+ if (pktlen < 0)
+ return pktlen;
+
+ if ((ctx->types_of_interest >> type) & 1) {
+ ret = ctx->process_packet(ctx, type, headerlen,
+ data, pktlen);
+ if (ret < 0)
+ return ret;
+ }
+ data += pktlen;
+ datalen -= pktlen;
+ }
+
+ if (datalen != 0) {
+ pr_debug("Excess octets in packet stream\n");
+ return -EBADMSG;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pgp_parse_packets);
+
+/**
+ * pgp_parse_public_key - Parse the common part of a PGP pubkey packet
+ * @_data: Content of packet (updated)
+ * @_datalen: Length of packet remaining (updated)
+ * @pk: Public key data
+ *
+ * Parse the common data struct for a PGP pubkey packet [RFC 4880: 5.5.2].
+ */
+int pgp_parse_public_key(const u8 **_data, size_t *_datalen,
+ struct pgp_parse_pubkey *pk)
+{
+ const u8 *data = *_data;
+ size_t datalen = *_datalen;
+ __be32 tmp;
+
+ if (datalen < 12) {
+ pr_debug("Public key packet too short\n");
+ return -EBADMSG;
+ }
+
+ pk->version = *data++;
+ switch (pk->version) {
+ case PGP_KEY_VERSION_2:
+ case PGP_KEY_VERSION_3:
+ case PGP_KEY_VERSION_4:
+ break;
+ default:
+ pr_debug("Public key packet with unhandled version %d\n",
+ pk->version);
+ return -EBADMSG;
+ }
+
+ tmp = *data++ << 24;
+ tmp |= *data++ << 16;
+ tmp |= *data++ << 8;
+ tmp |= *data++;
+ pk->creation_time = tmp;
+ if (pk->version == PGP_KEY_VERSION_4) {
+ pk->expires_at = 0; /* Have to get it from the selfsignature */
+ } else {
+ unsigned short ndays;
+ ndays = *data++ << 8;
+ ndays |= *data++;
+ if (ndays)
+ pk->expires_at = pk->creation_time + ndays * 86400UL;
+ else
+ pk->expires_at = 0;
+ datalen -= 2;
+ }
+
+ pk->pubkey_algo = *data++;
+ datalen -= 6;
+
+ pr_devel("%x,%x,%lx,%lx",
+ pk->version, pk->pubkey_algo, pk->creation_time,
+ pk->expires_at);
+
+ *_data = data;
+ *_datalen = datalen;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pgp_parse_public_key);

2012-05-22 23:04:09

by David Howells

[permalink] [raw]
Subject: [PATCH 12/23] PGPLIB: Signature parser

Provide some PGP signature parsing helpers:

(1) A function to parse V4 signature subpackets and pass the desired ones to
a processor function:

int pgp_parse_sig_subpkts(const u8 *data, size_t datalen,
struct pgp_parse_sig_context *ctx);

(2) A function to parse out basic signature parameters from any PGP signature
such that the algorithms and public key can be selected:

int pgp_parse_sig_params(const u8 **_data, size_t *_datalen,
struct pgp_sig_parameters *p);

Signed-off-by: David Howells <[email protected]>
---

include/linux/pgp.h | 24 +++
security/keys/crypto/pgp_library.c | 282 ++++++++++++++++++++++++++++++++++++
2 files changed, 306 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 b6cf860..ee08b86 100644
--- a/security/keys/crypto/pgp_library.c
+++ b/security/keys/crypto/pgp_library.c
@@ -266,3 +266,285 @@ 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 */
+ pr_devel("datalen=%zu size=%zu", datalen, size);
+ if (datalen < size)
+ goto short_subpacket;
+ if (size == 0)
+ goto very_short_subpacket;
+ if ((int)size < 0)
+ goto too_big;
+
+ type = *data++ & ~PGP_SIG_SUBPKT_TYPE_CRITICAL_MASK;
+ datalen--;
+ size--;
+
+ *_data = data;
+ *_datalen = datalen;
+ *_type = type;
+ pr_devel("Found subpkt type=%u size=%zd\n", type, size);
+ return size;
+
+very_short_subpacket:
+ pr_debug("Signature subpacket size can't be zero\n");
+ return -EBADMSG;
+short_subpacket:
+ pr_debug("Attempt to parse short signature subpacket\n");
+ return -EBADMSG;
+too_big:
+ pr_debug("Signature subpacket size >2G\n");
+ return -EMSGSIZE;
+}
+
+/**
+ * pgp_parse_sig_subpkts - Parse a set of PGP V4 signatute subpackets
+ * @_data: Data to be parsed (updated)
+ * @_datalen: Amount of data (updated)
+ * @ctx: Parsing context
+ *
+ * Parse a set of PGP signature subpackets [RFC 4880: 5.2.3].
+ */
+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);

2012-05-22 23:04:22

by David Howells

[permalink] [raw]
Subject: [PATCH 14/23] KEYS: PGP-based public key signature verification

Provide handlers for PGP-based public-key algorithm signature verification.
This does most of the work involved in signature verification as most of it is
public-key algorithm agnostic. The public-key verification algorithm itself
is just the last little bit and is supplied the complete hash data to process.

This requires glue logic putting on top to make use of it - something the next
patch provides.

Signed-off-by: David Howells <[email protected]>
---

security/keys/crypto/Makefile | 3
security/keys/crypto/pgp_parser.h | 6 +
security/keys/crypto/pgp_pubkey_sig.c | 324 +++++++++++++++++++++++++++++++++
3 files changed, 332 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..bc02dfa
--- /dev/null
+++ b/security/keys/crypto/pgp_pubkey_sig.c
@@ -0,0 +1,324 @@
+/* Handling for PGP public key signature data [RFC 4880]
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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(-ENOPKG);
+ }
+
+ if (pgp_public_key_algorithms[p.params.pubkey_algo] != key->algo) {
+ kleave(" = -EKEYREJECTED [wrong pk algo]");
+ return ERR_PTR(-EKEYREJECTED);
+ }
+
+ if (!(key->capabilities & PKEY_CAN_VERIFY)) {
+ kleave(" = -EKEYREJECTED [key can't verify]");
+ return ERR_PTR(-EKEYREJECTED);
+ }
+
+ if (p.params.hash_algo >= PGP_HASH__LAST ||
+ !pgp_hash_algorithms[p.params.hash_algo]) {
+ pr_debug("Unsupported hash algorithm %u\n",
+ p.params.hash_algo);
+ return ERR_PTR(-ENOPKG);
+ }
+
+ pr_debug("Signature generated with %s hash\n",
+ pgp_hash_algorithms[p.params.hash_algo]);
+
+ if (memcmp(&p.params.issuer, key->key_id, 8) != 0) {
+ kleave(" = -ENOKEY [wrong key ID]");
+ return ERR_PTR(-ENOKEY);
+ }
+
+ if (p.params.signature_type != PGP_SIG_BINARY_DOCUMENT_SIG &&
+ p.params.signature_type != PGP_SIG_STANDALONE_SIG) {
+ /* We don't want to canonicalise */
+ kleave(" = -EOPNOTSUPP [canon]");
+ return ERR_PTR(-EOPNOTSUPP);
+ }
+
+ /* Allocate the hashing algorithm we're going to need and find out how
+ * big the hash operational data will be.
+ */
+ tfm = crypto_alloc_shash(pgp_hash_algorithms[p.params.hash_algo], 0, 0);
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm) == -ENOENT ?
+ ERR_PTR(-ENOPKG) : ERR_CAST(tfm);
+
+ desc_size = crypto_shash_descsize(tfm);
+ digest_size = crypto_shash_digestsize(tfm);
+
+ /* We allocate the hash operational data storage on the end of our
+ * context data.
+ */
+ sig = kzalloc(sizeof(*sig) + desc_size + digest_size, GFP_KERNEL);
+ if (!sig) {
+ crypto_free_shash(tfm);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ sig->base.key = crypto_key;
+ sig->base.add_data = pgp_pkey_verify_sig_add_data;
+ sig->base.end = pgp_pkey_verify_sig_end;
+ sig->base.cancel = pgp_pkey_verify_sig_cancel;
+ sig->pkey_hash_algo = pgp_pubkey_hash[p.params.hash_algo].algo;
+ sig->digest = (u8 *)sig + sizeof(*sig) + desc_size;
+ sig->digest_size = digest_size;
+ sig->hash.tfm = tfm;
+ sig->hash.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+ ret = crypto_shash_init(&sig->hash);
+ if (ret < 0) {
+ crypto_free_shash(sig->hash.tfm);
+ kfree(sig);
+ return ERR_PTR(ret);
+ }
+
+ key_get(sig->base.key);
+ kleave(" = %p", sig);
+ return &sig->base;
+}
+
+/*
+ * Load data into the hash
+ */
+static int pgp_pkey_verify_sig_add_data(struct crypto_key_verify_context *ctx,
+ const void *data, size_t datalen)
+{
+ struct public_key_signature *sig =
+ container_of(ctx, struct public_key_signature, base);
+
+ return crypto_shash_update(&sig->hash, data, datalen);
+}
+
+struct pgp_pkey_sig_digest_context {
+ struct pgp_parse_context pgp;
+ const struct public_key *key;
+ struct public_key_signature *sig;
+};
+
+/*
+ * Extract required metadata from the signature packet and add what we need to
+ * to the hash.
+ */
+static int pgp_pkey_digest_signature(struct pgp_parse_context *context,
+ enum pgp_packet_tag type,
+ u8 headerlen,
+ const u8 *data,
+ size_t datalen)
+{
+ struct pgp_pkey_sig_digest_context *ctx =
+ container_of(context, struct pgp_pkey_sig_digest_context, pgp);
+ enum pgp_signature_version version;
+ int i;
+
+ kenter(",%u,%u,,%zu", type, headerlen, datalen);
+
+ version = *data;
+ if (version == PGP_SIG_VERSION_3) {
+ /* We just include an excerpt of the metadata from a V3
+ * signature.
+ */
+ crypto_shash_update(&ctx->sig->hash, data + 1, 5);
+ data += sizeof(struct pgp_signature_v3_packet);
+ datalen -= sizeof(struct pgp_signature_v3_packet);
+ } else if (version == PGP_SIG_VERSION_4) {
+ /* We add the whole metadata header and some of the hashed data
+ * for a V4 signature, plus a trailer.
+ */
+ size_t hashedsz, unhashedsz;
+ u8 trailer[6];
+
+ hashedsz = 4 + 2 + (data[4] << 8) + data[5];
+ crypto_shash_update(&ctx->sig->hash, data, hashedsz);
+
+ trailer[0] = version;
+ trailer[1] = 0xffU;
+ trailer[2] = hashedsz >> 24;
+ trailer[3] = hashedsz >> 16;
+ trailer[4] = hashedsz >> 8;
+ trailer[5] = hashedsz;
+
+ crypto_shash_update(&ctx->sig->hash, trailer, 6);
+ data += hashedsz;
+ datalen -= hashedsz;
+
+ unhashedsz = 2 + (data[0] << 8) + data[1];
+ data += unhashedsz;
+ datalen -= unhashedsz;
+ }
+
+ if (datalen <= 2) {
+ kleave(" = -EBADMSG");
+ return -EBADMSG;
+ }
+
+ /* There's a quick check on the hash available. */
+ ctx->sig->signed_hash_msw[0] = *data++;
+ ctx->sig->signed_hash_msw[1] = *data++;
+ datalen -= 2;
+
+ /* And then the cryptographic data, which we'll need for the
+ * algorithm.
+ */
+ for (i = 0; i < ctx->key->algo->n_sig_mpi; i++) {
+ unsigned int remaining = datalen;
+ if (remaining == 0) {
+ pr_debug("short %zu mpi %d\n", datalen, i);
+ return -EBADMSG;
+ }
+ ctx->sig->mpi[i] = mpi_read_from_buffer(data, &remaining);
+ if (!ctx->sig->mpi[i])
+ return -ENOMEM;
+ data += remaining;
+ datalen -= remaining;
+ }
+
+ if (datalen != 0) {
+ kleave(" = -EBADMSG [trailer %zu]", datalen);
+ return -EBADMSG;
+ }
+
+ kleave(" = 0");
+ return 0;
+}
+
+/*
+ * The data is now all loaded into the hash; load the metadata, finalise the
+ * hash and perform the verification step.
+ */
+static int pgp_pkey_verify_sig_end(struct crypto_key_verify_context *ctx,
+ const u8 *sigdata, size_t siglen)
+{
+ struct public_key_signature *sig =
+ container_of(ctx, struct public_key_signature, base);
+ const struct public_key *key = sig->base.key->payload.data;
+ struct pgp_pkey_sig_digest_context p;
+ int ret;
+
+ kenter("");
+
+ /* Firstly we add metadata, starting with some of the data from the
+ * signature packet */
+ p.pgp.types_of_interest = (1 << PGP_PKT_SIGNATURE);
+ p.pgp.process_packet = pgp_pkey_digest_signature;
+ p.key = key;
+ p.sig = sig;
+ ret = pgp_parse_packets(sigdata, siglen, &p.pgp);
+ if (ret < 0)
+ goto error_free_ctx;
+
+ crypto_shash_final(&sig->hash, sig->digest);
+
+ ret = key->algo->verify(key, sig);
+
+error_free_ctx:
+ pgp_pkey_verify_sig_cancel(ctx);
+ kleave(" = %d", ret);
+ return ret;
+}
+
+/*
+ * Cancel an in-progress data loading
+ */
+static void pgp_pkey_verify_sig_cancel(struct crypto_key_verify_context *ctx)
+{
+ struct public_key_signature *sig =
+ container_of(ctx, struct public_key_signature, base);
+ int i;
+
+ kenter("");
+
+ /* !!! Do we need to tell the crypto layer to cancel too? */
+ crypto_free_shash(sig->hash.tfm);
+ key_put(sig->base.key);
+ for (i = 0; i < ARRAY_SIZE(sig->mpi); i++)
+ mpi_free(sig->mpi[i]);
+ kfree(sig);
+
+ kleave("");
+}

2012-05-22 23:04:31

by David Howells

[permalink] [raw]
Subject: [PATCH 15/23] KEYS: PGP format signature parser

Implement a signature parser that will attempt to parse a signature blob as a
PGP packet format message. If it can, it will find an appropriate crypto key
and set the public-key algorithm according to the data in the signature.

Signed-off-by: David Howells <[email protected]>
---

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 | 113 +++++++++++++++++++++++++++++++++
4 files changed, 121 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 9cfedc2..84ce457 100644
--- a/security/keys/crypto/pgp_key_parser.c
+++ b/security/keys/crypto/pgp_key_parser.c
@@ -327,6 +327,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..3dd223f
--- /dev/null
+++ b/security/keys/crypto/pgp_sig_parser.c
@@ -0,0 +1,113 @@
+/* Handling for PGP public key signature data [RFC 4880]
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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(-ENOMSG);
+
+ sprintf(criterion, "id:%08x%08x",
+ be32_to_cpu(p.params.issuer32[0]),
+ be32_to_cpu(p.params.issuer32[1]));
+
+ pr_debug("Look up: %s\n", criterion);
+
+ key = keyring_search(make_key_ref(keyring, 1),
+ &key_type_crypto, criterion);
+ if (IS_ERR(key)) {
+ switch (PTR_ERR(key)) {
+ /* Hide some search errors */
+ case -EACCES:
+ case -ENOTDIR:
+ case -EAGAIN:
+ return ERR_PTR(-ENOKEY);
+ default:
+ return ERR_CAST(key);
+ }
+ }
+
+ pr_debug("Found key %x\n", key_serial(key_ref_to_ptr(key)));
+ return key_ref_to_ptr(key);
+}
+
+/*
+ * Attempt to parse a signature as a PGP packet format blob and find a
+ * matching key.
+ */
+struct crypto_key_verify_context *pgp_verify_sig_begin(
+ struct key *keyring, const u8 *sig, size_t siglen)
+{
+ struct crypto_key_verify_context *ctx;
+ struct key *key;
+
+ key = find_key_for_pgp_sig(keyring, sig, siglen);
+ if (IS_ERR(key))
+ return ERR_CAST(key);
+
+ /* We only handle in-kernel public key signatures for the moment */
+ ctx = pgp_pkey_verify_sig_begin(key, sig, siglen);
+ key_put(key);
+ return ctx;
+}

2012-05-22 23:04:36

by David Howells

[permalink] [raw]
Subject: [PATCH 16/23] KEYS: Provide a function to load keys from a PGP keyring blob

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

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

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

The keys are attached to the keyring supplied.

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

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

Signed-off-by: David Howells <[email protected]>
---

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 ([email protected])
+ *
+ * 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);
+}

2012-05-22 23:04:44

by David Howells

[permalink] [raw]
Subject: [PATCH 17/23] MODSIGN: Provide gitignore and make clean rules for extra files

Provide gitignore and make clean rules for extra files to hide and clean up the
extra files produced by module signing stuff once it is added. Also add a
clean up rule for the module content extractor program used to extract the data
to be signed.

Signed-off-by: David Howells <[email protected]>
---

.gitignore | 13 +++++++++++++
Makefile | 1 +
2 files changed, 14 insertions(+), 0 deletions(-)


diff --git a/.gitignore b/.gitignore
index 57af07c..4d63081 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,10 @@
*.o.*
*.a
*.s
+*.ko.unsigned
+*.ko.stripped
+*.ko.stripped.sig
+*.ko.trailer
*.ko
*.so
*.so.dbg
@@ -84,3 +88,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.*' \

2012-05-22 23:04:53

by David Howells

[permalink] [raw]
Subject: [PATCH 18/23] MODSIGN: Provide Documentation and Kconfig options

Provide documentation and kernel configuration options for module signing.

The documentation can be found in:

Documentation/module-signing.txt

The following configuration options are added:

(1) CONFIG_MODULE_SIG

Enable module signing. This will both cause the build process to sign
modules and the kernel to check modules when they're loaded.

(2) CONFIG_MODULE_SIG_SHA1
CONFIG_MODULE_SIG_SHA224
CONFIG_MODULE_SIG_SHA256
CONFIG_MODULE_SIG_SHA384
CONFIG_MODULE_SIG_SHA512

Select the cryptographic hash used to digest the data prior to signing.
Additionally, the crypto module selected will be built into the kernel as
it won't be possible to load it as a module without incurring a circular
dependency when the kernel tries to check its signature.

(3) CONFIG_MODULE_SIG_FORCE

Require that any module loaded must be signed with a key compiled into
the kernel. All other modules are rejected with EKEYREJECTED.

Signed-off-by: David Howells <[email protected]>
---

Documentation/module-signing.txt | 183 ++++++++++++++++++++++++++++++++++++++
include/linux/modsign.h | 27 ++++++
init/Kconfig | 62 +++++++++++++
3 files changed, 272 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..b355aa2
--- /dev/null
+++ b/Documentation/module-signing.txt
@@ -0,0 +1,183 @@
+ ==============================
+ 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.
+
+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.
+
+
+============================
+SIGNED MODULES AND STRIPPING
+============================
+
+The module signature is just appended to the module binary with a magic number
+at the end of file, a couple of fixed-size lengths prior to that and the
+signature prior to that.
+
+WARNING! Signed modules are BRITTLE as the signature is outside of the defined
+ELF container. Thus they MAY NOT be stripped once the signature is computed
+and attached, lest the signature be discarded or the payload be modified. Note
+that the entire module is the signed payload, including all the debug
+information present at the time of signing so it must still be present when the
+signature is checked.
+
+As the module may need to be included in a ramdisk image of limited capacity,
+modules are maximally stripped prior to signing by the build process.
+
+Note that if FIPS mode is engaged, a module for which the signature does not
+match the payload will panic the box.
+
+
+======================
+LOADING SIGNED MODULES
+======================
+
+Modules are loaded with insmod, exactly as for unsigned modules. The signature
+checker will check at the end of the file for the signature marker and apply
+signature checking if found.
+
+
+=========================================
+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
+
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 ([email protected])
+ *
+ * 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..f461fd2 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1409,6 +1409,68 @@ 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_RSA
+ select PGP_PARSER
+ select PGP_PRELOAD
+ help
+ Check modules for valid signatures upon load. For more information
+ see:
+
+ Documentation/module-signing.txt
+
+ !!!WARNING!!! If you enable this option, you MUST make sure that the
+ module DOES NOT get stripped after being signed. This includes the
+ debuginfo strip done by some packagers (such as rpmbuild) and
+ inclusion into an initramfs that wants the module size reduced.
+
+ The signed module is brittle, and any change is likely to result in
+ the module load being rejected due to the signature being discarded
+ or the signed payload being altered. If FIPS mode is engaged, the
+ kernel will panic if the signature is detected, but does not match.
+
+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

2012-05-22 23:05:00

by David Howells

[permalink] [raw]
Subject: [PATCH 19/23] MODSIGN: Sign modules during the build process

If CONFIG_MODULE_SIG is set, then this patch will cause the module to get a
signature installed. The following steps will occur:

(1) The module will be linked to foo.ko.unsigned instead of foo.ko

(2) The module will be stripped using both "strip -x -g" and "eu-strip" to
ensure minimal size for inclusion in an initramfs.

(3) The signature will be generated on the stripped module.

(4) The signature will be appended to the module, along with the payload
size, the signature size and a magic string.

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
STRIP [M] fs/foo/foo.ko.stripped
SIGN [M] fs/foo/foo.ko

will appear in the build log. If the signature step will be skipped and the
following will be seen:

LD [M] fs/foo/foo.ko.unsigned
STRIP [M] fs/foo/foo.ko.stripped
NO SIGN [M] fs/foo/foo.ko

NOTE! After the signature step, the signed module must not be passed through
strip. The unstripped, unsigned module is still available at the name on the
LD [M] line. This restriction may affect packaging tools (such as rpmbuild)
and initramfs composition tools.

Note that I do not agree with this method of attaching signatures to modules.

Signed-off-by: David Howells <[email protected]>
---

scripts/Makefile.modpost | 98 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 97 insertions(+), 1 deletions(-)


diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost
index 08dce14..bb761ab 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,99 @@ $(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
+
+# We strip the module as best we can - note that using both strip and eu-strip
+# results in a smaller module than using either alone.
+quiet_cmd_sign_ko_stripped_ko_unsigned = STRIP [M] $@
+ cmd_sign_ko_stripped_ko_unsigned = \
+ cp $< $@ && \
+ strip -x -g $@ && \
+ eu-strip $@
+
+ifeq ($(SIGN_MODULES),1)
+KEYRING_DEP := modsign.sec modsign.pub
+quiet_cmd_sign_ko_ko_stripped = SIGN [M] $@
+ cmd_sign_ko_ko_stripped = \
+ rm -f $<.sig && \
+ gpg --batch --no-greeting $(KEYFLAGS) -b $< && \
+ stat -c @%-8s@ $< >[email protected] && \
+ stat -c @%-8s@ $<.sig >>[email protected] && \
+ echo -n "This Is A Crypto Signed Module" >>[email protected] && \
+ cat $< $<.sig [email protected] >$@
+else
+KEYRING_DEP :=
+quiet_cmd_sign_ko_ko_unsigned = NO SIGN [M] $@
+ cmd_sign_ko_ko_unsigned = \
+ cp $< $@
+endif
+
+$(modules): %.ko :%.ko.stripped $(KEYRING_DEP) FORCE
+ $(call if_changed,sign_ko_ko_stripped)
+
+$(patsubst %.ko,%.ko.stripped,$(modules)): %.ko.stripped :%.ko.unsigned FORCE
+ $(call if_changed,sign_ko_stripped_ko_unsigned)

+targets += $(modules)
+endif

# Add FORCE to the prequisites of a target to force it to be always rebuilt.
# ---------------------------------------------------------------------------

2012-05-22 23:05:14

by David Howells

[permalink] [raw]
Subject: [PATCH 20/23] MODSIGN: Provide module signing public keys to the kernel

Include a PGP keyring containing the public keys required to perform module
verification in the kernel image during build and create a special keyring
during boot which is then populated with keys of crypto type holding the public
keys found in the PGP keyring.

These can be seen by root:

[root@andromeda ~]# cat /proc/keys
07ad4ee0 I----- 1 perm 3f010000 0 0 crypto modsign.0: RSA 87b9b3bd []
15c7f8c3 I----- 1 perm 1f030000 0 0 keyring .module_sign: 1/4
...

It is probably worth permitting root to invalidate these keys, resulting in
their removal and preventing further modules from being loaded with that key.

Signed-off-by: David Howells <[email protected]>
---

kernel/Makefile | 3 ++
kernel/modsign-pubkey.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++
kernel/module-verify.h | 14 +++++++++
3 files changed, 91 insertions(+), 0 deletions(-)
create mode 100644 kernel/modsign-pubkey.c
create mode 100644 kernel/module-verify.h


diff --git a/kernel/Makefile b/kernel/Makefile
index cb41b95..bde66b6 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) += modsign-pubkey.o
obj-$(CONFIG_KALLSYMS) += kallsyms.o
obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
obj-$(CONFIG_KEXEC) += kexec.o
@@ -127,3 +128,5 @@ quiet_cmd_timeconst = TIMEC $@
targets += timeconst.h
$(obj)/timeconst.h: $(src)/timeconst.pl FORCE
$(call if_changed,timeconst)
+
+kernel/modsign-pubkey.o: modsign.pub
diff --git a/kernel/modsign-pubkey.c b/kernel/modsign-pubkey.c
new file mode 100644
index 0000000..163b6f0
--- /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 ([email protected])
+ *
+ * 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.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.h b/kernel/module-verify.h
new file mode 100644
index 0000000..2f6cc16
--- /dev/null
+++ b/kernel/module-verify.h
@@ -0,0 +1,14 @@
+/* Module verification definitions
+ *
+ * Copyright (C) 2004, 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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
+extern struct key *modsign_keyring;
+#endif

2012-05-22 23:05:24

by David Howells

[permalink] [raw]
Subject: [PATCH 22/23] MODSIGN: Automatically generate module signing keys if missing

Automatically generate keys for module signing if they're absent so that
allyesconfig doesn't break. The builder should consider generating their own
keyrings, however, so that the keys are appropriately named and any extra keys
required get imported.

Also change the names of the keyring files to modsign.pub and modsign.sec so
that they are then a more obvious what they're about and add a dependency for
the signing rules on the keyring files so that the signatures get regenerated
if the keyrings change.

Signed-off-by: David Howells <[email protected]>
---

kernel/Makefile | 39 +++++++++++++++++++++++++++++++++++++++
1 files changed, 39 insertions(+), 0 deletions(-)


diff --git a/kernel/Makefile b/kernel/Makefile
index 28f0ec4..d8139bb 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -129,4 +129,43 @@ 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

2012-05-22 23:05:32

by David Howells

[permalink] [raw]
Subject: [PATCH 23/23] MODSIGN: Panic the kernel if FIPS is enabled upon module signing failure

If module signing fails when the kernel is running with FIPS enabled then the
kernel should panic lest the crypto layer be compromised. Possibly a panic
shouldn't happen on cases like ENOMEM.

Reported-by: Stephan Mueller <[email protected]>
Signed-off-by: David Howells <[email protected]>
---

kernel/module-verify.c | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)


diff --git a/kernel/module-verify.c b/kernel/module-verify.c
index f989fee..9baecc6 100644
--- a/kernel/module-verify.c
+++ b/kernel/module-verify.c
@@ -19,6 +19,7 @@
#include <linux/sched.h>
#include <linux/modsign.h>
#include <linux/moduleparam.h>
+#include <linux/fips.h>
#include <keys/crypto-type.h>
#include "module-verify.h"

@@ -109,6 +110,10 @@ int module_verify(const void *data, size_t size, bool *_gpgsig_ok)

pr_devel("module_verify_signature() = %d\n", ret);

+ if (ret < 0 && fips_enabled)
+ panic("Module verification failed with error %d in FIPS mode\n",
+ ret);
+
switch (ret) {
case 0: /* Good signature */
*_gpgsig_ok = true;

2012-05-22 23:06:20

by David Howells

[permalink] [raw]
Subject: [PATCH 21/23] MODSIGN: Module signature verification

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

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

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
Signed, pubkey algorithm unavailable ENOPKG ENOPKG
Signature without sig packet ENOMSG ENOMSG
Corrupt signature EBADMSG EBADMSG
Corrupt file ELIBBAD ELIBBAD


=======================
!!!IMPORTANT WARNING!!!
=======================

Signed modules generated by this kernel very likely CANNOT be used with
existing packaging and installation infrastructure. For example, in Fedora's
environment, the module is potentially stripped at least twice:

(1) by rpmbuild when the debuginfo is detached from the module, and

(2) by the initrd image composer to reduce the module size.

Both of these will potentially result in the module signature being discarded
or rendered unverifiable, resulting in the module load just going ahead if the
signature magic is not found and enforcemodulesig=1 not being supplied or
-EKEYREJECTED being given or a panic being forced if FIPS mode is engaged.

To aid with (2), the module is completely stripped prior to signing as it
cannot be stripped after signing. Both "strip -x -g" and "eu-strip" are
applied as the use of both of these results in a smaller binary.

That, however, means there is no debug information directly available for the
module.

The original unstripped binary for the foo.ko module can be found as
foo.ko.unsigned in the build tree. It may be possible to use this as the debug
info source.

Signed-off-by: David Howells <[email protected]>
---

include/linux/module.h | 3 +
kernel/Makefile | 2 -
kernel/module-verify.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++
kernel/module-verify.h | 6 ++
kernel/module.c | 26 +++++++-
5 files changed, 179 insertions(+), 6 deletions(-)
create mode 100644 kernel/module-verify.c


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 bde66b6..28f0ec4 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -51,7 +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) += modsign-pubkey.o
+obj-$(CONFIG_MODULE_SIG) += module-verify.o modsign-pubkey.o
obj-$(CONFIG_KALLSYMS) += kallsyms.o
obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
obj-$(CONFIG_KEXEC) += kexec.o
diff --git a/kernel/module-verify.c b/kernel/module-verify.c
new file mode 100644
index 0000000..f989fee
--- /dev/null
+++ b/kernel/module-verify.c
@@ -0,0 +1,148 @@
+/* Module signature verification
+ *
+ * The code in this file examines a signed kernel module and attempts to
+ * determine if the PGP signature attached to the end of the module matches the
+ * entire content of the module without the signature attached.
+ *
+ * Copyright (C) 2004, 2011, 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ * - Method specified by Rusty Russell.
+ *
+ * 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/module.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/modsign.h>
+#include <linux/moduleparam.h>
+#include <keys/crypto-type.h>
+#include "module-verify.h"
+
+#ifdef CONFIG_MODULE_SIG_FORCE
+#define modsign_signedonly true
+#else
+static bool modsign_signedonly;
+#endif
+
+static const char modsign_magic[] = "This Is A Crypto Signed Module";
+
+/*
+ * Verify a module's signature, if it has one
+ *
+ * Returns 0 if module is validly signed, 1 if there's no signature and a
+ * negative error code otherwise.
+ */
+static int module_verify_signature(const void *data, size_t size)
+{
+ struct crypto_key_verify_context *mod_sig;
+ const char *cp, *sig;
+ char *end;
+ size_t magic_size, sig_size, mod_size;
+ int ret;
+
+ magic_size = sizeof(modsign_magic) - 1;
+ if (size <= 11 + 11 + magic_size)
+ return 1;
+
+ if (memcmp(data + size - magic_size, modsign_magic, magic_size) != 0)
+ return 1;
+ size -= 11 + 11 + magic_size;
+
+ cp = data + size;
+ if (cp[ 0] != '@' && cp[ 9] != '@' && cp[10] != '\n' &&
+ cp[11] != '@' && cp[20] != '@' && cp[21] != '\n')
+ return -ELIBBAD;
+ mod_size = simple_strtoul(cp + 1, &end, 10);
+ if (mod_size > size || (*end != ' ' && *end != '@'))
+ return -ELIBBAD;
+ sig_size = simple_strtoul(cp + 12, &end, 10);
+ if (sig_size > size || (*end != ' ' && *end != '@'))
+ return -ELIBBAD;
+
+ pr_devel("sig at %zu, size %zu (to %zu)\n", mod_size, sig_size, size);
+ if (size - mod_size != sig_size)
+ return -ELIBBAD;
+
+ sig = data + mod_size;
+ pr_devel("sig dump: %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);
+ }
+
+ /* Load the module contents into the digest */
+ ret = verify_sig_add_data(mod_sig, data, mod_size);
+ if (ret < 0) {
+ verify_sig_cancel(mod_sig);
+ return ret;
+ }
+
+ /* Do the actual signature verification */
+ ret = verify_sig_end(mod_sig, sig, sig_size);
+ pr_devel("verify-sig : %d\n", ret);
+ return ret;
+}
+
+/*
+ * Verify a module's integrity
+ */
+int module_verify(const void *data, size_t size, bool *_gpgsig_ok)
+{
+ int ret;
+
+ pr_devel("-->module_verify(,%zu,)\n", size);
+
+ ret = module_verify_signature(data, size);
+
+ pr_devel("module_verify_signature() = %d\n", ret);
+
+ switch (ret) {
+ case 0: /* Good signature */
+ *_gpgsig_ok = true;
+ break;
+ case 1: /* Unsigned module */
+ if (modsign_signedonly) {
+ pr_err("An attempt to load unsigned module was rejected\n");
+ return -EKEYREJECTED;
+ }
+ ret = 0;
+ 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
index 2f6cc16..d59e7c9 100644
--- a/kernel/module-verify.h
+++ b/kernel/module-verify.h
@@ -11,4 +11,10 @@

#ifdef CONFIG_MODULE_SIG
extern struct key *modsign_keyring;
+extern int module_verify(const void *data, size_t size, bool *_gpgsig_ok);
+#else
+static inline int module_verify(const void *data, 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);

2012-05-22 23:04:17

by David Howells

[permalink] [raw]
Subject: [PATCH 13/23] KEYS: PGP data parser

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

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

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

Signed-off-by: David Howells <[email protected]>
---

security/keys/crypto/Kconfig | 12 +
security/keys/crypto/Makefile | 4
security/keys/crypto/pgp_key_parser.c | 346 +++++++++++++++++++++++++++++++++
security/keys/crypto/pgp_parser.h | 23 ++
4 files changed, 385 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..9cfedc2
--- /dev/null
+++ b/security/keys/crypto/pgp_key_parser.c
@@ -0,0 +1,346 @@
+/* Parser for PGP format key data [RFC 4880]
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * 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("");
+
+ for (i = 0; i < ARRAY_SIZE(pp); i++)
+ pp[i] = NULL;
+
+ n = (pgp->version < PGP_KEY_VERSION_4) ? 8 : 6;
+ for (i = 0; i < npkey; i++) {
+ nb[i] = mpi_get_nbits(key->mpi[i]);
+ pp[i] = mpi_get_buffer(key->mpi[i], nn + i, NULL);
+ if (!pp[i])
+ goto error;
+ n += 2 + nn[i];
+ }
+
+ digest_putc(digest, 0x99); /* ctb */
+ digest_putc(digest, n >> 8); /* 16-bit header length */
+ digest_putc(digest, n);
+ digest_putc(digest, pgp->version);
+
+ a32 = pgp->creation_time;
+ digest_putc(digest, a32 >> 24);
+ digest_putc(digest, a32 >> 16);
+ digest_putc(digest, a32 >> 8);
+ digest_putc(digest, a32 >> 0);
+
+ if (pgp->version < PGP_KEY_VERSION_4) {
+ u16 a16;
+
+ if (pgp->expires_at)
+ a16 = (pgp->expires_at - pgp->creation_time) / 86400UL;
+ else
+ a16 = 0;
+ digest_putc(digest, a16 >> 8);
+ digest_putc(digest, a16 >> 0);
+ }
+
+ digest_putc(digest, pgp->pubkey_algo);
+
+ for (i = 0; i < npkey; i++) {
+ digest_putc(digest, nb[i] >> 8);
+ digest_putc(digest, nb[i]);
+ crypto_shash_update(digest, pp[i], nn[i]);
+ }
+ ret = 0;
+
+error:
+ for (i = 0; i < npkey; i++)
+ kfree(pp[i]);
+ kleave(" = %d", ret);
+ return ret;
+}
+
+/*
+ * Calculate the public key ID fingerprint
+ */
+static int pgp_generate_fingerprint(struct pgp_key_data_parse_context *ctx,
+ struct pgp_parse_pubkey *pgp,
+ struct public_key *key)
+{
+ struct crypto_shash *tfm;
+ struct shash_desc *digest;
+ char *fingerprint;
+ u8 *raw_fingerprint;
+ int digest_size, offset;
+ int ret, i;
+
+ ret = -ENOMEM;
+ tfm = crypto_alloc_shash(pgp->version < PGP_KEY_VERSION_4 ?
+ "md5" : "sha1", 0, 0);
+ if (!tfm)
+ goto cleanup;
+
+ digest = kmalloc(sizeof(*digest) + crypto_shash_descsize(tfm),
+ GFP_KERNEL);
+ if (!digest)
+ goto cleanup_tfm;
+
+ digest->tfm = tfm;
+ digest->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+ ret = crypto_shash_init(digest);
+ if (ret < 0)
+ goto cleanup_hash;
+
+ ret = pgp_calc_pkey_keyid(digest, pgp, key);
+ if (ret < 0)
+ goto cleanup_hash;
+
+ digest_size = crypto_shash_digestsize(tfm);
+
+ raw_fingerprint = kmalloc(digest_size, GFP_KERNEL);
+ if (!raw_fingerprint)
+ goto cleanup_hash;
+
+ ret = crypto_shash_final(digest, raw_fingerprint);
+ if (ret < 0)
+ goto cleanup_raw_fingerprint;
+
+ fingerprint = kmalloc(digest_size * 2 + 1, GFP_KERNEL);
+ if (!fingerprint)
+ goto cleanup_raw_fingerprint;
+
+ offset = digest_size - 8;
+ pr_debug("offset %u/%u\n", offset, digest_size);
+
+ for (i = 0; i < digest_size; i++)
+ sprintf(fingerprint + i * 2, "%02x", raw_fingerprint[i]);
+ pr_debug("fingerprint %s\n", fingerprint);
+
+ memcpy(&key->key_id, raw_fingerprint + offset, 8);
+ key->key_id_size = 8;
+
+ ctx->fingerprint = fingerprint;
+ ret = 0;
+cleanup_raw_fingerprint:
+ kfree(raw_fingerprint);
+cleanup_hash:
+ kfree(digest);
+cleanup_tfm:
+ crypto_free_shash(tfm);
+cleanup:
+ kleave(" = %d", ret);
+ return ret;
+}
+
+/*
+ * Extract a public key or public subkey from the PGP stream.
+ */
+static int pgp_process_public_key(struct pgp_parse_context *context,
+ enum pgp_packet_tag type,
+ u8 headerlen,
+ const u8 *data,
+ size_t datalen)
+{
+ const struct public_key_algorithm *algo;
+ struct pgp_key_data_parse_context *ctx =
+ container_of(context, struct pgp_key_data_parse_context, pgp);
+ struct pgp_parse_pubkey pgp;
+ struct public_key *key;
+ int i, ret;
+
+ kenter(",%u,%u,,%zu", type, headerlen, datalen);
+
+ if (ctx->subtype) {
+ kleave(" = -ENOKEY [already]");
+ return -EBADMSG;
+ }
+
+ key = kzalloc(sizeof(struct public_key), GFP_KERNEL);
+ if (!key)
+ return -ENOMEM;
+
+ ret = pgp_parse_public_key(&data, &datalen, &pgp);
+ if (ret < 0)
+ goto cleanup;
+
+ if (pgp.pubkey_algo >= PGP_PUBKEY__LAST ||
+ !pgp_public_key_algorithms[pgp.pubkey_algo]) {
+ pr_debug("Unsupported public key algorithm %u\n",
+ pgp.pubkey_algo);
+ ret = -ENOPKG;
+ goto cleanup;
+ }
+
+ algo = key->algo = pgp_public_key_algorithms[pgp.pubkey_algo];
+
+ /* It's a public key, so that only gives us encrypt and verify
+ * capabilities.
+ */
+ key->capabilities = pgp_public_key_capabilities[pgp.pubkey_algo] &
+ (PKEY_CAN_ENCRYPT | PKEY_CAN_VERIFY);
+
+ 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 ([email protected])
+ *
+ * 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];

2012-05-23 12:54:24

by Rusty Russell

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

On Wed, 23 May 2012 00:02:19 +0100, David Howells <[email protected]> wrote:
> The extended module file format is as follows:
>
> The module signature is appended to the end of the module file, followed by
> "@mod_size@\n@sig_size@\nThis Is A Crypto Signed Module".
>
> The EOF must follow the magic string directly, so nothing further must be
> appended to the file.
>
> mod_size and sig_size are left-aligned decimal numbers, space-padded to 8
> characters. mod_size is the size of the module payload part of the file and
> sig_size is the size of the signature blob.

Hi David,

That's pretty weird. Why not put the "@This Is A Crypto Signed
Module\n" before the signature? Then module-size is implied: everything
before that signature. The signature size is implied: everything after
that signature.

In fact, I'd modify this slightly, to allow multiple signatures.
This would work nicely with a deterministic strip. Find the signatures
backward, and truncate as they fail.

If you want to be super-paranoid, scan for that signature before
signing the module.

Cheers,
Rusty.

2012-05-23 14:21:40

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

Rusty Russell <[email protected]> wrote:

> That's pretty weird. Why not put the "@This Is A Crypto Signed
> Module\n" before the signature? Then module-size is implied: everything
> before that signature. The signature size is implied: everything after
> that signature.

This makes it simpler. No scanning required. The magic number can only be in
one place and you can find it by dead reckoning.

> In fact, I'd modify this slightly, to allow multiple signatures.
> This would work nicely with a deterministic strip. Find the signatures
> backward, and truncate as they fail.

Why would you want multiple signatures? That just complicates things. If
you're in FIPS mode, you probably have to panic if any of them fail.

I suppose I may as well punt the signature detection and removal to userspace
and pass the signature as an argument to init_module() as Dmitry suggested.
Then the signature need not be in the file at all (he wants to use an xattr or
hardware, I think). mkinitrd and rpmbuild/kernel spec have to be changed to
accommodate enablement of these patches, so why not module-init-tools, dracut
and busybox whilst we're at it?

David

2012-05-24 12:08:09

by Rusty Russell

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

On Wed, 23 May 2012 15:20:47 +0100, David Howells <[email protected]> wrote:
> Rusty Russell <[email protected]> wrote:
>
> > That's pretty weird. Why not put the "@This Is A Crypto Signed
> > Module\n" before the signature? Then module-size is implied: everything
> > before that signature. The signature size is implied: everything after
> > that signature.
>
> This makes it simpler. No scanning required. The magic number can only be in
> one place and you can find it by dead reckoning.

Scanning isn't complicated. Slow, sure, but I doubt you can really
measure it when you're doing crypto.

Compare:

(1) Your scheme signing looks something like this:
gpg --sign $m > $m.sig
MSIZE=`ls -l $m | awk '{ print $5 }'`
SSIZE=`ls -l $m.sig | awk '{ print $5 }'`

printf '@%-10i@%-10i@This Is A Crypto Signed Module' $MSIZE $SSIZE >> $m

(2) Your verification scheme looks like this:

+ magic_size = sizeof(modsign_magic) - 1;
+ if (size <= 11 + 11 + magic_size)
+ return 1;
+
+ if (memcmp(data + size - magic_size, modsign_magic, magic_size) != 0)
+ return 1;
+ size -= 11 + 11 + magic_size;
+
+ cp = data + size;
+ if (cp[ 0] != '@' && cp[ 9] != '@' && cp[10] != '\n' &&
+ cp[11] != '@' && cp[20] != '@' && cp[21] != '\n')
+ return -ELIBBAD;
+ mod_size = simple_strtoul(cp + 1, &end, 10);
+ if (mod_size > size || (*end != ' ' && *end != '@'))
+ return -ELIBBAD;
+ sig_size = simple_strtoul(cp + 12, &end, 10);
+ if (sig_size > size || (*end != ' ' && *end != '@'))
+ return -ELIBBAD;
+
+ pr_devel("sig at %zu, size %zu (to %zu)\n", mod_size, sig_size,
size);
+ if (size - mod_size != sig_size)
+ return -ELIBBAD;

Now, the scheme I suggested looks like this:

(1) Signing:
gpg --sign $m > $m.sig
(echo @This Is A Crypto Signed Module; cat $m.sig) >> $m

(2) Verification:
size_t i;

if (i < modsign_magic)
return 1;

for (i = size - modsign_magic;
memcmp(data + i, modsign_magic, magic_size) != 0);
i++) {
if (i == 0)
return 1;
}

/* module: "data", size "i".
* sig: "data + i + magic_size", size "size - (i + magic_size)" */

> > In fact, I'd modify this slightly, to allow multiple signatures.
> > This would work nicely with a deterministic strip. Find the signatures
> > backward, and truncate as they fail.
>
> Why would you want multiple signatures? That just complicates things.

The code above stays pretty simple; if the signature fails, you set size
to i, and loop again. As I said, if you know exactly how you're going
to strip the modules, you can avoid storing the stripped module and
simply append both signatures.

> If you're in FIPS mode, you probably have to panic if any of them fail.

I had to look up what FIPS was, so I'm not qualified to comment.

> I suppose I may as well punt the signature detection and removal to userspace
> and pass the signature as an argument to init_module() as Dmitry suggested.
> Then the signature need not be in the file at all (he wants to use an xattr or
> hardware, I think). mkinitrd and rpmbuild/kernel spec have to be changed to
> accommodate enablement of these patches, so why not module-init-tools, dracut
> and busybox whilst we're at it?

In some ways that is cleaner, but it's also nice to avoid adding another
syscall.

Cheers,
Rusty.

2012-05-24 14:01:11

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

Rusty Russell <[email protected]> wrote:

> (1) Your scheme signing looks something like this:
> gpg --sign $m > $m.sig
> MSIZE=`ls -l $m | awk '{ print $5 }'`
> SSIZE=`ls -l $m.sig | awk '{ print $5 }'`
>
> printf '@%-10i@%-10i@This Is A Crypto Signed Module' $MSIZE $SSIZE >> $m

It doesn't really need the @ signs or the MSIZE; they can be dropped.

gpg --batch --no-greeting $(KEYFLAGS) -b $< && \
stat --printf %-5s $<.sig >[email protected] && \
echo -n "This Is A Crypto Signed Module" >>[email protected] && \
cat $< $<.sig [email protected] >$@

Further, the signature size field can be reduced to 5 decimal digits easily -
if the signature is larger than 99999 bytes, there's very likely a problem
somewhere.

Oh, and I should use --printf, not -c with stat as the newline char is unneeded.

That then adds 5 bytes to the magic string. Is that really so bad?

And if you object to generating a foo.ko.trailer file, then:

gpg --batch --no-greeting $(KEYFLAGS) -b $< && \
(cat $< $<.sig && \
stat --printf %-5s $<.sig && \
echo -n "This Is A Crypto Signed Module" && \
) >$@

Note that this scheme does not preclude using multiple signatures as you
desire, but since you have no record of the module length you'd either have to
parse all the signature records first to find the end of the module, or include
each preceding signature and marker in the digest for the next one (which
shouldn't be a problem). You should still check all signatures anyway and
verify all for which you have the public key.

You could even stick other types of record in with different magic strings
terminating them, provided you include a length.

> (2) Your verification scheme looks like this:

A chunk of which can be discarded with the above reductions.

> Now, the scheme I suggested looks like this:
> ...
> for (i = size - modsign_magic;
> memcmp(data + i, modsign_magic, magic_size) != 0);
> i++) {
> if (i == 0)
> return 1;
> }

Which will likely oops. You need to decrement i, not increment it, but that's
a minor detail. You're also subtracting a pointer from a size.

And no, I won't do this. It's unnecessary and a potentially large overhead.
Say you've got a module that's 7.7M in size (an unstripped, unsigned CIFS
module for example)... That's nearly eight *million* calls to memcmp() if
there's no signature. I suspect that's on the order of a tenth of a second or
longer on most machines.

Stripped, CIFS is still on the order of half a meg - which in itself translates
to half a million calls to memcmp() if there's no signature.

Furthermore, the data cache may be of limited utility as it can't do readahead
as you're scanning backwards through the image. You'd be much better off doing
a memchr() or just open-coding a forward search for 'T', and then doing the
memcmp() at each instance. Better still, don't do the scan at all.

Doing a SHA digest on some machines will be done with hardware assistance
(s390, for example) - so this scan may take longer than the digest there.

> > Why would you want multiple signatures? That just complicates things.
>
> The code above stays pretty simple; if the signature fails, you set size
> to i, and loop again. As I said, if you know exactly how you're going
> to strip the modules, you can avoid storing the stripped module and
> simply append both signatures.

You still haven't justified it. One of your arguments about rejecting the ELF
parsing version was that it was too big for no useful extra value that I could
justify. Supporting multiple signatures adds extra size and complexity for no
obvious value.

More importantly, a major problem with (multiple) signatures is that each
signing event has to risk exposing the private key - so you really only want to
sign once unless you cannot avoid it. Further, in an automated system, the
private key cannot be protected by a password as all the secrets have to be
passed to the signer.

Trying to automatically save a during-build generated private key or trying to
get a private key into the build from within the RHEL and Fedora automated
build systems risks having a key stolen or having someone substitute their own
key - and also makes it more complicated to build a kernel outside of the build
system.

The way I handle the private key in these patches is with transience: A fresh
key is generated during a build from a clean tree and then discarded when the
build tree is deleted. This key is then a one-off. If it is stolen or
cracked, it can only affect a single build of the kernel.

Note that we _do_ allow extra public keys to be installed in the kernel as
there's much less risk there, and further they have to be there to permit
signature verification.

David

2012-05-25 11:16:03

by Kasatkin, Dmitry

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

On Thu, May 24, 2012 at 3:04 PM, Rusty Russell <[email protected]> wrote:
> On Wed, 23 May 2012 15:20:47 +0100, David Howells <[email protected]> wrote:
>> Rusty Russell <[email protected]> wrote:
>>
>> >         That's pretty weird.  Why not put the "@This Is A Crypto Signed
>> > Module\n" before the signature?  Then module-size is implied: everything
>> > before that signature.  The signature size is implied: everything after
>> > that signature.
>>
>> This makes it simpler.  No scanning required.  The magic number can only be in
>> one place and you can find it by dead reckoning.
>
> Scanning isn't complicated.  Slow, sure, but I doubt you can really
> measure it when you're doing crypto.
>
> Compare:
>
> (1) Your scheme signing looks something like this:
>        gpg --sign $m > $m.sig
>        MSIZE=`ls -l $m | awk '{ print $5 }'`
>        SSIZE=`ls -l $m.sig | awk '{ print $5 }'`
>
>        printf '@%-10i@%-10i@This Is A Crypto Signed Module' $MSIZE $SSIZE >> $m
>
> (2) Your verification scheme looks like this:
>
>  +      magic_size = sizeof(modsign_magic) - 1;
>  +      if (size <= 11 + 11 + magic_size)
>  +              return 1;
>  +
>  +      if (memcmp(data + size - magic_size, modsign_magic, magic_size) != 0)
>  +              return 1;
>  +      size -= 11 + 11 + magic_size;
>  +
>  +      cp = data + size;
>  +      if (cp[ 0] != '@' && cp[ 9] != '@' && cp[10] != '\n' &&
>  +          cp[11] != '@' && cp[20] != '@' && cp[21] != '\n')
>  +              return -ELIBBAD;
>  +      mod_size = simple_strtoul(cp + 1, &end, 10);
>  +      if (mod_size > size || (*end != ' ' && *end != '@'))
>  +              return -ELIBBAD;
>  +      sig_size = simple_strtoul(cp + 12, &end, 10);
>  +      if (sig_size > size || (*end != ' ' && *end != '@'))
>  +              return -ELIBBAD;
>  +
>  +      pr_devel("sig at %zu, size %zu (to %zu)\n", mod_size, sig_size,
>  size);
>  +      if (size - mod_size != sig_size)
>  +              return -ELIBBAD;
>
> Now, the scheme I suggested looks like this:
>
> (1) Signing:
>        gpg --sign $m > $m.sig
>        (echo @This Is A Crypto Signed Module; cat $m.sig) >> $m
>
> (2) Verification:
>        size_t i;
>
>        if (i < modsign_magic)
>                return 1;
>
>        for (i = size - modsign_magic;
>             memcmp(data + i, modsign_magic, magic_size) != 0);
>             i++) {
>                if (i == 0)
>                        return 1;
>        }
>
>        /* module: "data", size "i".
>         * sig: "data + i + magic_size", size "size - (i + magic_size)" */
>
>> >         In fact, I'd modify this slightly, to allow multiple signatures.
>> > This would work nicely with a deterministic strip.  Find the signatures
>> > backward, and truncate as they fail.
>>
>> Why would you want multiple signatures?  That just complicates things.
>
> The code above stays pretty simple; if the signature fails, you set size
> to i, and loop again.  As I said, if you know exactly how you're going
> to strip the modules, you can avoid storing the stripped module and
> simply append both signatures.
>
>> If you're in FIPS mode, you probably have to panic if any of them fail.
>
> I had to look up what FIPS was, so I'm not qualified to comment.
>
>> I suppose I may as well punt the signature detection and removal to userspace
>> and pass the signature as an argument to init_module() as Dmitry suggested.
>> Then the signature need not be in the file at all (he wants to use an xattr or
>> hardware, I think).  mkinitrd and rpmbuild/kernel spec have to be changed to
>> accommodate enablement of these patches, so why not module-init-tools, dracut
>> and busybox whilst we're at it?

Actually it is not for hw.
I have implemented reading of signature from security.ima xattr and
from .sig file.

http://linux-ima.git.sourceforge.net/git/gitweb.cgi?p=linux-ima/module-init-tools.git;a=summary

>
> In some ways that is cleaner, but it's also nice to avoid adding another
> syscall.

There is no additional syscall.
signature is passed is 'ima=' parameter to init_module()
like

----------------------------------------------------------
root@ubuntu:~# modprobe -v button
insmod /lib/modules/3.4.0-rc5-kds+/kernel/drivers/acpi/button.ko
ima=016e7cbb4f00005d2b05fc633ee3e8010400ad30f2e50d52456ef4a4f0c540f3c8d9955b7ea125cd2dd0cb41216d1388801427c6bddc1431c82e4e82372e6a2101afdcf0daa4f59e1b3d9581d9f1fd0f003fede88d6679814a0887e056a7ddabf070e96cdf5901201d7a6cd4717af68500bd2af88d078a9f1cfc136f5e2d8d0df710121fbb5658c248714f77bb6879aba7b4
----------------------------------------------------------

- Dmitry
>
> Cheers,
> Rusty.
> --
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to [email protected]
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

2012-05-25 11:38:16

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

Kasatkin, Dmitry <[email protected]> wrote:

> > In some ways that is cleaner, but it's also nice to avoid adding another
> > syscall.
>
> There is no additional syscall.
> signature is passed is 'ima=' parameter to init_module()
> like

Exactly what I was thinking of, though I wouldn't call it "ima=" as that
doesn't seem specific enough.

You could also append it to the binary image and have the kernel extract it
from there as if the module file had had the signature attached during the
build.

I don't particularly favour holding the signature in an xattr, though, as that
assumes that underlying fs has the capability to hold it and means that the
package installation mechanism must know how to fill in xattrs.

David

2012-05-25 12:18:36

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing


I've posted a new version with Tetsuo's comments fixed and
module_verify_signature() reduced to:

static int module_verify_signature(const void *data, size_t size)
{
struct crypto_key_verify_context *mod_sig;
const char *cp, *sig;
char *end;
size_t magic_size, sig_size, mod_size;
int ret;

magic_size = sizeof(modsign_magic) - 1;
if (size <= 5 + magic_size)
return 1;

if (memcmp(data + size - magic_size, modsign_magic, magic_size) != 0)
return 1;
size -= 5 + magic_size;

cp = data + size;
sig_size = simple_strtoul(cp, &end, 10);
if (sig_size >= size || (*end != ' ' && *end != 'T'))
return -ELIBBAD;

mod_size = size - sig_size;
sig = data + mod_size;

/* 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);
}

/* Load the module contents into the digest */
ret = verify_sig_add_data(mod_sig, data, mod_size);
if (ret < 0) {
verify_sig_cancel(mod_sig);
return ret;
}

/* Do the actual signature verification */
ret = verify_sig_end(mod_sig, sig, sig_size);
pr_devel("verify-sig : %d\n", ret);
return ret;
}

See:

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

David

2012-05-25 13:09:25

by Mimi Zohar

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

On Fri, 2012-05-25 at 12:37 +0100, David Howells wrote:
> Kasatkin, Dmitry <[email protected]> wrote:
>
> > > In some ways that is cleaner, but it's also nice to avoid adding another
> > > syscall.
> >
> > There is no additional syscall.
> > signature is passed is 'ima=' parameter to init_module()
> > like
>
> Exactly what I was thinking of, though I wouldn't call it "ima=" as that
> doesn't seem specific enough.

IMA-appraisal maintains the file integrity metadata.

> You could also append it to the binary image and have the kernel extract it
> from there as if the module file had had the signature attached during the
> build.
>
> I don't particularly favour holding the signature in an xattr, though, as that
> assumes that underlying fs has the capability to hold it and means that the
> package installation mechanism must know how to fill in xattrs.

Labeling files for integrity is a general issue, not limited to kernel
modules/ELF files. For this reason, we need a generic mechanism that
works for all files, not only those that are executables. For local
files, such as configuration files, which are system specific, files are
labeled with hashes(IMA-appraisal) and HMAC(EVM).

Yes, the package update mechanisms will need to be modified to install
both the file data and the associated file metadata - hash, signature.
Tar supports xattrs. The original CPIO format doesn't, but there are
extensions that do.

The issue here is whether we want the integrity metadata for kernel
modules to be stored differently than for all other files.

Mimi

2012-05-25 13:54:11

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

Mimi Zohar <[email protected]> wrote:

> The issue here is whether we want the integrity metadata for kernel
> modules to be stored differently than for all other files.

Surely it's handled differently. The kernel is told by insmod what the
signature should be in your scheme rather than going looking for it itself. In
such a case, why not include the signature in the module file? It's more
efficient on the filesystem, doesn't require xattr support and is easier for
things like the initramfs composer to deal with.

Btw, am I right in thinking that with IMA, the kernel itself normally goes and
finds the signature (if there is one) for a file when it needs to open a file?
Do you only check the IMA when exec'ing a file or whenever you open it?

David

2012-05-25 14:41:06

by Mimi Zohar

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

On Fri, 2012-05-25 at 14:53 +0100, David Howells wrote:
> Mimi Zohar <[email protected]> wrote:
>
> > The issue here is whether we want the integrity metadata for kernel
> > modules to be stored differently than for all other files.
>
> Surely it's handled differently. The kernel is told by insmod what the
> signature should be in your scheme rather than going looking for it itself. In
> such a case, why not include the signature in the module file? It's more
> efficient on the filesystem, doesn't require xattr support and is easier for
> things like the initramfs composer to deal with.

Yes, unfortunately, it is handled differently. Instead of passing a
file descriptor, a buffer containing the file data is passed. Without
the file descriptor, you also need to pass the file metadata, in this
case, the signature as well. Whether the signature is passed as a
separate parameter or appended to the file, it still is being passed to
the kernel.

> Btw, am I right in thinking that with IMA, the kernel itself normally goes and
> finds the signature (if there is one) for a file when it needs to open a file?
> Do you only check the IMA when exec'ing a file or whenever you open it?
>
> David

IMA measures a file and appraises file data integrity based on the IMA
measurement/appraisal policy. There are currently 3 hooks: open -
ima_file_check(), exec - ima_bprm_check() and mmap - ima_file_mapp().

I don't know the reasons or history for passing the file data content,
instead of a file descriptor, but if a file descriptor were passed, then
module integrity appraisal could simply be another integrity hook.

Mimi

2012-05-25 15:42:39

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing


Hi Rusty,

If you prefer to have userspace extract the module signature and pass it in
uargs, here's a tree that will do that:

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

You can use it with the attached patch to kmod.git. I've passed the signature
length in the argument as it permits the kernel to preallocate the buffer it
decodes the signature into, but that's not actually necessary.

David
---
commit e4c9290fc949bc41f942f00a1460d9d90fc1775c
Author: David Howells <[email protected]>
Date: Fri May 25 16:35:19 2012 +0100

libkmod: Extract module signature and prepend on module argument list

Extract the module signature from the module image, if present, and prepend on
the module argument list hexcoded as

"modsign=l,x "

Where "l" is the length of the signature as a decimal number and "x" is the
hex-encoded signature data.

Signed-off-by: David Howells <[email protected]>

diff --git a/libkmod/libkmod-module.c b/libkmod/libkmod-module.c
index f5384a7..d358eec 100644
--- a/libkmod/libkmod-module.c
+++ b/libkmod/libkmod-module.c
@@ -756,6 +756,88 @@ KMOD_EXPORT int kmod_module_remove_module(struct kmod_module *mod,
return err;
}

+static const char kmod_modsign_magic[] = "This Is A Crypto Signed Module";
+static const char kmod_arg_key[] = "modsign=";
+
+/**
+ * kmod_find_signature:
+ * @mod: kmod module
+ * @mem: The module contents
+ * @_size: The size of the module contents (updated if sig removed)
+ * @args: Argument string
+ * @_new_args: Where to place the updated argument list
+ *
+ * Returns: 0 on success or < 0 on failure. -ELIBBAD is returned is the module
+ * format cannot be parsed.
+ */
+static int kmod_find_signature(struct kmod_module *mod,
+ const void *mem, off_t *_size,
+ const char *args, char **_new_args)
+{
+ const unsigned char *sig, *data = mem;
+ const char *cp;
+ char *new_args, *dp, *end;
+ size_t magic_size, sig_size, mod_size, arg_size, new_arg_size, size = *_size;
+
+ INFO(mod->ctx, "Look for signature in module image\n");
+
+ magic_size = sizeof(kmod_modsign_magic) - 1;
+ if (size <= 5 + magic_size)
+ return 0;
+
+ if (memcmp(data + size - magic_size, kmod_modsign_magic, magic_size) != 0)
+ /* Probably want to check for IMA signature file or xattr here */
+ return 0;
+ size -= 5 + magic_size;
+
+ INFO(mod->ctx, "Signature marker found in module\n");
+
+ cp = (const char *)data + size;
+ sig_size = strtoul(cp, &end, 10);
+ if (sig_size >= size || (*end != ' ' && *end != 'T'))
+ return -ELIBBAD;
+
+ mod_size = size - sig_size;
+ INFO(mod->ctx, "Module size %zu Signature size %zu\n", mod_size, sig_size);
+ if (sig_size <= 0)
+ return -ENOMSG;
+ if (sig_size > 65535)
+ return -EMSGSIZE;
+
+ sig = data + mod_size;
+ INFO(mod->ctx, "Signature dump: %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]);
+
+ arg_size = strlen(args) + 1;
+ new_arg_size = sizeof(kmod_arg_key) - 1 + 5 + 1 + sig_size * 2 + 1 + arg_size;
+ new_args = malloc(new_arg_size);
+ if (!new_args)
+ return -ENOMEM;
+
+ /* Hex encode the signature as "modsign=l,xxxxxx " at the front of the
+ * argument list.
+ */
+ dp = memcpy(new_args, kmod_arg_key, sizeof(kmod_arg_key) - 1);
+ dp += sizeof(kmod_arg_key) - 1;
+ dp += sprintf(dp, "%zu,", sig_size);
+ do {
+ dp += sprintf(dp, "%02x", *sig++);
+ } while (--sig_size > 0);
+ *dp++ = ' ';
+ memcpy(dp, args, arg_size);
+
+ dp += arg_size;
+ if (dp > new_args + new_arg_size) {
+ fprintf(stderr, "%p > %p\n", dp, new_args + new_arg_size);
+ abort();
+ }
+
+ *_new_args = new_args;
+ *_size = mod_size;
+ return 0;
+}
+
extern long init_module(const void *mem, unsigned long len, const char *args);

/**
@@ -782,6 +864,7 @@ KMOD_EXPORT int kmod_module_insert_module(struct kmod_module *mod,
struct kmod_elf *elf = NULL;
const char *path;
const char *args = options ? options : "";
+ char *new_args = NULL;

if (mod == NULL)
return -ENOENT;
@@ -823,6 +906,12 @@ KMOD_EXPORT int kmod_module_insert_module(struct kmod_module *mod,
mem = kmod_elf_get_memory(elf);
}

+ err = kmod_find_signature(mod, mem, &size, args, &new_args);
+ if (err < 0)
+ goto elf_failed;
+ if (new_args)
+ args = new_args;
+
err = init_module(mem, size, args);
if (err < 0) {
err = -errno;
@@ -831,6 +920,7 @@ KMOD_EXPORT int kmod_module_insert_module(struct kmod_module *mod,

if (elf != NULL)
kmod_elf_unref(elf);
+ free(new_args);
elf_failed:
kmod_file_unref(file);

2012-05-27 22:45:52

by Rusty Russell

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

On Thu, 24 May 2012 15:00:51 +0100, David Howells <[email protected]> wrote:
> It doesn't really need the @ signs or the MSIZE; they can be dropped.
...
> That then adds 5 bytes to the magic string. Is that really so bad?

Yes, because it's unnecessary.

Now you've decided to encode some data, you need to decide how. And you
discovered that they all sucked in different ways. Binary is a PITA for
the shell, so you chose to put ASCII (in the middle of an
otherwise-binary blob). Then you chose a fixed length with space-based
padding as the least of all evils.

But it's still completely tasteless.

Sure, my code didn't even compile. And yes, it takes about 70ms on an
8M file on my laptop, and yes, that drops to about 6ms if we check one
char manually. It would drop further if we searched forwards, using
memchr then memcmp, but it's already below my care factor.

> > > Why would you want multiple signatures? That just complicates things.
> >
> > The code above stays pretty simple; if the signature fails, you set size
> > to i, and loop again. As I said, if you know exactly how you're going
> > to strip the modules, you can avoid storing the stripped module and
> > simply append both signatures.
>
> You still haven't justified it. One of your arguments about rejecting the ELF
> parsing version was that it was too big for no useful extra value that I could
> justify. Supporting multiple signatures adds extra size and complexity for no
> obvious value.

One loop is a lot easier to justify that the ELF-parsing mess. And it
can be done in a backwards compatible way tomorrow: old kernels will
only check the last signature.

I had assumed you'd rather maintain a stable strip util which you can
use on kernel modules than rework your module builds. I guess not.

Cheers,
Rusty.

2012-05-31 14:11:22

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

Rusty Russell <[email protected]> wrote:

> > That then adds 5 bytes to the magic string. Is that really so bad?
>
> Yes, because it's unnecessary.

I'm sorry Rusty, but this argument is disingenuous.

Yes, a length field in the file is unnecessary - BUT SO TOO is scanning! By
this argument, your idea is really so bad too. It's all about the trade off
one chooses to make. I do not accept your chosen trade off[*] as being the
best one.

David

[*] And, yes, it *is* a trade off: you are trading CPU time and permanently
resident kernel code space in order to save a tiny amount of disk
space[**].

[**] Assuming 512 byte blocks and a 5 byte size field, probably fewer than 1%
of modules will expand sufficiently to consume an extra block. Further,
making it a 2-byte binary field would make it even less intrusive, both
in the file and in the module verifier.

2012-05-31 15:35:27

by Josh Boyer

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

On Sun, May 27, 2012 at 1:41 AM, Rusty Russell <[email protected]> wrote:
> On Thu, 24 May 2012 15:00:51 +0100, David Howells <[email protected]> wrote:
>> > > Why would you want multiple signatures? ?That just complicates things.
>> >
>> > The code above stays pretty simple; if the signature fails, you set size
>> > to i, and loop again. ?As I said, if you know exactly how you're going
>> > to strip the modules, you can avoid storing the stripped module and
>> > simply append both signatures.
>>
>> You still haven't justified it. ?One of your arguments about rejecting the ELF
>> parsing version was that it was too big for no useful extra value that I could
>> justify. ?Supporting multiple signatures adds extra size and complexity for no
>> obvious value.
>
> One loop is a lot easier to justify that the ELF-parsing mess. ?And it
> can be done in a backwards compatible way tomorrow: old kernels will
> only check the last signature.
>
> I had assumed you'd rather maintain a stable strip util which you can
> use on kernel modules than rework your module builds. ?I guess not.

Could you elaborate on this part a bit? Do you mean integrate a
standalone strip utility in the kernel sources and maintain that for
use during module builds? Or am I misunderstanding and you meant
something else?

I can see how that sounds simple and desirable from one aspect, but
it seems somewhat odd to me to duplicate the existing (or create from
scratch) strip utilities.

josh

2012-06-04 04:48:41

by Rusty Russell

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

On Fri, 25 May 2012 16:42:19 +0100, David Howells <[email protected]> wrote:
>
> Hi Rusty,
>
> If you prefer to have userspace extract the module signature and pass it in
> uargs, here's a tree that will do that:
>
> http://git.kernel.org/?p=linux/kernel/git/dhowells/linux-modsign.git;a=shortlog;h=refs/heads/modsign-uarg

OK, there's merit in this approach: it certainly moves the argument
about how to encode the signature out of my backyard :)

Should we just bite the bullet and create a new syscall:

SYSCALL_DEFINE5(init_module2, void __user *, umod,
unsigned long, len, const char __user *, uargs,
unsigned int, siglen, const char __user *, sig)

But I'm easily swayed if you prefer the current approach.

Thanks,
Rusty.

2012-06-04 04:49:35

by Rusty Russell

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

On Thu, 31 May 2012 11:35:23 -0400, Josh Boyer <[email protected]> wrote:
> On Sun, May 27, 2012 at 1:41 AM, Rusty Russell <[email protected]> wrote:
> > On Thu, 24 May 2012 15:00:51 +0100, David Howells <[email protected]> wrote:
> >> > > Why would you want multiple signatures?  That just complicates things.
> >> >
> >> > The code above stays pretty simple; if the signature fails, you set size
> >> > to i, and loop again.  As I said, if you know exactly how you're going
> >> > to strip the modules, you can avoid storing the stripped module and
> >> > simply append both signatures.
> >>
> >> You still haven't justified it.  One of your arguments about rejecting the ELF
> >> parsing version was that it was too big for no useful extra value that I could
> >> justify.  Supporting multiple signatures adds extra size and complexity for no
> >> obvious value.
> >
> > One loop is a lot easier to justify that the ELF-parsing mess.  And it
> > can be done in a backwards compatible way tomorrow: old kernels will
> > only check the last signature.
> >
> > I had assumed you'd rather maintain a stable strip util which you can
> > use on kernel modules than rework your module builds.  I guess not.
>
> Could you elaborate on this part a bit? Do you mean integrate a
> standalone strip utility in the kernel sources and maintain that for
> use during module builds? Or am I misunderstanding and you meant
> something else?

In the kernel sources, no. But could RH maintain such a thing? Surely.

Whether they want to guarantee that their strip is stable on kernel
modules, or create a minimal 'kmod-strip' is up to them.

> I can see how that sounds simple and desirable from one aspect, but
> it seems somewhat odd to me to duplicate the existing (or create from
> scratch) strip utilities.

Mangling a module after it is signed is very odd, and odd things aren't
nice for security features. That's how we got here; I'm trying to move
the oddness out of the verification path.

Cheers,
Rusty.

2012-06-04 12:49:14

by Mimi Zohar

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

On Mon, 2012-06-04 at 11:01 +0930, Rusty Russell wrote:
> On Fri, 25 May 2012 16:42:19 +0100, David Howells <[email protected]> wrote:
> >
> > Hi Rusty,
> >
> > If you prefer to have userspace extract the module signature and pass it in
> > uargs, here's a tree that will do that:
> >
> > http://git.kernel.org/?p=linux/kernel/git/dhowells/linux-modsign.git;a=shortlog;h=refs/heads/modsign-uarg
>
> OK, there's merit in this approach: it certainly moves the argument
> about how to encode the signature out of my backyard :)
>
> Should we just bite the bullet and create a new syscall:
>
> SYSCALL_DEFINE5(init_module2, void __user *, umod,
> unsigned long, len, const char __user *, uargs,
> unsigned int, siglen, const char __user *, sig)
>
> But I'm easily swayed if you prefer the current approach.
>
> Thanks,
> Rusty.

If you're really considering creating a new syscall, then perhaps this
discussion should include passing the file descriptor instead of a
buffer and signature. As I said https://lkml.org/lkml/2012/5/25/261, I
don't know the historical reasons for passing a buffer instead of the
file descriptor itself. If the file descriptor was passed, it would
allow IMA-appraisal, which is in the process of being upstreamed, to
verify and enforce file data and metadata integrity like on the other
hooks open, execve, and mmap.

thanks,

Mimi

2012-06-04 13:38:46

by Josh Boyer

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

On Sun, Jun 3, 2012 at 9:16 PM, Rusty Russell <[email protected]> wrote:
>> > I had assumed you'd rather maintain a stable strip util which you can
>> > use on kernel modules than rework your module builds. ?I guess not.
>>
>> Could you elaborate on this part a bit? ?Do you mean integrate a
>> standalone strip utility in the kernel sources and maintain that for
>> use during module builds? ?Or am I misunderstanding and you meant
>> something else?
>
> In the kernel sources, no. ?But could RH maintain such a thing? ?Surely.

Sure. RH could continue to maintain the original modsign patch too.
I thought the point was to get the mechanism into the upstream kernel
so that it was generally available though. Having the patches in the
mainline kernel but not the strip tool seems somewhat unhelpful. I
would think such a tool would be part of the Kbuild infrastructure.

> Whether they want to guarantee that their strip is stable on kernel
> modules, or create a minimal 'kmod-strip' is up to them.
>
>> I can see how that sounds simple and desirable from one aspect, but
>> it seems somewhat odd to me to duplicate the existing (or create from
>> scratch) strip utilities.
>
> Mangling a module after it is signed is very odd, and odd things aren't
> nice for security features. ?That's how we got here; I'm trying to move
> the oddness out of the verification path.

It's unfortunate, yes. The biggest case I can think of is splitting
the debug symbols out of the modules after they are built (David might
have other cases). Perhaps we could upstream that as well and
organize it such that the modules are built, split for debuginfo, and
then signed?

josh

2012-06-05 01:07:13

by Rusty Russell

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

On Mon, 4 Jun 2012 09:38:43 -0400, Josh Boyer <[email protected]> wrote:
> On Sun, Jun 3, 2012 at 9:16 PM, Rusty Russell <[email protected]> wrote:
> > Mangling a module after it is signed is very odd, and odd things aren't
> > nice for security features.  That's how we got here; I'm trying to move
> > the oddness out of the verification path.
>
> It's unfortunate, yes. The biggest case I can think of is splitting
> the debug symbols out of the modules after they are built (David might
> have other cases). Perhaps we could upstream that as well and
> organize it such that the modules are built, split for debuginfo, and
> then signed?

That was my original suggestion. Just prepare all the module variants
at build time, and sign them all.

See: https://lkml.org/lkml/2011/12/10/16

Cheers,
Rusty.

2012-06-05 01:07:12

by Rusty Russell

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

On Mon, 04 Jun 2012 08:47:51 -0400, Mimi Zohar <[email protected]> wrote:
> On Mon, 2012-06-04 at 11:01 +0930, Rusty Russell wrote:
> > On Fri, 25 May 2012 16:42:19 +0100, David Howells <[email protected]> wrote:
> > >
> > > Hi Rusty,
> > >
> > > If you prefer to have userspace extract the module signature and pass it in
> > > uargs, here's a tree that will do that:
> > >
> > > http://git.kernel.org/?p=linux/kernel/git/dhowells/linux-modsign.git;a=shortlog;h=refs/heads/modsign-uarg
> >
> > OK, there's merit in this approach: it certainly moves the argument
> > about how to encode the signature out of my backyard :)
> >
> > Should we just bite the bullet and create a new syscall:
> >
> > SYSCALL_DEFINE5(init_module2, void __user *, umod,
> > unsigned long, len, const char __user *, uargs,
> > unsigned int, siglen, const char __user *, sig)
> >
> > But I'm easily swayed if you prefer the current approach.
> >
> > Thanks,
> > Rusty.
>
> If you're really considering creating a new syscall, then perhaps this
> discussion should include passing the file descriptor instead of a
> buffer and signature. As I said https://lkml.org/lkml/2012/5/25/261, I
> don't know the historical reasons for passing a buffer instead of the
> file descriptor itself. If the file descriptor was passed, it would
> allow IMA-appraisal, which is in the process of being upstreamed, to
> verify and enforce file data and metadata integrity like on the other
> hooks open, execve, and mmap.

It's flexible. Compressed modules, for example. And who knew if we
would be runtime generating modules? But I don't think even the ksplice
guys generate modules on the fly for insertion.

modprobe has --force-vermagic and --force-modversion, but frankly that
could be replaced by a single "force" flag handed to the kernel.

If there's real benefit, it could be done. Do we still want a separate
signature blob?

SYSCALL_DEFINE5(init_module_fd,
int, fd,
unsigned int, flags,
const char *__user *, uargs,
unsigned int, siglen,
const char __user *, sig);

Cheers,
Rusty.

2012-06-05 11:40:41

by Mimi Zohar

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

On Tue, 2012-06-05 at 10:35 +0930, Rusty Russell wrote:
> On Mon, 04 Jun 2012 08:47:51 -0400, Mimi Zohar <[email protected]> wrote:
> > On Mon, 2012-06-04 at 11:01 +0930, Rusty Russell wrote:
> > > On Fri, 25 May 2012 16:42:19 +0100, David Howells <[email protected]> wrote:
> > > >
> > > > Hi Rusty,
> > > >
> > > > If you prefer to have userspace extract the module signature and pass it in
> > > > uargs, here's a tree that will do that:
> > > >
> > > > http://git.kernel.org/?p=linux/kernel/git/dhowells/linux-modsign.git;a=shortlog;h=refs/heads/modsign-uarg
> > >
> > > OK, there's merit in this approach: it certainly moves the argument
> > > about how to encode the signature out of my backyard :)
> > >
> > > Should we just bite the bullet and create a new syscall:
> > >
> > > SYSCALL_DEFINE5(init_module2, void __user *, umod,
> > > unsigned long, len, const char __user *, uargs,
> > > unsigned int, siglen, const char __user *, sig)
> > >
> > > But I'm easily swayed if you prefer the current approach.
> > >
> > > Thanks,
> > > Rusty.
> >
> > If you're really considering creating a new syscall, then perhaps this
> > discussion should include passing the file descriptor instead of a
> > buffer and signature. As I said https://lkml.org/lkml/2012/5/25/261, I
> > don't know the historical reasons for passing a buffer instead of the
> > file descriptor itself. If the file descriptor was passed, it would
> > allow IMA-appraisal, which is in the process of being upstreamed, to
> > verify and enforce file data and metadata integrity like on the other
> > hooks open, execve, and mmap.
>
> It's flexible. Compressed modules, for example. And who knew if we
> would be runtime generating modules? But I don't think even the ksplice
> guys generate modules on the fly for insertion.
>
> modprobe has --force-vermagic and --force-modversion, but frankly that
> could be replaced by a single "force" flag handed to the kernel.
>
> If there's real benefit, it could be done. Do we still want a separate
> signature blob?
>
> SYSCALL_DEFINE5(init_module_fd,
> int, fd,
> unsigned int, flags,
> const char *__user *, uargs,
> unsigned int, siglen,
> const char __user *, sig);

As the signature would be stored as an extended attribute, we wouldn't
need to pass it. Unfortunately not all filesystems have xattr support,
nor do all of the package installation mechanims. The benefit of
storing the signature as an extended attribute, however, is that there
is a consistent mechanism for verifying file data integrity for all
files, not only ELF.

Mimi

2012-06-05 13:36:53

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

Rusty Russell <[email protected]> wrote:

> > If you prefer to have userspace extract the module signature and pass it in
> > uargs, here's a tree that will do that:
> >
> > http://git.kernel.org/?p=linux/kernel/git/dhowells/linux-modsign.git;a=shortlog;h=refs/heads/modsign-uarg
>
> OK, there's merit in this approach: it certainly moves the argument
> about how to encode the signature out of my backyard :)

Not really. The signature still has to be created by the kernel build. It's
just that you no longer have to care about the trade off when it comes to
parsing it.

> Should we just bite the bullet and create a new syscall:
>
> SYSCALL_DEFINE5(init_module2, void __user *, umod,
> unsigned long, len, const char __user *, uargs,
> unsigned int, siglen, const char __user *, sig)
>
> But I'm easily swayed if you prefer the current approach.

"The current approach" being to attach signature to the blob? Or to pass the
signature separately but in the uargs?

I would very much prefer to keep the signature in the blob and have the kernel
extract it as there's no particular need for it to be detached - even if you
are using IMA.

However, I don't think an extra syscall would hurt particularly - except that
it uses up more space in the syscall table... It would, however, be smaller
in the signature verification department as the signature neither needs
decoding from uargs nor extracting from the blob.

David

2012-06-05 13:38:10

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

Mimi Zohar <[email protected]> wrote:

> As the signature would be stored as an extended attribute, we wouldn't
> need to pass it. Unfortunately not all filesystems have xattr support,
> nor do all of the package installation mechanims. The benefit of
> storing the signature as an extended attribute, however, is that there
> is a consistent mechanism for verifying file data integrity for all
> files, not only ELF.

We also want to be able to do module signature verification with CONFIG_IMA=n.

David

2012-06-05 14:36:48

by Kasatkin, Dmitry

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

On Tue, Jun 5, 2012 at 4:37 PM, David Howells <[email protected]> wrote:
> Mimi Zohar <[email protected]> wrote:
>
>> As the signature would be stored as an extended attribute, we wouldn't
>> need to pass it.  Unfortunately not all filesystems have xattr support,
>> nor do all of the package installation mechanims.  The benefit of
>> storing the signature as an extended attribute, however, is that there
>> is a consistent mechanism for verifying file data integrity for all
>> files, not only ELF.
>
> We also want to be able to do module signature verification with CONFIG_IMA=n.

Sure. In the patchset I sent some time ago, signature verification
does not require CONFIG_IMA=y.
modprobe reads signature from xattr or .sig file and pass it as kernel
module parameter.

- Dmitry


>
> David

2012-06-10 06:38:35

by Rusty Russell

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

On Tue, 05 Jun 2012 14:35:56 +0100, David Howells <[email protected]> wrote:
> Rusty Russell <[email protected]> wrote:
>
> > > If you prefer to have userspace extract the module signature and pass it in
> > > uargs, here's a tree that will do that:
> > >
> > > http://git.kernel.org/?p=linux/kernel/git/dhowells/linux-modsign.git;a=shortlog;h=refs/heads/modsign-uarg
> >
> > OK, there's merit in this approach: it certainly moves the argument
> > about how to encode the signature out of my backyard :)
>
> Not really. The signature still has to be created by the kernel build. It's
> just that you no longer have to care about the trade off when it comes to
> parsing it.

Yes, exactly.

> > Should we just bite the bullet and create a new syscall:
> >
> > SYSCALL_DEFINE5(init_module2, void __user *, umod,
> > unsigned long, len, const char __user *, uargs,
> > unsigned int, siglen, const char __user *, sig)
> >
> > But I'm easily swayed if you prefer the current approach.
>
> "The current approach" being to attach signature to the blob? Or to pass the
> signature separately but in the uargs?

The former.

> I would very much prefer to keep the signature in the blob and have the kernel
> extract it as there's no particular need for it to be detached - even if you
> are using IMA.
>
> However, I don't think an extra syscall would hurt particularly - except that
> it uses up more space in the syscall table... It would, however, be smaller
> in the signature verification department as the signature neither needs
> decoding from uargs nor extracting from the blob.

Good. Let's have init_module2(), and let userspace decide where to get
the signature from.

Thanks,
Rusty.

2012-06-11 08:30:47

by Kasatkin, Dmitry

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

On Sun, Jun 10, 2012 at 8:47 AM, Rusty Russell <[email protected]> wrote:
> On Tue, 05 Jun 2012 14:35:56 +0100, David Howells <[email protected]> wrote:
>> Rusty Russell <[email protected]> wrote:
>>
>> > > If you prefer to have userspace extract the module signature and pass it in
>> > > uargs, here's a tree that will do that:
>> > >
>> > >   http://git.kernel.org/?p=linux/kernel/git/dhowells/linux-modsign.git;a=shortlog;h=refs/heads/modsign-uarg
>> >
>> > OK, there's merit in this approach: it certainly moves the argument
>> > about how to encode the signature out of my backyard :)
>>
>> Not really.  The signature still has to be created by the kernel build.  It's
>> just that you no longer have to care about the trade off when it comes to
>> parsing it.
>
> Yes, exactly.
>
>> > Should we just bite the bullet and create a new syscall:
>> >
>> > SYSCALL_DEFINE5(init_module2, void __user *, umod,
>> >             unsigned long, len, const char __user *, uargs,
>> >                 unsigned int, siglen, const char __user *, sig)
>> >
>> > But I'm easily swayed if you prefer the current approach.
>>
>> "The current approach" being to attach signature to the blob?  Or to pass the
>> signature separately but in the uargs?
>
> The former.
>
>> I would very much prefer to keep the signature in the blob and have the kernel
>> extract it as there's no particular need for it to be detached - even if you
>> are using IMA.
>>
>> However, I don't think an extra syscall would hurt particularly - except that
>> it uses up more space in the syscall table...  It would, however, be smaller
>> in the signature verification department as the signature neither needs
>> decoding from uargs nor extracting from the blob.
>
> Good.  Let's have init_module2(), and let userspace decide where to get
> the signature from.
>

Nice.

> Thanks,
> Rusty.
> --
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to [email protected]
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

2012-06-22 01:53:48

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

On Sun, May 27, 2012 at 03:11:23PM +0930, Rusty Russell wrote:
> > > > Why would you want multiple signatures? That just complicates things.
> > >
> > > The code above stays pretty simple; if the signature fails, you set size
> > > to i, and loop again. As I said, if you know exactly how you're going
> > > to strip the modules, you can avoid storing the stripped module and
> > > simply append both signatures.
> >
> > You still haven't justified it. One of your arguments about rejecting the ELF
> > parsing version was that it was too big for no useful extra value that I could
> > justify. Supporting multiple signatures adds extra size and complexity for no
> > obvious value.
>
> One loop is a lot easier to justify that the ELF-parsing mess. And it
> can be done in a backwards compatible way tomorrow: old kernels will
> only check the last signature.
>
> I had assumed you'd rather maintain a stable strip util which you can
> use on kernel modules than rework your module builds. I guess not.

To dig an old thread up, but what really is wrong with the original ELF
section stuff? Why encode "magic" values on the end of the kernel
module that then require all userspace tools to be modified in order to
properly handle this?

When I first did this so many many years ago an elf section made it so
easy to handle. Userspace didn't need to be modified, and everyone
knows how to handle elf sections, even the kernel does :)

And I think we really want the ability to have multiple signatures, the
whole "chain of trust" thing that is needed will work out much better if
multiple signatures are allowed. Putting it in an elf section allows
this to work out easier, right?

confused,

greg k-h

2012-06-22 03:30:10

by Lucas De Marchi

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

Hi,

Sorry to jump into this discussion only now, but
[email protected] was not CC'ed and I was not following
LKML last month.

On Thu, Jun 21, 2012 at 10:53 PM, Greg KH <[email protected]> wrote:
> On Sun, May 27, 2012 at 03:11:23PM +0930, Rusty Russell wrote:
>> > > > Why would you want multiple signatures?  That just complicates things.
>> > >
>> > > The code above stays pretty simple; if the signature fails, you set size
>> > > to i, and loop again.  As I said, if you know exactly how you're going
>> > > to strip the modules, you can avoid storing the stripped module and
>> > > simply append both signatures.
>> >
>> > You still haven't justified it.  One of your arguments about rejecting the ELF
>> > parsing version was that it was too big for no useful extra value that I could
>> > justify.  Supporting multiple signatures adds extra size and complexity for no
>> > obvious value.
>>
>> One loop is a lot easier to justify that the ELF-parsing mess.  And it
>> can be done in a backwards compatible way tomorrow: old kernels will
>> only check the last signature.
>>
>> I had assumed you'd rather maintain a stable strip util which you can
>> use on kernel modules than rework your module builds.  I guess not.
>
> To dig an old thread up, but what really is wrong with the original ELF
> section stuff?  Why encode "magic" values on the end of the kernel
> module that then require all userspace tools to be modified in order to
> properly handle this?
>
> When I first did this so many many years ago an elf section made it so
> easy to handle.  Userspace didn't need to be modified, and everyone
> knows how to handle elf sections, even the kernel does :)

Indeed. What's wrong with creating an ELF section for this and let
kernel deal with it? I fail to see the need for init_module2()

I need to catch up with this discussion though since I was not aware of that.


Lucas De Marchi

>
> And I think we really want the ability to have multiple signatures, the
> whole "chain of trust" thing that is needed will work out much better if
> multiple signatures are allowed.  Putting it in an elf section allows
> this to work out easier, right?
>
> confused,

me too.


Lucas De Marchi

2012-06-22 04:51:53

by Rusty Russell

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

On Thu, 21 Jun 2012 18:53:41 -0700, Greg KH <[email protected]> wrote:
> On Sun, May 27, 2012 at 03:11:23PM +0930, Rusty Russell wrote:
> > > > > Why would you want multiple signatures? That just complicates things.
> > > >
> > > > The code above stays pretty simple; if the signature fails, you set size
> > > > to i, and loop again. As I said, if you know exactly how you're going
> > > > to strip the modules, you can avoid storing the stripped module and
> > > > simply append both signatures.
> > >
> > > You still haven't justified it. One of your arguments about rejecting the ELF
> > > parsing version was that it was too big for no useful extra value that I could
> > > justify. Supporting multiple signatures adds extra size and complexity for no
> > > obvious value.
> >
> > One loop is a lot easier to justify that the ELF-parsing mess. And it
> > can be done in a backwards compatible way tomorrow: old kernels will
> > only check the last signature.
> >
> > I had assumed you'd rather maintain a stable strip util which you can
> > use on kernel modules than rework your module builds. I guess not.
>
> To dig an old thread up, but what really is wrong with the original ELF
> section stuff? Why encode "magic" values on the end of the kernel
> module that then require all userspace tools to be modified in order to
> properly handle this?

1) No userspace needs to be modified to use the appended signature.
modprobe doesn't. depmod doesn't. Even strip doesn't (not that
that's much use).

2) It's far easier to add an appended signature than to add an elf
section.

3) It's far easier to generate an appended signature than to generate
a signature for the module which will change when you add the
signature section (roughly: gpg --sign module.ko > sig && echo
'@@sig@@ >> module.ko && cat sig >> module.ko).

4) It's trivial to verify a module with an appended signature before you
touch it. With a section you need to carefully parse the module,
make sure you don't include the could-be-modified stuff in the
signature, and avoid any possible overflows or exploits.

5) It wasn't just that they wanted an elf section. They wanted the
signature to work against both a stripped and unstripped module, so
only the unstrippable parts of the module were signed.

> When I first did this so many many years ago an elf section made it so
> easy to handle. Userspace didn't need to be modified, and everyone
> knows how to handle elf sections, even the kernel does :)

And there have been bugs in the kernel elf handling code. There will
undoubtedly be bugs in this, and for what? A more complex kernel to
match our more complex userspace?

> And I think we really want the ability to have multiple signatures, the
> whole "chain of trust" thing that is needed will work out much better if
> multiple signatures are allowed. Putting it in an elf section allows
> this to work out easier, right?

Not at all. Multiple appended signatures is trivial. Figuring out the
semantics (do they chain, or is any one sufficient?), well that's the
same whether you're talking about an ELF section or not.

Hope that clarifies,
Rusty.

2012-06-22 11:03:36

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

Rusty Russell <[email protected]> wrote:

> 1) No userspace needs to be modified to use the appended signature.
> modprobe doesn't. depmod doesn't. Even strip doesn't (not that
> that's much use).

Anything that currently automatically strips the module: mkinitrd perhaps?

If we use a new system call, then the list includes a few more things.

Your idea of providing multiple variants of the module, each signed and each
with different levels of strippedness makes things more complicated - both at
build/package generation time and at usage time (where 'use' may be loading
the module or packaging it into a initrd).

> 2) It's far easier to add an appended signature than to add an elf
> section.

That's not true. That bit of complexity in my implementation comes because
I'm adding it as an ELF Note - which someone suggested I should do instead of
just using an unstructured section.

If we reverted to an unstructured section, it's just one objcopy command, eg:

objcopy \
--add-section .modsign=/etc/redhat-release \
--set-section-flags .modsign=load \
/bin/ls /tmp/ls

And for debugging purposes, removing it is:

objcopy \
-R .modsign \
/bin/ls /tmp/ls

> 3) It's far easier to generate an appended signature than to generate
> a signature for the module which will change when you add the
> signature section (roughly: gpg --sign module.ko > sig && echo
> '@@sig@@ >> module.ko && cat sig >> module.ko).

You would be better off putting the magic number last and including a length
field right before. That's much more efficient and much simpler.

> 4) It's trivial to verify a module with an appended signature before you
> touch it. With a section you need to carefully parse the module,
> make sure you don't include the could-be-modified stuff in the
> signature, and avoid any possible overflows or exploits.

I have to say that here Rusty is correct. If the signature is embedded in the
ELF, then the ELF needs a bit of careful checking first. But, excluding the
crypto bits which are the same in all cases, I managed to get the entire ELF
parser/checker/canonicaliser, digest extractor and policy determiner down to a
little over 2K of x86_64 code.

Since the ELF loader/linker has to parse much of this stuff anyway, it might
be possible to combine the two to some extent.

> 5) It wasn't just that they wanted an elf section. They wanted the
> signature to work against both a stripped and unstripped module, so
> only the unstrippable parts of the module were signed.

Yes. Those are the bits that the module loader needs...

This is quite a good a trade off. It simplifies building and installation a
lot. There is only one binary for each module. That binary can be stripped
quite aggressively - any strip that would ordinarily leave the module
functional won't affect the signature verification. Any content or metadata
change that affects the module's operational code and data is detected.

> > And I think we really want the ability to have multiple signatures, the
> > whole "chain of trust" thing that is needed will work out much better if
> > multiple signatures are allowed. Putting it in an elf section allows
> > this to work out easier, right?
>
> Not at all. Multiple appended signatures is trivial. Figuring out the
> semantics (do they chain, or is any one sufficient?), well that's the
> same whether you're talking about an ELF section or not.

Agreed, it makes no difference either way.

But it doesn't necessarily work with a new syscall that has an extra pair of
args for passing a signature. The kernel really wants to have all the
available sigs available in one go so that it can implement its policy (which
might be to panic in FIPS mode).

David

2012-06-23 00:28:53

by Rusty Russell

[permalink] [raw]
Subject: Re: [PATCH 00/23] Crypto keys and module signing

On Fri, 22 Jun 2012 12:03:13 +0100, David Howells <[email protected]> wrote:
> Rusty Russell <[email protected]> wrote:
>
> > 1) No userspace needs to be modified to use the appended signature.
> > modprobe doesn't. depmod doesn't. Even strip doesn't (not that
> > that's much use).
>
> Anything that currently automatically strips the module: mkinitrd perhaps?

True, if mkinitrd strips the module, it will break the signature.

> If we use a new system call, then the list includes a few more things.

Sure, but that's kind of independent of what Greg was asking.

> Your idea of providing multiple variants of the module, each signed and each
> with different levels of strippedness makes things more complicated - both at
> build/package generation time and at usage time (where 'use' may be loading
> the module or packaging it into a initrd).

Userspace a bit more complex. Kernel simpler. Win.

> > 2) It's far easier to add an appended signature than to add an elf
> > section.
>
> That's not true. That bit of complexity in my implementation comes because
> I'm adding it as an ELF Note - which someone suggested I should do instead of
> just using an unstructured section.
>
> If we reverted to an unstructured section, it's just one objcopy command, eg:
>
> objcopy \
> --add-section .modsign=/etc/redhat-release \
> --set-section-flags .modsign=load \
> /bin/ls /tmp/ls
>
> And for debugging purposes, removing it is:
>
> objcopy \
> -R .modsign \
> /bin/ls /tmp/ls

OK, s/far//. We already rely on objcopy for kernel builds, so no
additional requirements.

> > 3) It's far easier to generate an appended signature than to generate
> > a signature for the module which will change when you add the
> > signature section (roughly: gpg --sign module.ko > sig && echo
> > '@@sig@@ >> module.ko && cat sig >> module.ko).
>
> You would be better off putting the magic number last and including a length
> field right before. That's much more efficient and much simpler.

No, that was more complex and tasteless.

> > 4) It's trivial to verify a module with an appended signature before you
> > touch it. With a section you need to carefully parse the module,
> > make sure you don't include the could-be-modified stuff in the
> > signature, and avoid any possible overflows or exploits.
>
> I have to say that here Rusty is correct. If the signature is embedded in the
> ELF, then the ELF needs a bit of careful checking first. But, excluding the
> crypto bits which are the same in all cases, I managed to get the entire ELF
> parser/checker/canonicaliser, digest extractor and policy determiner down to a
> little over 2K of x86_64 code.

I've been tempted to search for a hole in your parsing code. But while
finding one would be satisfying, not finding one isn't conclusive. So
it's a waste of time.

> This is quite a good a trade off. It simplifies building and installation a
> lot. There is only one binary for each module. That binary can be stripped
> quite aggressively - any strip that would ordinarily leave the module
> functional won't affect the signature verification.

Wouldn't it be nice if strip removed nop padding at the end of text
sections?

Cheers,
Rusty.