2011-11-29 23:43:10

by David Howells

[permalink] [raw]
Subject: [RFC][PATCH 00/16] Crypto keys and module signing [ver #2]


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

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

The basic crypto key has no requirements as to how the key is implemented; it's
basically a jump table for the operations and an anchor for any relevant data.

I have provided a couple of subtypes: DSA and RSA. Both types have signature
verification facilities available within the kernel, and both can be used for
module signature verification with any encryption algorithm known by the PGP
parser, provided the appropriate algorithm is compiled directly into the
kernel.

These two subtypes store their public key data attached to the key and
implement the algorithms within the kernel. However, it would be possible to
merely refer to keys held in a hardware keystore (such as a TPM) and have the
accessor methods offload the actual work to that keystore to be done in
hardware.

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

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

Module signing combinations that have been tested: DSA with SHA1 and RSA with
all the SHA algorithms.

The patches break down into a number of areas:

(0) Dmitry Kasatkin's MPI library patches - which I have not included in this
posting, but can be obtained from the GIT tree.

(1) Utility: MPI function exports and make key_serial() handle a const
pointer. Also fix a bug in the MPI library.

(2) PGP defs and PGP packet parser; basic crypto key infrastructure.

(3) DSA key and RSA key basic implementations.

(4) PGP signature parser; signature verification operations.

(5) DSA and RSA verification algorithms.

(6) Module ELF verification and module signature verification.

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

http://git.kernel.org/?p=linux/kernel/git/dhowells/linux-modsign.git;a=blob;f=Documentation/security/keys-crypto.txt;h=35e61b1bc636543bc77c0c2a33c3ef323b9f96b5;hb=6eea6dc28e465163611d26112f0ea68ec0e0a0d2

and the module signature verification documentation can be found here:

http://git.kernel.org/?p=linux/kernel/git/dhowells/linux-modsign.git;a=blob;f=Documentation/module-signing.txt;h=300b91a701818409f37f6065d5f14c5c73ce1b44;hb=20258f1df3a9580a26c5fc5028086bf02d28974d

---
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
---
David Howells (16):
MODSIGN: Apply signature checking to modules on module load
MODSIGN: Module ELF verifier
MODSIGN: Add indications of module ELF types
KEYS: Provide a function to load keys from a PGP keyring blob
KEYS: Add a crypto key request function
KEYS: RSA key signature verification
KEYS: DSA key signature verification
KEYS: Add signature verification facility
PGP: Add signature parser
KEYS: Add a RSA crypto key subtype
KEYS: Add a DSA crypto key subtype
KEYS: Create a key type that can be used for general cryptographic operations
PGP: Add definitions (RFC 4880) and packet parser
KEYS: Permit key_serial() to be called with a const key pointer
MPILIB: Add a missing ENOMEM check
MPILIB: Export some more symbols


.gitignore | 15 +
Documentation/module-signing.txt | 186 +++++++
Documentation/security/keys-crypto.txt | 299 ++++++++++
Makefile | 1
arch/alpha/include/asm/module.h | 3
arch/arm/include/asm/module.h | 5
arch/cris/include/asm/module.h | 5
arch/h8300/include/asm/module.h | 5
arch/ia64/include/asm/module.h | 5
arch/m32r/include/asm/module.h | 5
arch/m68k/include/asm/module.h | 5
arch/mips/include/asm/module.h | 12
arch/parisc/include/asm/module.h | 8
arch/powerpc/include/asm/module.h | 10
arch/s390/include/asm/module.h | 3
include/asm-generic/module.h | 10
include/keys/crypto-subtype.h | 61 ++
include/keys/crypto-type.h | 40 +
include/linux/elfnote.h | 4
include/linux/key.h | 2
include/linux/modsign.h | 27 +
include/linux/module.h | 3
include/linux/pgp.h | 254 +++++++++
init/Kconfig | 62 ++
kernel/Makefile | 4
kernel/modsign-pubkey.c | 44 ++
kernel/module-verify-elf.c | 344 ++++++++++++
kernel/module-verify-sig.c | 535 +++++++++++++++++++
kernel/module-verify.c | 44 ++
kernel/module-verify.h | 68 ++
kernel/module.c | 25 +
lib/mpi/mpi-cmp.c | 2
lib/mpi/mpi-div.c | 1
lib/mpi/mpi-inv.c | 1
lib/mpi/mpi-mpow.c | 1
lib/mpi/mpi-mul.c | 1
lib/mpi/mpicoder.c | 2
scripts/Makefile.modpost | 85 +++
scripts/mod/.gitignore | 1
scripts/mod/Makefile | 2
scripts/mod/mod-extract.c | 913 ++++++++++++++++++++++++++++++++
scripts/mod/modsign-note.sh | 16 +
security/Kconfig | 27 +
security/keys/Makefile | 7
security/keys/crypto_dsa.h | 47 ++
security/keys/crypto_dsa_subtype.c | 338 ++++++++++++
security/keys/crypto_dsa_verify.c | 384 +++++++++++++
security/keys/crypto_keys.h | 21 +
security/keys/crypto_request.c | 153 +++++
security/keys/crypto_rsa.h | 47 ++
security/keys/crypto_rsa_subtype.c | 386 ++++++++++++++
security/keys/crypto_rsa_verify.c | 519 ++++++++++++++++++
security/keys/crypto_type.c | 315 +++++++++++
security/keys/crypto_verify.c | 87 +++
security/keys/pgp_parse.c | 528 +++++++++++++++++++
55 files changed, 5968 insertions(+), 10 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-elf.c
create mode 100644 kernel/module-verify-sig.c
create mode 100644 kernel/module-verify.c
create mode 100644 kernel/module-verify.h
create mode 100644 scripts/mod/mod-extract.c
create mode 100644 scripts/mod/modsign-note.sh
create mode 100644 security/keys/crypto_dsa.h
create mode 100644 security/keys/crypto_dsa_subtype.c
create mode 100644 security/keys/crypto_dsa_verify.c
create mode 100644 security/keys/crypto_keys.h
create mode 100644 security/keys/crypto_request.c
create mode 100644 security/keys/crypto_rsa.h
create mode 100644 security/keys/crypto_rsa_subtype.c
create mode 100644 security/keys/crypto_rsa_verify.c
create mode 100644 security/keys/crypto_type.c
create mode 100644 security/keys/crypto_verify.c
create mode 100644 security/keys/pgp_parse.c


2011-11-29 23:53:19

by David Howells

[permalink] [raw]
Subject: [PATCH 03/16] KEYS: Permit key_serial() to be called with a const key pointer [ver #2]

Permit key_serial() to be called with a const key pointer.

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

include/linux/key.h | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)


diff --git a/include/linux/key.h b/include/linux/key.h
index 183a6af..f87b51b 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -271,7 +271,7 @@ extern int keyring_add_key(struct key *keyring,

extern struct key *key_lookup(key_serial_t id);

-static inline key_serial_t key_serial(struct key *key)
+static inline key_serial_t key_serial(const struct key *key)
{
return key ? key->serial : 0;
}

2011-11-29 23:53:20

by David Howells

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

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 | 19 +++++++++
include/keys/crypto-type.h | 3 +
security/keys/crypto_request.c | 66 ++++++++++++++++++++++++++++++++
3 files changed, 88 insertions(+), 0 deletions(-)


diff --git a/Documentation/security/keys-crypto.txt b/Documentation/security/keys-crypto.txt
index e1c19ea..35e61b1 100644
--- a/Documentation/security/keys-crypto.txt
+++ b/Documentation/security/keys-crypto.txt
@@ -9,6 +9,7 @@ Contents:
- Crypto subtypes.
- Accessing crypto keys.
- Signature verification.
+ - Initial pgp key loading.
- Implementing crypto subtypes.
- Registration.

@@ -170,6 +171,24 @@ hardware keystore (such as a TPM) for a usable signature matching service and
generate a key to provide an access method to that service.


+INITIAL PGP KEY LOADING
+-----------------------
+
+A function is provided to perform an initial load of a set of public keys bound
+into a PGP keyring blob:
+
+ int load_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.
+
+
============================
IMPLEMENTING CRYPTO SUBTYPES
============================
diff --git a/include/keys/crypto-type.h b/include/keys/crypto-type.h
index 142611b..ef961d9 100644
--- a/include/keys/crypto-type.h
+++ b/include/keys/crypto-type.h
@@ -34,4 +34,7 @@ extern void verify_sig_cancel(struct crypto_key_verify_context *ctx);
extern struct key *request_crypto_key_for_PGP_sig(struct key *keyring,
const u8 *sig, size_t siglen);

+extern __init int load_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_request.c b/security/keys/crypto_request.c
index 0a1819a3..416c69f 100644
--- a/security/keys/crypto_request.c
+++ b/security/keys/crypto_request.c
@@ -85,3 +85,69 @@ struct key *request_crypto_key_for_PGP_sig(struct key *keyring,
return ERR_CAST(key);
return key_ref_to_ptr(key);
}
+
+struct load_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 found_PGP_key(struct pgp_parse_context *context,
+ enum pgp_packet_tag type, u8 headerlen,
+ const u8 *data, size_t datalen)
+{
+ struct load_PGP_keys_context *ctx =
+ container_of(context, struct load_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);
+ key_ref_put(key);
+ return 0;
+}
+
+/**
+ * load_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.
+ *
+ * Load 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.
+ */
+int __init load_PGP_keys(const u8 *pgpdata, size_t pgpdatalen,
+ struct key *keyring, const char *descprefix)
+{
+ struct load_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);
+}

2011-11-29 23:44:52

by David Howells

[permalink] [raw]
Subject: [PATCH 09/16] KEYS: Add signature verification facility [ver #2]

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

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

Documentation/security/keys-crypto.txt | 106 +++++++++++++++++++++++++++++++-
include/keys/crypto-subtype.h | 15 +++++
include/keys/crypto-type.h | 9 +++
security/keys/Makefile | 2 -
security/keys/crypto_verify.c | 87 ++++++++++++++++++++++++++
5 files changed, 214 insertions(+), 5 deletions(-)
create mode 100644 security/keys/crypto_verify.c


diff --git a/Documentation/security/keys-crypto.txt b/Documentation/security/keys-crypto.txt
index 44c936a..236d750 100644
--- a/Documentation/security/keys-crypto.txt
+++ b/Documentation/security/keys-crypto.txt
@@ -8,7 +8,9 @@ Contents:
- Key identification.
- Crypto subtypes.
- Accessing crypto keys.
+ - Signature verification.
- Implementing crypto subtypes.
+ - Registration.


========
@@ -98,14 +100,69 @@ 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 a key to provide the public key:
+
+ (1) Begin verification procedure.
+
+ struct crypto_key_verify_context *
+ verify_sig_begin(struct key *key, const void *sig, size_t siglen);
+
+ This function sets up a verification context from the specified key and
+ the signature blob. The signature blob must be presented again at the end
+ of the procedure. The key is checked against parameters in the signature,
+ and if it's not the right key then an error will be given.
+
+ If such a thing applies, the hashing algorithm, will be extracted from 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 SUBTYPES
============================

-If a suitable subtype is found, then key->type_data.p[0] is set to point to the
-subtype definition and the module usage count is incremented.
-
-The subtype definition structure looks like the following:
+Each subtype is specified through a definition structure:

struct crypto_key_subtype {
struct module *owner;
@@ -118,6 +175,14 @@ The subtype definition structure looks like the following:

void (*revoke)(struct key *key);
void (*destroy)(struct key *key);
+
+ struct crypto_key_verify_context *(*verify_sig_begin)(
+ struct key *key, const u8 *sig, size_t siglen);
+ int (*verify_sig_add_data)(struct crypto_key_verify_context *ctx,
+ const void *data, size_t datalen);
+ int (*verify_sig_end)(struct crypto_key_verify_context *ctx,
+ const u8 *sig, size_t siglen);
+ void (*verify_sig_cancel)(struct crypto_key_verify_context *ctx);
};

The owner and name fields should be set to the owning module and the name of
@@ -160,6 +225,39 @@ management of the key itself:
must free whatever key->payload refers to.


+There are then sets of method pointers to actually use the key for things:
+
+ (*) Signature verification
+
+ Then there are functions to verify a signature using the public key stored in
+ this key:
+
+ (1) verify_sig_begin().
+ (2) verify_sig_add_data().
+ (3) verify_sig_end().
+ (4) verify_sig_cancel().
+
+ These correspond to the accessor functions mentioned in the previous
+ section. The first function is optional - if it is not provided, then
+ verification is not a service available with this key. The other three
+ are mandatory if the first is supplied and unnecessary otherwise.
+
+ The subtype should allocate a context in ->verify_sig_begin() and embed
+ the following struct in it:
+
+ struct crypto_key_verify_context {
+ struct key *key;
+ };
+
+ A pointer to this struct is then returned to the caller. This is used by
+ the master routines to route the operations to the right place. This is
+ passed to the _add_data, _end and _cancel routines - which should use
+ container_of() on it to get the full context.
+
+
+REGISTRATION
+------------
+
Functions are provided to register and unregister key subtypes:

int register_crypto_key_subtype(struct crypto_key_subtype *subtype);
diff --git a/include/keys/crypto-subtype.h b/include/keys/crypto-subtype.h
index 218cb2b..76dbd50 100644
--- a/include/keys/crypto-subtype.h
+++ b/include/keys/crypto-subtype.h
@@ -20,6 +20,14 @@
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;
+};
+
+/*
* Keys of this type declare a subtype that indicates the handlers and
* capabilities.
*/
@@ -38,6 +46,13 @@ struct crypto_key_subtype {
void (*revoke)(struct key *key);
void (*destroy)(struct key *key);

+ struct crypto_key_verify_context *(*verify_sig_begin)(
+ struct key *key, const u8 *sig, size_t siglen);
+ int (*verify_sig_add_data)(struct crypto_key_verify_context *ctx,
+ const void *data, size_t datalen);
+ int (*verify_sig_end)(struct crypto_key_verify_context *ctx,
+ const u8 *sig, size_t siglen);
+ void (*verify_sig_cancel)(struct crypto_key_verify_context *ctx);
};

extern int register_crypto_key_subtype(struct crypto_key_subtype *);
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/Makefile b/security/keys/Makefile
index 0cb7d08..8f48527 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -22,6 +22,6 @@ obj-$(CONFIG_KEYS_COMPAT) += compat.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSCTL) += sysctl.o

-crypto_keys-y := crypto_type.o pgp_parse.o
+crypto_keys-y := crypto_type.o pgp_parse.o crypto_verify.o
crypto_dsa-y := crypto_dsa_subtype.o
crypto_rsa-y := crypto_rsa_subtype.o
diff --git a/security/keys/crypto_verify.c b/security/keys/crypto_verify.c
new file mode 100644
index 0000000..ac97cc9
--- /dev/null
+++ b/security/keys/crypto_verify.c
@@ -0,0 +1,87 @@
+/* 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/seq_file.h>
+#include <linux/module.h>
+#include "crypto_keys.h"
+
+/**
+ * verify_sig_begin - Initiate the use of a crypto key to verify a signature
+ * @key: The public key 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 *key, const void *sig, size_t siglen)
+{
+ const struct crypto_key_subtype *subtype = crypto_key_subtype(key);
+
+ if (!subtype || !subtype->verify_sig_begin)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ return subtype->verify_sig_begin(key, sig, siglen);
+}
+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)
+{
+ const struct crypto_key_subtype *subtype;
+
+ BUG_ON(!ctx);
+ subtype = crypto_key_subtype(ctx->key);
+ return subtype->verify_sig_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)
+{
+ const struct crypto_key_subtype *subtype;
+
+ BUG_ON(!ctx);
+ subtype = crypto_key_subtype(ctx->key);
+ return subtype->verify_sig_end(ctx, sig, siglen);
+}
+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)
+{
+ const struct crypto_key_subtype *subtype;
+
+ BUG_ON(!ctx);
+ subtype = crypto_key_subtype(ctx->key);
+ subtype->verify_sig_cancel(ctx);
+}
+EXPORT_SYMBOL_GPL(verify_sig_cancel);

2011-11-29 23:43:11

by David Howells

[permalink] [raw]
Subject: [PATCH 01/16] MPILIB: Export some more symbols [ver #2]

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

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

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


diff --git a/lib/mpi/mpi-cmp.c b/lib/mpi/mpi-cmp.c
index 914bc42..1871e7b 100644
--- a/lib/mpi/mpi-cmp.c
+++ b/lib/mpi/mpi-cmp.c
@@ -39,6 +39,7 @@ int mpi_cmp_ui(MPI u, unsigned long v)
else
return -1;
}
+EXPORT_SYMBOL_GPL(mpi_cmp_ui);

int mpi_cmp(MPI u, MPI v)
{
@@ -66,3 +67,4 @@ int mpi_cmp(MPI u, MPI v)
return 1;
return -1;
}
+EXPORT_SYMBOL_GPL(mpi_cmp);
diff --git a/lib/mpi/mpi-div.c b/lib/mpi/mpi-div.c
index c3087d1..3da9402 100644
--- a/lib/mpi/mpi-div.c
+++ b/lib/mpi/mpi-div.c
@@ -59,6 +59,7 @@ nomem:
mpi_free(temp_divisor);
return rc;
}
+EXPORT_SYMBOL_GPL(mpi_fdiv_r);

/****************
* Division rounding the quotient towards -infinity.
diff --git a/lib/mpi/mpi-inv.c b/lib/mpi/mpi-inv.c
index 0951f98..bfc5ca1 100644
--- a/lib/mpi/mpi-inv.c
+++ b/lib/mpi/mpi-inv.c
@@ -185,3 +185,4 @@ cleanup:
mpi_free(v);
return rc;
}
+EXPORT_SYMBOL_GPL(mpi_invm);
diff --git a/lib/mpi/mpi-mpow.c b/lib/mpi/mpi-mpow.c
index 4cc7593..5752194 100644
--- a/lib/mpi/mpi-mpow.c
+++ b/lib/mpi/mpi-mpow.c
@@ -131,3 +131,4 @@ nomem:
kfree(G);
return rc;
}
+EXPORT_SYMBOL_GPL(mpi_mulpowm);
diff --git a/lib/mpi/mpi-mul.c b/lib/mpi/mpi-mul.c
index 1f3219e..3d514b9 100644
--- a/lib/mpi/mpi-mul.c
+++ b/lib/mpi/mpi-mul.c
@@ -192,3 +192,4 @@ int mpi_mulm(MPI w, MPI u, MPI v, MPI m)
return -ENOMEM;
return mpi_fdiv_r(w, w, m);
}
+EXPORT_SYMBOL_GPL(mpi_mulm);

2011-11-29 23:45:31

by David Howells

[permalink] [raw]
Subject: [PATCH 12/16] KEYS: Add a crypto key request function [ver #2]

Add a function by which crypto keys can be requested. A keyring is supplied
for the function to search (which can be, say, a system keyring containing keys
for kernel module signature checking). The function also provides a point at
which hardware key caches, such as a TPM, can be consulted.

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

Documentation/security/keys-crypto.txt | 12 ++++
include/keys/crypto-type.h | 3 +
security/keys/Makefile | 2 -
security/keys/crypto_request.c | 87 ++++++++++++++++++++++++++++++++
4 files changed, 103 insertions(+), 1 deletions(-)
create mode 100644 security/keys/crypto_request.c


diff --git a/Documentation/security/keys-crypto.txt b/Documentation/security/keys-crypto.txt
index 236d750..e1c19ea 100644
--- a/Documentation/security/keys-crypto.txt
+++ b/Documentation/security/keys-crypto.txt
@@ -158,6 +158,18 @@ using a key to provide the public key:
not necessary if verify_sig_end() was called.


+To find a key to use for signature verification, the following function may be
+called:
+
+ struct key *request_crypto_key_for_PGP_sig(struct key *keyring,
+ const u8 *sig, size_t siglen);
+
+This parses the specified signature blob to find the signing key identity and
+then searches the given keyring for a matching key. It may also examine a
+hardware keystore (such as a TPM) for a usable signature matching service and
+generate a key to provide an access method to that service.
+
+
============================
IMPLEMENTING CRYPTO SUBTYPES
============================
diff --git a/include/keys/crypto-type.h b/include/keys/crypto-type.h
index 6b93366..142611b 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 struct key *request_crypto_key_for_PGP_sig(struct key *keyring,
+ const u8 *sig, size_t siglen);
+
#endif /* _KEYS_CRYPTO_TYPE_H */
diff --git a/security/keys/Makefile b/security/keys/Makefile
index 78a3aa6..722be34 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -22,6 +22,6 @@ obj-$(CONFIG_KEYS_COMPAT) += compat.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSCTL) += sysctl.o

-crypto_keys-y := crypto_type.o pgp_parse.o crypto_verify.o
+crypto_keys-y := crypto_type.o pgp_parse.o crypto_verify.o crypto_request.o
crypto_dsa-y := crypto_dsa_subtype.o crypto_dsa_verify.o
crypto_rsa-y := crypto_rsa_subtype.o crypto_rsa_verify.o
diff --git a/security/keys/crypto_request.c b/security/keys/crypto_request.c
new file mode 100644
index 0000000..0a1819a3
--- /dev/null
+++ b/security/keys/crypto_request.c
@@ -0,0 +1,87 @@
+/* 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 <keys/crypto-subtype.h>
+#include <linux/seq_file.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pgp.h>
+#include "crypto_keys.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_sig_parse_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);
+}
+
+/**
+ * request_crypto_key_for_PGP_sig - Request a key matching a PGP signature
+ * @keyring: The keyring that might contain the key.
+ * @sig: The PGP signature blob.
+ * @siglen: The size of the PGP signature blob.
+ *
+ * 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 will be added to
+ * the keyring for future reference.
+ */
+struct key *request_crypto_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_sig_parse_signature;
+ p.found_sig = false;
+ ret = pgp_parse_packets(sig, siglen, &p.pgp);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ if (!p.found_sig)
+ return ERR_PTR(-EINVAL);
+
+ sprintf(criterion, "id:%08x%08x",
+ be32_to_cpu(p.params.issuer32[0]),
+ be32_to_cpu(p.params.issuer32[1]));
+
+ pr_debug("Look up: %s", criterion);
+
+ key = keyring_search(make_key_ref(keyring, 1),
+ &key_type_crypto, criterion);
+ if (IS_ERR(key))
+ return ERR_CAST(key);
+ return key_ref_to_ptr(key);
+}

2011-11-29 23:45:18

by David Howells

