2011-11-28 15:44:36

by David Howells

[permalink] [raw]
Subject: [PATCH 01/14] MPILIB: Export some more symbols

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-28 15:44:48

by David Howells

[permalink] [raw]
Subject: [PATCH 02/14] KEYS: Permit key_serial() to be called with a const key pointer

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-28 15:45:13

by David Howells

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

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

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

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

Documentation/security/keys-crypto.txt | 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 | 322 ++++++++++++++++++++++++++++++++
7 files changed, 595 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..3ab9e5e
--- /dev/null
+++ b/security/keys/crypto_type.c
@@ -0,0 +1,322 @@
+/* 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
+ */
+#define __KDEBUG
+#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"
+#include "internal.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;
+
+ kenter("");
+
+ 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. */
+ kleave(" = -EINVAL [not pgp]");
+ 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);
+ kleave(" = %d", ret);
+ return ret;
+
+error_up:
+ up_read(&crypto_key_subtypes_sem);
+ kleave(" = %d [nosub]", ret);
+ 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-28 15:45:26

by David Howells

[permalink] [raw]
Subject: [PATCH 05/14] KEYS: Add a DSA crypto key subtype

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-28 15:45:39

by David Howells

[permalink] [raw]
Subject: [PATCH 06/14] KEYS: Add a RSA crypto key subtype

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 | 1
security/keys/crypto_rsa.c | 394 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 404 insertions(+), 0 deletions(-)
create mode 100644 security/keys/crypto_rsa.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..293eefb 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -17,6 +17,7 @@ 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
diff --git a/security/keys/crypto_rsa.c b/security/keys/crypto_rsa.c
new file mode 100644
index 0000000..0f9b08d
--- /dev/null
+++ b/security/keys/crypto_rsa.c
@@ -0,0 +1,394 @@
+/* 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>
+#define __KDEBUG
+#include "internal.h"
+
+MODULE_LICENSE("GPL");
+
+#define RSA_NPKEY 2 /* number of MPI's in RSA public key */
+
+static struct crypto_key_subtype RSA_crypto_key_subtype;
+
+/*
+ * 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;
+};
+
+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
+ */
+static 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,
+};
+
+static 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,
+};
+
+static 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-28 15:46:05

by David Howells

[permalink] [raw]
Subject: [PATCH 08/14] KEYS: Add signature verification facility

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

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..236d750c 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 293eefb..f37c750 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -22,5 +22,5 @@ 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
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-28 15:46:30

by David Howells

[permalink] [raw]
Subject: [PATCH 10/14] KEYS: Add a crypto key request function

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 | 94 ++++++++++++++++++++++++++++++++
4 files changed, 110 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 236d750c..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 d6b691b..63def08 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -22,5 +22,5 @@ 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
diff --git a/security/keys/crypto_request.c b/security/keys/crypto_request.c
new file mode 100644
index 0000000..98e4a17
--- /dev/null
+++ b/security/keys/crypto_request.c
@@ -0,0 +1,94 @@
+/* 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
+ */
+
+#define __KDEBUG
+#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"
+#include "internal.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;
+
+ kenter("");
+
+ 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]));
+
+ kdebug("Look up: %s", criterion);
+
+ key = keyring_search(make_key_ref(keyring, 1),
+ &key_type_crypto, criterion);
+ if (IS_ERR(key)) {
+ kleave(" = %ld", PTR_ERR(key));
+ return ERR_CAST(key);
+ }
+
+ return key_ref_to_ptr(key);
+}

2011-11-28 15:45:01

by David Howells

[permalink] [raw]
Subject: [PATCH 03/14] PGP: Add definitions (RFC 4880) and packet parser

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-28 15:46:18

by David Howells

[permalink] [raw]
Subject: [PATCH 09/14] KEYS: DSA key signature verification

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 | 375 +++++++++++++++++++++++++++++++++++++
3 files changed, 387 insertions(+), 1 deletions(-)
create mode 100644 security/keys/crypto_dsa_verify.c


diff --git a/security/keys/Makefile b/security/keys/Makefile
index f37c750..d6b691b 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -23,4 +23,4 @@ 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
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..679c11e
--- /dev/null
+++ b/security/keys/crypto_dsa_verify.c
@@ -0,0 +1,375 @@
+/* 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>
+#define __KDEBUG
+#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) {
+ kleave(" = -ENOKEY [hash]");
+ return ERR_PTR(-ENOKEY);
+ }
+
+ 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 (!tfm)
+ return ERR_PTR(-ENOMEM);
+
+ /* 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--;
+
+ /* 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;
+ }
+
+ 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-28 15:47:09

by David Howells

[permalink] [raw]
Subject: [PATCH 13/14] MODSIGN: Module ELF verifier

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-28 15:47:32

by David Howells

[permalink] [raw]
Subject: [PATCH 14/14] MODSIGN: Apply signature checking to modules on module load

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 | 143 ++++++
Makefile | 1
include/linux/elfnote.h | 4
include/linux/modsign.h | 27 +
include/linux/module.h | 3
init/Kconfig | 22 +
kernel/Makefile | 1
kernel/modsign-pubkey.c | 38 ++
kernel/module-verify-sig.c | 535 ++++++++++++++++++++++
kernel/module-verify.c | 5
kernel/module-verify.h | 19 +
kernel/module.c | 21 +
scripts/Makefile.modpost | 63 +++
scripts/mod/.gitignore | 1
scripts/mod/Makefile | 2
scripts/mod/mod-extract.c | 913 ++++++++++++++++++++++++++++++++++++++
scripts/mod/modsign-note.sh | 16 +
18 files changed, 1817 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..a7424c9
--- /dev/null
+++ b/Documentation/module-signing.txt
@@ -0,0 +1,143 @@
+ ==============================
+ 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.
+
+
+=====================
+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 that no GPG password is used in the above scriptlet.
+
+The kernel.pub file is compiled into the kernel directly by means of an
+".incbin" directive in kernel/modsign-pubkey.c.
+
+
+==============
+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
diff --git a/Makefile b/Makefile
index 3a8f064..b38ca61 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..6b44002 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1389,10 +1389,30 @@ 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 CRYPTO_SHA1
+ select MODULE_VERIFY_ELF
+ help
+ Check modules for valid signatures upon load. For more information
+ see:
+
+ Documentation/module-signing.txt
+
+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..c5da83f 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -53,6 +53,7 @@ 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
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..bdafd3b
--- /dev/null
+++ b/kernel/modsign-pubkey.c
@@ -0,0 +1,38 @@
+/* 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:"
+ );
+
+/*
+ * 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..b8c4cce 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,64 @@ $(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 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-28 15:47:28

by David Howells

[permalink] [raw]
Subject: [PATCH 12/14] MODSIGN: Add indications of module ELF types

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-28 15:48:01

by David Howells

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

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

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

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

The keys are attached to the keyring supplied.

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

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

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

Documentation/security/keys-crypto.txt | 19 ++++++++
include/keys/crypto-type.h | 3 +
security/keys/crypto_request.c | 73 ++++++++++++++++++++++++++++++++
3 files changed, 95 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 98e4a17..1932d18 100644
--- a/security/keys/crypto_request.c
+++ b/security/keys/crypto_request.c
@@ -92,3 +92,76 @@ struct key *request_crypto_key_for_PGP_sig(struct key *keyring,

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;
+
+ kenter("");
+
+ 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)) {
+ kleave(" = %ld", PTR_ERR(key));
+ return PTR_ERR(key);
+ }
+ key_ref_put(key);
+ kleave(" = 0");
+ 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;
+
+ kenter("");
+
+ 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-28 15:45:52

by David Howells

[permalink] [raw]
Subject: [PATCH 07/14] PGP: Add signature parser

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-28 16:27:53

by David Howells

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


Here are a set of patches that create a framework for using cryptographic keys
within the kernel. 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. The DSA type has signature
verification facilities available within the kernel, and is used for module
signature verification.

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.


The patches break down into a number of areas:

(0) Dmitry Kasatkin's MPI library patches - which I have not included here
but that can be obtained from the security GIT tree.

(1) Utility: MPI function exports and make key_serial() handle a const pointer.

(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 verification algorithm.

(6) Module ELF verification and module signature verification.

I have included the documentation for the crypto key type below.

David

---
======================
CRYPTOGRAPHIC KEY TYPE
======================

Contents:

- Overview.
- Key identification.
- Crypto subtypes.
- Accessing crypto keys.
- Signature verification.
- Initial pgp key loading.
- Implementing crypto subtypes.
- Registration.


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


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.


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.


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
============================

Each subtype is specified through a definition structure:

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

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


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