[permalink] [raw]
Subject: [PATCH 11/16] KEYS: RSA key signature verification [ver #2]

Signature verification routines for RSA crypto key subtype.

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

security/keys/Makefile | 2
security/keys/crypto_rsa.h | 11 +
security/keys/crypto_rsa_subtype.c | 15 +
security/keys/crypto_rsa_verify.c | 519 ++++++++++++++++++++++++++++++++++++
4 files changed, 546 insertions(+), 1 deletions(-)
create mode 100644 security/keys/crypto_rsa_verify.c


diff --git a/security/keys/Makefile b/security/keys/Makefile
index bde336e..78a3aa6 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -24,4 +24,4 @@ obj-$(CONFIG_SYSCTL) += sysctl.o

crypto_keys-y := crypto_type.o pgp_parse.o crypto_verify.o
crypto_dsa-y := crypto_dsa_subtype.o crypto_dsa_verify.o
-crypto_rsa-y := crypto_rsa_subtype.o
+crypto_rsa-y := crypto_rsa_subtype.o crypto_rsa_verify.o
diff --git a/security/keys/crypto_rsa.h b/security/keys/crypto_rsa.h
index 2ec8edc..6670458 100644
--- a/security/keys/crypto_rsa.h
+++ b/security/keys/crypto_rsa.h
@@ -34,3 +34,14 @@ struct RSA_payload {
u8 key_id_size; /* Number of bytes in key_id */
struct RSA_public_key *public_key;
};
+
+/*
+ * crypto_rsa_verify.c
+ */
+extern struct crypto_key_verify_context *RSA_verify_sig_begin(
+ struct key *key, const u8 *sig, size_t siglen);
+extern int RSA_verify_sig_add_data(struct crypto_key_verify_context *ctx,
+ const void *data, size_t datalen);
+extern int RSA_verify_sig_end(struct crypto_key_verify_context *ctx,
+ const u8 *sig, size_t siglen);
+extern void RSA_verify_sig_cancel(struct crypto_key_verify_context *ctx);
diff --git a/security/keys/crypto_rsa_subtype.c b/security/keys/crypto_rsa_subtype.c
index da0d4cf..1eb5839 100644
--- a/security/keys/crypto_rsa_subtype.c
+++ b/security/keys/crypto_rsa_subtype.c
@@ -313,6 +313,11 @@ struct crypto_key_subtype RSA_crypto_key_subtype = {
.info = CRYPTO_KEY_IS_PUBKEY_ALGO,
.instantiate = RSA_instantiate,
.destroy = RSA_destroy,
+
+ .verify_sig_begin = RSA_verify_sig_begin,
+ .verify_sig_add_data = RSA_verify_sig_add_data,
+ .verify_sig_end = RSA_verify_sig_end,
+ .verify_sig_cancel = RSA_verify_sig_cancel,
};

struct crypto_key_subtype RSA_crypto_key_subtype_2 = {
@@ -322,6 +327,11 @@ struct crypto_key_subtype RSA_crypto_key_subtype_2 = {
.info = CRYPTO_KEY_IS_PUBKEY_ALGO,
.instantiate = RSA_instantiate,
.destroy = RSA_destroy,
+
+ .verify_sig_begin = RSA_verify_sig_begin,
+ .verify_sig_add_data = RSA_verify_sig_add_data,
+ .verify_sig_end = RSA_verify_sig_end,
+ .verify_sig_cancel = RSA_verify_sig_cancel,
};

struct crypto_key_subtype RSA_crypto_key_subtype_3 = {
@@ -331,6 +341,11 @@ struct crypto_key_subtype RSA_crypto_key_subtype_3 = {
.info = CRYPTO_KEY_IS_PUBKEY_ALGO,
.instantiate = RSA_instantiate,
.destroy = RSA_destroy,
+
+ .verify_sig_begin = RSA_verify_sig_begin,
+ .verify_sig_add_data = RSA_verify_sig_add_data,
+ .verify_sig_end = RSA_verify_sig_end,
+ .verify_sig_cancel = RSA_verify_sig_cancel,
};

/*
diff --git a/security/keys/crypto_rsa_verify.c b/security/keys/crypto_rsa_verify.c
new file mode 100644
index 0000000..736e2f9
--- /dev/null
+++ b/security/keys/crypto_rsa_verify.c
@@ -0,0 +1,519 @@
+/* RSA signature verification 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 DEBUG
+#define pr_fmt(fmt) "RSA: "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 "crypto_rsa.h"
+#include "internal.h"
+
+struct RSA_signature {
+ struct crypto_key_verify_context base;
+ enum pgp_hash_algo hash_algo : 8;
+ u8 signed_hash_msw[2];
+ union {
+ MPI mpi[1];
+ struct {
+ MPI s; /* m^d mod n */
+ };
+ };
+ struct shash_desc hash; /* This must go last! */
+};
+
+/*
+ * 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[PGP_HASH__LAST] = {
+#define _(X) { RSA_digest_info_##X, sizeof(RSA_digest_info_##X) }
+ [PGP_HASH_MD5] = _(MD5),
+ [PGP_HASH_SHA1] = _(SHA1),
+ [PGP_HASH_RIPE_MD_160] = _(RIPE_MD_160),
+ [PGP_HASH_SHA256] = _(SHA256),
+ [PGP_HASH_SHA384] = _(SHA384),
+ [PGP_HASH_SHA512] = _(SHA512),
+ [PGP_HASH_SHA224] = _(SHA224),
+#undef _
+};
+
+struct RSA_sig_parse_context {
+ struct pgp_parse_context pgp;
+ struct pgp_sig_parameters params;
+};
+
+static int RSA_parse_signature(struct pgp_parse_context *context,
+ enum pgp_packet_tag type,
+ u8 headerlen,
+ const u8 *data,
+ size_t datalen)
+{
+ struct RSA_sig_parse_context *ctx =
+ container_of(context, struct RSA_sig_parse_context, pgp);
+
+ return pgp_parse_sig_params(&data, &datalen, &ctx->params);
+}
+
+/*
+ * Begin the process of verifying a RSA 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 *RSA_verify_sig_begin(
+ struct key *key, const u8 *sigdata, size_t siglen)
+{
+ struct RSA_sig_parse_context p;
+ struct RSA_signature *sig;
+ struct crypto_shash *tfm;
+ struct RSA_payload *rsa = key->payload.data;
+ int ret;
+
+ kenter("{%d},,%zu", key_serial(key), siglen);
+
+ if (!rsa->public_key) {
+ kleave(" = -ENOKEY [no public key]");
+ return ERR_PTR(-ENOKEY);
+ }
+
+ p.pgp.types_of_interest = (1 << PGP_PKT_SIGNATURE);
+ p.pgp.process_packet = RSA_parse_signature;
+ ret = pgp_parse_packets(sigdata, siglen, &p.pgp);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ if (p.params.pubkey_algo != PGP_PUBKEY_RSA_ENC_OR_SIG &&
+ p.params.pubkey_algo != PGP_PUBKEY_RSA_SIG_ONLY) {
+ kleave(" = -ENOKEY [wrong pk algo]");
+ return ERR_PTR(-ENOKEY);
+ }
+
+ if (p.params.hash_algo >= PGP_HASH__LAST ||
+ !pgp_hash_algorithms[p.params.hash_algo]) {
+ kleave(" = -ENOPKG [hash]");
+ return ERR_PTR(-ENOPKG);
+ }
+
+ pr_notice("Signature generated with %s hash\n",
+ pgp_hash_algorithms[p.params.hash_algo]);
+
+ if (memcmp(&p.params.issuer, rsa->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);
+
+ /* We allocate the hash operational data storage on the end of our
+ * context data.
+ */
+ sig = kzalloc(sizeof(*sig) + crypto_shash_descsize(tfm), GFP_KERNEL);
+ if (!sig) {
+ crypto_free_shash(tfm);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ sig->base.key = key;
+ sig->hash_algo = p.params.hash_algo;
+ 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
+ */
+int RSA_verify_sig_add_data(struct crypto_key_verify_context *ctx,
+ const void *data, size_t datalen)
+{
+ struct RSA_signature *sig =
+ container_of(ctx, struct RSA_signature, base);
+
+ return crypto_shash_update(&sig->hash, data, datalen);
+}
+
+/*
+ * 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;
+}
+
+struct RSA_sig_digest_context {
+ struct pgp_parse_context pgp;
+ struct RSA_signature *sig;
+};
+
+/*
+ * Extract required metadata from the signature packet and add what we need to
+ * to the hash.
+ */
+static int RSA_digest_signature(struct pgp_parse_context *context,
+ enum pgp_packet_tag type,
+ u8 headerlen,
+ const u8 *data,
+ size_t datalen)
+{
+ enum pgp_signature_version version;
+ struct RSA_sig_digest_context *ctx =
+ container_of(context, struct RSA_sig_digest_context, pgp);
+ int i;
+
+ kenter("");
+
+ 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 < ARRAY_SIZE(ctx->sig->mpi); i++) {
+ unsigned int remaining = datalen;
+ 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;
+}
+
+/*
+ * RSAVP1() function [RFC3447 sec 5.2.2]
+ */
+static int RSAVP1(struct RSA_public_key *pub, 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, pub->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, pub->e, pub->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);
+ kdebug("size(x)=%u xLen*8=%zu", 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;
+}
+
+/*
+ * The data is now all loaded into the hash; load the metadata, finalise the
+ * hash and perform the verification step [RFC3447 sec 8.2.2].
+ */
+int RSA_verify_sig_end(struct crypto_key_verify_context *ctx,
+ const u8 *sigdata, size_t siglen)
+{
+ struct RSA_signature *sig =
+ container_of(ctx, struct RSA_signature, base);
+ struct RSA_payload *rsa = sig->base.key->payload.data;
+ struct RSA_sig_digest_context p;
+ size_t digest_size, tsize;
+ int ret;
+
+ /* Variables as per RFC3447 sec 8.2.2 */
+ void *H = NULL;
+ u8 *EM = NULL;
+ MPI m = NULL;
+ size_t k;
+
+ 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 = RSA_digest_signature;
+ p.sig = sig;
+ ret = pgp_parse_packets(sigdata, siglen, &p.pgp);
+ if (ret < 0)
+ goto error_free_ctx;
+
+ ret = -ENOMEM;
+ digest_size = crypto_shash_digestsize(sig->hash.tfm);
+ H = kmalloc(digest_size, GFP_KERNEL);
+ if (!H)
+ goto error_free_ctx;
+
+ crypto_shash_final(&sig->hash, H);
+
+ /* (1) Check the signature size against the public key modulus size */
+ k = (mpi_get_nbits(rsa->public_key->n) + 7) / 8;
+
+ tsize = (mpi_get_nbits(sig->s) + 7) / 8;
+ kdebug("step 1: k=%zu size(S)=%zu", k, tsize);
+ if (tsize != k) {
+ ret = -EBADMSG;
+ goto error_free_digest;
+ }
+
+ /* (2b) Apply the RSAVP1 verification primitive to the public key */
+ ret = RSAVP1(rsa->public_key, sig->s, &m);
+ if (ret < 0)
+ goto error_free_mpi;
+
+ /* (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_free_mpi;
+
+ ret = RSA_verify(H, EM - 1, k, digest_size,
+ RSA_ASN1_templates[sig->hash_algo].data,
+ RSA_ASN1_templates[sig->hash_algo].size);
+
+error_free_mpi:
+ kfree(EM);
+ mpi_free(m);
+error_free_digest:
+ kfree(H);
+error_free_ctx:
+ RSA_verify_sig_cancel(ctx);
+ kleave(" = %d", ret);
+ return ret;
+}
+
+/*
+ * Cancel an in-progress data loading
+ */
+void RSA_verify_sig_cancel(struct crypto_key_verify_context *_ctx)
+{
+ struct RSA_signature *sig =
+ container_of(_ctx, struct RSA_signature, base);
+
+ kenter("");
+
+ /* !!! Do we need to tell the crypto layer to cancel too? */
+ crypto_free_shash(sig->hash.tfm);
+ key_put(sig->base.key);
+ mpi_free(sig->s);
+ kfree(sig);
+
+ kleave("");
+}

2011-11-29 23:45:57

by David Howells

[permalink] [raw]
Subject: [PATCH 14/16] MODSIGN: Add indications of module ELF types [ver #2]

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

Signed-Off-By: David Howells <[email protected]>
---

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


diff --git a/arch/alpha/include/asm/module.h b/arch/alpha/include/asm/module.h
index 7b63743..3d5a3ea 100644
--- a/arch/alpha/include/asm/module.h
+++ b/arch/alpha/include/asm/module.h
@@ -6,6 +6,7 @@ struct mod_arch_specific
unsigned int gotsecindex;
};

+#define MODULES_ARE_ELF64
#define Elf_Sym Elf64_Sym
#define Elf_Shdr Elf64_Shdr
#define Elf_Ehdr Elf64_Ehdr
@@ -13,6 +14,8 @@ struct mod_arch_specific
#define Elf_Dyn Elf64_Dyn
#define Elf_Rel Elf64_Rel
#define Elf_Rela Elf64_Rela
+#define ELF_R_TYPE(X) ELF64_R_TYPE(X)
+#define ELF_R_SYM(X) ELF64_R_SYM(X)

#define ARCH_SHF_SMALL SHF_ALPHA_GPREL

diff --git a/arch/arm/include/asm/module.h b/arch/arm/include/asm/module.h
index 6c6809f..f47d9cd 100644
--- a/arch/arm/include/asm/module.h
+++ b/arch/arm/include/asm/module.h
@@ -1,9 +1,14 @@
#ifndef _ASM_ARM_MODULE_H
#define _ASM_ARM_MODULE_H

+#define MODULES_ARE_ELF32
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Rel Elf32_Rel
+#define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X) ELF32_R_TYPE(X)
+#define ELF_R_SYM(X) ELF32_R_SYM(X)

struct unwind_table;

diff --git a/arch/cris/include/asm/module.h b/arch/cris/include/asm/module.h
index 7ee7231..03f7b2e 100644
--- a/arch/cris/include/asm/module.h
+++ b/arch/cris/include/asm/module.h
@@ -3,7 +3,12 @@
/* cris is simple */
struct mod_arch_specific { };

+#define MODULES_ARE_ELF32
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Rel Elf32_Rel
+#define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X) ELF32_R_TYPE(X)
+#define ELF_R_SYM(X) ELF32_R_SYM(X)
#endif /* _ASM_CRIS_MODULE_H */
diff --git a/arch/h8300/include/asm/module.h b/arch/h8300/include/asm/module.h
index 8e46724..5140128 100644
--- a/arch/h8300/include/asm/module.h
+++ b/arch/h8300/include/asm/module.h
@@ -4,8 +4,13 @@
* This file contains the H8/300 architecture specific module code.
*/
struct mod_arch_specific { };
+#define MODULES_ARE_ELF32
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Rel Elf32_Rel
+#define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X) ELF32_R_TYPE(X)
+#define ELF_R_SYM(X) ELF32_R_SYM(X)

#endif /* _ASM_H8/300_MODULE_H */
diff --git a/arch/ia64/include/asm/module.h b/arch/ia64/include/asm/module.h
index 908eaef..3c4cd94 100644
--- a/arch/ia64/include/asm/module.h
+++ b/arch/ia64/include/asm/module.h
@@ -29,9 +29,14 @@ struct mod_arch_specific {
unsigned int next_got_entry; /* index of next available got entry */
};

+#define MODULES_ARE_ELF64
#define Elf_Shdr Elf64_Shdr
#define Elf_Sym Elf64_Sym
#define Elf_Ehdr Elf64_Ehdr
+#define Elf_Rel Elf64_Rel
+#define Elf_Rela Elf64_Rela
+#define ELF_R_TYPE(X) ELF64_R_TYPE(X)
+#define ELF_R_SYM(X) ELF64_R_SYM(X)

#define MODULE_PROC_FAMILY "ia64"
#define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY \
diff --git a/arch/m32r/include/asm/module.h b/arch/m32r/include/asm/module.h
index eb73ee0..7146455 100644
--- a/arch/m32r/include/asm/module.h
+++ b/arch/m32r/include/asm/module.h
@@ -3,8 +3,13 @@

struct mod_arch_specific { };

+#define MODULES_ARE_ELF32
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Rel Elf32_Rel
+#define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X) ELF32_R_TYPE(X)
+#define ELF_R_SYM(X) ELF32_R_SYM(X)

#endif /* _ASM_M32R_MODULE_H */
diff --git a/arch/m68k/include/asm/module.h b/arch/m68k/include/asm/module.h
index edffe66..9e2cd74 100644
--- a/arch/m68k/include/asm/module.h
+++ b/arch/m68k/include/asm/module.h
@@ -36,8 +36,13 @@ struct module;
extern void module_fixup(struct module *mod, struct m68k_fixup_info *start,
struct m68k_fixup_info *end);

+#define MODULES_ARE_ELF32
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Rel Elf32_Rel
+#define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X) ELF32_R_TYPE(X)
+#define ELF_R_SYM(X) ELF32_R_SYM(X)

#endif /* _ASM_M68K_MODULE_H */
diff --git a/arch/mips/include/asm/module.h b/arch/mips/include/asm/module.h
index bc01a02..73c71a2 100644
--- a/arch/mips/include/asm/module.h
+++ b/arch/mips/include/asm/module.h
@@ -33,11 +33,15 @@ typedef struct {
} Elf64_Mips_Rela;

#ifdef CONFIG_32BIT
-
+#define MODULES_ARE_ELF32
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define Elf_Ehdr Elf32_Ehdr
#define Elf_Addr Elf32_Addr
+#define Elf_Rel Elf32_Rel
+#define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X) ELF32_R_TYPE(X)
+#define ELF_R_SYM(X) ELF32_R_SYM(X)

#define Elf_Mips_Rel Elf32_Rel
#define Elf_Mips_Rela Elf32_Rela
@@ -48,11 +52,15 @@ typedef struct {
#endif

#ifdef CONFIG_64BIT
-
+#define MODULES_ARE_ELF64
#define Elf_Shdr Elf64_Shdr
#define Elf_Sym Elf64_Sym
#define Elf_Ehdr Elf64_Ehdr
#define Elf_Addr Elf64_Addr
+#define Elf_Rel Elf64_Rel
+#define Elf_Rela Elf64_Rela
+#define ELF_R_TYPE(X) ELF64_R_TYPE(X)
+#define ELF_R_SYM(X) ELF64_R_SYM(X)

#define Elf_Mips_Rel Elf64_Mips_Rel
#define Elf_Mips_Rela Elf64_Mips_Rela
diff --git a/arch/parisc/include/asm/module.h b/arch/parisc/include/asm/module.h
index 1f41234..3e13f69 100644
--- a/arch/parisc/include/asm/module.h
+++ b/arch/parisc/include/asm/module.h
@@ -4,17 +4,25 @@
* This file contains the parisc architecture specific module code.
*/
#ifdef CONFIG_64BIT
+#define MODULES_ARE_ELF64
#define Elf_Shdr Elf64_Shdr
#define Elf_Sym Elf64_Sym
#define Elf_Ehdr Elf64_Ehdr
#define Elf_Addr Elf64_Addr
+#define Elf_Rel Elf64_Rel
#define Elf_Rela Elf64_Rela
+#define ELF_R_TYPE(X) ELF64_R_TYPE(X)
+#define ELF_R_SYM(X) ELF64_R_SYM(X)
#else
+#define MODULES_ARE_ELF32
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define Elf_Ehdr Elf32_Ehdr
#define Elf_Addr Elf32_Addr
+#define Elf_Rel Elf32_Rel
#define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X) ELF32_R_TYPE(X)
+#define ELF_R_SYM(X) ELF32_R_SYM(X)
#endif

struct unwind_table;
diff --git a/arch/powerpc/include/asm/module.h b/arch/powerpc/include/asm/module.h
index 0192a4e..e949704 100644
--- a/arch/powerpc/include/asm/module.h
+++ b/arch/powerpc/include/asm/module.h
@@ -60,16 +60,26 @@ struct mod_arch_specific {
*/

#ifdef __powerpc64__
+# define MODULES_ARE_ELF64
# define Elf_Shdr Elf64_Shdr
# define Elf_Sym Elf64_Sym
# define Elf_Ehdr Elf64_Ehdr
+# define Elf_Rel Elf64_Rel
+# define Elf_Rela Elf64_Rela
+# define ELF_R_TYPE(X) ELF64_R_TYPE(X)
+# define ELF_R_SYM(X) ELF64_R_SYM(X)
# ifdef MODULE
asm(".section .stubs,\"ax\",@nobits; .align 3; .previous");
# endif
#else
+# define MODULES_ARE_ELF32
# define Elf_Shdr Elf32_Shdr
# define Elf_Sym Elf32_Sym
# define Elf_Ehdr Elf32_Ehdr
+# define Elf_Rel Elf32_Rel
+# define Elf_Rela Elf32_Rela
+# define ELF_R_TYPE(X) ELF32_R_TYPE(X)
+# define ELF_R_SYM(X) ELF32_R_SYM(X)
# ifdef MODULE
asm(".section .plt,\"ax\",@nobits; .align 3; .previous");
asm(".section .init.plt,\"ax\",@nobits; .align 3; .previous");
diff --git a/arch/s390/include/asm/module.h b/arch/s390/include/asm/module.h
index 1cc1c5a..b64dab0 100644
--- a/arch/s390/include/asm/module.h
+++ b/arch/s390/include/asm/module.h
@@ -29,14 +29,17 @@ struct mod_arch_specific
};

#ifdef __s390x__
+#define MODULES_ARE_ELF64
#define ElfW(x) Elf64_ ## x
#define ELFW(x) ELF64_ ## x
#else
+#define MODULES_ARE_ELF32
#define ElfW(x) Elf32_ ## x
#define ELFW(x) ELF32_ ## x
#endif

#define Elf_Addr ElfW(Addr)
+#define Elf_Rel ElfW(Rel)
#define Elf_Rela ElfW(Rela)
#define Elf_Shdr ElfW(Shdr)
#define Elf_Sym ElfW(Sym)
diff --git a/include/asm-generic/module.h b/include/asm-generic/module.h
index ed5b44d..e053617b 100644
--- a/include/asm-generic/module.h
+++ b/include/asm-generic/module.h
@@ -10,13 +10,23 @@ struct mod_arch_specific
};

#ifdef CONFIG_64BIT
+#define MODULES_ARE_ELF64
#define Elf_Shdr Elf64_Shdr
#define Elf_Sym Elf64_Sym
#define Elf_Ehdr Elf64_Ehdr
+#define Elf_Rel Elf64_Rel
+#define Elf_Rela Elf64_Rela
+#define ELF_R_TYPE(X) ELF64_R_TYPE(X)
+#define ELF_R_SYM(X) ELF64_R_SYM(X)
#else
+#define MODULES_ARE_ELF32
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Rel Elf32_Rel
+#define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X) ELF32_R_TYPE(X)
+#define ELF_R_SYM(X) ELF32_R_SYM(X)
#endif

#endif /* __ASM_GENERIC_MODULE_H */


2011-11-29 23:45:04

by David Howells

[permalink] [raw]
Subject: [PATCH 10/16] KEYS: DSA key signature verification [ver #2]

Signature verification routines for DSA crypto key subtype.

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

security/keys/Makefile | 2
security/keys/crypto_dsa.h | 11 +
security/keys/crypto_dsa_verify.c | 384 +++++++++++++++++++++++++++++++++++++
3 files changed, 396 insertions(+), 1 deletions(-)
create mode 100644 security/keys/crypto_dsa_verify.c


diff --git a/security/keys/Makefile b/security/keys/Makefile
index 8f48527..bde336e 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -23,5 +23,5 @@ obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSCTL) += sysctl.o

crypto_keys-y := crypto_type.o pgp_parse.o crypto_verify.o
-crypto_dsa-y := crypto_dsa_subtype.o
+crypto_dsa-y := crypto_dsa_subtype.o crypto_dsa_verify.o
crypto_rsa-y := crypto_rsa_subtype.o
diff --git a/security/keys/crypto_dsa.h b/security/keys/crypto_dsa.h
index 0455634..8c7c6e9 100644
--- a/security/keys/crypto_dsa.h
+++ b/security/keys/crypto_dsa.h
@@ -34,3 +34,14 @@ struct DSA_payload {
u8 key_id_size; /* Number of bytes in key_id */
struct DSA_public_key *public_key;
};
+
+/*
+ * crypto_dsa_verify.c
+ */
+extern struct crypto_key_verify_context *DSA_verify_sig_begin(
+ struct key *key, const u8 *sig, size_t siglen);
+extern int DSA_verify_sig_add_data(struct crypto_key_verify_context *ctx,
+ const void *data, size_t datalen);
+extern int DSA_verify_sig_end(struct crypto_key_verify_context *ctx,
+ const u8 *sig, size_t siglen);
+extern void DSA_verify_sig_cancel(struct crypto_key_verify_context *ctx);
diff --git a/security/keys/crypto_dsa_verify.c b/security/keys/crypto_dsa_verify.c
new file mode 100644
index 0000000..c9dc180
--- /dev/null
+++ b/security/keys/crypto_dsa_verify.c
@@ -0,0 +1,384 @@
+/* DSA signature verification algorithm
+ *
+ * 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 DEBUG
+#define pr_fmt(fmt) "DSA: "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 "internal.h"
+#include "crypto_dsa.h"
+
+#define DSA_NSIG 2 /* number of MPI's in DSA signature */
+
+struct DSA_signature {
+ struct crypto_key_verify_context base;
+ enum pgp_hash_algo hash_algo : 8;
+ u8 signed_hash_msw[2];
+ union {
+ MPI mpi[2];
+ struct {
+ MPI r;
+ MPI s;
+ };
+ };
+ struct shash_desc hash; /* This must go last! */
+};
+
+struct DSA_sig_parse_context {
+ struct pgp_parse_context pgp;
+ struct pgp_sig_parameters params;
+};
+
+static int DSA_parse_signature(struct pgp_parse_context *context,
+ enum pgp_packet_tag type,
+ u8 headerlen,
+ const u8 *data,
+ size_t datalen)
+{
+ struct DSA_sig_parse_context *ctx =
+ container_of(context, struct DSA_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 *DSA_verify_sig_begin(
+ struct key *key, const u8 *sigdata, size_t siglen)
+{
+ struct DSA_sig_parse_context p;
+ struct DSA_signature *sig;
+ struct crypto_shash *tfm;
+ struct DSA_payload *dsa = key->payload.data;
+ int ret;
+
+ kenter("{%d},,%zu", key_serial(key), siglen);
+
+ if (!dsa->public_key) {
+ kleave(" = -ENOKEY [no public key]");
+ return ERR_PTR(-ENOKEY);
+ }
+
+ p.pgp.types_of_interest = (1 << PGP_PKT_SIGNATURE);
+ p.pgp.process_packet = DSA_parse_signature;
+ ret = pgp_parse_packets(sigdata, siglen, &p.pgp);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ if (p.params.pubkey_algo != PGP_PUBKEY_DSA) {
+ kleave(" = -ENOKEY [wrong pk algo]");
+ return ERR_PTR(-ENOKEY);
+ }
+
+ if (p.params.hash_algo >= PGP_HASH__LAST ||
+ !pgp_hash_algorithms[p.params.hash_algo]) {
+ kleave(" = -ENOPKG [hash]");
+ return ERR_PTR(-ENOPKG);
+ }
+
+ pr_notice("Signature generated with %s hash\n",
+ pgp_hash_algorithms[p.params.hash_algo]);
+
+ if (memcmp(&p.params.issuer, dsa->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);
+
+ /* We allocate the hash operational data storage on the end of our
+ * context data.
+ */
+ sig = kzalloc(sizeof(*sig) + crypto_shash_descsize(tfm), GFP_KERNEL);
+ if (!sig) {
+ crypto_free_shash(tfm);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ sig->base.key = key;
+ sig->hash_algo = p.params.hash_algo;
+ 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
+ */
+int DSA_verify_sig_add_data(struct crypto_key_verify_context *ctx,
+ const void *data, size_t datalen)
+{
+ struct DSA_signature *sig =
+ container_of(ctx, struct DSA_signature, base);
+
+ return crypto_shash_update(&sig->hash, data, datalen);
+}
+
+/*
+ * Perform the actual mathematical DSA signature verification.
+ */
+static int DSA_verify(const MPI datahash,
+ const struct DSA_signature *sig,
+ const struct DSA_public_key *pub)
+{
+ MPI w = NULL, u1 = NULL, u2 = NULL, v = NULL;
+ MPI base[3];
+ MPI exp[3];
+ int rc;
+
+ kenter("");
+
+ if (!(mpi_cmp_ui(sig->r, 0) > 0 && mpi_cmp(sig->r, pub->q) < 0)) {
+ pr_warning("Assertion failed [0 < r < q]\n");
+ return -EKEYREJECTED;
+ }
+
+ if (!(mpi_cmp_ui(sig->s, 0) > 0 && mpi_cmp(sig->s, pub->q) < 0)) {
+ pr_warning("Assertion failed [0 < s < q]\n");
+ return -EKEYREJECTED;
+ }
+
+ rc = -ENOMEM;
+ w = mpi_alloc(mpi_get_nlimbs(pub->q)); if (!w ) goto cleanup;
+ u1 = mpi_alloc(mpi_get_nlimbs(pub->q)); if (!u1) goto cleanup;
+ u2 = mpi_alloc(mpi_get_nlimbs(pub->q)); if (!u2) goto cleanup;
+ v = mpi_alloc(mpi_get_nlimbs(pub->p)); if (!v ) goto cleanup;
+
+ /* w = s^(-1) mod q */
+ if (mpi_invm(w, sig->s, pub->q) < 0)
+ goto cleanup;
+
+ /* u1 = (datahash * w) mod q */
+ if (mpi_mulm(u1, datahash, w, pub->q) < 0)
+ goto cleanup;
+
+ /* u2 = r * w mod q */
+ if (mpi_mulm(u2, sig->r, w, pub->q) < 0)
+ goto cleanup;
+
+ /* v = g^u1 * y^u2 mod p mod q */
+ base[0] = pub->g; exp[0] = u1;
+ base[1] = pub->y; exp[1] = u2;
+ base[2] = NULL; exp[2] = NULL;
+
+ if (mpi_mulpowm(v, base, exp, pub->p) < 0)
+ goto cleanup;
+
+ if (mpi_fdiv_r(v, v, pub->q) < 0)
+ goto cleanup;
+
+ rc = (mpi_cmp(v, sig->r) == 0) ? 0 : -EKEYREJECTED;
+
+cleanup:
+ mpi_free(w);
+ mpi_free(u1);
+ mpi_free(u2);
+ mpi_free(v);
+ kleave(" = %d", rc);
+ return rc;
+}
+
+struct DSA_sig_digest_context {
+ struct pgp_parse_context pgp;
+ struct DSA_signature *sig;
+};
+
+/*
+ * Extract required metadata from the signature packet and add what we need to
+ * to the hash.
+ */
+static int DSA_digest_signature(struct pgp_parse_context *context,
+ enum pgp_packet_tag type,
+ u8 headerlen,
+ const u8 *data,
+ size_t datalen)
+{
+ enum pgp_signature_version version;
+ struct DSA_sig_digest_context *ctx =
+ container_of(context, struct DSA_sig_digest_context, pgp);
+ int i;
+
+ kenter("");
+
+ 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 < DSA_NSIG; i++) {
+ unsigned int remaining = datalen;
+ 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.
+ */
+int DSA_verify_sig_end(struct crypto_key_verify_context *ctx,
+ const u8 *sigdata, size_t siglen)
+{
+ struct DSA_signature *sig =
+ container_of(ctx, struct DSA_signature, base);
+ struct DSA_payload *dsa = sig->base.key->payload.data;
+ struct DSA_sig_digest_context p;
+ void *digest = NULL;
+ MPI datahash = NULL;
+ size_t digest_size;
+ 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 = DSA_digest_signature;
+ p.sig = sig;
+ ret = pgp_parse_packets(sigdata, siglen, &p.pgp);
+ if (ret < 0)
+ goto error_free_ctx;
+
+ ret = -ENOMEM;
+ digest_size = crypto_shash_digestsize(sig->hash.tfm);
+ digest = kmalloc(digest_size, GFP_KERNEL);
+ if (!digest)
+ goto error_free_ctx;
+
+ crypto_shash_final(&sig->hash, digest);
+
+ ret = -ENOMEM;
+ datahash = mpi_alloc((digest_size + BYTES_PER_MPI_LIMB - 1) /
+ BYTES_PER_MPI_LIMB);
+ if (!datahash)
+ goto error_free_digest;
+
+ ret = mpi_set_buffer(datahash, digest, digest_size, 0);
+ if (ret < 0)
+ goto error_free_mpi;
+
+ ret = DSA_verify(datahash, sig, dsa->public_key);
+
+error_free_mpi:
+ mpi_free(datahash);
+error_free_digest:
+ kfree(digest);
+error_free_ctx:
+ DSA_verify_sig_cancel(ctx);
+ kleave(" = %d", ret);
+ return ret;
+}
+
+/*
+ * Cancel an in-progress data loading
+ */
+void DSA_verify_sig_cancel(struct crypto_key_verify_context *_ctx)
+{
+ struct DSA_signature *sig =
+ container_of(_ctx, struct DSA_signature, base);
+
+ kenter("");
+
+ /* !!! Do we need to tell the crypto layer to cancel too? */
+ crypto_free_shash(sig->hash.tfm);
+ key_put(sig->base.key);
+ mpi_free(sig->r);
+ mpi_free(sig->s);
+ kfree(sig);
+
+ kleave("");
+}

2011-11-29 23:46:25

by David Howells

[permalink] [raw]
Subject: [PATCH 16/16] MODSIGN: Apply signature checking to modules on module load [ver #2]

Apply signature checking to modules on module load, checking the signature
against the ring of public keys compiled into the kernel (if enabled by
CONFIG_MODULE_SIG). Turning on signature checking will also force the module's
ELF metadata to be verified first.

These patches have been in use by RHEL and Fedora kernels for years, and so
have been thoroughly tested. The signed modules survive both the debuginfo
separation performed by rpmbuild and the strip performed when modules are being
reduced as much as possible before being included in an initial ramdisk
composition. Signed modules have been tested to work with LE and BE, 32- and
64-bit arch kernels, including i386, x86_64, ppc64, ia64, s390 and s390x.

There are several reasons why these patches are useful, amongst which are:

(1) to protect against accidentally-corrupted modules causing damage;

(2) to protect against maliciously modified modules 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.

Now, 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.

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


Use of the module signing facility is documentated in:

Documentation/module-signing.txt

which I've included here for reference:

==============================
KERNEL MODULE SIGNING FACILITY
==============================

The module signing facilitiy applies cryptographic signature checking to
modules on module load, checking the signature against a ring of public keys
compiled into the kernel. GPG is used to do the cryptographic work and
determines the format of the signature and key data. The facility uses GPG's
MPI library to handle the huge numbers involved.

This facility is enabled through CONFIG_MODULE_SIG. Turning on signature
checking will also force the module's ELF metadata to be verified before the
signature is checked.


=====================
SUPPLYING PUBLIC KEYS
=====================

A set of public keys must be supplied at main kernel compile time. This is
done by taking a GPG public key file, running it through the kernel's bin2c
program and writing the result over crypto/signature/key.h. To automate this
process, something like this could be done:

cat >genkey <<EOF
%pubring kernel.pub
%secring kernel.sec
Key-Type: DSA
Key-Length: 512
Name-Real: A. N. Other
Name-Comment: Kernel Module GPG key
%commit
EOF
make scripts/bin2c
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 more by running:

rngd -r /dev/urandom

in the background.

Note:

(1) That "keyname" is the name of the key in the keyring. This differentiates
it from any other keys that may be added to the keyring.

(2) That no GPG password is used in the above scriptlet.

(3) It may be desirable to shred and delete the private key file after signing
the modules.


==============
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 "./kernel.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 "./kernel.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

with:

--default-key $(MODKEYNAME)

being added if requested.

The resulting module.ko file will be the signed module.


========================
STRIPPING SIGNED MODULES
========================

Signed modules may be safely stripped as the signature only covers those parts
of the module the kernel actually uses and any ELF metadata required to deal
with them. Any necessary ELF metadata that is affected by stripping is
canonicalised by the sig generator and the sig checker to hide strip effects.

This permits the debuginfo to be detached from the module and placed in another
spot so that gdb can find it when referring to that module without the need for
multiple signed versions of the module. Such is done by rpmbuild when
producing RPMs.

It also permits the module to be stripped as far as possible for when modules
are being reduced prior to being included in an initial ramdisk composition.


======================
LOADING SIGNED MODULES
======================

Modules are loaded with insmod, exactly as for unsigned modules. The signature
is inserted into the module object file as an ELF section called ".module_sig".
The signature checker will spot it and apply signature checking.


=========================================
NON-VALID SIGNATURES AND UNSIGNED MODULES
=========================================

If CONFIG_MODULE_SIG_FORCE is enabled or "enforcemodulesig=1" is supplied on
the kernel command line, the kernel will _only_ load validly signed modules
for which it has a public key. Otherwise, it will also load modules that are
unsigned. Any module for which the kernel has a key, but which proves to have
a signature mismatch will not be permitted to load (returning EKEYREJECTED).

This table indicates the behaviours of the various situations:

MODULE STATE PERMISSIVE MODE ENFORCING MODE
=============================== =============== ===============
Unsigned Ok EKEYREJECTED
Signed, no public key ENOKEY ENOKEY
Validly signed, public key Ok Ok
Invalidly signed, public key EKEYREJECTED EKEYREJECTED
Validly signed, expired key EKEYEXPIRED EKEYEXPIRED
Corrupt signature ELIBBAD ELIBBAD
Corrupt ELF ELIBBAD ELIBBAD


Signed-Off-By: David Howells <[email protected]>
---

.gitignore | 15 +
Documentation/module-signing.txt | 186 ++++++++
Makefile | 1
include/linux/elfnote.h | 4
include/linux/modsign.h | 27 +
include/linux/module.h | 3
init/Kconfig | 53 ++
kernel/Makefile | 2
kernel/modsign-pubkey.c | 44 ++
kernel/module-verify-sig.c | 535 ++++++++++++++++++++++
kernel/module-verify.c | 5
kernel/module-verify.h | 19 +
kernel/module.c | 21 +
scripts/Makefile.modpost | 85 +++-
scripts/mod/.gitignore | 1
scripts/mod/Makefile | 2
scripts/mod/mod-extract.c | 913 ++++++++++++++++++++++++++++++++++++++
scripts/mod/modsign-note.sh | 16 +
18 files changed, 1920 insertions(+), 12 deletions(-)
create mode 100644 Documentation/module-signing.txt
create mode 100644 include/linux/modsign.h
create mode 100644 kernel/modsign-pubkey.c
create mode 100644 kernel/module-verify-sig.c
create mode 100644 scripts/mod/mod-extract.c
create mode 100644 scripts/mod/modsign-note.sh


diff --git a/.gitignore b/.gitignore
index 57af07c..841a17ee 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,9 @@
*.o.*
*.a
*.s
+*.ko.unsigned
+*.ko.digest
+*.ko.digest.sig
*.ko
*.so
*.so.dbg
@@ -84,3 +87,15 @@ GTAGS
*.orig
*~
\#*#
+
+#
+# GPG leavings from module signing
+#
+trustdb.gpg
+random_seed
+kernel.pub
+kernel.sec
+extract.pub
+secring.gpg
+pubring.gpg
+crypto/signature/key.h
diff --git a/Documentation/module-signing.txt b/Documentation/module-signing.txt
new file mode 100644
index 0000000..300b91a
--- /dev/null
+++ b/Documentation/module-signing.txt
@@ -0,0 +1,186 @@
+ ==============================
+ KERNEL MODULE SIGNING FACILITY
+ ==============================
+
+The module signing facility applies cryptographic signature checking to modules
+on module load, checking the signature against a ring of public keys compiled
+into the kernel. GPG is used to do the cryptographic work and determines the
+format of the signature and key data. The facility uses GPG's MPI library to
+handle the huge numbers involved.
+
+This facility is enabled through CONFIG_MODULE_SIG. Turning on signature
+checking will also force the module's ELF metadata to be verified before the
+signature is checked.
+
+The signature checker in the kernel is capable of handling multiple keys of
+either DSA or RSA type, and can support any of MD5, RIPE-MD-160, SHA-1,
+SHA-224, SHA-256, SHA-384 and SHA-512 hashes - PROVIDED(!) the requisite
+algorithms are compiled into the kernel.
+
+(!) NOTE: Modules may only be verified initially with algorithms compiled into
+the kernel. Further algorithm modules may be loaded and used - but these must
+first pass a verification step using already loaded/compiled-in algorithms.
+
+
+=====================
+SUPPLYING PUBLIC KEYS
+=====================
+
+A set of public keys must be supplied at kernel image build time. This is done
+by taking a GPG public key file and placing it in the base of the kernel
+directory in a file called kernel.pub.
+
+For example, a throwaway key could be generated automatically by something like
+the following:
+
+ cat >genkey <<EOF
+ %pubring kernel.pub
+ %secring kernel.sec
+ Key-Type: DSA
+ Key-Length: 512
+ 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 more by running:
+
+ rngd -r /dev/urandom
+
+in the background.
+
+Note that no GPG password is used in the above scriptlet.
+
+The kernel.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 "./kernel.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 "./kernel.pub".
+
+ (*) MODKEYNAME=<key-name>
+
+ The name of the key pair to be used from the aforementioned keyrings.
+ This defaults to being unset, thus leaving the choice of default key to
+ gpg.
+
+ (*) KEYFLAGS="gpg-options"
+
+ Override the complete gpg command line, including the preceding three
+ options. The default options supplied to gpg are:
+
+ --no-default-keyring
+ --secret-keyring $(MODSECKEY)
+ --keyring $(MODPUBKEY)
+ --no-default-keyring
+ --homedir .
+ --no-options
+ --no-auto-check-trustdb
+ --no-permission-warning
+ --digest-algo=<hash-algorithm>
+
+ with:
+
+ --default-key $(MODKEYNAME)
+
+ being added if requested.
+
+The resulting module.ko file will be the signed module.
+
+
+========================
+STRIPPING SIGNED MODULES
+========================
+
+Signed modules may be safely stripped as the signature only covers those parts
+of the module the kernel actually uses and any ELF metadata required to deal
+with them. Any necessary ELF metadata that is affected by stripping is
+canonicalised by the sig generator and the sig checker to hide strip effects.
+
+This permits the debuginfo to be detached from the module and placed in another
+spot so that gdb can find it when referring to that module without the need for
+multiple signed versions of the module. Such is done by rpmbuild when
+producing RPMs.
+
+It also permits the module to be stripped as far as possible for when modules
+are being reduced prior to being included in an initial ramdisk composition.
+
+
+======================
+LOADING SIGNED MODULES
+======================
+
+Modules are loaded with insmod, exactly as for unsigned modules. The signature
+is inserted into the module object file as an ELF section called ".module_sig".
+The signature checker will detect it and apply signature checking.
+
+
+=========================================
+NON-VALID SIGNATURES AND UNSIGNED MODULES
+=========================================
+
+If CONFIG_MODULE_SIG_FORCE is enabled or "enforcemodulesig=1" is supplied on
+the kernel command line, the kernel will _only_ load validly signed modules
+for which it has a public key. Otherwise, it will also load modules that are
+unsigned. Any module for which the kernel has a key, but which proves to have
+a signature mismatch will not be permitted to load (returning EKEYREJECTED).
+
+This table indicates the behaviours of the various situations:
+
+ MODULE STATE PERMISSIVE MODE ENFORCING MODE
+ ======================================= =============== ===============
+ Unsigned Ok EKEYREJECTED
+ Signed, no public key ENOKEY ENOKEY
+ Validly signed, public key Ok Ok
+ Invalidly signed, public key EKEYREJECTED EKEYREJECTED
+ Validly signed, expired key EKEYEXPIRED EKEYEXPIRED
+ Signed, hash algorithm unavailable ENOPKG ENOPKG
+ Corrupt signature EBADMSG EBADMSG
+ Corrupt ELF ELIBBAD ELIBBAD
diff --git a/Makefile b/Makefile
index 361e4f0..9f246d3 100644
--- a/Makefile
+++ b/Makefile
@@ -1404,6 +1404,7 @@ clean: $(clean-dirs)
$(call cmd,rmfiles)
@find $(if $(KBUILD_EXTMOD), $(KBUILD_EXTMOD), .) $(RCS_FIND_IGNORE) \
\( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \
+ -o -name '*.ko.*' \
-o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \
-o -name '*.symtypes' -o -name 'modules.order' \
-o -name modules.builtin -o -name '.tmp_*.o.*' \
diff --git a/include/linux/elfnote.h b/include/linux/elfnote.h
index 278e3ef..949d494 100644
--- a/include/linux/elfnote.h
+++ b/include/linux/elfnote.h
@@ -58,6 +58,7 @@
ELFNOTE_END

#else /* !__ASSEMBLER__ */
+#include <linux/stringify.h>
#include <linux/elf.h>
/*
* Use an anonymous structure which matches the shape of
@@ -93,6 +94,9 @@

#define ELFNOTE32(name, type, desc) ELFNOTE(32, name, type, desc)
#define ELFNOTE64(name, type, desc) ELFNOTE(64, name, type, desc)
+
+#define ELFNOTE_NAME(name) __stringify(name)
+#define ELFNOTE_SECTION(name) ".note."ELFNOTE_NAME(name)
#endif /* __ASSEMBLER__ */

#endif /* _LINUX_ELFNOTE_H */
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/include/linux/module.h b/include/linux/module.h
index 3cb7839..527db2b 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -280,6 +280,9 @@ struct module

unsigned int taints; /* same bits as kernel:tainted */

+ /* Is this module GPG signed */
+ int gpgsig_ok;
+
#ifdef CONFIG_GENERIC_BUG
/* Support for BUG */
unsigned num_bugs;
diff --git a/init/Kconfig b/init/Kconfig
index 42e685d..461a0b2 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1389,10 +1389,61 @@ config MODULE_VERIFY_ELF
help
Check ELF structure of modules upon load

+config MODULE_SIG
+ bool "Module signature verification (EXPERIMENTAL)"
+ depends on MODULES && EXPERIMENTAL
+ select CRYPTO_KEY_TYPE
+ select CRYPTO_KEY_DSA
+ select MODULE_VERIFY_ELF
+ help
+ Check modules for valid signatures upon load. For more information
+ see:
+
+ Documentation/module-signing.txt
+
+choice
+ prompt "Which hash algorithm should modules be signed with?"
+ depends on MODULE_SIG
+ help
+ This determines which sort of hashing algorithm will be used during
+ signature generation. This algorithm _must_ be built into the kernel
+ directly so that signature verification can take place. It is not
+ possible to load a signed module containing the algorithm to check
+ the signature on that module.
+
+config MODULE_SIG_SHA1
+ bool "Sign modules with SHA-1"
+ select CRYPTO_SHA1
+
+config MODULE_SIG_SHA224
+ bool "Sign modules with SHA-224"
+ select CRYPTO_SHA224
+
+config MODULE_SIG_SHA256
+ bool "Sign modules with SHA-256"
+ select CRYPTO_SHA256
+
+config MODULE_SIG_SHA384
+ bool "Sign modules with SHA-384"
+ select CRYPTO_SHA384
+
+config MODULE_SIG_SHA512
+ bool "Sign modules with SHA-512"
+ select CRYPTO_SHA512
+
+endchoice
+
+config MODULE_SIG_FORCE
+ bool "Required modules to be validly signed (EXPERIMENTAL)"
+ depends on MODULE_SIG
+ help
+ Reject unsigned modules or signed modules for which we don't have a
+ key.
+
config MODULE_VERIFY
bool
depends on MODULES
- default y if MODULE_VERIFY_ELF
+ default y if MODULE_VERIFY_ELF || MODULE_SIG

endif # MODULES

diff --git a/kernel/Makefile b/kernel/Makefile
index 3c34fab..1d1c91c 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -53,6 +53,8 @@ obj-$(CONFIG_UID16) += uid16.o
obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_MODULE_VERIFY) += module-verify.o
obj-$(CONFIG_MODULE_VERIFY_ELF) += module-verify-elf.o
+obj-$(CONFIG_MODULE_SIG) += module-verify-sig.o modsign-pubkey.o
+kernel/modsign-pubkey.o: kernel.pub
obj-$(CONFIG_KALLSYMS) += kallsyms.o
obj-$(CONFIG_PM) += power/
obj-$(CONFIG_FREEZER) += power/
diff --git a/kernel/modsign-pubkey.c b/kernel/modsign-pubkey.c
new file mode 100644
index 0000000..191a153
--- /dev/null
+++ b/kernel/modsign-pubkey.c
@@ -0,0 +1,44 @@
+/* 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 <keys/keyring-type.h>
+#include "module-verify.h"
+
+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 \"kernel.pub\"\n"
+ "modsign_public_keys_end:"
+ );
+
+/*
+ * We need to make sure ccache doesn't cache the .o file as it doesn't notice
+ * if kernel.pub changes.
+ */
+static __initdata const char annoy_ccache[] = __TIME__ "foo";
+
+/*
+ * Load the compiled-in keys
+ */
+static __init int modsign_pubkey_init(void)
+{
+ pr_notice("Load module verification keys\n");
+
+ if (load_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-sig.c b/kernel/module-verify-sig.c
new file mode 100644
index 0000000..21b82f3
--- /dev/null
+++ b/kernel/module-verify-sig.c
@@ -0,0 +1,535 @@
+/* Module signature checker
+ *
+ * Copyright (C) 2004,2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ * - Derived from GregKH's RSA module signer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#define DEBUG
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/elf.h>
+#include <linux/sched.h>
+#include <linux/cred.h>
+#include <linux/modsign.h>
+#include <keys/keyring-type.h>
+#include "module-verify.h"
+
+#undef MODSIGN_DEBUG
+
+struct key *modsign_keyring;
+
+int modsign_debug;
+core_param(modsign_debug, modsign_debug, bool, 0644);
+
+#define _debug(FMT, ...) \
+ do { \
+ if (unlikely(modsign_debug)) \
+ pr_debug(FMT, ##__VA_ARGS__); \
+ } while(0)
+
+#ifdef MODSIGN_DEBUG
+#define count_and_csum(C, __p, __n) \
+do { \
+ int __loop; \
+ for (__loop = 0; __loop < __n; __loop++) { \
+ (C)->csum += __p[__loop]; \
+ (C)->xcsum += __p[__loop]; \
+ } \
+ (C)->signed_size += __n; \
+} while (0)
+#else
+#define count_and_csum(C, __p, __n) \
+do { \
+ (C)->signed_size += __n; \
+} while (0)
+#endif
+
+#define crypto_digest_update_data(C, PTR, N) \
+do { \
+ uint8_t *__p = (uint8_t *)(PTR); \
+ size_t __n = (N); \
+ count_and_csum((C), __p, __n); \
+ verify_sig_add_data((C)->mod_sig, __p, __n); \
+} while (0)
+
+#define crypto_digest_update_val(C, VAL) \
+do { \
+ uint8_t *__p = (uint8_t *)&(VAL); \
+ size_t __n = sizeof(VAL); \
+ count_and_csum((C), __p, __n); \
+ verify_sig_add_data((C)->mod_sig, __p, __n); \
+} while (0)
+
+static int module_verify_canonicalise(struct module_verify_data *mvdata);
+
+static int extract_elf_rela(struct module_verify_data *mvdata,
+ int secix,
+ const Elf_Rela *relatab, size_t nrels,
+ const char *sh_name);
+
+static int extract_elf_rel(struct module_verify_data *mvdata,
+ int secix,
+ const Elf_Rel *reltab, size_t nrels,
+ const char *sh_name);
+
+#ifdef CONFIG_MODULE_SIG_FORCE
+static int signedonly = 1;
+#else
+static int signedonly;
+#endif
+
+static int __init sign_setup(char *str)
+{
+ signedonly = 1;
+ return 0;
+}
+__setup("enforcemodulesig", sign_setup);
+
+static const char modsign_note_name[] = ELFNOTE_NAME(MODSIGN_NOTE_NAME);
+static const char modsign_note_section[] = ELFNOTE_SECTION(MODSIGN_NOTE_NAME);
+
+/*
+ * verify a module's signature
+ */
+int module_verify_signature(struct module_verify_data *mvdata,
+ int *_gpgsig_ok)
+{
+ struct crypto_key_verify_context *mod_sig;
+ const struct elf_note *note;
+ const Elf_Shdr *sechdrs = mvdata->sections;
+ const char *secstrings = mvdata->secstrings;
+ const char *sig;
+ struct key *key;
+ unsigned note_size, sig_size, note_namesz;
+ int loop, ret;
+
+ _debug("looking for sig section '%s'\n", modsign_note_section);
+
+ for (loop = 1; loop < mvdata->nsects; loop++) {
+ switch (sechdrs[loop].sh_type) {
+ case SHT_NOTE:
+ if (strcmp(mvdata->secstrings + sechdrs[loop].sh_name,
+ modsign_note_section) == 0)
+ mvdata->sig_index = loop;
+ break;
+ }
+ }
+
+ if (mvdata->sig_index <= 0)
+ goto no_signature;
+
+ note = mvdata->buffer + sechdrs[mvdata->sig_index].sh_offset;
+ note_size = sechdrs[mvdata->sig_index].sh_size;
+
+ /* there should be one note of the appropriate type */
+ if (note_size < sizeof(*note) + 2 * 4)
+ goto format_error_no_free;
+ note_namesz = note->n_namesz;
+ sig_size = note->n_descsz;
+ if (note_namesz != sizeof(modsign_note_name))
+ goto format_error_no_free;
+ if (note->n_type != MODSIGN_NOTE_TYPE)
+ goto format_error_no_free;
+ if (memcmp(note + 1, modsign_note_name, note_namesz) != 0)
+ goto format_error_no_free;
+ sig = (void *)(note + 1) + roundup(note_namesz, 4);
+
+ _debug("sig in section %d (size %d)\n",
+ mvdata->sig_index, sig_size);
+ _debug("%02x%02x%02x%02x%02x%02x%02x%02x\n",
+ sig[0], sig[1], sig[2], sig[3],
+ sig[4], sig[5], sig[6], sig[7]);
+
+ /* produce a canonicalisation map for the sections */
+ ret = module_verify_canonicalise(mvdata);
+ if (ret < 0)
+ return ret;
+
+ /* Find the crypto key for the module signature
+ * - !!! if this tries to load the sha1.ko module, we will deadlock!!!
+ */
+ key = request_crypto_key_for_PGP_sig(modsign_keyring, sig, sig_size);
+ if (IS_ERR(key)) {
+ pr_err("Couldn't get key to verify module signature: %ld\n",
+ PTR_ERR(key));
+ return PTR_ERR(key);
+ }
+
+ mod_sig = verify_sig_begin(key, sig, sig_size);
+ key_put(key);
+ if (IS_ERR(mod_sig)) {
+ pr_err("Couldn't begin to verify module signature: %ld\n",
+ PTR_ERR(mod_sig));
+ return PTR_ERR(mod_sig);
+ }
+
+ mvdata->mod_sig = mod_sig;
+
+#ifdef MODSIGN_DEBUG
+ mvdata->xcsum = 0;
+#endif
+
+ /* load data from each relevant section into the digest */
+ for (loop = 0; loop < mvdata->ncanon; loop++) {
+ int sect = mvdata->canonlist[loop];
+ unsigned long sh_type = sechdrs[sect].sh_type;
+ unsigned long sh_info = sechdrs[sect].sh_info;
+ unsigned long sh_size = sechdrs[sect].sh_size;
+ unsigned long sh_flags = sechdrs[sect].sh_flags;
+ const char *sh_name = secstrings + sechdrs[sect].sh_name;
+ const void *data = mvdata->buffer + sechdrs[sect].sh_offset;
+
+#ifdef MODSIGN_DEBUG
+ mvdata->csum = 0;
+#endif
+
+ /* it would be nice to include relocation sections, but the act
+ * of adding a signature to the module seems changes their
+ * contents, because the symtab gets changed when sections are
+ * added or removed */
+ if (sh_type == SHT_REL || sh_type == SHT_RELA) {
+ uint32_t xsh_info = mvdata->canonmap[sh_info];
+
+ crypto_digest_update_data(mvdata, sh_name, strlen(sh_name));
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_type);
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_flags);
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_size);
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_addralign);
+ crypto_digest_update_val(mvdata, xsh_info);
+
+ if (sh_type == SHT_RELA)
+ ret = extract_elf_rela(
+ mvdata, sect,
+ data,
+ sh_size / sizeof(Elf_Rela),
+ sh_name);
+ else
+ ret = extract_elf_rel(
+ mvdata, sect,
+ data,
+ sh_size / sizeof(Elf_Rel),
+ sh_name);
+
+ if (ret < 0)
+ goto format_error;
+ continue;
+ }
+
+ /* include the headers of BSS sections */
+ if (sh_type == SHT_NOBITS && sh_flags & SHF_ALLOC) {
+ crypto_digest_update_data(mvdata, sh_name, strlen(sh_name));
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_type);
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_flags);
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_size);
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_addralign);
+ goto digested;
+ }
+
+ /* include allocatable loadable sections */
+ if (sh_type != SHT_NOBITS && sh_flags & SHF_ALLOC)
+ goto include_section;
+
+ continue;
+
+ include_section:
+ crypto_digest_update_data(mvdata, sh_name, strlen(sh_name));
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_type);
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_flags);
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_size);
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_addralign);
+
+ crypto_digest_update_data(mvdata, data, sh_size);
+
+ digested:
+ _debug("%08zx %02x digested the %s section, size %ld\n",
+ mvdata->signed_size, mvdata->csum, sh_name, sh_size);
+ }
+
+ _debug("Contributed %zu bytes to the digest (csum 0x%02x)\n",
+ mvdata->signed_size, mvdata->xcsum);
+
+ /* do the actual signature verification */
+ ret = verify_sig_end(mvdata->mod_sig, sig, sig_size);
+ mvdata->mod_sig = NULL;
+
+ _debug("verify-sig : %d\n", ret);
+
+ switch (ret) {
+ case 0: /* good signature */
+ *_gpgsig_ok = 1;
+ 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;
+
+format_error:
+ verify_sig_cancel(mvdata->mod_sig);
+ mvdata->mod_sig = NULL;
+format_error_no_free:
+ pr_err("Module format error encountered\n");
+ return -ELIBBAD;
+
+ /* deal with the case of an unsigned module */
+no_signature:
+ _debug("no signature found\n");
+ if (!signedonly)
+ return 0;
+ pr_err("An attempt to load unsigned module was rejected\n");
+ return -EKEYREJECTED;
+}
+
+/*
+ * canonicalise the section table index numbers
+ */
+static int module_verify_canonicalise(struct module_verify_data *mvdata)
+{
+ int canon, loop, changed, tmp;
+
+ /* produce a list of index numbers of sections that contribute
+ * to the kernel's module image
+ */
+ mvdata->canonlist =
+ kmalloc(sizeof(int) * mvdata->nsects * 2, GFP_KERNEL);
+ if (!mvdata->canonlist)
+ return -ENOMEM;
+
+ mvdata->canonmap = mvdata->canonlist + mvdata->nsects;
+ canon = 0;
+
+ for (loop = 1; loop < mvdata->nsects; loop++) {
+ const Elf_Shdr *section = mvdata->sections + loop;
+
+ if (loop == mvdata->sig_index)
+ continue;
+
+ /* we only need to canonicalise allocatable sections */
+ if (section->sh_flags & SHF_ALLOC)
+ mvdata->canonlist[canon++] = loop;
+ else if ((section->sh_type == SHT_REL ||
+ section->sh_type == SHT_RELA) &&
+ mvdata->sections[section->sh_info].sh_flags & SHF_ALLOC)
+ mvdata->canonlist[canon++] = loop;
+ }
+
+ /* canonicalise the index numbers of the contributing section */
+ do {
+ changed = 0;
+
+ for (loop = 0; loop < canon - 1; loop++) {
+ const char *x, *y;
+
+ x = mvdata->secstrings +
+ mvdata->sections[mvdata->canonlist[loop + 0]].sh_name;
+ y = mvdata->secstrings +
+ mvdata->sections[mvdata->canonlist[loop + 1]].sh_name;
+
+ if (strcmp(x, y) > 0) {
+ tmp = mvdata->canonlist[loop + 0];
+ mvdata->canonlist[loop + 0] =
+ mvdata->canonlist[loop + 1];
+ mvdata->canonlist[loop + 1] = tmp;
+ changed = 1;
+ }
+ }
+
+ } while (changed);
+
+ for (loop = 0; loop < canon; loop++)
+ mvdata->canonmap[mvdata->canonlist[loop]] = loop + 1;
+ mvdata->ncanon = canon;
+ return 0;
+}
+
+/*
+ * extract an ELF RELA table
+ * - need to canonicalise the entries in case section addition/removal has
+ * rearranged the symbol table and the section table
+ */
+static int extract_elf_rela(struct module_verify_data *mvdata,
+ int secix,
+ const Elf_Rela *relatab, size_t nrels,
+ const char *sh_name)
+{
+ struct {
+#if defined(MODULES_ARE_ELF32)
+ uint32_t r_offset;
+ uint32_t r_addend;
+ uint32_t st_value;
+ uint32_t st_size;
+ uint16_t st_shndx;
+ uint8_t r_type;
+ uint8_t st_info;
+ uint8_t st_other;
+#elif defined(MODULES_ARE_ELF64)
+ uint64_t r_offset;
+ uint64_t r_addend;
+ uint64_t st_value;
+ uint64_t st_size;
+ uint32_t r_type;
+ uint16_t st_shndx;
+ uint8_t st_info;
+ uint8_t st_other;
+#else
+#error unsupported module type
+#endif
+ } __attribute__((packed)) relocation;
+
+ const Elf_Rela *reloc;
+ const Elf_Sym *symbol;
+ size_t loop;
+
+ /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
+ for (loop = 0; loop < nrels; loop++) {
+ int st_shndx;
+
+ reloc = &relatab[loop];
+
+ /* decode the relocation */
+ relocation.r_offset = reloc->r_offset;
+ relocation.r_addend = reloc->r_addend;
+ relocation.r_type = ELF_R_TYPE(reloc->r_info);
+
+ /* decode the symbol referenced by the relocation */
+ symbol = &mvdata->symbols[ELF_R_SYM(reloc->r_info)];
+ relocation.st_info = symbol->st_info;
+ relocation.st_other = symbol->st_other;
+ relocation.st_value = symbol->st_value;
+ relocation.st_size = symbol->st_size;
+ relocation.st_shndx = symbol->st_shndx;
+ st_shndx = symbol->st_shndx;
+
+ /* canonicalise the section used by the symbol */
+ if (st_shndx > SHN_UNDEF && st_shndx < mvdata->nsects)
+ relocation.st_shndx = mvdata->canonmap[st_shndx];
+
+ crypto_digest_update_val(mvdata, relocation);
+
+ /* undefined symbols must be named if referenced */
+ if (st_shndx == SHN_UNDEF) {
+ const char *name = mvdata->strings + symbol->st_name;
+ crypto_digest_update_data(mvdata,
+ name, strlen(name) + 1);
+ }
+ }
+
+ _debug("%08zx %02x digested the %s section, nrels %zu\n",
+ mvdata->signed_size, mvdata->csum, sh_name, nrels);
+
+ return 0;
+}
+
+/*
+ * extract an ELF REL table
+ * - need to canonicalise the entries in case section addition/removal has
+ * rearranged the symbol table and the section table
+ */
+static int extract_elf_rel(struct module_verify_data *mvdata,
+ int secix,
+ const Elf_Rel *reltab, size_t nrels,
+ const char *sh_name)
+{
+ struct {
+#if defined(MODULES_ARE_ELF32)
+ uint32_t r_offset;
+ uint32_t st_value;
+ uint32_t st_size;
+ uint16_t st_shndx;
+ uint8_t r_type;
+ uint8_t st_info;
+ uint8_t st_other;
+#elif defined(MODULES_ARE_ELF64)
+ uint64_t r_offset;
+ uint64_t st_value;
+ uint64_t st_size;
+ uint32_t r_type;
+ uint16_t st_shndx;
+ uint8_t st_info;
+ uint8_t st_other;
+#else
+#error unsupported module type
+#endif
+ } __attribute__((packed)) relocation;
+
+ const Elf_Rel *reloc;
+ const Elf_Sym *symbol;
+ size_t loop;
+
+ /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
+ for (loop = 0; loop < nrels; loop++) {
+ int st_shndx;
+
+ reloc = &reltab[loop];
+
+ /* decode the relocation */
+ relocation.r_offset = reloc->r_offset;
+ relocation.r_type = ELF_R_TYPE(reloc->r_info);
+
+ /* decode the symbol referenced by the relocation */
+ symbol = &mvdata->symbols[ELF_R_SYM(reloc->r_info)];
+ relocation.st_info = symbol->st_info;
+ relocation.st_other = symbol->st_other;
+ relocation.st_value = symbol->st_value;
+ relocation.st_size = symbol->st_size;
+ relocation.st_shndx = symbol->st_shndx;
+ st_shndx = symbol->st_shndx;
+
+ /* canonicalise the section used by the symbol */
+ if (st_shndx > SHN_UNDEF && st_shndx < mvdata->nsects)
+ relocation.st_shndx = mvdata->canonmap[st_shndx];
+
+ crypto_digest_update_val(mvdata, relocation);
+
+ /* undefined symbols must be named if referenced */
+ if (st_shndx == SHN_UNDEF) {
+ const char *name = mvdata->strings + symbol->st_name;
+ crypto_digest_update_data(mvdata,
+ name, strlen(name) + 1);
+ }
+ }
+
+ _debug("%08zx %02x digested the %s section, nrels %zu\n",
+ mvdata->signed_size, mvdata->csum, sh_name, nrels);
+
+ return 0;
+}
+
+/*
+ * 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);
diff --git a/kernel/module-verify.c b/kernel/module-verify.c
index 875279f..64c5813 100644
--- a/kernel/module-verify.c
+++ b/kernel/module-verify.c
@@ -16,8 +16,9 @@
/*
* verify a module's integrity
* - check the ELF is viable
+ * - check the module's signature
*/
-int module_verify(const Elf_Ehdr *hdr, size_t size)
+int module_verify(const Elf_Ehdr *hdr, size_t size, int *_gpgsig_ok)
{
struct module_verify_data mvdata;
int ret;
@@ -34,6 +35,8 @@ int module_verify(const Elf_Ehdr *hdr, size_t size)
goto error;
}

+ ret = module_verify_signature(&mvdata, _gpgsig_ok);
+
error:
kfree(mvdata.secsizes);
kfree(mvdata.canonlist);
diff --git a/kernel/module-verify.h b/kernel/module-verify.h
index 20884fc..0ccdb71 100644
--- a/kernel/module-verify.h
+++ b/kernel/module-verify.h
@@ -10,10 +10,13 @@
*/

#include <linux/types.h>
+#include <linux/elf.h>
+#include <keys/crypto-type.h>
#include <asm/module.h>

#ifdef CONFIG_MODULE_VERIFY
struct module_verify_data {
+ struct crypto_key_verify_context *mod_sig; /* Module signing context */
const void *buffer; /* module buffer */
const Elf_Ehdr *hdr; /* ELF header */
const Elf_Shdr *sections; /* ELF section table */
@@ -37,7 +40,7 @@ struct module_verify_data {
/*
* module-verify.c
*/
-extern int module_verify(const Elf_Ehdr *hdr, size_t size);
+extern int module_verify(const Elf_Ehdr *hdr, size_t size, int *_gpgsig_ok);

/*
* module-verify-elf.c
@@ -48,6 +51,18 @@ extern int module_verify_elf(struct module_verify_data *mvdata);
#define module_verify_elf(m) (0)
#endif

+/*
+ * module-verify-sig.c
+ */
+#ifdef CONFIG_MODULE_SIG
+extern struct key *modsign_keyring;
+
+extern int module_verify_signature(struct module_verify_data *mvdata,
+ int *_gpgsig_ok);
+#else
+#define module_verify_signature(m, g) (0)
+#endif
+
#else
-#define module_verify(h, s) (0)
+#define module_verify(h, s, g) (0)
#endif
diff --git a/kernel/module.c b/kernel/module.c
index 8309389..9672d8a 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2333,7 +2333,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,
+ int *_gpgsig_ok)
{
int err;
Elf_Ehdr *hdr;
@@ -2367,7 +2368,7 @@ static int copy_and_check(struct load_info *info,
}

/* Verify the module's contents */
- err = module_verify(hdr, len);
+ err = module_verify(hdr, len, _gpgsig_ok);
if (err < 0)
goto free_hdr;

@@ -2713,7 +2714,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,
+ int gpgsig_ok)
{
/* Module within temporary copy. */
struct module *mod;
@@ -2723,6 +2725,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)
@@ -2816,17 +2819,18 @@ static struct module *load_module(void __user *umod,
struct load_info info = { NULL, };
struct module *mod;
long err;
+ int gpgsig_ok;

DEBUGP("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;
@@ -3476,8 +3480,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);
diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost
index 08dce14..b436b04 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,86 @@ $(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 = ./kernel.sec
+MODPUBKEY = ./kernel.pub
+KEYFLAGS = --no-default-keyring --secret-keyring $(MODSECKEY) --keyring $(MODPUBKEY) --no-default-keyring --homedir . --no-options --no-auto-check-trustdb --no-permission-warning
+
+ifdef CONFIG_MODULE_SIG_SHA1
+KEYFLAGS += --digest-algo=SHA1
+else
+ifdef CONFIG_MODULE_SIG_SHA224
+KEYFLAGS += --digest-algo=SHA224
+else
+ifdef CONFIG_MODULE_SIG_SHA256
+KEYFLAGS += --digest-algo=SHA256
+else
+ifdef CONFIG_MODULE_SIG_SHA384
+KEYFLAGS += --digest-algo=SHA384
+else
+ifdef CONFIG_MODULE_SIG_SHA512
+KEYFLAGS += --digest-algo=SHA512
+else
+endif
+endif
+endif
+endif
+endif
+
+ifdef MODKEYNAME
+KEYFLAGS += --default-key $(MODKEYNAME)
+endif

+ifeq ($(wildcard $(MODSECKEY))+$(wildcard $(MODPUBKEY)),$(MODSECKEY)+$(MODPUBKEY))
+ifeq ($(KBUILD_SRC),)
+ # no O= is being used
+ SCRIPTS_DIR := scripts
+else
+ SCRIPTS_DIR := $(KBUILD_SRC)/scripts
+endif
+SIGN_MODULES := 1
+else
+SIGN_MODULES := 0
+endif
+
+# only sign if it's an in-tree module
+ifneq ($(KBUILD_EXTMOD),)
+SIGN_MODULES := 0
+endif
+
+ifeq ($(SIGN_MODULES),1)
+quiet_cmd_sign_ko_ko_unsigned = SIGN [M] $@
+ cmd_sign_ko_ko_unsigned = \
+ scripts/mod/mod-extract $< [email protected] && \
+ rm -f [email protected] && \
+ gpg --batch --no-greeting $(KEYFLAGS) -b [email protected] && \
+ sh $(SCRIPTS_DIR)/mod/modsign-note.sh [email protected] | \
+ $(CC) -x assembler-with-cpp $(c_flags) $(CFLAGS_MODULE) -c -o [email protected] - && \
+ $(LD) -r $(LDFLAGS) -o $@ $< [email protected]
+else
+quiet_cmd_sign_ko_ko_unsigned = NO SIGN [M] $@
+ cmd_sign_ko_ko_unsigned = \
+ cp $< $@
+endif
+
+$(modules): %.ko :%.ko.unsigned FORCE
+ $(call if_changed,sign_ko_ko_unsigned)
+
+targets += $(modules)
+endif

# Add FORCE to the prequisites of a target to force it to be always rebuilt.
# ---------------------------------------------------------------------------
diff --git a/scripts/mod/.gitignore b/scripts/mod/.gitignore
index e9b7abe..223dfd6 100644
--- a/scripts/mod/.gitignore
+++ b/scripts/mod/.gitignore
@@ -1,4 +1,5 @@
elfconfig.h
mk_elfconfig
modpost
+mod-extract

diff --git a/scripts/mod/Makefile b/scripts/mod/Makefile
index ff954f8..4654e3b 100644
--- a/scripts/mod/Makefile
+++ b/scripts/mod/Makefile
@@ -1,4 +1,4 @@
-hostprogs-y := modpost mk_elfconfig
+hostprogs-y := modpost mk_elfconfig mod-extract
always := $(hostprogs-y) empty.o

modpost-objs := modpost.o file2alias.o sumversion.o
diff --git a/scripts/mod/mod-extract.c b/scripts/mod/mod-extract.c
new file mode 100644
index 0000000..0c0e3e3
--- /dev/null
+++ b/scripts/mod/mod-extract.c
@@ -0,0 +1,913 @@
+/* mod-extract.c: module extractor for signing
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <elf.h>
+#include <asm/byteorder.h>
+
+static void extract_elf64(void *buffer, size_t size, Elf64_Ehdr *hdr);
+static void extract_elf32(void *buffer, size_t size, Elf32_Ehdr *hdr);
+
+struct byteorder {
+ uint16_t (*get16)(const uint16_t *);
+ uint32_t (*get32)(const uint32_t *);
+ uint64_t (*get64)(const uint64_t *);
+ void (*set16)(uint16_t *, uint16_t);
+ void (*set32)(uint32_t *, uint32_t);
+ void (*set64)(uint64_t *, uint64_t);
+};
+
+static uint16_t get16_le(const uint16_t *p) { return __le16_to_cpu(*p); }
+static uint32_t get32_le(const uint32_t *p) { return __le32_to_cpu(*p); }
+static uint64_t get64_le(const uint64_t *p) { return __le64_to_cpu(*p); }
+static uint16_t get16_be(const uint16_t *p) { return __be16_to_cpu(*p); }
+static uint32_t get32_be(const uint32_t *p) { return __be32_to_cpu(*p); }
+static uint64_t get64_be(const uint64_t *p) { return __be64_to_cpu(*p); }
+
+static void set16_le(uint16_t *p, uint16_t n) { *p = __cpu_to_le16(n); }
+static void set32_le(uint32_t *p, uint32_t n) { *p = __cpu_to_le32(n); }
+static void set64_le(uint64_t *p, uint64_t n) { *p = __cpu_to_le64(n); }
+static void set16_be(uint16_t *p, uint16_t n) { *p = __cpu_to_be16(n); }
+static void set32_be(uint32_t *p, uint32_t n) { *p = __cpu_to_be32(n); }
+static void set64_be(uint64_t *p, uint64_t n) { *p = __cpu_to_be64(n); }
+
+static const struct byteorder byteorder_le = {
+ get16_le, get32_le, get64_le,
+ set16_le, set32_le, set64_le
+};
+static const struct byteorder byteorder_be = {
+ get16_be, get32_be, get64_be,
+ set16_be, set32_be, set64_be
+};
+static const struct byteorder *order;
+
+static inline uint16_t get16(const uint16_t *p) { return order->get16(p); }
+static inline uint32_t get32(const uint32_t *p) { return order->get32(p); }
+static inline uint64_t get64(const uint64_t *p) { return order->get64(p); }
+static inline void set16(uint16_t *p, uint16_t n) { order->set16(p, n); }
+static inline void set32(uint32_t *p, uint32_t n) { order->set32(p, n); }
+static inline void set64(uint64_t *p, uint64_t n) { order->set64(p, n); }
+
+static FILE *outfd;
+static uint8_t csum, xcsum;
+
+static void write_out(const void *data, size_t size)
+{
+ const uint8_t *p = data;
+ size_t loop;
+
+ for (loop = 0; loop < size; loop++) {
+ csum += p[loop];
+ xcsum += p[loop];
+ }
+
+ if (fwrite(data, 1, size, outfd) != size) {
+ perror("write");
+ exit(1);
+ }
+}
+
+#define write_out_val(VAL) write_out(&(VAL), sizeof(VAL))
+
+static int is_verbose;
+
+static __attribute__((format(printf, 1, 2)))
+void verbose(const char *fmt, ...)
+{
+ va_list va;
+
+ if (is_verbose) {
+ va_start(va, fmt);
+ vprintf(fmt, va);
+ va_end(va);
+ }
+}
+
+static __attribute__((noreturn))
+void usage(void)
+{
+ fprintf(stderr, "Usage: mod-extract [-v] <modulefile> <extractfile>\n");
+ exit(2);
+}
+
+/*
+ *
+ */
+int main(int argc, char **argv)
+{
+ struct stat st;
+ Elf32_Ehdr *hdr32;
+ Elf64_Ehdr *hdr64;
+ size_t len;
+ void *buffer;
+ int fd, be, b64;
+
+ while (argc > 1 && strcmp("-v", argv[1]) == 0) {
+ argv++;
+ argc--;
+ is_verbose++;
+ }
+
+ if (argc != 3)
+ usage();
+
+ /* map the module into memory */
+ fd = open(argv[1], O_RDONLY);
+ if (fd < 0) {
+ perror("open input");
+ exit(1);
+ }
+
+ if (fstat(fd, &st) < 0) {
+ perror("fstat");
+ exit(1);
+ }
+
+ len = st.st_size;
+
+ buffer = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
+ if (buffer == MAP_FAILED) {
+ perror("mmap");
+ exit(1);
+ }
+
+ if (close(fd) < 0) {
+ perror("close input");
+ exit(1);
+ }
+
+ /* check it's an ELF object */
+ hdr32 = buffer;
+ hdr64 = buffer;
+
+ if (hdr32->e_ident[EI_MAG0] != ELFMAG0 ||
+ hdr32->e_ident[EI_MAG1] != ELFMAG1 ||
+ hdr32->e_ident[EI_MAG2] != ELFMAG2 ||
+ hdr32->e_ident[EI_MAG3] != ELFMAG3
+ ) {
+ fprintf(stderr, "Module does not appear to be ELF\n");
+ exit(3);
+ }
+
+ /* determine endianness and word size */
+ b64 = (hdr32->e_ident[EI_CLASS] == ELFCLASS64);
+ be = (hdr32->e_ident[EI_DATA] == ELFDATA2MSB);
+ order = be ? &byteorder_be : &byteorder_le;
+
+ verbose("Module is %s-bit %s-endian\n",
+ b64 ? "64" : "32",
+ be ? "big" : "little");
+
+ /* open the output file */
+ outfd = fopen(argv[2], "w");
+ if (!outfd) {
+ perror("open output");
+ exit(1);
+ }
+
+ /* perform the extraction */
+ if (b64)
+ extract_elf64(buffer, len, hdr64);
+ else
+ extract_elf32(buffer, len, hdr32);
+
+ /* done */
+ if (fclose(outfd) == EOF) {
+ perror("close output");
+ exit(1);
+ }
+
+ return 0;
+}
+
+/*
+ * extract a RELA table
+ * - need to canonicalise the entries in case section addition/removal has
+ * rearranged the symbol table and the section table
+ */
+static void extract_elf64_rela(const void *buffer, int secix, int targetix,
+ const Elf64_Rela *relatab, size_t nrels,
+ const Elf64_Sym *symbols, size_t nsyms,
+ const Elf64_Shdr *sections, size_t nsects, int *canonmap,
+ const char *strings, size_t nstrings,
+ const char *sh_name)
+{
+ struct {
+ uint64_t r_offset;
+ uint64_t r_addend;
+ uint64_t st_value;
+ uint64_t st_size;
+ uint32_t r_type;
+ uint16_t st_shndx;
+ uint8_t st_info;
+ uint8_t st_other;
+
+ } __attribute__((packed)) relocation;
+
+ const Elf64_Sym *symbol;
+ size_t loop;
+
+ /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
+ for (loop = 0; loop < nrels; loop++) {
+ Elf64_Section st_shndx;
+ Elf64_Xword r_info;
+
+ /* decode the relocation */
+ r_info = get64(&relatab[loop].r_info);
+ relocation.r_offset = relatab[loop].r_offset;
+ relocation.r_addend = relatab[loop].r_addend;
+ set32(&relocation.r_type, ELF64_R_TYPE(r_info));
+
+ if (ELF64_R_SYM(r_info) >= nsyms) {
+ fprintf(stderr, "Invalid symbol ID %zx in relocation %zu\n",
+ (size_t)ELF64_R_SYM(r_info), loop);
+ exit(1);
+ }
+
+ /* decode the symbol referenced by the relocation */
+ symbol = &symbols[ELF64_R_SYM(r_info)];
+ relocation.st_info = symbol->st_info;
+ relocation.st_other = symbol->st_other;
+ relocation.st_value = symbol->st_value;
+ relocation.st_size = symbol->st_size;
+ relocation.st_shndx = symbol->st_shndx;
+ st_shndx = get16(&symbol->st_shndx);
+
+ /* canonicalise the section used by the symbol */
+ if (st_shndx > SHN_UNDEF && st_shndx < nsects)
+ set16(&relocation.st_shndx, canonmap[st_shndx]);
+
+ write_out_val(relocation);
+
+ /* undefined symbols must be named if referenced */
+ if (st_shndx == SHN_UNDEF) {
+ const char *name = strings + get32(&symbol->st_name);
+ write_out(name, strlen(name) + 1);
+ }
+ }
+
+ verbose("%02x %4d %s [canon]\n", csum, secix, sh_name);
+}
+
+/*
+ * extract a REL table
+ * - need to canonicalise the entries in case section addition/removal has
+ * rearranged the symbol table and the section table
+ */
+static void extract_elf64_rel(const void *buffer, int secix, int targetix,
+ const Elf64_Rel *relatab, size_t nrels,
+ const Elf64_Sym *symbols, size_t nsyms,
+ const Elf64_Shdr *sections, size_t nsects, int *canonmap,
+ const char *strings, size_t nstrings,
+ const char *sh_name)
+{
+ struct {
+ uint64_t r_offset;
+ uint64_t st_value;
+ uint64_t st_size;
+ uint32_t r_type;
+ uint16_t st_shndx;
+ uint8_t st_info;
+ uint8_t st_other;
+
+ } __attribute__((packed)) relocation;
+
+ const Elf64_Sym *symbol;
+ size_t loop;
+
+ /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
+ for (loop = 0; loop < nrels; loop++) {
+ Elf64_Section st_shndx;
+ Elf64_Xword r_info;
+
+ /* decode the relocation */
+ r_info = get64(&relatab[loop].r_info);
+ relocation.r_offset = relatab[loop].r_offset;
+ set32(&relocation.r_type, ELF64_R_TYPE(r_info));
+
+ if (ELF64_R_SYM(r_info) >= nsyms) {
+ fprintf(stderr, "Invalid symbol ID %zx in relocation %zu\n",
+ (size_t)ELF64_R_SYM(r_info), loop);
+ exit(1);
+ }
+
+ /* decode the symbol referenced by the relocation */
+ symbol = &symbols[ELF64_R_SYM(r_info)];
+ relocation.st_info = symbol->st_info;
+ relocation.st_other = symbol->st_other;
+ relocation.st_value = symbol->st_value;
+ relocation.st_size = symbol->st_size;
+ relocation.st_shndx = symbol->st_shndx;
+ st_shndx = get16(&symbol->st_shndx);
+
+ /* canonicalise the section used by the symbol */
+ if (st_shndx > SHN_UNDEF && st_shndx < nsects)
+ set16(&relocation.st_shndx, canonmap[st_shndx]);
+
+ write_out_val(relocation);
+
+ /* undefined symbols must be named if referenced */
+ if (st_shndx == SHN_UNDEF) {
+ const char *name = strings + get32(&symbol->st_name);
+ write_out(name, strlen(name) + 1);
+ }
+ }
+
+ verbose("%02x %4d %s [canon]\n", csum, secix, sh_name);
+}
+
+/*
+ * extract the data from a 64-bit module
+ */
+static void extract_elf64(void *buffer, size_t len, Elf64_Ehdr *hdr)
+{
+ const Elf64_Sym *symbols;
+ Elf64_Shdr *sections;
+ const char *secstrings, *strings;
+ size_t nsyms, nstrings;
+ int loop, shnum, *canonlist, *canonmap, canon, changed, tmp;
+
+ sections = buffer + get64(&hdr->e_shoff);
+ secstrings = buffer + get64(&sections[get16(&hdr->e_shstrndx)].sh_offset);
+ shnum = get16(&hdr->e_shnum);
+
+ /* find the symbol table and the string table and produce a list of
+ * index numbers of sections that contribute to the kernel's module
+ * image
+ */
+ canonlist = calloc(sizeof(int), shnum * 2);
+ if (!canonlist) {
+ perror("calloc");
+ exit(1);
+ }
+ canonmap = canonlist + shnum;
+ canon = 0;
+
+ symbols = NULL;
+ strings = NULL;
+ nstrings = 0;
+ nsyms = 0;
+
+ for (loop = 1; loop < shnum; loop++) {
+ const char *sh_name = secstrings + get32(&sections[loop].sh_name);
+ Elf64_Word sh_type = get32(&sections[loop].sh_type);
+ Elf64_Xword sh_size = get64(&sections[loop].sh_size);
+ Elf64_Xword sh_flags = get64(&sections[loop].sh_flags);
+ Elf64_Word sh_info = get32(&sections[loop].sh_info);
+ Elf64_Off sh_offset = get64(&sections[loop].sh_offset);
+ void *data = buffer + sh_offset;
+
+ /* quick sanity check */
+ if (sh_type != SHT_NOBITS && len < sh_offset + sh_size) {
+ fprintf(stderr, "Section goes beyond EOF\n");
+ exit(3);
+ }
+
+ /* we only need to canonicalise allocatable sections */
+ if (sh_flags & SHF_ALLOC)
+ canonlist[canon++] = loop;
+ else if ((sh_type == SHT_REL || sh_type == SHT_RELA) &&
+ get64(&sections[sh_info].sh_flags) & SHF_ALLOC)
+ canonlist[canon++] = loop;
+
+ /* keep track of certain special sections */
+ switch (sh_type) {
+ case SHT_SYMTAB:
+ if (strcmp(sh_name, ".symtab") == 0) {
+ symbols = data;
+ nsyms = sh_size / sizeof(Elf64_Sym);
+ }
+ break;
+
+ case SHT_STRTAB:
+ if (strcmp(sh_name, ".strtab") == 0) {
+ strings = data;
+ nstrings = sh_size;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (!symbols) {
+ fprintf(stderr, "Couldn't locate symbol table\n");
+ exit(3);
+ }
+
+ if (!strings) {
+ fprintf(stderr, "Couldn't locate strings table\n");
+ exit(3);
+ }
+
+ /* canonicalise the index numbers of the contributing section */
+ do {
+ changed = 0;
+
+ for (loop = 0; loop < canon - 1; loop++) {
+ const char *x = secstrings + get32(&sections[canonlist[loop + 0]].sh_name);
+ const char *y = secstrings + get32(&sections[canonlist[loop + 1]].sh_name);
+ if (strcmp(x, y) > 0) {
+ tmp = canonlist[loop + 0];
+ canonlist[loop + 0] = canonlist[loop + 1];
+ canonlist[loop + 1] = tmp;
+ changed = 1;
+ }
+ }
+
+ } while (changed);
+
+ for (loop = 0; loop < canon; loop++)
+ canonmap[canonlist[loop]] = loop + 1;
+
+ if (is_verbose > 1) {
+ printf("\nSection canonicalisation map:\n");
+ for (loop = 1; loop < shnum; loop++) {
+ const char *x = secstrings + get32(&sections[loop].sh_name);
+ printf("%4d %s\n", canonmap[loop], x);
+ }
+
+ printf("\nAllocated section list in canonical order:\n");
+ for (loop = 0; loop < canon; loop++) {
+ const char *x = secstrings + get32(&sections[canonlist[loop]].sh_name);
+ printf("%4d %s\n", canonlist[loop], x);
+ }
+ }
+
+ /* iterate through the section table looking for sections we want to
+ * contribute to the signature */
+ verbose("\n");
+ verbose("CAN FILE POS CS SECT NAME\n");
+ verbose("=== ======== == ==== ==============================\n");
+
+ for (loop = 0; loop < canon; loop++) {
+ int sect = canonlist[loop];
+ const char *sh_name = secstrings + get32(&sections[sect].sh_name);
+ Elf64_Word sh_type = get32(&sections[sect].sh_type);
+ Elf64_Xword sh_size = get64(&sections[sect].sh_size);
+ Elf64_Xword sh_flags = get64(&sections[sect].sh_flags);
+ Elf64_Word sh_info = get32(&sections[sect].sh_info);
+ Elf64_Off sh_offset = get64(&sections[sect].sh_offset);
+ void *data = buffer + sh_offset;
+
+ csum = 0;
+
+ /* include canonicalised relocation sections */
+ if (sh_type == SHT_REL || sh_type == SHT_RELA) {
+ Elf32_Word canon_sh_info;
+
+ if (sh_info <= 0 && sh_info >= hdr->e_shnum) {
+ fprintf(stderr,
+ "Invalid ELF - REL/RELA sh_info does"
+ " not refer to a valid section\n");
+ exit(3);
+ }
+
+ verbose("%3u %08lx ", loop, ftell(outfd));
+
+ set32(&canon_sh_info, canonmap[sh_info]);
+
+ /* write out selected portions of the section header */
+ write_out(sh_name, strlen(sh_name));
+ write_out_val(sections[sect].sh_type);
+ write_out_val(sections[sect].sh_flags);
+ write_out_val(sections[sect].sh_size);
+ write_out_val(sections[sect].sh_addralign);
+ write_out_val(canon_sh_info);
+
+ if (sh_type == SHT_RELA)
+ extract_elf64_rela(buffer, sect, sh_info,
+ data, sh_size / sizeof(Elf64_Rela),
+ symbols, nsyms,
+ sections, shnum, canonmap,
+ strings, nstrings,
+ sh_name);
+ else
+ extract_elf64_rel(buffer, sect, sh_info,
+ data, sh_size / sizeof(Elf64_Rel),
+ symbols, nsyms,
+ sections, shnum, canonmap,
+ strings, nstrings,
+ sh_name);
+ continue;
+ }
+
+ /* include the headers of BSS sections */
+ if (sh_type == SHT_NOBITS && sh_flags & SHF_ALLOC) {
+ verbose("%3u %08lx ", loop, ftell(outfd));
+
+ /* write out selected portions of the section header */
+ write_out(sh_name, strlen(sh_name));
+ write_out_val(sections[sect].sh_type);
+ write_out_val(sections[sect].sh_flags);
+ write_out_val(sections[sect].sh_size);
+ write_out_val(sections[sect].sh_addralign);
+
+ verbose("%02x %4d %s\n", csum, sect, sh_name);
+ }
+
+ /* include allocatable loadable sections */
+ if (sh_type != SHT_NOBITS && sh_flags & SHF_ALLOC)
+ goto include_section;
+
+ /* not this section */
+ continue;
+
+ include_section:
+ verbose("%3u %08lx ", loop, ftell(outfd));
+
+ /* write out selected portions of the section header */
+ write_out(sh_name, strlen(sh_name));
+ write_out_val(sections[sect].sh_type);
+ write_out_val(sections[sect].sh_flags);
+ write_out_val(sections[sect].sh_size);
+ write_out_val(sections[sect].sh_addralign);
+
+ /* write out the section data */
+ write_out(data, sh_size);
+
+ verbose("%02x %4d %s\n", csum, sect, sh_name);
+ }
+
+ verbose("%08lx (%lu bytes csum 0x%02x)\n",
+ ftell(outfd), ftell(outfd), xcsum);
+}
+
+/*
+ * extract a RELA table
+ * - need to canonicalise the entries in case section addition/removal has
+ * rearranged the symbol table and the section table
+ */
+static void extract_elf32_rela(const void *buffer, int secix, int targetix,
+ const Elf32_Rela *relatab, size_t nrels,
+ const Elf32_Sym *symbols, size_t nsyms,
+ const Elf32_Shdr *sections, size_t nsects,
+ int *canonmap,
+ const char *strings, size_t nstrings,
+ const char *sh_name)
+{
+ struct {
+ uint32_t r_offset;
+ uint32_t r_addend;
+ uint32_t st_value;
+ uint32_t st_size;
+ uint16_t st_shndx;
+ uint8_t r_type;
+ uint8_t st_info;
+ uint8_t st_other;
+
+ } __attribute__((packed)) relocation;
+
+ const Elf32_Sym *symbol;
+ size_t loop;
+
+ /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
+ for (loop = 0; loop < nrels; loop++) {
+ Elf32_Section st_shndx;
+ Elf32_Word r_info;
+
+ /* decode the relocation */
+ r_info = get32(&relatab[loop].r_info);
+ relocation.r_offset = relatab[loop].r_offset;
+ relocation.r_addend = relatab[loop].r_addend;
+ relocation.r_type = ELF32_R_TYPE(r_info);
+
+ if (ELF32_R_SYM(r_info) >= nsyms) {
+ fprintf(stderr, "Invalid symbol ID %x in relocation %zu\n",
+ ELF32_R_SYM(r_info), loop);
+ exit(1);
+ }
+
+ /* decode the symbol referenced by the relocation */
+ symbol = &symbols[ELF32_R_SYM(r_info)];
+ relocation.st_info = symbol->st_info;
+ relocation.st_other = symbol->st_other;
+ relocation.st_value = symbol->st_value;
+ relocation.st_size = symbol->st_size;
+ relocation.st_shndx = symbol->st_shndx;
+ st_shndx = get16(&symbol->st_shndx);
+
+ /* canonicalise the section used by the symbol */
+ if (st_shndx > SHN_UNDEF && st_shndx < nsects)
+ set16(&relocation.st_shndx, canonmap[st_shndx]);
+
+ write_out_val(relocation);
+
+ /* undefined symbols must be named if referenced */
+ if (st_shndx == SHN_UNDEF) {
+ const char *name = strings + get32(&symbol->st_name);
+ write_out(name, strlen(name) + 1);
+ }
+ }
+
+ verbose("%02x %4d %s [canon]\n", csum, secix, sh_name);
+}
+
+/*
+ * extract a REL table
+ * - need to canonicalise the entries in case section addition/removal has
+ * rearranged the symbol table and the section table
+ */
+static void extract_elf32_rel(const void *buffer, int secix, int targetix,
+ const Elf32_Rel *relatab, size_t nrels,
+ const Elf32_Sym *symbols, size_t nsyms,
+ const Elf32_Shdr *sections, size_t nsects,
+ int *canonmap,
+ const char *strings, size_t nstrings,
+ const char *sh_name)
+{
+ struct {
+ uint32_t r_offset;
+ uint32_t st_value;
+ uint32_t st_size;
+ uint16_t st_shndx;
+ uint8_t r_type;
+ uint8_t st_info;
+ uint8_t st_other;
+
+ } __attribute__((packed)) relocation;
+
+ const Elf32_Sym *symbol;
+ size_t loop;
+
+ /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
+ for (loop = 0; loop < nrels; loop++) {
+ Elf32_Section st_shndx;
+ Elf32_Word r_info;
+
+ /* decode the relocation */
+ r_info = get32(&relatab[loop].r_info);
+ relocation.r_offset = relatab[loop].r_offset;
+ relocation.r_type = ELF32_R_TYPE(r_info);
+
+ if (ELF32_R_SYM(r_info) >= nsyms) {
+ fprintf(stderr, "Invalid symbol ID %x in relocation %zu\n",
+ ELF32_R_SYM(r_info), loop);
+ exit(1);
+ }
+
+ /* decode the symbol referenced by the relocation */
+ symbol = &symbols[ELF32_R_SYM(r_info)];
+ relocation.st_info = symbol->st_info;
+ relocation.st_other = symbol->st_other;
+ relocation.st_value = symbol->st_value;
+ relocation.st_size = symbol->st_size;
+ relocation.st_shndx = symbol->st_shndx;
+ st_shndx = get16(&symbol->st_shndx);
+
+ /* canonicalise the section used by the symbol */
+ if (st_shndx > SHN_UNDEF && st_shndx < nsects)
+ set16(&relocation.st_shndx, canonmap[st_shndx]);
+
+ write_out_val(relocation);
+
+ /* undefined symbols must be named if referenced */
+ if (st_shndx == SHN_UNDEF) {
+ const char *name = strings + get32(&symbol->st_name);
+ write_out(name, strlen(name) + 1);
+ }
+ }
+
+ verbose("%02x %4d %s [canon]\n", csum, secix, sh_name);
+}
+
+/*
+ * extract the data from a 32-bit module
+ */
+static void extract_elf32(void *buffer, size_t len, Elf32_Ehdr *hdr)
+{
+ const Elf32_Sym *symbols;
+ Elf32_Shdr *sections;
+ const char *secstrings, *strings;
+ size_t nsyms, nstrings;
+ int loop, shnum, *canonlist, *canonmap, canon, changed, tmp;
+
+ sections = buffer + get32(&hdr->e_shoff);
+ secstrings = buffer + get32(&sections[get16(&hdr->e_shstrndx)].sh_offset);
+ shnum = get16(&hdr->e_shnum);
+
+ /* find the symbol table and the string table and produce a list of
+ * index numbers of sections that contribute to the kernel's module
+ * image
+ */
+ canonlist = calloc(sizeof(int), shnum * 2);
+ if (!canonlist) {
+ perror("calloc");
+ exit(1);
+ }
+ canonmap = canonlist + shnum;
+ canon = 0;
+
+ symbols = NULL;
+ strings = NULL;
+ nstrings = 0;
+ nsyms = 0;
+
+ for (loop = 1; loop < shnum; loop++) {
+ const char *sh_name = secstrings + get32(&sections[loop].sh_name);
+ Elf32_Word sh_type = get32(&sections[loop].sh_type);
+ Elf32_Xword sh_size = get32(&sections[loop].sh_size);
+ Elf32_Xword sh_flags = get32(&sections[loop].sh_flags);
+ Elf64_Word sh_info = get32(&sections[loop].sh_info);
+ Elf32_Off sh_offset = get32(&sections[loop].sh_offset);
+ void *data = buffer + sh_offset;
+
+ /* quick sanity check */
+ if (sh_type != SHT_NOBITS && len < sh_offset + sh_size) {
+ fprintf(stderr, "Section goes beyond EOF\n");
+ exit(3);
+ }
+
+ /* we only need to canonicalise allocatable sections */
+ if (sh_flags & SHF_ALLOC)
+ canonlist[canon++] = loop;
+ else if ((sh_type == SHT_REL || sh_type == SHT_RELA) &&
+ get32(&sections[sh_info].sh_flags) & SHF_ALLOC)
+ canonlist[canon++] = loop;
+
+ /* keep track of certain special sections */
+ switch (sh_type) {
+ case SHT_SYMTAB:
+ if (strcmp(sh_name, ".symtab") == 0) {
+ symbols = data;
+ nsyms = sh_size / sizeof(Elf32_Sym);
+ }
+ break;
+
+ case SHT_STRTAB:
+ if (strcmp(sh_name, ".strtab") == 0) {
+ strings = data;
+ nstrings = sh_size;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (!symbols) {
+ fprintf(stderr, "Couldn't locate symbol table\n");
+ exit(3);
+ }
+
+ if (!strings) {
+ fprintf(stderr, "Couldn't locate strings table\n");
+ exit(3);
+ }
+
+ /* canonicalise the index numbers of the contributing section */
+ do {
+ changed = 0;
+
+ for (loop = 0; loop < canon - 1; loop++) {
+ const char *x = secstrings + get32(&sections[canonlist[loop + 0]].sh_name);
+ const char *y = secstrings + get32(&sections[canonlist[loop + 1]].sh_name);
+ if (strcmp(x, y) > 0) {
+ tmp = canonlist[loop + 0];
+ canonlist[loop + 0] = canonlist[loop + 1];
+ canonlist[loop + 1] = tmp;
+ changed = 1;
+ }
+ }
+
+ } while (changed);
+
+ for (loop = 0; loop < canon; loop++)
+ canonmap[canonlist[loop]] = loop + 1;
+
+ if (is_verbose > 1) {
+ printf("\nSection canonicalisation map:\n");
+ for (loop = 1; loop < shnum; loop++) {
+ const char *x = secstrings + get32(&sections[loop].sh_name);
+ printf("%4d %s\n", canonmap[loop], x);
+ }
+
+ printf("\nAllocated section list in canonical order:\n");
+ for (loop = 0; loop < canon; loop++) {
+ const char *x = secstrings + get32(&sections[canonlist[loop]].sh_name);
+ printf("%4d %s\n", canonlist[loop], x);
+ }
+ }
+
+ /* iterate through the section table looking for sections we want to
+ * contribute to the signature */
+ verbose("\n");
+ verbose("CAN FILE POS CS SECT NAME\n");
+ verbose("=== ======== == ==== ==============================\n");
+
+ for (loop = 0; loop < canon; loop++) {
+ int sect = canonlist[loop];
+ const char *sh_name = secstrings + get32(&sections[sect].sh_name);
+ Elf32_Word sh_type = get32(&sections[sect].sh_type);
+ Elf32_Xword sh_size = get32(&sections[sect].sh_size);
+ Elf32_Xword sh_flags = get32(&sections[sect].sh_flags);
+ Elf32_Word sh_info = get32(&sections[sect].sh_info);
+ Elf32_Off sh_offset = get32(&sections[sect].sh_offset);
+ void *data = buffer + sh_offset;
+
+ csum = 0;
+
+ /* quick sanity check */
+ if (sh_type != SHT_NOBITS && len < sh_offset + sh_size) {
+ fprintf(stderr, "section goes beyond EOF\n");
+ exit(3);
+ }
+
+ /* include canonicalised relocation sections */
+ if (sh_type == SHT_REL || sh_type == SHT_RELA) {
+ Elf32_Word canon_sh_info;
+
+ if (sh_info <= 0 && sh_info >= hdr->e_shnum) {
+ fprintf(stderr,
+ "Invalid ELF - REL/RELA sh_info does"
+ " not refer to a valid section\n");
+ exit(3);
+ }
+
+ verbose("%3u %08lx ", loop, ftell(outfd));
+
+ set32(&canon_sh_info, canonmap[sh_info]);
+
+ /* write out selected portions of the section header */
+ write_out(sh_name, strlen(sh_name));
+ write_out_val(sections[sect].sh_type);
+ write_out_val(sections[sect].sh_flags);
+ write_out_val(sections[sect].sh_size);
+ write_out_val(sections[sect].sh_addralign);
+ write_out_val(canon_sh_info);
+
+ if (sh_type == SHT_RELA)
+ extract_elf32_rela(buffer, sect, sh_info,
+ data, sh_size / sizeof(Elf32_Rela),
+ symbols, nsyms,
+ sections, shnum, canonmap,
+ strings, nstrings,
+ sh_name);
+ else
+ extract_elf32_rel(buffer, sect, sh_info,
+ data, sh_size / sizeof(Elf32_Rel),
+ symbols, nsyms,
+ sections, shnum, canonmap,
+ strings, nstrings,
+ sh_name);
+ continue;
+ }
+
+ /* include the headers of BSS sections */
+ if (sh_type == SHT_NOBITS && sh_flags & SHF_ALLOC) {
+ verbose("%3u %08lx ", loop, ftell(outfd));
+
+ /* write out selected portions of the section header */
+ write_out(sh_name, strlen(sh_name));
+ write_out_val(sections[sect].sh_type);
+ write_out_val(sections[sect].sh_flags);
+ write_out_val(sections[sect].sh_size);
+ write_out_val(sections[sect].sh_addralign);
+
+ verbose("%02x %4d %s\n", csum, sect, sh_name);
+ }
+
+ /* include allocatable loadable sections */
+ if (sh_type != SHT_NOBITS && sh_flags & SHF_ALLOC)
+ goto include_section;
+
+ /* not this section */
+ continue;
+
+ include_section:
+ verbose("%3u %08lx ", loop, ftell(outfd));
+
+ /* write out selected portions of the section header */
+ write_out(sh_name, strlen(sh_name));
+ write_out_val(sections[sect].sh_type);
+ write_out_val(sections[sect].sh_flags);
+ write_out_val(sections[sect].sh_size);
+ write_out_val(sections[sect].sh_addralign);
+
+ /* write out the section data */
+ write_out(data, sh_size);
+
+ verbose("%02x %4d %s\n", csum, sect, sh_name);
+ }
+
+ verbose("%08lx (%lu bytes csum 0x%02x)\n",
+ ftell(outfd), ftell(outfd), xcsum);
+}
diff --git a/scripts/mod/modsign-note.sh b/scripts/mod/modsign-note.sh
new file mode 100644
index 0000000..bca67c0
--- /dev/null
+++ b/scripts/mod/modsign-note.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+#
+# Generate a module signature note source file
+#
+# mod-sign.sh <sig-file> ><note-src-file>
+#
+
+SIG=$1
+
+cat <<EOF
+#include <linux/modsign.h>
+
+ELFNOTE(MODSIGN_NOTE_NAME, MODSIGN_NOTE_TYPE, .incbin "$SIG")
+EOF
+
+exit 0


2011-11-30 00:04:38

by David Howells

[permalink] [raw]
Subject: [PATCH 08/16] PGP: Add signature parser [ver #2]

Add 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/pgp_parse.c | 274 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 298 insertions(+), 0 deletions(-)


diff --git a/include/linux/pgp.h b/include/linux/pgp.h
index 7e86a06..29b9758 100644
--- a/include/linux/pgp.h
+++ b/include/linux/pgp.h
@@ -227,4 +227,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/pgp_parse.c b/security/keys/pgp_parse.c
index fb8d64a..b7bfeb1 100644
--- a/security/keys/pgp_parse.c
+++ b/security/keys/pgp_parse.c
@@ -252,3 +252,277 @@ int pgp_parse_public_key(const u8 **_data, size_t *_datalen,
return 0;
}
EXPORT_SYMBOL_GPL(pgp_parse_public_key);
+
+/**
+ * pgp_parse_sig_subpkt_header - Parse a PGP V4 signature subpacket header
+ * @_data: Start of the subpacket (updated to subpacket data)
+ * @_datalen: Amount of data remaining in buffer (decreased)
+ * @_type: Where the subpacket type will be returned
+ *
+ * Parse a PGP V4 signature subpacket header [RFC 4880: 5.2.3.1].
+ *
+ * Returns packet data size on success; non-zero on error. If successful,
+ * *_data and *_datalen will have been updated and *_headerlen will be set to
+ * hold the length of the packet header.
+ */
+ssize_t pgp_parse_sig_subpkt_header(const u8 **_data, size_t *_datalen,
+ enum pgp_sig_subpkt_type *_type)
+{
+ enum pgp_sig_subpkt_type type;
+ const u8 *data = *_data;
+ size_t size, datalen = *_datalen;
+
+ pr_devel("-->pgp_parse_sig_subpkt_header(,%zu,,)", datalen);
+
+ if (datalen < 2)
+ goto short_subpacket;
+
+ pr_devel("subpkt hdr %02x, %02x\n", data[0], data[1]);
+
+ switch (data[0]) {
+ case 0x00 ... 0xbf:
+ /* One-byte length */
+ size = data[0];
+ data++;
+ datalen--;
+ break;
+ case 0xc0 ... 0xfe:
+ /* Two-byte length */
+ if (datalen < 3)
+ goto short_subpacket;
+ size = (data[0] - 192) * 256;
+ size += data[1] + 192;
+ data += 2;
+ datalen -= 2;
+ break;
+ case 0xff:
+ if (datalen < 6)
+ goto short_subpacket;
+ size = data[1] << 24;
+ size |= data[2] << 16;
+ size |= data[3] << 8;
+ size |= data[4];
+ data += 5;
+ datalen -= 5;
+ break;
+ }
+
+ /* The type octet is included in the size */
+ if (size == 0) {
+ pr_warning("Signature subpacket size can't be zero\n");
+ return -EBADMSG;
+ }
+
+ type = *data++ & ~PGP_SIG_SUBPKT_TYPE_CRITICAL_MASK;
+ datalen--;
+ size--;
+
+ pr_devel("datalen=%zu size=%zu", datalen, size);
+ if (datalen < size)
+ goto short_subpacket;
+
+ *_data = data;
+ *_datalen = datalen;
+ *_type = type;
+ pr_devel("Found subpkt type=%u size=%zd\n", type, size);
+ return size;
+
+short_subpacket:
+ pr_warning("Attempt to parse short signature subpacket\n");
+ return -EBADMSG;
+}
+
+/**
+ * pgp_parse_sig_subpkts - Parse a set of PGP V4 signatute subpackets
+ * @_data: Data to be parsed (updated)
+ * @_datalen: Amount of data (updated)
+ * @ctx: Parsing context
+ *
+ * Parse a set of PGP signature subpackets [RFC 4880: 5.2.3].
+ */
+int pgp_parse_sig_subpkts(const u8 *data, size_t datalen,
+ struct pgp_parse_sig_context *ctx)
+{
+ enum pgp_sig_subpkt_type type;
+ ssize_t pktlen;
+ int ret;
+
+ pr_devel("-->pgp_parse_sig_subpkts(,%zu,,)", datalen);
+
+ while (datalen > 2) {
+ pktlen = pgp_parse_sig_subpkt_header(&data, &datalen, &type);
+ if (pktlen < 0)
+ return pktlen;
+ if (test_bit(type, ctx->types_of_interest)) {
+ ret = ctx->process_packet(ctx, type, data, pktlen);
+ if (ret < 0)
+ return ret;
+ }
+ data += pktlen;
+ datalen -= pktlen;
+ }
+
+ if (datalen != 0) {
+ pr_warning("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_warning("V4 signature packet has multiple issuers\n");
+ return -EBADMSG;
+ }
+
+ if (datalen != 8) {
+ pr_warning("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_warning("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_warning("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_warning("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_warning("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_warning("V4 signature packet lacks issuer\n");
+ return -EBADMSG;
+ }
+ } else {
+ pr_warning("Signature packet with unhandled version %d\n", version);
+ return -EBADMSG;
+ }
+
+ *_data = data;
+ *_datalen = datalen;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pgp_parse_sig_params);

2011-11-30 00:27:58

by David Howells

[permalink] [raw]
Subject: [PATCH 07/16] KEYS: Add a RSA crypto key subtype [ver #2]

Add a key subtype for handling RSA crypto keys. For the moment it only
provides a signature verification facility.

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

security/Kconfig | 9 +
security/keys/Makefile | 2
security/keys/crypto_rsa.h | 36 +++
security/keys/crypto_rsa_subtype.c | 371 ++++++++++++++++++++++++++++++++++++
4 files changed, 418 insertions(+), 0 deletions(-)
create mode 100644 security/keys/crypto_rsa.h
create mode 100644 security/keys/crypto_rsa_subtype.c


diff --git a/security/Kconfig b/security/Kconfig
index 48926af..4167b4f 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -72,6 +72,15 @@ config CRYPTO_KEY_DSA
This option makes DSA cryptographic keys available. They can be used
for signature verification.

+config CRYPTO_KEY_RSA
+ tristate "RSA key type"
+ depends on CRYPTO_KEY_TYPE
+ select CRYPTO
+ select MPILIB
+ help
+ This option makes RSA cryptographic keys available. They can be used
+ for signature verification.
+
config KEYS_DEBUG_PROC_KEYS
bool "Enable the /proc/keys file by which keys may be viewed"
depends on KEYS
diff --git a/security/keys/Makefile b/security/keys/Makefile
index 8c499b1..0cb7d08 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -17,9 +17,11 @@ obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o
obj-$(CONFIG_CRYPTO_KEY_DSA) += crypto_dsa.o
+obj-$(CONFIG_CRYPTO_KEY_RSA) += crypto_rsa.o
obj-$(CONFIG_KEYS_COMPAT) += compat.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSCTL) += sysctl.o

crypto_keys-y := crypto_type.o pgp_parse.o
crypto_dsa-y := crypto_dsa_subtype.o
+crypto_rsa-y := crypto_rsa_subtype.o
diff --git a/security/keys/crypto_rsa.h b/security/keys/crypto_rsa.h
new file mode 100644
index 0000000..2ec8edc
--- /dev/null
+++ b/security/keys/crypto_rsa.h
@@ -0,0 +1,36 @@
+/* RSA 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.
+ */
+
+#define RSA_NPKEY 2 /* number of MPI's in an RSA public key */
+
+extern struct crypto_key_subtype RSA_crypto_key_subtype;
+extern struct crypto_key_subtype RSA_crypto_key_subtype_2;
+extern struct crypto_key_subtype RSA_crypto_key_subtype_3;
+
+/*
+ * public key record
+ */
+struct RSA_public_key {
+ struct pgp_parse_pubkey pgp;
+ union {
+ MPI pkey[RSA_NPKEY];
+ struct {
+ MPI n; /* RSA public modulus */
+ MPI e; /* RSA public encryption exponent */
+ };
+ };
+};
+
+struct RSA_payload {
+ u8 key_id[8]; /* ID of this key pair */
+ u8 key_id_size; /* Number of bytes in key_id */
+ struct RSA_public_key *public_key;
+};
diff --git a/security/keys/crypto_rsa_subtype.c b/security/keys/crypto_rsa_subtype.c
new file mode 100644
index 0000000..da0d4cf
--- /dev/null
+++ b/security/keys/crypto_rsa_subtype.c
@@ -0,0 +1,371 @@
+/* RSA crypto key subtype
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * This file is derived from GnuPG.
+ * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#define DEBUG
+#define pr_fmt(fmt) "RSA: "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 "crypto_rsa.h"
+#define __KDEBUG
+#include "internal.h"
+
+MODULE_LICENSE("GPL");
+
+static inline void digest_putc(struct shash_desc *digest, uint8_t ch)
+{
+ crypto_shash_update(digest, &ch, 1);
+}
+
+/*
+ * Destroy the contents of a RSA payload
+ */
+static void RSA_destroy_payload(struct RSA_payload *rsa)
+{
+ int i;
+
+ if (rsa->public_key) {
+ for (i = 0; i < RSA_NPKEY; i++)
+ mpi_free(rsa->public_key->pkey[i]);
+ kfree(rsa->public_key);
+ }
+ kfree(rsa);
+}
+
+/*
+ * Calculate the public key ID (RFC4880 12.2)
+ */
+static void RSA_calc_pk_keyid(struct shash_desc *digest,
+ struct RSA_public_key *pk)
+{
+ unsigned n;
+ unsigned nb[RSA_NPKEY];
+ unsigned nn[RSA_NPKEY];
+ u8 *pp[RSA_NPKEY];
+ u32 a32;
+ int i;
+ int npkey = RSA_NPKEY;
+
+ kenter("");
+
+ n = (pk->pgp.version < PGP_KEY_VERSION_4) ? 8 : 6;
+ for (i = 0; i < npkey; i++) {
+ nb[i] = mpi_get_nbits(pk->pkey[i]);
+ pp[i] = mpi_get_buffer(pk->pkey[i], nn + i, NULL);
+ 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, pk->pgp.version);
+
+ a32 = pk->pgp.creation_time;
+ digest_putc(digest, a32 >> 24);
+ digest_putc(digest, a32 >> 16);
+ digest_putc(digest, a32 >> 8);
+ digest_putc(digest, a32 >> 0);
+
+ if (pk->pgp.version < PGP_KEY_VERSION_4) {
+ u16 a16;
+
+ if( pk->pgp.expires_at)
+ a16 = (pk->pgp.expires_at - pk->pgp.creation_time) / 86400UL;
+ else
+ a16 = 0;
+ digest_putc(digest, a16 >> 8);
+ digest_putc(digest, a16 >> 0);
+ }
+
+ digest_putc(digest, pk->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]);
+ kfree(pp[i]);
+ }
+
+ kleave("");
+}
+
+/*
+ * Calculate and check the public key ID fingerprint against the key ID
+ */
+static int RSA_get_fingerprint(struct RSA_payload *rsa,
+ struct RSA_public_key *pk,
+ char **_fingerprint)
+{
+ struct crypto_shash *tfm;
+ struct shash_desc *digest;
+ int digest_size, offset;
+ char *fingerprint;
+ u8 *raw_fingerprint;
+ int ret, i;
+
+ ret = -ENOMEM;
+ tfm = crypto_alloc_shash(pk->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;
+
+ RSA_calc_pk_keyid(digest, pk);
+
+ 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;
+ kdebug("offset %u/%u", offset, digest_size);
+
+ for (i = 0; i < digest_size; i++)
+ sprintf(fingerprint + i * 2, "%02x", raw_fingerprint[i]);
+ kdebug("fingerprint %s", fingerprint);
+
+ memcpy(&rsa->key_id, raw_fingerprint + offset, 8);
+ rsa->key_id_size = 8;
+
+ *_fingerprint = fingerprint;
+ fingerprint = NULL;
+ ret = 0;
+cleanup_raw_fingerprint:
+ kfree(raw_fingerprint);
+cleanup_hash:
+ kfree(digest);
+cleanup_tfm:
+ crypto_free_shash(tfm);
+cleanup:
+ kleave(" = %d", ret);
+ return ret;
+}
+
+struct RSA_pk_parse_context {
+ struct pgp_parse_context pgp;
+ struct RSA_payload *rsa;
+ struct RSA_public_key *pk;
+ char *fingerprint;
+};
+
+/*
+ * Extract a public key or subkey from the PGP stream.
+ */
+static int RSA_parse_public_key(struct pgp_parse_context *context,
+ enum pgp_packet_tag type,
+ u8 headerlen,
+ const u8 *data,
+ size_t datalen)
+{
+ struct RSA_pk_parse_context *ctx =
+ container_of(context, struct RSA_pk_parse_context, pgp);
+ struct RSA_payload *rsa = ctx->rsa;
+ struct RSA_public_key *pk;
+ int i, ret;
+
+ kenter(",%u,%u,,%zu", type, headerlen, datalen);
+
+ if (rsa->public_key) {
+ kleave(" = -ENOKEY [already]");
+ return -ENOKEY;
+ }
+
+ pk = kzalloc(sizeof(struct RSA_public_key), GFP_KERNEL);
+ if (!pk)
+ return -ENOMEM;
+
+ ret = pgp_parse_public_key(&data, &datalen, &pk->pgp);
+ if (pk->pgp.pubkey_algo != PGP_PUBKEY_RSA_ENC_OR_SIG &&
+ pk->pgp.pubkey_algo != PGP_PUBKEY_RSA_ENC_ONLY &&
+ pk->pgp.pubkey_algo != PGP_PUBKEY_RSA_SIG_ONLY) {
+ pr_debug("Ignoring non-RSA public key [%u]\n",
+ pk->pgp.pubkey_algo);
+ ret = -ENOKEY;
+ goto cleanup;
+ }
+
+ ret = -ENOMEM;
+ for (i = 0; i < RSA_NPKEY; i++) {
+ unsigned int remaining = datalen;
+ pk->pkey[i] = mpi_read_from_buffer(data, &remaining);
+ if (!pk->pkey[i])
+ goto cleanup;
+ data += remaining;
+ datalen -= remaining;
+ }
+
+ ret = RSA_get_fingerprint(rsa, pk, &ctx->fingerprint);
+ if (ret < 0)
+ goto cleanup;
+
+ rsa->public_key = pk;
+ kleave(" = 0 [use]");
+ return 0;
+
+cleanup:
+ kdebug("cleanup");
+ if (pk) {
+ for (i = 0; i < RSA_NPKEY; i++)
+ mpi_free(pk->pkey[i]);
+ kfree(pk);
+ }
+ kleave(" = %d", ret);
+ return ret;
+}
+
+/*
+ * Instantiate a RSA key
+ */
+static int RSA_instantiate(struct key *key,
+ const void *data, size_t datalen)
+{
+ struct RSA_pk_parse_context ctx;
+ struct RSA_payload *rsa;
+ int ret;
+
+ ret = key_payload_reserve(key, datalen);
+ if (ret < 0)
+ return ret;
+
+ rsa = kzalloc(sizeof(struct RSA_payload), GFP_KERNEL);
+ if (!rsa)
+ return -ENOMEM;
+
+ ctx.pgp.types_of_interest = 1 << PGP_PKT_PUBLIC_KEY;
+ ctx.pgp.process_packet = RSA_parse_public_key;
+ ctx.rsa = rsa;
+ ctx.pk = NULL;
+ ctx.fingerprint = NULL;
+
+ ret = pgp_parse_packets(data, datalen, &ctx.pgp);
+ if (ret < 0) {
+ RSA_destroy_payload(rsa);
+ kfree(ctx.fingerprint);
+ key_payload_reserve(key, 0);
+ return ret;
+ }
+
+ key->type_data.p[0] = &RSA_crypto_key_subtype;
+ key->type_data.p[1] = ctx.fingerprint;
+ key->payload.data = rsa;
+ return 0;
+}
+
+/*
+ * Destroy a RSA key
+ */
+static void RSA_destroy(struct key *key)
+{
+ if (key->payload.data)
+ RSA_destroy_payload(key->payload.data);
+}
+
+/*
+ * RSA crypto key subtype
+ */
+struct crypto_key_subtype RSA_crypto_key_subtype = {
+ .owner = THIS_MODULE,
+ .name = "rsa",
+ .pubkey_algo = PGP_PUBKEY_RSA_ENC_OR_SIG,
+ .info = CRYPTO_KEY_IS_PUBKEY_ALGO,
+ .instantiate = RSA_instantiate,
+ .destroy = RSA_destroy,
+};
+
+struct crypto_key_subtype RSA_crypto_key_subtype_2 = {
+ .owner = THIS_MODULE,
+ .name = "rsa",
+ .pubkey_algo = PGP_PUBKEY_RSA_ENC_ONLY,
+ .info = CRYPTO_KEY_IS_PUBKEY_ALGO,
+ .instantiate = RSA_instantiate,
+ .destroy = RSA_destroy,
+};
+
+struct crypto_key_subtype RSA_crypto_key_subtype_3 = {
+ .owner = THIS_MODULE,
+ .name = "rsa",
+ .pubkey_algo = PGP_PUBKEY_RSA_SIG_ONLY,
+ .info = CRYPTO_KEY_IS_PUBKEY_ALGO,
+ .instantiate = RSA_instantiate,
+ .destroy = RSA_destroy,
+};
+
+/*
+ * Module stuff
+ */
+static int __init RSA_init(void)
+{
+ int ret;
+
+ ret = register_crypto_key_subtype(&RSA_crypto_key_subtype);
+ if (ret < 0)
+ return ret;
+
+ ret = register_crypto_key_subtype(&RSA_crypto_key_subtype_2);
+ if (ret < 0)
+ goto error_1;
+
+ ret = register_crypto_key_subtype(&RSA_crypto_key_subtype_3);
+ if (ret < 0)
+ goto error_2;
+ return 0;
+
+error_2:
+ unregister_crypto_key_subtype(&RSA_crypto_key_subtype_2);
+error_1:
+ unregister_crypto_key_subtype(&RSA_crypto_key_subtype);
+ return ret;
+}
+
+static void __exit RSA_cleanup(void)
+{
+ unregister_crypto_key_subtype(&RSA_crypto_key_subtype);
+ unregister_crypto_key_subtype(&RSA_crypto_key_subtype_2);
+ unregister_crypto_key_subtype(&RSA_crypto_key_subtype_3);
+}
+
+module_init(RSA_init);
+module_exit(RSA_cleanup);

2011-11-30 00:27:58

by David Howells

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

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 | 170 +++++++++++++++++
include/keys/crypto-subtype.h | 46 +++++
include/keys/crypto-type.h | 25 +++
security/Kconfig | 8 +
security/keys/Makefile | 3
security/keys/crypto_keys.h | 21 ++
security/keys/crypto_type.c | 315 ++++++++++++++++++++++++++++++++
7 files changed, 588 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_keys.h
create mode 100644 security/keys/crypto_type.c


diff --git a/Documentation/security/keys-crypto.txt b/Documentation/security/keys-crypto.txt
new file mode 100644
index 0000000..44c936a
--- /dev/null
+++ b/Documentation/security/keys-crypto.txt
@@ -0,0 +1,170 @@
+ ======================
+ CRYPTOGRAPHIC KEY TYPE
+ ======================
+
+Contents:
+
+ - Overview.
+ - Key identification.
+ - Crypto subtypes.
+ - Accessing crypto keys.
+ - 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 what operations might be performed with it. However, no
+requirement is made that the key data actually be loaded into the key or that
+the operations are done by the kernel.
+
+For instance, cryptographic hardware (such as a TPM) might be used to both
+retain the relevant key and provide 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 or is not 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 key subtype's instantiation
+routine calculates the key fingerprint and stores a copy in the key struct.
+
+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 []
+
+
+===============
+CRYPTO SUBTYPES
+===============
+
+The crypto key is just a simple container. It contains no data of its own and
+does very little except provide a place to hang a function pointer table. The
+key subtype does the actual work.
+
+When a crypto key is instantiated, it looks through its list of registered
+subtypes to try and find one that can handle the data blob it is given. If the
+data blob begins with a byte with the top bit set, it is assumed to be a PGP
+packet format blob [RFC 4880] and is treated so. The blob is parsed to find a
+PGP key, and then a subtype is looked for that says it can handle the
+appropriate algorithm type.
+
+
+=====================
+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 SUBTYPES
+============================
+
+If a suitable subtype is found, then key->type_data.p[0] is set to point to the
+subtype definition and the module usage count is incremented.
+
+The subtype definition structure looks like the following:
+
+ struct crypto_key_subtype {
+ struct module *owner;
+ const char *name;
+ enum pgp_pubkey_algo pubkey_algo : 8;
+ unsigned short info;
+
+ int (*instantiate)(struct key *key,
+ const void *data, size_t datalen);
+
+ void (*revoke)(struct key *key);
+ void (*destroy)(struct key *key);
+ };
+
+The owner and name fields should be set to the owning module and the name of
+the subtype.
+
+If the subtype represents a PGP public key algorithm the info field should have
+CRYPTO_KEY_IS_PUBKEY_ALGO OR'd into it and pubkey_algo should be set to the
+appropriate PGP_PUBKEY_ constant from the enumeration in <linux/pgp.h>.
+
+
+There are a number of operations defined by the subtype. The first few are for
+management of the key itself:
+
+ (1) instantiate().
+
+ Mandatory. When the subtype is selected, the instantiate() method will be
+ given the key being instantiated and the data blob. If the first byte of
+ the data blob has bit 7 set, then it's a PGP packet blob and can be parsed
+ with the routines declared in <linux/pgp.h>.
+
+ If the key has a fingerprint or other auxiliary identifier, this should be
+ determined or calculated and a copy attached to key->type_data.p[1].
+
+ If successful, the subtype must set key->type_data.p[0] to point to its
+ definition.
+
+ The subtype may use key->payload in anyway it sees fit.
+
+ (2) revoke().
+
+ Optional. Notification that the key has been revoked. This provides the
+ subtype the opportunity to discard some memory, but care should be taken
+ as the key may be in use when this is called.
+
+ (3) destroy().
+
+ Mandatory. key->type_data.p[0] is cleared by the caller and the module
+ usage will be decremented upon return. The memory pointed to by
+ key->type_data.[1] will be freed after this method returns. This method
+ must free whatever key->payload refers to.
+
+
+Functions are provided to register and unregister key subtypes:
+
+ int register_crypto_key_subtype(struct crypto_key_subtype *subtype);
+ void unregister_crypto_key_subtype(struct crypto_key_subtype *subtype);
+
+Key subtypes may have the same name, provided they differ in some other
+criterion, such as the public key algorithm ID. This makes it possible to
+handle algorithms such as RSA that have multiple algorithm IDs.
diff --git a/include/keys/crypto-subtype.h b/include/keys/crypto-subtype.h
new file mode 100644
index 0000000..218cb2b
--- /dev/null
+++ b/include/keys/crypto-subtype.h
@@ -0,0 +1,46 @@
+/* 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 <keys/crypto-type.h>
+#include <linux/pgp.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 list_head link;
+ struct module *owner;
+ const char *name;
+ enum pgp_pubkey_algo pubkey_algo : 8;
+ unsigned short info;
+#define CRYPTO_KEY_IS_PUBKEY_ALGO 0x1U
+ unsigned short name_len; /* length of name */
+
+ int (*instantiate)(struct key *key,
+ const void *data, size_t datalen);
+
+ void (*revoke)(struct key *key);
+ void (*destroy)(struct key *key);
+
+};
+
+extern int register_crypto_key_subtype(struct crypto_key_subtype *);
+extern void unregister_crypto_key_subtype(struct crypto_key_subtype *);
+
+#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/Kconfig b/security/Kconfig
index 51bd5a0..ef39878 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -54,6 +54,14 @@ config ENCRYPTED_KEYS

If you are unsure as to whether this is required, answer N.

+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.
+
config KEYS_DEBUG_PROC_KEYS
bool "Enable the /proc/keys file by which keys may be viewed"
depends on KEYS
diff --git a/security/keys/Makefile b/security/keys/Makefile
index a56f1ff..ca85b01 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -15,6 +15,9 @@ obj-y := \

obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
+obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o
obj-$(CONFIG_KEYS_COMPAT) += compat.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSCTL) += sysctl.o
+
+crypto_keys-y := crypto_type.o pgp_parse.o
diff --git a/security/keys/crypto_keys.h b/security/keys/crypto_keys.h
new file mode 100644
index 0000000..12db62a
--- /dev/null
+++ b/security/keys/crypto_keys.h
@@ -0,0 +1,21 @@
+/* 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];
+}
diff --git a/security/keys/crypto_type.c b/security/keys/crypto_type.c
new file mode 100644
index 0000000..d6e9021
--- /dev/null
+++ b/security/keys/crypto_type.c
@@ -0,0 +1,315 @@
+/* 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 <linux/pgp.h>
+#include "crypto_keys.h"
+
+MODULE_LICENSE("GPL");
+
+static LIST_HEAD(crypto_key_subtypes);
+static DECLARE_RWSEM(crypto_key_subtypes_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, ": ");
+ seq_puts(m, subtype->name);
+
+ 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, ']');
+ }
+}
+
+struct crypto_key_parse_context {
+ struct pgp_parse_context pgp;
+ enum pgp_pubkey_algo *pubkey_algo;
+};
+
+/*
+ * Extract a public key or subkey from the PGP stream.
+ */
+static int crypto_key_pgp_parse_public_key(struct pgp_parse_context *context,
+ enum pgp_packet_tag type,
+ u8 headerlen,
+ const u8 *data,
+ size_t datalen)
+{
+ struct crypto_key_parse_context *ctx =
+ container_of(context, struct crypto_key_parse_context, pgp);
+ struct pgp_parse_pubkey pgp;
+ int ret;
+
+ ret = pgp_parse_public_key(&data, &datalen, &pgp);
+ if (ret < 0)
+ return ret;
+ *ctx->pubkey_algo = pgp.pubkey_algo;
+ return -EEXIST; /* Signal we found what we were looking for */
+}
+
+/*
+ * Treat as a PGP blob
+ */
+static int crypto_key_is_pgp(const void *data, size_t datalen,
+ enum pgp_pubkey_algo *algo)
+{
+ struct crypto_key_parse_context ctx;
+ int ret;
+
+ ctx.pgp.types_of_interest =
+ (1 << PGP_PKT_PUBLIC_KEY) | (1 << PGP_PKT_PUBLIC_SUBKEY);
+ ctx.pgp.process_packet = crypto_key_pgp_parse_public_key;
+ ctx.pubkey_algo = algo;
+
+ ret = pgp_parse_packets(data, datalen, &ctx.pgp);
+ if (ret == -EEXIST)
+ return 0;
+ return ret == 0 ? -EBADMSG : ret;
+}
+
+/*
+ * Instantiate a crypto_key defined key
+ */
+static int crypto_key_instantiate(struct key *key,
+ const void *data, size_t datalen)
+{
+ struct crypto_key_subtype *subtype;
+ enum pgp_pubkey_algo pubkey_algo;
+ bool is_pgp;
+ int ret;
+
+ if (datalen == 0)
+ return -EINVAL;
+
+ if (((u8 *)data)[0] & 0x80) {
+ /* PGP tags have the top bit set */
+ ret = crypto_key_is_pgp(data, datalen, &pubkey_algo);
+ if (ret < 0)
+ return ret;
+ is_pgp = true;
+ } else {
+ /* We only support PGP blobs for now, but we could, for
+ * instance, support a subtype that offloads checks to a
+ * system's TPM. */
+ return -EINVAL;
+ }
+
+ down_read(&crypto_key_subtypes_sem);
+
+ list_for_each_entry(subtype, &crypto_key_subtypes, link) {
+ if (is_pgp &&
+ subtype->info & CRYPTO_KEY_IS_PUBKEY_ALGO &&
+ subtype->pubkey_algo == pubkey_algo)
+ goto found;
+ }
+
+ /* Not found; load module? */
+ ret = -ENOPKG;
+ goto error_up;
+
+found:
+ if (!try_module_get(subtype->owner)) {
+ ret = -ENOPKG;
+ goto error_up;
+ }
+
+ up_read(&crypto_key_subtypes_sem);
+
+ ret = subtype->instantiate(key, data, datalen);
+ if (ret != 0)
+ module_put(subtype->owner);
+ return ret;
+
+error_up:
+ up_read(&crypto_key_subtypes_sem);
+ return ret;
+}
+
+/*
+ * dispose of the links from a revoked keyring
+ * - called with the key sem write-locked
+ */
+static void crypto_key_revoke(struct key *key)
+{
+ struct crypto_key_subtype *subtype = crypto_key_subtype(key);
+ if (subtype && subtype->revoke)
+ subtype->revoke(key);
+}
+
+/*
+ * 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);
+ 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,
+ .revoke = crypto_key_revoke,
+ .destroy = crypto_key_destroy,
+ .describe = crypto_key_describe,
+};
+EXPORT_SYMBOL_GPL(key_type_crypto);
+
+/**
+ * register_crypto_key_subtype - Register a crypto key subtype
+ * @subtype: The subtype to register
+ */
+int register_crypto_key_subtype(struct crypto_key_subtype *subtype)
+{
+ struct crypto_key_subtype *cursor;
+ int ret;
+
+ subtype->name_len = strlen(subtype->name);
+
+ down_write(&crypto_key_subtypes_sem);
+
+ list_for_each_entry(cursor, &crypto_key_subtypes, link) {
+ if (strcmp(cursor->name, subtype->name) == 0 &&
+ cursor->pubkey_algo == subtype->pubkey_algo) {
+ pr_err("Crypto key subtype '%s'/%u already registered\n",
+ subtype->name, subtype->pubkey_algo);
+ ret = -EEXIST;
+ goto out;
+ }
+ }
+
+ list_add_tail(&subtype->link, &crypto_key_subtypes);
+
+ pr_notice("Crypto key subtype '%s' registered\n", subtype->name);
+ ret = 0;
+
+out:
+ up_write(&crypto_key_subtypes_sem);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(register_crypto_key_subtype);
+
+/**
+ * unregister_crypto_key_subtype - Unregister a crypto key subtype
+ * @subtype: The subtype to unregister
+ */
+void unregister_crypto_key_subtype(struct crypto_key_subtype *subtype)
+{
+ down_write(&crypto_key_subtypes_sem);
+ list_del(&subtype->link);
+ up_write(&crypto_key_subtypes_sem);
+
+ pr_notice("Crypto key subtype '%s' unregistered\n", subtype->name);
+}
+EXPORT_SYMBOL_GPL(unregister_crypto_key_subtype);
+
+/*
+ * 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);

2011-11-30 00:27:59

by David Howells

[permalink] [raw]
Subject: [PATCH 02/16] MPILIB: Add a missing ENOMEM check [ver #2]

Add a missing ENOMEM check.

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

lib/mpi/mpicoder.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)


diff --git a/lib/mpi/mpicoder.c b/lib/mpi/mpicoder.c
index fe84bb9..6e225a8 100644
--- a/lib/mpi/mpicoder.c
+++ b/lib/mpi/mpicoder.c
@@ -255,6 +255,8 @@ void *mpi_get_buffer(MPI a, unsigned *nbytes, int *sign)
if (!n)
n++; /* avoid zero length allocation */
p = buffer = kmalloc(n, GFP_KERNEL);
+ if (p < 0)
+ return NULL;

for (i = a->nlimbs - 1; i >= 0; i--) {
alimb = a->d[i];

2011-11-29 23:44:13

by David Howells

[permalink] [raw]
Subject: [PATCH 06/16] KEYS: Add a DSA crypto key subtype [ver #2]

Add a key subtype for handling DSA crypto keys. For the moment it only
provides a signature verification facility.

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

security/Kconfig | 10 +
security/keys/Makefile | 2
security/keys/crypto_dsa.h | 36 ++++
security/keys/crypto_dsa_subtype.c | 338 ++++++++++++++++++++++++++++++++++++
4 files changed, 386 insertions(+), 0 deletions(-)
create mode 100644 security/keys/crypto_dsa.h
create mode 100644 security/keys/crypto_dsa_subtype.c


diff --git a/security/Kconfig b/security/Kconfig
index ef39878..48926af 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -62,6 +62,16 @@ config CRYPTO_KEY_TYPE
required for cryptographic operations such as encryption, decryption,
signature generation and signature verification.

+config CRYPTO_KEY_DSA
+ tristate "DSA key type"
+ depends on CRYPTO_KEY_TYPE
+ select CRYPTO
+ select MPILIB
+ select MPILIB_EXTRA
+ help
+ This option makes DSA cryptographic keys available. They can be used
+ for signature verification.
+
config KEYS_DEBUG_PROC_KEYS
bool "Enable the /proc/keys file by which keys may be viewed"
depends on KEYS
diff --git a/security/keys/Makefile b/security/keys/Makefile
index ca85b01..8c499b1 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -16,8 +16,10 @@ obj-y := \
obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o
+obj-$(CONFIG_CRYPTO_KEY_DSA) += crypto_dsa.o
obj-$(CONFIG_KEYS_COMPAT) += compat.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSCTL) += sysctl.o

crypto_keys-y := crypto_type.o pgp_parse.o
+crypto_dsa-y := crypto_dsa_subtype.o
diff --git a/security/keys/crypto_dsa.h b/security/keys/crypto_dsa.h
new file mode 100644
index 0000000..0455634
--- /dev/null
+++ b/security/keys/crypto_dsa.h
@@ -0,0 +1,36 @@
+/* DSA 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.
+ */
+
+#define DSA_NPKEY 4 /* number of MPI's in DSA public key */
+
+extern struct crypto_key_subtype DSA_crypto_key_subtype;
+
+/*
+ * public key record
+ */
+struct DSA_public_key {
+ struct pgp_parse_pubkey pgp;
+ union {
+ MPI pkey[DSA_NPKEY];
+ 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 */
+ };
+ };
+};
+
+struct DSA_payload {
+ u8 key_id[8]; /* ID of this key pair */
+ u8 key_id_size; /* Number of bytes in key_id */
+ struct DSA_public_key *public_key;
+};
diff --git a/security/keys/crypto_dsa_subtype.c b/security/keys/crypto_dsa_subtype.c
new file mode 100644
index 0000000..ae6133b
--- /dev/null
+++ b/security/keys/crypto_dsa_subtype.c
@@ -0,0 +1,338 @@
+/* DSA crypto key subtype
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * This file is derived from GnuPG.
+ * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#define DEBUG
+#define pr_fmt(fmt) "DSA: "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>
+#define __KDEBUG
+#include "internal.h"
+#include "crypto_dsa.h"
+
+MODULE_LICENSE("GPL");
+
+static inline void digest_putc(struct shash_desc *digest, uint8_t ch)
+{
+ crypto_shash_update(digest, &ch, 1);
+}
+
+/*
+ * Destroy the contents of a DSA payload
+ */
+static void DSA_destroy_payload(struct DSA_payload *dsa)
+{
+ int i;
+
+ if (dsa->public_key) {
+ for (i = 0; i < DSA_NPKEY; i++)
+ mpi_free(dsa->public_key->pkey[i]);
+ kfree(dsa->public_key);
+ }
+ kfree(dsa);
+}
+
+/*
+ * Calculate the public key ID (RFC4880 12.2)
+ */
+static void DSA_calc_pk_keyid(struct shash_desc *digest,
+ struct DSA_public_key *pk)
+{
+ unsigned n;
+ unsigned nb[DSA_NPKEY];
+ unsigned nn[DSA_NPKEY];
+ u8 *pp[DSA_NPKEY];
+ u32 a32;
+ int i;
+ int npkey = DSA_NPKEY;
+
+ kenter("");
+
+ n = (pk->pgp.version < PGP_KEY_VERSION_4) ? 8 : 6;
+ for (i = 0; i < npkey; i++) {
+ nb[i] = mpi_get_nbits(pk->pkey[i]);
+ pp[i] = mpi_get_buffer(pk->pkey[i], nn + i, NULL);
+ 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, pk->pgp.version);
+
+ a32 = pk->pgp.creation_time;
+ digest_putc(digest, a32 >> 24);
+ digest_putc(digest, a32 >> 16);
+ digest_putc(digest, a32 >> 8);
+ digest_putc(digest, a32 >> 0);
+
+ if (pk->pgp.version < PGP_KEY_VERSION_4) {
+ u16 a16;
+
+ if( pk->pgp.expires_at)
+ a16 = (pk->pgp.expires_at - pk->pgp.creation_time) / 86400UL;
+ else
+ a16 = 0;
+ digest_putc(digest, a16 >> 8);
+ digest_putc(digest, a16 >> 0);
+ }
+
+ digest_putc(digest, PGP_PUBKEY_DSA);
+
+ 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]);
+ kfree(pp[i]);
+ }
+
+ kleave("");
+}
+
+/*
+ * Calculate and check the public key ID fingerprint against the key ID
+ */
+static int DSA_get_fingerprint(struct DSA_payload *dsa,
+ struct DSA_public_key *pk,
+ char **_fingerprint)
+{
+ struct crypto_shash *tfm;
+ struct shash_desc *digest;
+ int digest_size, offset;
+ char *fingerprint;
+ u8 *raw_fingerprint;
+ int ret, i;
+
+ ret = -ENOMEM;
+ tfm = crypto_alloc_shash(pk->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;
+
+ DSA_calc_pk_keyid(digest, pk);
+
+ 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;
+ kdebug("offset %u/%u", offset, digest_size);
+
+ for (i = 0; i < digest_size; i++)
+ sprintf(fingerprint + i * 2, "%02x", raw_fingerprint[i]);
+ kdebug("fingerprint %s", fingerprint);
+
+ memcpy(&dsa->key_id, raw_fingerprint + offset, 8);
+ dsa->key_id_size = 8;
+
+ *_fingerprint = fingerprint;
+ fingerprint = NULL;
+ ret = 0;
+cleanup_raw_fingerprint:
+ kfree(raw_fingerprint);
+cleanup_hash:
+ kfree(digest);
+cleanup_tfm:
+ crypto_free_shash(tfm);
+cleanup:
+ kleave(" = %d", ret);
+ return ret;
+}
+
+struct DSA_pk_parse_context {
+ struct pgp_parse_context pgp;
+ struct DSA_payload *dsa;
+ struct DSA_public_key *pk;
+ char *fingerprint;
+};
+
+/*
+ * Extract a public key or subkey from the PGP stream.
+ */
+static int DSA_parse_public_key(struct pgp_parse_context *context,
+ enum pgp_packet_tag type,
+ u8 headerlen,
+ const u8 *data,
+ size_t datalen)
+{
+ struct DSA_pk_parse_context *ctx =
+ container_of(context, struct DSA_pk_parse_context, pgp);
+ struct DSA_payload *dsa = ctx->dsa;
+ struct DSA_public_key *pk;
+ int i, ret;
+
+ kenter(",%u,%u,,%zu", type, headerlen, datalen);
+
+ if (dsa->public_key) {
+ kleave(" = -ENOKEY [already]");
+ return -ENOKEY;
+ }
+
+ pk = kzalloc(sizeof(struct DSA_public_key), GFP_KERNEL);
+ if (!pk)
+ return -ENOMEM;
+
+ ret = pgp_parse_public_key(&data, &datalen, &pk->pgp);
+ if (pk->pgp.pubkey_algo != PGP_PUBKEY_DSA) {
+ pr_debug("Ignoring non-DSA public key [%u]\n",
+ pk->pgp.pubkey_algo);
+ ret = -ENOKEY;
+ goto cleanup;
+ }
+
+ ret = -ENOMEM;
+ for (i = 0; i < DSA_NPKEY; i++) {
+ unsigned int remaining = datalen;
+ pk->pkey[i] = mpi_read_from_buffer(data, &remaining);
+ if (!pk->pkey[i])
+ goto cleanup;
+ data += remaining;
+ datalen -= remaining;
+ }
+
+ ret = DSA_get_fingerprint(dsa, pk, &ctx->fingerprint);
+ if (ret < 0)
+ goto cleanup;
+
+ dsa->public_key = pk;
+ kleave(" = 0 [use]");
+ return 0;
+
+cleanup:
+ kdebug("cleanup");
+ if (pk) {
+ for (i = 0; i < DSA_NPKEY; i++)
+ mpi_free(pk->pkey[i]);
+ kfree(pk);
+ }
+ kleave(" = %d", ret);
+ return ret;
+}
+
+/*
+ * Instantiate a DSA key
+ */
+static int DSA_instantiate(struct key *key,
+ const void *data, size_t datalen)
+{
+ struct DSA_pk_parse_context ctx;
+ struct DSA_payload *dsa;
+ int ret;
+
+ kenter("");
+
+ ret = key_payload_reserve(key, datalen);
+ if (ret < 0)
+ return ret;
+
+ dsa = kzalloc(sizeof(struct DSA_payload), GFP_KERNEL);
+ if (!dsa)
+ return -ENOMEM;
+
+ ctx.pgp.types_of_interest =
+ (1 << PGP_PKT_PUBLIC_KEY) | (1 << PGP_PKT_PUBLIC_SUBKEY);
+ ctx.pgp.process_packet = DSA_parse_public_key;
+ ctx.dsa = dsa;
+ ctx.pk = NULL;
+ ctx.fingerprint = NULL;
+
+ ret = pgp_parse_packets(data, datalen, &ctx.pgp);
+ if (ret < 0) {
+ DSA_destroy_payload(dsa);
+ kfree(ctx.fingerprint);
+ key_payload_reserve(key, 0);
+ return ret;
+ }
+
+ key->type_data.p[0] = &DSA_crypto_key_subtype;
+ key->type_data.p[1] = ctx.fingerprint;
+ key->payload.data = dsa;
+ return 0;
+}
+
+/*
+ * Destroy a DSA key
+ */
+static void DSA_destroy(struct key *key)
+{
+ if (key->payload.data)
+ DSA_destroy_payload(key->payload.data);
+}
+
+/*
+ * DSA crypto key subtype
+ */
+struct crypto_key_subtype DSA_crypto_key_subtype = {
+ .owner = THIS_MODULE,
+ .name = "dsa",
+ .pubkey_algo = PGP_PUBKEY_DSA,
+ .info = CRYPTO_KEY_IS_PUBKEY_ALGO,
+ .instantiate = DSA_instantiate,
+ .destroy = DSA_destroy,
+
+ .verify_sig_begin = DSA_verify_sig_begin,
+ .verify_sig_add_data = DSA_verify_sig_add_data,
+ .verify_sig_end = DSA_verify_sig_end,
+ .verify_sig_cancel = DSA_verify_sig_cancel,
+};
+
+/*
+ * Module stuff
+ */
+static int __init DSA_init(void)
+{
+ return register_crypto_key_subtype(&DSA_crypto_key_subtype);
+}
+
+static void __exit DSA_cleanup(void)
+{
+ unregister_crypto_key_subtype(&DSA_crypto_key_subtype);
+}
+
+module_init(DSA_init);
+module_exit(DSA_cleanup);


2011-11-30 00:38:24

by David Howells

[permalink] [raw]
Subject: [PATCH 15/16] MODSIGN: Module ELF verifier [ver #2]

Do preliminary verification of the ELF structure of a module. This is used to
make sure that the ELF structure can then be used to check the module signature
and access the module data without breaking the module loader.

If the module's ELF metadata is determined to be bad, then ELIBBAD will be
returned and a message will be logged to the kernel log.

Signed-Off-By: David Howells <[email protected]>
---

init/Kconfig | 11 +
kernel/Makefile | 2
kernel/module-verify-elf.c | 344 ++++++++++++++++++++++++++++++++++++++++++++
kernel/module-verify.c | 41 +++++
kernel/module-verify.h | 53 +++++++
kernel/module.c | 6 +
6 files changed, 457 insertions(+), 0 deletions(-)
create mode 100644 kernel/module-verify-elf.c
create mode 100644 kernel/module-verify.c
create mode 100644 kernel/module-verify.h


diff --git a/init/Kconfig b/init/Kconfig
index 43298f9..42e685d 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1383,6 +1383,17 @@ 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_VERIFY_ELF
+ bool "Module ELF structure verification"
+ depends on MODULES
+ help
+ Check ELF structure of modules upon load
+
+config MODULE_VERIFY
+ bool
+ depends on MODULES
+ default y if MODULE_VERIFY_ELF
+
endif # MODULES

config INIT_ALL_POSSIBLE
diff --git a/kernel/Makefile b/kernel/Makefile
index e898c5b..3c34fab 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -51,6 +51,8 @@ obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o
obj-$(CONFIG_PROVE_LOCKING) += spinlock.o
obj-$(CONFIG_UID16) += uid16.o
obj-$(CONFIG_MODULES) += module.o
+obj-$(CONFIG_MODULE_VERIFY) += module-verify.o
+obj-$(CONFIG_MODULE_VERIFY_ELF) += module-verify-elf.o
obj-$(CONFIG_KALLSYMS) += kallsyms.o
obj-$(CONFIG_PM) += power/
obj-$(CONFIG_FREEZER) += power/
diff --git a/kernel/module-verify-elf.c b/kernel/module-verify-elf.c
new file mode 100644
index 0000000..4dea8d0
--- /dev/null
+++ b/kernel/module-verify-elf.c
@@ -0,0 +1,344 @@
+/* module-verify-elf.c: module ELF verifier
+ *
+ * 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/elf.h>
+#include <linux/ctype.h>
+#include "module-verify.h"
+
+#if 0
+#define _debug(FMT, ...) printk(FMT, ##__VA_ARGS__)
+#else
+#define _debug(FMT, ...) do {} while (0)
+#endif
+
+/*
+ * verify the ELF structure of a module
+ */
+int module_verify_elf(struct module_verify_data *mvdata)
+{
+ const struct elf_note *note;
+ const Elf_Ehdr *hdr = mvdata->hdr;
+ const Elf_Shdr *section, *section2, *secstop;
+ const Elf_Rela *relas, *rela, *relastop;
+ const Elf_Rel *rels, *rel, *relstop;
+ const Elf_Sym *symbol, *symstop;
+ const void *start, *p, *stop;
+ const char *q, *qs;
+ size_t size, sssize, *secsize, tmp, tmp2;
+ long last;
+ int line;
+
+ size = mvdata->size;
+ mvdata->nsects = hdr->e_shnum;
+
+#define elfcheck(X) \
+do { if (unlikely(!(X))) { line = __LINE__; goto elfcheck_error; } } while(0)
+
+#define seccheck(X) \
+do { if (unlikely(!(X))) { line = __LINE__; goto seccheck_error; } } while(0)
+
+#define symcheck(X) \
+do { if (unlikely(!(X))) { line = __LINE__; goto symcheck_error; } } while(0)
+
+#define relcheck(X) \
+do { if (unlikely(!(X))) { line = __LINE__; goto relcheck_error; } } while(0)
+
+#define relacheck(X) \
+do { if (unlikely(!(X))) { line = __LINE__; goto relacheck_error; } } while(0)
+
+#define notecheck(X) \
+do { if (unlikely(!(X))) { line = __LINE__; goto notecheck_error; } } while(0)
+
+ /* validate the ELF header */
+ elfcheck(hdr->e_ehsize < size);
+ /*elfcheck(hdr->e_entry == 0);*/
+ elfcheck(hdr->e_phoff == 0);
+ elfcheck(hdr->e_phnum == 0);
+
+ elfcheck(hdr->e_shnum < SHN_LORESERVE);
+ elfcheck(hdr->e_shoff < size);
+ elfcheck(hdr->e_shoff >= hdr->e_ehsize);
+ elfcheck((hdr->e_shoff & (sizeof(long) - 1)) == 0);
+ elfcheck(hdr->e_shstrndx > 0);
+ elfcheck(hdr->e_shstrndx < hdr->e_shnum);
+ elfcheck(hdr->e_shentsize == sizeof(Elf_Shdr));
+
+ tmp = (size_t) hdr->e_shentsize * (size_t) hdr->e_shnum;
+ elfcheck(tmp <= size - hdr->e_shoff);
+
+ /* allocate a table to hold in-file section sizes */
+ mvdata->secsizes = kcalloc(hdr->e_shnum, sizeof(size_t), GFP_KERNEL);
+ if (!mvdata->secsizes)
+ return -ENOMEM;
+
+ /* validate the ELF section headers */
+ mvdata->sections = mvdata->buffer + hdr->e_shoff;
+ secstop = mvdata->sections + mvdata->nsects;
+
+ sssize = mvdata->sections[hdr->e_shstrndx].sh_size;
+ elfcheck(sssize > 0);
+
+ section = mvdata->sections;
+ seccheck(section->sh_type == SHT_NULL);
+ seccheck(section->sh_size == 0);
+ seccheck(section->sh_offset == 0);
+
+ secsize = mvdata->secsizes + 1;
+ for (section++; section < secstop; secsize++, section++) {
+ seccheck(section->sh_name < sssize);
+ seccheck(section->sh_link < hdr->e_shnum);
+
+ if (section->sh_entsize > 0)
+ seccheck(section->sh_size % section->sh_entsize == 0);
+
+ seccheck(section->sh_offset >= hdr->e_ehsize);
+ seccheck(section->sh_offset < size);
+
+ /* determine the section's in-file size */
+ tmp = size - section->sh_offset;
+ if (section->sh_offset < hdr->e_shoff)
+ tmp = hdr->e_shoff - section->sh_offset;
+
+ for (section2 = mvdata->sections + 1;
+ section2 < secstop;
+ section2++) {
+ if (section->sh_offset < section2->sh_offset) {
+ tmp2 = section2->sh_offset -
+ section->sh_offset;
+ if (tmp2 < tmp)
+ tmp = tmp2;
+ }
+ }
+ *secsize = tmp;
+
+ _debug("Section %ld: %zx bytes at %lx\n",
+ section - mvdata->sections,
+ *secsize,
+ (unsigned long) section->sh_offset);
+
+ /* perform section type specific checks */
+ switch (section->sh_type) {
+ case SHT_NOBITS:
+ break;
+
+ case SHT_REL:
+ seccheck(section->sh_entsize == sizeof(Elf_Rel));
+ goto more_rel_checks;
+
+ case SHT_RELA:
+ seccheck(section->sh_entsize == sizeof(Elf_Rela));
+ more_rel_checks:
+ seccheck(section->sh_info > 0);
+ seccheck(section->sh_info < hdr->e_shnum);
+ goto more_sec_checks;
+
+ case SHT_SYMTAB:
+ seccheck(section->sh_entsize == sizeof(Elf_Sym));
+ goto more_sec_checks;
+
+ default:
+ more_sec_checks:
+ /* most types of section must be contained entirely
+ * within the file */
+ seccheck(section->sh_size <= *secsize);
+ break;
+ }
+ }
+
+ /* validate the ELF section names */
+ section = &mvdata->sections[hdr->e_shstrndx];
+
+ seccheck(section->sh_offset != hdr->e_shoff);
+
+ mvdata->secstrings = mvdata->buffer + section->sh_offset;
+
+ last = -1;
+ for (section = mvdata->sections + 1; section < secstop; section++) {
+ const char *secname;
+ tmp = sssize - section->sh_name;
+ secname = mvdata->secstrings + section->sh_name;
+ seccheck(secname[0] != 0);
+ if (section->sh_name > last)
+ last = section->sh_name;
+ }
+
+ if (last > -1) {
+ tmp = sssize - last;
+ elfcheck(memchr(mvdata->secstrings + last, 0, tmp) != NULL);
+ }
+
+ /* look for various sections in the module */
+ for (section = mvdata->sections + 1; section < secstop; section++) {
+ switch (section->sh_type) {
+ case SHT_SYMTAB:
+ if (strcmp(mvdata->secstrings + section->sh_name,
+ ".symtab") == 0
+ ) {
+ seccheck(mvdata->symbols == NULL);
+ mvdata->symbols =
+ mvdata->buffer + section->sh_offset;
+ mvdata->nsyms =
+ section->sh_size / sizeof(Elf_Sym);
+ seccheck(section->sh_size > 0);
+ }
+ break;
+
+ case SHT_STRTAB:
+ if (strcmp(mvdata->secstrings + section->sh_name,
+ ".strtab") == 0
+ ) {
+ seccheck(mvdata->strings == NULL);
+ mvdata->strings =
+ mvdata->buffer + section->sh_offset;
+ sssize = mvdata->nstrings = section->sh_size;
+ seccheck(section->sh_size > 0);
+ }
+ break;
+ }
+ }
+
+ if (!mvdata->symbols) {
+ printk("Couldn't locate module symbol table\n");
+ goto format_error;
+ }
+
+ if (!mvdata->strings) {
+ printk("Couldn't locate module strings table\n");
+ goto format_error;
+ }
+
+ /* validate the symbol table */
+ symstop = mvdata->symbols + mvdata->nsyms;
+
+ symbol = mvdata->symbols;
+ symcheck(ELF_ST_TYPE(symbol[0].st_info) == STT_NOTYPE);
+ symcheck(symbol[0].st_shndx == SHN_UNDEF);
+ symcheck(symbol[0].st_value == 0);
+ symcheck(symbol[0].st_size == 0);
+
+ last = -1;
+ for (symbol++; symbol < symstop; symbol++) {
+ symcheck(symbol->st_name < sssize);
+ if (symbol->st_name > last)
+ last = symbol->st_name;
+ symcheck(symbol->st_shndx < mvdata->nsects ||
+ symbol->st_shndx >= SHN_LORESERVE);
+ }
+
+ if (last > -1) {
+ tmp = sssize - last;
+ elfcheck(memchr(mvdata->strings + last, 0, tmp) != NULL);
+ }
+
+ /* validate each relocation table and note list as best we can */
+ for (section = mvdata->sections + 1; section < secstop; section++) {
+ section2 = mvdata->sections + section->sh_info;
+ start = mvdata->buffer + section->sh_offset;
+ stop = start + section->sh_size;
+
+ switch (section->sh_type) {
+ case SHT_REL:
+ rels = start;
+ relstop = stop;
+
+ for (rel = rels; rel < relstop; rel++) {
+ relcheck(rel->r_offset < section2->sh_size);
+ relcheck(ELF_R_SYM(rel->r_info) <
+ mvdata->nsyms);
+ }
+
+ break;
+
+ case SHT_RELA:
+ relas = start;
+ relastop = stop;
+
+ for (rela = relas; rela < relastop; rela++) {
+ relacheck(rela->r_offset < section2->sh_size);
+ relacheck(ELF_R_SYM(rela->r_info) <
+ mvdata->nsyms);
+ }
+
+ break;
+
+ case SHT_NOTE:
+ p = start;
+ while (p < stop) {
+ note = p;
+ notecheck(stop - p >= sizeof(*note));
+ p += sizeof(*note);
+ tmp = note->n_namesz;
+ if (tmp > 0) {
+ notecheck(stop - p >= tmp);
+ qs = p + tmp - 1;
+ notecheck(*qs == '\0');
+ for (q = p; q < qs; q++)
+ notecheck(*q != '\0');
+ tmp = roundup(tmp, 4);
+ notecheck(stop - p >= tmp);
+ p += tmp;
+ }
+ tmp = note->n_descsz;
+ if (tmp > 0) {
+ notecheck(stop - p >= tmp);
+ tmp = roundup(tmp, 4);
+ notecheck(stop - p >= tmp);
+ p += tmp;
+ }
+ }
+ seccheck(p == stop);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ _debug("ELF okay\n");
+ return 0;
+
+elfcheck_error:
+ printk("Verify ELF error (assertion %d)\n", line);
+ goto format_error;
+
+seccheck_error:
+ printk("Verify ELF error [sec %ld] (assertion %d)\n",
+ (long)(section - mvdata->sections), line);
+ goto format_error;
+
+symcheck_error:
+ printk("Verify ELF error [sym %ld] (assertion %d)\n",
+ (long)(symbol - mvdata->symbols), line);
+ goto format_error;
+
+relcheck_error:
+ printk("Verify ELF error [sec %ld rel %ld] (assertion %d)\n",
+ (long)(section - mvdata->sections),
+ (long)(rel - rels), line);
+ goto format_error;
+
+relacheck_error:
+ printk("Verify ELF error [sec %ld rela %ld] (assertion %d)\n",
+ (long)(section - mvdata->sections),
+ (long)(rela - relas), line);
+ goto format_error;
+
+notecheck_error:
+ printk("Verify ELF error [sec %ld note %ld] (assertion %d)\n",
+ (long)(section - mvdata->sections),
+ (long)(p - start), line);
+ goto format_error;
+
+format_error:
+ return -ELIBBAD;
+}
diff --git a/kernel/module-verify.c b/kernel/module-verify.c
new file mode 100644
index 0000000..875279f
--- /dev/null
+++ b/kernel/module-verify.c
@@ -0,0 +1,41 @@
+/* module-verify.c: module verifier
+ *
+ * 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "module-verify.h"
+
+/*
+ * verify a module's integrity
+ * - check the ELF is viable
+ */
+int module_verify(const Elf_Ehdr *hdr, size_t size)
+{
+ struct module_verify_data mvdata;
+ int ret;
+
+ memset(&mvdata, 0, sizeof(mvdata));
+ mvdata.buffer = hdr;
+ mvdata.hdr = hdr;
+ mvdata.size = size;
+
+ ret = module_verify_elf(&mvdata);
+ if (ret < 0) {
+ if (ret == -ELIBBAD)
+ printk("Module failed ELF checks\n");
+ goto error;
+ }
+
+error:
+ kfree(mvdata.secsizes);
+ kfree(mvdata.canonlist);
+ return ret;
+}
diff --git a/kernel/module-verify.h b/kernel/module-verify.h
new file mode 100644
index 0000000..20884fc
--- /dev/null
+++ b/kernel/module-verify.h
@@ -0,0 +1,53 @@
+/* module-verify.h: module verification definitions
+ *
+ * Copyright (C) 2004 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 License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/types.h>
+#include <asm/module.h>
+
+#ifdef CONFIG_MODULE_VERIFY
+struct module_verify_data {
+ const void *buffer; /* module buffer */
+ const Elf_Ehdr *hdr; /* ELF header */
+ const Elf_Shdr *sections; /* ELF section table */
+ const Elf_Sym *symbols; /* ELF symbol table */
+ const char *secstrings; /* ELF section string table */
+ const char *strings; /* ELF string table */
+ size_t *secsizes; /* section size list */
+ size_t size; /* module object size */
+ size_t nsects; /* number of sections */
+ size_t nsyms; /* number of symbols */
+ size_t nstrings; /* size of strings section */
+ size_t signed_size; /* count of bytes contributed to digest */
+ int *canonlist; /* list of canonicalised sections */
+ int *canonmap; /* section canonicalisation map */
+ int ncanon; /* number of canonicalised sections */
+ int sig_index; /* module signature section index */
+ uint8_t xcsum; /* checksum of bytes contributed to digest */
+ uint8_t csum; /* checksum of bytes representing a section */
+};
+
+/*
+ * module-verify.c
+ */
+extern int module_verify(const Elf_Ehdr *hdr, size_t size);
+
+/*
+ * module-verify-elf.c
+ */
+#ifdef CONFIG_MODULE_VERIFY_ELF
+extern int module_verify_elf(struct module_verify_data *mvdata);
+#else
+#define module_verify_elf(m) (0)
+#endif
+
+#else
+#define module_verify(h, s) (0)
+#endif
diff --git a/kernel/module.c b/kernel/module.c
index 178333c..8309389 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>
@@ -2365,6 +2366,11 @@ static int copy_and_check(struct load_info *info,
goto free_hdr;
}

+ /* Verify the module's contents */
+ err = module_verify(hdr, len);
+ if (err < 0)
+ goto free_hdr;
+
info->hdr = hdr;
info->len = len;
return 0;

2011-11-29 23:43:48

by David Howells

[permalink] [raw]
Subject: [PATCH 04/16] PGP: Add definitions (RFC 4880) and packet parser [ver #2]

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

Also add a simple parser that extracts the packets from a PGP 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);

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

include/linux/pgp.h | 230 +++++++++++++++++++++++++++++++++++++++++
security/keys/pgp_parse.c | 254 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 484 insertions(+), 0 deletions(-)
create mode 100644 include/linux/pgp.h
create mode 100644 security/keys/pgp_parse.c


diff --git a/include/linux/pgp.h b/include/linux/pgp.h
new file mode 100644
index 0000000..7e86a06
--- /dev/null
+++ b/include/linux/pgp.h
@@ -0,0 +1,230 @@
+/* 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 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;
+};
+
+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;
+};
+
+/*
+ * 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];
+};
+
+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];
+};
+
+/*
+ * PGP 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/pgp_parse.c b/security/keys/pgp_parse.c
new file mode 100644
index 0000000..fb8d64a
--- /dev/null
+++ b/security/keys/pgp_parse.c
@@ -0,0 +1,254 @@
+/* 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_warning("Packet type does not have MSB set\n");
+ return -EBADMSG;
+ }
+ type &= ~0x80;
+
+ if (type & 0x40) {
+ /* New packet length format */
+ type &= ~0x40;
+ pr_devel("new format: t=%u\n", type);
+ switch (data[0]) {
+ case 0x00 ... 0xbf:
+ /* One-byte length */
+ size = data[0];
+ data++;
+ datalen--;
+ *_headerlen = 2;
+ break;
+ case 0xc0 ... 0xdf:
+ /* Two-byte length */
+ if (datalen < 2)
+ goto short_packet;
+ size = (data[0] - 192) * 256;
+ size += data[1] + 192;
+ data += 2;
+ datalen -= 2;
+ *_headerlen = 3;
+ break;
+ case 0xff:
+ pr_warning("Five-byte packet length not supported\n");
+ return -EBADMSG;
+ default:
+ pr_warning("Error parsing packet length\n");
+ return -EBADMSG;
+ }
+ } else {
+ /* Old packet length format */
+ u8 length_type = type & 0x03;
+ type >>= 2;
+ pr_devel("old format: t=%u lt=%u\n", type, length_type);
+
+ switch (length_type) {
+ case 0:
+ /* One-byte length */
+ size = data[0];
+ data++;
+ datalen--;
+ *_headerlen = 2;
+ break;
+ case 1:
+ /* Two-byte length */
+ if (datalen < 2)
+ goto short_packet;
+ size = data[0] << 8;
+ size |= data[1];
+ data += 2;
+ datalen -= 2;
+ *_headerlen = 3;
+ break;
+ case 2:
+ /* Four-byte length */
+ if (datalen < 4)
+ goto short_packet;
+ size = data[0] << 24;
+ size |= data[1] << 16;
+ size |= data[2] << 8;
+ size |= data[3];
+ data += 4;
+ datalen -= 4;
+ *_headerlen = 5;
+ break;
+ default:
+ pr_warning("Indefinite length packet not supported\n");
+ return -EBADMSG;
+ }
+ }
+
+ pr_devel("datalen=%zu size=%zu", datalen, size);
+ if (datalen < size)
+ goto short_packet;
+
+ *_data = data;
+ *_datalen = datalen;
+ *_type = type;
+ pr_devel("Found packet type=%u size=%zd\n", type, size);
+ return size;
+
+short_packet:
+ pr_warning("Attempt to parse short packet\n");
+ return -EBADMSG;
+}
+
+/**
+ * pgp_parse_packets - Parse a set of PGP packets
+ * @_data: Data to be parsed (updated)
+ * @_datalen: Amount of data (updated)
+ * @ctx: Parsing context
+ *
+ * Parse a set of PGP packets [RFC 4880: 4].
+ */
+int pgp_parse_packets(const u8 *data, size_t datalen,
+ struct pgp_parse_context *ctx)
+{
+ enum pgp_packet_tag type;
+ ssize_t pktlen;
+ u8 headerlen;
+ int ret;
+
+ while (datalen > 2) {
+ pktlen = pgp_parse_packet_header(&data, &datalen, &type,
+ &headerlen);
+ if (pktlen < 0)
+ return pktlen;
+
+ if ((ctx->types_of_interest >> type) & 1) {
+ ret = ctx->process_packet(ctx, type, headerlen,
+ data, pktlen);
+ if (ret < 0)
+ return ret;
+ }
+ data += pktlen;
+ datalen -= pktlen;
+ }
+
+ if (datalen != 0) {
+ pr_warning("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_warning("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_warning("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);

2011-11-30 15:28:19

by Serge Hallyn

[permalink] [raw]
Subject: Re: [PATCH 02/16] MPILIB: Add a missing ENOMEM check [ver #2]

On 11/29/2011 05:43 PM, David Howells wrote:
> Add a missing ENOMEM check.
>
> Signed-off-by: David Howells<[email protected]>
> ---
>
> lib/mpi/mpicoder.c | 2 ++
> 1 files changed, 2 insertions(+), 0 deletions(-)
>
>
> diff --git a/lib/mpi/mpicoder.c b/lib/mpi/mpicoder.c
> index fe84bb9..6e225a8 100644
> --- a/lib/mpi/mpicoder.c
> +++ b/lib/mpi/mpicoder.c
> @@ -255,6 +255,8 @@ void *mpi_get_buffer(MPI a, unsigned *nbytes, int *sign)
> if (!n)
> n++; /* avoid zero length allocation */
> p = buffer = kmalloc(n, GFP_KERNEL);
> + if (p< 0)
> + return NULL;

Sorry, maybe i'm having a confused morning, but shouldn't this check be
for p==NULL rather than p<0?

>
> for (i = a->nlimbs - 1; i>= 0; i--) {
> alimb = a->d[i];
>
> --
> 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

2011-11-30 17:00:13

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 02/16] MPILIB: Add a missing ENOMEM check [ver #2]

Serge Hallyn <[email protected]> wrote:

> > p = buffer = kmalloc(n, GFP_KERNEL);
> > + if (p< 0)
> > + return NULL;
>
> Sorry, maybe i'm having a confused morning, but shouldn't this check be for
> p==NULL rather than p<0?

Bah. You are, of course, right. Updated for the next version.

David

2011-11-30 11:01:46

by Greg KH

[permalink] [raw]
Subject: Re: [RFC][PATCH 00/16] Crypto keys and module signing [ver #2]

On Tue, Nov 29, 2011 at 11:42:58PM +0000, David Howells wrote:
>
> Here are a set of patches that create a framework for using cryptographic keys
> within the kernel. The patches can also be found at:
>
> http://git.kernel.org/?p=linux/kernel/git/dhowells/linux-modsign.git;a=shortlog;h=refs/heads/devel

Very nice stuff, thanks for pushing this, it's much needed.

greg k-h

2011-12-04 16:03:02

by Ben Hutchings

[permalink] [raw]
Subject: Re: [PATCH 04/16] PGP: Add definitions (RFC 4880) and packet parser [ver #2]

On Tue, 2011-11-29 at 23:43 +0000, David Howells wrote:
> Add some useful PGP definitions from RFC 4880. These describe details of
> public key crypto as used by crypto keys for things like signature
> verification.
[...]
> +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;
> +};
> +
> +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;
> +};
[...]
> +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];
> +};
> +
> +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];
> +};
[...]

I'm a little uneasy about these structure definitions. There have been
C ABIs that set the structure alignment to at least 4 by default. It
may be better to play safe by declaring them '__packed'.

Ben.

--
Ben Hutchings
Klipstein's 4th Law of Prototyping and Production:
A fail-safe circuit will destroy others.


Attachments:
signature.asc (828.00 B)
This is a digitally signed message part

2011-12-05 11:21:42

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 04/16] PGP: Add definitions (RFC 4880) and packet parser [ver #2]

Ben Hutchings <[email protected]> wrote:

> I'm a little uneasy about these structure definitions. There have been
> C ABIs that set the structure alignment to at least 4 by default. It
> may be better to play safe by declaring them '__packed'.

Yeah. I'll add that.

David

2011-12-05 11:32:13

by H. Peter Anvin

[permalink] [raw]
Subject: Re: [RFC][PATCH 00/16] Crypto keys and module signing [ver #2]

On 11/29/2011 03:42 PM, David Howells wrote:
>
> I have provided a couple of subtypes: DSA and RSA. Both types have signature
> verification facilities available within the kernel, and both can be used for
> module signature verification with any encryption algorithm known by the PGP
> parser, provided the appropriate algorithm is compiled directly into the
> kernel.
>

Do we really need the complexity of a full OpenPGP parser? Parsers are
notorious security problems. Furthermore, using DSA in anything but a
hard legacy application is not something you want to encourage, so why
support DSA?

-hpa

2011-12-05 11:43:39

by David Howells

[permalink] [raw]
Subject: Re: [RFC][PATCH 00/16] Crypto keys and module signing [ver #2]

H. Peter Anvin <[email protected]> wrote:

> Do we really need the complexity of a full OpenPGP parser? Parsers are
> notorious security problems.

Actually, I don't have a full PGP parser. I only handle the minimum I need.
It can parse the packet stream, public key packets and signature packets.
That's it. I cannot guarantee that GPG is always going to put packets in the
stream in the same order, and dealing with the possible variation should be
simple enough.

Note that it might be possible to share the parser with other things like
eCryptFS.

> Furthermore, using DSA in anything but a hard legacy application is not
> something you want to encourage, so why support DSA?

Because DSA is what Red Hat currently uses in its module signing. So the first
thing to do was to get that working again and then work on getting RSA working.
If I just jumped straight into the RSA parser, I would have a lot more stuff
that might be wrong; doing DSA first at least validated the common stuff.

However, we don't have to include the DSA stuff in the kernel; I can always
discard that patch from the upstream-aimed patchset.

David

2011-12-06 00:54:59

by James Morris

[permalink] [raw]
Subject: Re: [Keyrings] [RFC][PATCH 00/16] Crypto keys and module signing [ver #2]

On Mon, 5 Dec 2011, David Howells wrote:

> However, we don't have to include the DSA stuff in the kernel; I can always
> discard that patch from the upstream-aimed patchset.

I'd encourage dropping DSA, as there appears to be no legacy justification
for its inclusion.


- James
--
James Morris
<[email protected]>

2011-12-07 14:29:32

by David Howells

[permalink] [raw]
Subject: Re: [Keyrings] [RFC][PATCH 00/16] Crypto keys and module signing [ver #2]

James Morris <[email protected]> wrote:

> > However, we don't have to include the DSA stuff in the kernel; I can always
> > discard that patch from the upstream-aimed patchset.
>
> I'd encourage dropping DSA, as there appears to be no legacy justification
> for its inclusion.

Okay, done that.

David