Discussion:
[RFC][PATCH 00/21] Crypto keys and module signing [ver #3]
(too old to reply)
David Howells
2011-12-02 18:42:50 UTC
Permalink
Here are a set of patches that create a framework for using cryptographic keys
within the kernel. The patches can also be found at:

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

[!!Note I've changed the architecture of this quite a lot. See the changelog
below.]

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

I have provided an asymmetric public-key subtype with a couple of public-key
algorithms (DSA and RSA). These provide signature verification facilities only
at this time. The public-key subtype defines a structure for storing key data
that is useful to both algorithms.

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

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

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

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

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

The patches break down into a number of areas:

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

(1) MPI library alterations.

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

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

(4) Public key subtype and DSA and RSA algorithms.

(5) PGP definitions and parsing utilities library.

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

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

(6) Module ELF verification and module signature verification.

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

http://git.kernel.org/?p=linux/kernel/git/dhowells/linux-modsign.git;a=blob;f=Documentation/security/keys-crypto.txt;h=ba2ab554fafc6db194448fd6791ba26874d60e53;hb=9f4ee2281ca85f32be544bbe5b196baa7562a090

and the module signature verification documentation can be found here:

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

---
Changes made 29/11/2011:

(*) Added RSA signature verification.

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

(*) Fixed ENOMEM handling bug in MPI.

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

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

(*) Cleaned out some debugging code.

(*) Updated documentation.

Changes made 02/12/2011:

(*) Completely overhauled the architecture.

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

David
---
David Howells (21):
MODSIGN: Apply signature checking to modules on module load
MODSIGN: Module ELF verifier
MODSIGN: Add indications of module ELF types
KEYS: Provide a function to load keys from a PGP keyring blob
KEYS: PGP format signature parser
KEYS: PGP-based public key signature verification
KEYS: PGP data parser
PGPLIB: Signature parser
PGPLIB: Basic packet parser
PGPLIB: PGP definitions (RFC 4880)
KEYS: RSA signature verification algorithm
KEYS: DSA signature verification algorithm
KEYS: Asymmetric public-key algorithm crypto key subtype
KEYS: Add signature verification facility
KEYS: Create a key type that can be used for general cryptographic operations
KEYS: Reorganise keys Makefile
KEYS: Announce key type (un)registration
KEYS: Move the key config into security/keys/Kconfig
KEYS: Permit key_serial() to be called with a const key pointer
MPILIB: Add a missing ENOMEM check
MPILIB: Export some more symbols


.gitignore | 15 +
Documentation/module-signing.txt | 186 +++++++
Documentation/security/keys-crypto.txt | 302 +++++++++++
Makefile | 1
arch/alpha/include/asm/module.h | 3
arch/arm/include/asm/module.h | 5
arch/cris/include/asm/module.h | 5
arch/h8300/include/asm/module.h | 5
arch/ia64/include/asm/module.h | 5
arch/m32r/include/asm/module.h | 5
arch/m68k/include/asm/module.h | 5
arch/mips/include/asm/module.h | 12
arch/parisc/include/asm/module.h | 8
arch/powerpc/include/asm/module.h | 10
arch/s390/include/asm/module.h | 3
include/asm-generic/module.h | 10
include/keys/crypto-subtype.h | 77 +++
include/keys/crypto-type.h | 37 +
include/linux/elfnote.h | 4
include/linux/key.h | 2
include/linux/modsign.h | 27 +
include/linux/module.h | 3
include/linux/pgp.h | 255 +++++++++
init/Kconfig | 65 ++
kernel/Makefile | 4
kernel/modsign-pubkey.c | 44 ++
kernel/module-verify-elf.c | 344 ++++++++++++
kernel/module-verify-sig.c | 526 ++++++++++++++++++
kernel/module-verify.c | 44 ++
kernel/module-verify.h | 68 ++
kernel/module.c | 25 +
lib/mpi/mpi-cmp.c | 2
lib/mpi/mpi-div.c | 1
lib/mpi/mpi-inv.c | 1
lib/mpi/mpi-mpow.c | 1
lib/mpi/mpi-mul.c | 1
lib/mpi/mpicoder.c | 2
net/dns_resolver/dns_key.c | 5
scripts/Makefile.modpost | 85 +++
scripts/mod/.gitignore | 1
scripts/mod/Makefile | 2
scripts/mod/mod-extract.c | 913 ++++++++++++++++++++++++++++++++
scripts/mod/modsign-note.sh | 16 +
security/Kconfig | 68 --
security/keys/Kconfig | 128 ++++
security/keys/Makefile | 26 +
security/keys/crypto_dsa.c | 126 ++++
security/keys/crypto_keys.h | 28 +
security/keys/crypto_rsa.c | 282 ++++++++++
security/keys/crypto_type.c | 230 ++++++++
security/keys/crypto_verify.c | 111 ++++
security/keys/key.c | 3
security/keys/pgp_key_parser.c | 343 ++++++++++++
security/keys/pgp_library.c | 531 +++++++++++++++++++
security/keys/pgp_parser.h | 35 +
security/keys/pgp_preload.c | 90 +++
security/keys/pgp_pubkey_sig.c | 323 +++++++++++
security/keys/pgp_sig_parser.c | 104 ++++
security/keys/public_key.c | 55 ++
security/keys/public_key.h | 109 ++++
60 files changed, 5642 insertions(+), 85 deletions(-)
create mode 100644 Documentation/module-signing.txt
create mode 100644 Documentation/security/keys-crypto.txt
create mode 100644 include/keys/crypto-subtype.h
create mode 100644 include/keys/crypto-type.h
create mode 100644 include/linux/modsign.h
create mode 100644 include/linux/pgp.h
create mode 100644 kernel/modsign-pubkey.c
create mode 100644 kernel/module-verify-elf.c
create mode 100644 kernel/module-verify-sig.c
create mode 100644 kernel/module-verify.c
create mode 100644 kernel/module-verify.h
create mode 100644 scripts/mod/mod-extract.c
create mode 100644 scripts/mod/modsign-note.sh
create mode 100644 security/keys/Kconfig
create mode 100644 security/keys/crypto_dsa.c
create mode 100644 security/keys/crypto_keys.h
create mode 100644 security/keys/crypto_rsa.c
create mode 100644 security/keys/crypto_type.c
create mode 100644 security/keys/crypto_verify.c
create mode 100644 security/keys/pgp_key_parser.c
create mode 100644 security/keys/pgp_library.c
create mode 100644 security/keys/pgp_parser.h
create mode 100644 security/keys/pgp_preload.c
create mode 100644 security/keys/pgp_pubkey_sig.c
create mode 100644 security/keys/pgp_sig_parser.c
create mode 100644 security/keys/public_key.c
create mode 100644 security/keys/public_key.h

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-02 18:42:56 UTC
Permalink
Export some more symbols for use by the DSA key subtype.

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

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


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

int mpi_cmp(MPI u, MPI v)
{
@@ -66,3 +67,4 @@ int mpi_cmp(MPI u, MPI v)
return 1;
return -1;
}
+EXPORT_SYMBOL_GPL(mpi_cmp);
diff --git a/lib/mpi/mpi-div.c b/lib/mpi/mpi-div.c
index 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);

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-02 18:43:11 UTC
Permalink
Add a missing ENOMEM check.

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

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


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

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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-02 18:43:23 UTC
Permalink
Permit key_serial() to be called with a const key pointer.

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

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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-02 18:43:35 UTC
Permalink
Move the key config into security/keys/Kconfig as there are going to be a lot
of key-related options.

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

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


diff --git a/security/Kconfig b/security/Kconfig
index 51bd5a0..1c5a7a4 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -4,73 +4,7 @@

menu "Security options"

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

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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-02 18:43:46 UTC
Permalink
Announce the (un)registration of a key type in the core key code rather than
in the callers.

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

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


diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c
index fa000d2..689d7c8 100644
--- a/net/dns_resolver/dns_key.c
+++ b/net/dns_resolver/dns_key.c
@@ -249,9 +249,6 @@ static int __init init_dns_resolver(void)
struct key *keyring;
int ret;

- printk(KERN_NOTICE "Registering the %s key type\n",
- key_type_dns_resolver.name);
-
/* create an override credential set with a special thread keyring in
* which DNS requests are cached
*
@@ -300,8 +297,6 @@ static void __exit exit_dns_resolver(void)
key_revoke(dns_resolver_cache->thread_keyring);
unregister_key_type(&key_type_dns_resolver);
put_cred(dns_resolver_cache);
- printk(KERN_NOTICE "Unregistered %s key type\n",
- key_type_dns_resolver.name);
}

module_init(init_dns_resolver)
diff --git a/security/keys/key.c b/security/keys/key.c
index 4414abd..a8086a1 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -957,6 +957,8 @@ int register_key_type(struct key_type *ktype)

/* store the type */
list_add(&ktype->link, &key_types_list);
+
+ pr_notice("Key type %s registered\n", ktype->name);
ret = 0;

out:
@@ -979,6 +981,7 @@ void unregister_key_type(struct key_type *ktype)
list_del_init(&ktype->link);
downgrade_write(&key_types_sem);
key_gc_keytype(ktype);
+ pr_notice("Key type %s unregistered\n", ktype->name);
up_read(&key_types_sem);
}
EXPORT_SYMBOL(unregister_key_type);

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-02 18:43:58 UTC
Permalink
Reorganise the keys directory Makefile to put all the core bits together and
the type-specific bits after.

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

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


diff --git a/security/keys/Makefile b/security/keys/Makefile
index a56f1ff..504aaa0 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -2,6 +2,9 @@
# Makefile for key management
#

+#
+# Core
+#
obj-y := \
gc.o \
key.o \
@@ -12,9 +15,12 @@ obj-y := \
request_key.o \
request_key_auth.o \
user_defined.o
-
-obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
-obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
obj-$(CONFIG_KEYS_COMPAT) += compat.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSCTL) += sysctl.o
+
+#
+# Key types
+#
+obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
+obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-02 18:44:14 UTC
Permalink
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 <***@redhat.com>
---

Documentation/security/keys-crypto.txt | 181 +++++++++++++++++++++++++
include/keys/crypto-subtype.h | 56 ++++++++
include/keys/crypto-type.h | 25 +++
security/keys/Kconfig | 8 +
security/keys/Makefile | 3
security/keys/crypto_keys.h | 28 ++++
security/keys/crypto_type.c | 230 ++++++++++++++++++++++++++++++++
7 files changed, 531 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..97dee80
--- /dev/null
+++ b/Documentation/security/keys-crypto.txt
@@ -0,0 +1,181 @@
+ ======================
+ CRYPTOGRAPHIC KEY TYPE
+ ======================
+
+Contents:
+
+ - Overview.
+ - Key identification.
+ - Accessing crypto keys.
+ - Implementing crypto parsers.
+ - Implementing crypto subtypes.
+
+
+========
+OVERVIEW
+========
+
+The "crypto" key type is designed to be a container for cryptographic keys,
+without imposing any particular restrictions on the form of the cryptography or
+the key.
+
+The crypto key is given a subtype that defines what sort of data is associated
+with the key and provides operations to describe and destroy it. However, no
+requirement is made that the key data actually be loaded into the key.
+
+The crypto key also has a number of data parsers registered with it. The data
+parsers are responsible for extracing information the blobs of data passed to
+the instantiator function. The first data parser that recognises the blob gets
+to set the subtype of the key and define the operations that can be done on
+that key.
+
+Completely in-kernel key retention and operation subtypes and parsers can be
+defined, but it would also be possible to provide access to cryptographic
+hardware (such as a TPM) that might be used to both retain the relevant key and
+perform operations using that key. In such a case, the crypto key would then
+merely be an interface to the TPM driver.
+
+
+==================
+KEY IDENTIFICATION
+==================
+
+Because the identity of a key is not necessarily known and may not be easily
+calculated when a crypto key is allocated, it may not be a simple matter to set
+a key description to something that's useful for determining whether this is
+the key you're looking for. Furthermore, it may be necessary to perform a
+partial match upon the key identity.
+
+To help with this, when a key is loaded, the parser calculates the key
+fingerprint and stores a copy in the key structure.
+
+The crypto key type's key matching function then performs more checks than just
+the straightforward comparison of the description with the criterion string:
+
+ (1) If the criterion string is of the form "id:<hexdigits>" then the match
+ function will examine a key's fingerprint to see if the hex digits given
+ after the "id:" match the tail. For instance:
+
+ keyctl search @s crypto id:5acc2142
+
+ will match a key with fingerprint:
+
+ 1A00 2040 7601 7889 DE11 882C 3823 04AD 5ACC 2142
+
+ (2) If the criterion string is of the form "<subtype>:<hexdigits>" then the
+ match will match the ID as in (1), but with the added restriction that
+ only keys of the specified subtype (e.g. dsa or rsa) will be matched. For
+ instance:
+
+ keyctl search @s crypto dsa:5acc2142
+
+Looking in /proc/keys, the last 8 hex digits of the key fingerprint are
+displayed, along with the subtype:
+
+ 1a39e171 I----- 1 perm 3f010000 0 0 crypto modsign.0: DSA 5acc2142 []
+
+
+=====================
+ACCESSING CRYPTO KEYS
+=====================
+
+To access crypto keys from within the kernel, the following inclusion is
+required:
+
+ #include <keys/crypto-type.h>
+
+This gives access to the key type:
+
+ struct key_type key_type_crypto;
+
+
+===========================
+IMPLEMENTING CRYPTO PARSERS
+===========================
+
+The crypto key type keeps a list of registered data parsers. An example of
+such a parser is one that parses OpenPGP packet formatted data [RFC 4880].
+
+During key instantiation each parser in the list is tried until one doesn't
+return -EBADMSG.
+
+The parser definition structure looks like the following:
+
+ struct crypto_key_parser {
+ struct module *owner;
+ const char *name;
+
+ int (*instantiate)(struct key *key,
+ const void *data, size_t datalen);
+ };
+
+The owner and name fields should be set to the owning module and the name of
+the parser.
+
+There are a number of operations defined by the parser. They are all optional,
+but it is expected that at least one will be defined.
+
+ (1) instantiate().
+
+ The arguments are the same as for the instantiate function in the key
+ type. 'key' is the crypto key being instantiated; data and datalen are
+ the instantiation data, presumably containing cryptographic key data, and
+ the length of that data.
+
+ If the data format is not recognised, -EBADMSG should be returned. If it
+ is recognised, but the key cannot for some reason be set up, some other
+ negative error code should be returned.
+
+ If the key can be successfully set up, then key->payload should be set to
+ point to the retained data, key->type_data.p[0] should be set to point to
+ the subtype chosen and key->type_data.p[1] should be set to point to a
+ copy of the key's identity string and 0 should be returned.
+
+ The key's identity string may be partially matched upon. For a public-key
+ algorithm such as RSA and DSA this will likely be a printable hex version
+ of the key's fingerprint.
+
+Functions are provided to register and unregister parsers:
+
+ int register_crypto_key_parser(struct crypto_key_parser *parser);
+ void unregister_crypto_key_parser(struct crypto_key_parser *subtype);
+
+Parsers may not have the same name. The names are only used for displaying in
+debugging messages.
+
+
+============================
+IMPLEMENTING CRYPTO SUBTYPES
+============================
+
+The parser selects the appropriate subtype directly and sets it on the key; the
+crypto key then retains a reference on the subtype module (which means the
+parser can be removed thereafter).
+
+The subtype definition structure looks like the following:
+
+ struct crypto_key_subtype {
+ struct module *owner;
+ const char *name;
+
+ void (*describe)(const struct key *key, struct seq_file *m);
+ void (*destroy)(void *payload);
+ };
+
+The owner and name fields should be set to the owning module and the name of
+the subtype.
+
+There are a number of operations defined by the subtype:
+
+ (1) describe().
+
+ Mandatory. This allows the subtype to display something in /proc/keys
+ against the key. For instance the name of the public key algorithm type
+ could be displayed. The key type will display the tail of the key
+ identity string after this.
+
+ (2) destroy().
+
+ Mandatory. This should free the memory associated with the key. The
+ crypto key will look after freeing the fingerprint and releasing the
+ reference on the subtype module.
diff --git a/include/keys/crypto-subtype.h b/include/keys/crypto-subtype.h
new file mode 100644
index 0000000..fa87555
--- /dev/null
+++ b/include/keys/crypto-subtype.h
@@ -0,0 +1,56 @@
+/* Cryptographic key subtype
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (***@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ *
+ * See Documentation/security/keys-crypto.txt
+ */
+
+#ifndef _KEYS_CRYPTO_SUBTYPE_H
+#define _KEYS_CRYPTO_SUBTYPE_H
+
+#include <linux/seq_file.h>
+#include <keys/crypto-type.h>
+
+extern struct key_type key_type_crypto;
+
+/*
+ * Keys of this type declare a subtype that indicates the handlers and
+ * capabilities.
+ */
+struct crypto_key_subtype {
+ struct module *owner;
+ const char *name;
+ unsigned short name_len; /* length of name */
+
+ void (*describe)(const struct key *key, struct seq_file *m);
+
+ void (*destroy)(void *payload);
+};
+
+/*
+ * Data parser. Called during instantiation and signature verification
+ * initiation.
+ */
+struct crypto_key_parser {
+ struct list_head link;
+ struct module *owner;
+ const char *name;
+
+ /* Attempt to instantiate a key from the data blob passed to add_key()
+ * or keyctl_instantiate().
+ *
+ * Return EBADMSG if not recognised.
+ */
+ int (*instantiate)(struct key *key, const void *data, size_t datalen);
+};
+
+extern int register_crypto_key_parser(struct crypto_key_parser *);
+extern void unregister_crypto_key_parser(struct crypto_key_parser *);
+
+#endif /* _KEYS_CRYPTO_SUBTYPE_H */
diff --git a/include/keys/crypto-type.h b/include/keys/crypto-type.h
new file mode 100644
index 0000000..47c00c7
--- /dev/null
+++ b/include/keys/crypto-type.h
@@ -0,0 +1,25 @@
+/* Cryptographic key type interface
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (***@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ *
+ * See Documentation/security/keys-crypto.txt
+ */
+
+#ifndef _KEYS_CRYPTO_TYPE_H
+#define _KEYS_CRYPTO_TYPE_H
+
+#include <linux/key-type.h>
+
+extern struct key_type key_type_crypto;
+
+/*
+ * The payload is at the discretion of the subtype.
+ */
+
+#endif /* _KEYS_CRYPTO_TYPE_H */
diff --git a/security/keys/Kconfig b/security/keys/Kconfig
index a90d6d3..290c9d3 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -69,3 +69,11 @@ config KEYS_DEBUG_PROC_KEYS
the resulting table.

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.
diff --git a/security/keys/Makefile b/security/keys/Makefile
index 504aaa0..67fceaa 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -24,3 +24,6 @@ obj-$(CONFIG_SYSCTL) += sysctl.o
#
obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
+obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o
+
+crypto_keys-y := crypto_type.o
diff --git a/security/keys/crypto_keys.h b/security/keys/crypto_keys.h
new file mode 100644
index 0000000..a339ce0
--- /dev/null
+++ b/security/keys/crypto_keys.h
@@ -0,0 +1,28 @@
+/* Internal crypto type stuff
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (***@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+static inline
+struct crypto_key_subtype *crypto_key_subtype(const struct key *key)
+{
+ return key->type_data.p[0];
+}
+
+static inline char *crypto_key_id(const struct key *key)
+{
+ return key->type_data.p[1];
+}
+
+
+/*
+ * crypto_type.c
+ */
+extern struct list_head crypto_key_parsers;
+extern struct rw_semaphore crypto_key_parsers_sem;
diff --git a/security/keys/crypto_type.c b/security/keys/crypto_type.c
new file mode 100644
index 0000000..a06413a
--- /dev/null
+++ b/security/keys/crypto_type.c
@@ -0,0 +1,230 @@
+/* Cryptographic key type
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (***@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ *
+ * See Documentation/security/keys-crypto.txt
+ */
+#include <keys/crypto-subtype.h>
+#include <linux/seq_file.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "crypto_keys.h"
+
+MODULE_LICENSE("GPL");
+
+LIST_HEAD(crypto_key_parsers);
+DECLARE_RWSEM(crypto_key_parsers_sem);
+
+/*
+ * Match crypto_keys on (part of) their name
+ * We have some shorthand methods for matching keys. We allow:
+ *
+ * "<desc>" - request a key by description
+ * "id:<id>" - request a key matching the ID
+ * "<subtype>:<id>" - request a key of a subtype
+ */
+static int crypto_key_match(const struct key *key, const void *description)
+{
+ const struct crypto_key_subtype *subtype = crypto_key_subtype(key);
+ const char *spec = description;
+ const char *id, *kid;
+ ptrdiff_t speclen;
+ size_t idlen, kidlen;
+
+ if (!subtype || !spec || !*spec)
+ return 0;
+
+ /* See if the full key description matches as is */
+ if (key->description && strcmp(key->description, description) == 0)
+ return 1;
+
+ /* All tests from here on break the criterion description into a
+ * specifier, a colon and then an identifier.
+ */
+ id = strchr(spec, ':');
+ if (!id)
+ return 0;
+
+ speclen = id - spec;
+ id++;
+
+ /* Anything after here requires a partial match on the ID string */
+ kid = crypto_key_id(key);
+ if (!kid)
+ return 0;
+
+ idlen = strlen(id);
+ kidlen = strlen(kid);
+ if (idlen > kidlen)
+ return 0;
+
+ kid += kidlen - idlen;
+ if (strcasecmp(id, kid) != 0)
+ return 0;
+
+ if (speclen == 2 &&
+ memcmp(spec, "id", 2) == 0)
+ return 1;
+
+ if (speclen == subtype->name_len &&
+ memcmp(spec, subtype->name, speclen) == 0)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Describe the crypto key
+ */
+static void crypto_key_describe(const struct key *key, struct seq_file *m)
+{
+ const struct crypto_key_subtype *subtype = crypto_key_subtype(key);
+ const char *kid = crypto_key_id(key);
+ size_t n;
+
+ seq_puts(m, key->description);
+
+ if (subtype) {
+ seq_puts(m, ": ");
+ subtype->describe(key, m);
+
+ if (kid) {
+ seq_putc(m, ' ');
+ n = strlen(kid);
+ if (n <= 8)
+ seq_puts(m, kid);
+ else
+ seq_puts(m, kid + n - 8);
+ }
+
+ seq_puts(m, " [");
+ /* put something here to indicate the key's capabilities */
+ seq_putc(m, ']');
+ }
+}
+
+/*
+ * Instantiate a crypto_key defined key
+ */
+static int crypto_key_instantiate(struct key *key,
+ const void *data, size_t datalen)
+{
+ struct crypto_key_parser *parser;
+ int ret;
+
+ pr_devel("==>%s()\n", __func__);
+
+ if (datalen == 0)
+ return -EINVAL;
+
+ down_read(&crypto_key_parsers_sem);
+
+ ret = -EBADMSG;
+ list_for_each_entry(parser, &crypto_key_parsers, link) {
+ pr_debug("Trying parser '%s'\n", parser->name);
+
+ ret = parser->instantiate(key, data, datalen);
+ if (ret != -EBADMSG) {
+ pr_debug("Parser recognised the format (ret %d)\n",
+ ret);
+ if (ret == 0)
+ module_get(crypto_key_subtype(key));
+ break;
+ }
+ }
+
+ up_read(&crypto_key_parsers_sem);
+ pr_devel("<==%s() = %d\n", __func__, ret);
+ return ret;
+}
+
+/*
+ * dispose of the data dangling from the corpse of a crypto key
+ */
+static void crypto_key_destroy(struct key *key)
+{
+ struct crypto_key_subtype *subtype = crypto_key_subtype(key);
+ if (subtype) {
+ subtype->destroy(key->payload.data);
+ module_put(subtype->owner);
+ key->type_data.p[0] = NULL;
+ }
+ kfree(key->type_data.p[1]);
+ key->type_data.p[1] = NULL;
+}
+
+struct key_type key_type_crypto = {
+ .name = "crypto",
+ .instantiate = crypto_key_instantiate,
+ .match = crypto_key_match,
+ .destroy = crypto_key_destroy,
+ .describe = crypto_key_describe,
+};
+EXPORT_SYMBOL_GPL(key_type_crypto);
+
+/**
+ * register_crypto_key_parser - Register a crypto key blob parser
+ * @parser: The parser to register
+ */
+int register_crypto_key_parser(struct crypto_key_parser *parser)
+{
+ struct crypto_key_parser *cursor;
+ int ret;
+
+ down_write(&crypto_key_parsers_sem);
+
+ list_for_each_entry(cursor, &crypto_key_parsers, link) {
+ if (strcmp(cursor->name, parser->name) == 0) {
+ pr_err("Crypto key parser '%s' already registered\n",
+ parser->name);
+ ret = -EEXIST;
+ goto out;
+ }
+ }
+
+ list_add_tail(&parser->link, &crypto_key_parsers);
+
+ pr_notice("Crypto key parser '%s' registered\n", parser->name);
+ ret = 0;
+
+out:
+ up_write(&crypto_key_parsers_sem);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(register_crypto_key_parser);
+
+/**
+ * unregister_crypto_key_parser - Unregister a crypto key blob parser
+ * @parser: The parser to unregister
+ */
+void unregister_crypto_key_parser(struct crypto_key_parser *parser)
+{
+ down_write(&crypto_key_parsers_sem);
+ list_del(&parser->link);
+ up_write(&crypto_key_parsers_sem);
+
+ pr_notice("Crypto key parser '%s' unregistered\n", parser->name);
+}
+EXPORT_SYMBOL_GPL(unregister_crypto_key_parser);
+
+/*
+ * Module stuff
+ */
+static int __init crypto_key_init(void)
+{
+ return register_key_type(&key_type_crypto);
+}
+
+static void __exit crypto_key_cleanup(void)
+{
+ unregister_key_type(&key_type_crypto);
+}
+
+module_init(crypto_key_init);
+module_exit(crypto_key_cleanup);

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Mimi Zohar
2012-01-16 12:56:21 UTC
Permalink
Post by David Howells
Create a key type that can be used for general cryptographic operations, such
as encryption, decryption, signature generation and signature verification.
The key type is "crypto" and can provide access to a variety of cryptographic
algorithms.
Nice! Basically the 'crypto' key type ties crypto/ with security/keys.
Other than the posted pgp key parser used for verifying kernel module
signatures, I assume another use case could be to expose kernel
cryptography to userspace. As there was a submission
https://lkml.org/lkml/2010/8/20/103 to do just this, there must be
userspace apps that would benefit. This architecture would address a
number of concerns raised with the prior submission. (Refer to
http://lwn.net/Articles/401548/.)

You'd probably want to move the 'crypto' key type to its own directory.

Acked-by: Mimi Zohar <***@us.ibm.com>

thanks,
Mimi
Post by David Howells
---
Documentation/security/keys-crypto.txt | 181 +++++++++++++++++++++++++
include/keys/crypto-subtype.h | 56 ++++++++
include/keys/crypto-type.h | 25 +++
security/keys/Kconfig | 8 +
security/keys/Makefile | 3
security/keys/crypto_keys.h | 28 ++++
security/keys/crypto_type.c | 230 ++++++++++++++++++++++++++++++++
7 files changed, 531 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..97dee80
--- /dev/null
+++ b/Documentation/security/keys-crypto.txt
@@ -0,0 +1,181 @@
+ ======================
+ CRYPTOGRAPHIC KEY TYPE
+ ======================
+
+
+ - Overview.
+ - Key identification.
+ - Accessing crypto keys.
+ - Implementing crypto parsers.
+ - Implementing crypto subtypes.
+
+
+========
+OVERVIEW
+========
+
+The "crypto" key type is designed to be a container for cryptographic keys,
+without imposing any particular restrictions on the form of the cryptography or
+the key.
+
+The crypto key is given a subtype that defines what sort of data is associated
+with the key and provides operations to describe and destroy it. However, no
+requirement is made that the key data actually be loaded into the key.
+
+The crypto key also has a number of data parsers registered with it. The data
+parsers are responsible for extracing information the blobs of data passed to
+the instantiator function. The first data parser that recognises the blob gets
+to set the subtype of the key and define the operations that can be done on
+that key.
+
+Completely in-kernel key retention and operation subtypes and parsers can be
+defined, but it would also be possible to provide access to cryptographic
+hardware (such as a TPM) that might be used to both retain the relevant key and
+perform operations using that key. In such a case, the crypto key would then
+merely be an interface to the TPM driver.
+
+
+==================
+KEY IDENTIFICATION
+==================
+
+Because the identity of a key is not necessarily known and may not be easily
+calculated when a crypto key is allocated, it may not be a simple matter to set
+a key description to something that's useful for determining whether this is
+the key you're looking for. Furthermore, it may be necessary to perform a
+partial match upon the key identity.
+
+To help with this, when a key is loaded, the parser calculates the key
+fingerprint and stores a copy in the key structure.
+
+The crypto key type's key matching function then performs more checks than just
+
+ (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
+
+
+
+ 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
+
+
+Looking in /proc/keys, the last 8 hex digits of the key fingerprint are
+
+ 1a39e171 I----- 1 perm 3f010000 0 0 crypto modsign.0: DSA 5acc2142 []
+
+
+=====================
+ACCESSING CRYPTO KEYS
+=====================
+
+To access crypto keys from within the kernel, the following inclusion is
+
+ #include <keys/crypto-type.h>
+
+
+ struct key_type key_type_crypto;
+
+
+===========================
+IMPLEMENTING CRYPTO PARSERS
+===========================
+
+The crypto key type keeps a list of registered data parsers. An example of
+such a parser is one that parses OpenPGP packet formatted data [RFC 4880].
+
+During key instantiation each parser in the list is tried until one doesn't
+return -EBADMSG.
+
+
+ struct crypto_key_parser {
+ struct module *owner;
+ const char *name;
+
+ int (*instantiate)(struct key *key,
+ const void *data, size_t datalen);
+ };
+
+The owner and name fields should be set to the owning module and the name of
+the parser.
+
+There are a number of operations defined by the parser. They are all optional,
+but it is expected that at least one will be defined.
+
+ (1) instantiate().
+
+ The arguments are the same as for the instantiate function in the key
+ type. 'key' is the crypto key being instantiated; data and datalen are
+ the instantiation data, presumably containing cryptographic key data, and
+ the length of that data.
+
+ If the data format is not recognised, -EBADMSG should be returned. If it
+ is recognised, but the key cannot for some reason be set up, some other
+ negative error code should be returned.
+
+ If the key can be successfully set up, then key->payload should be set to
+ point to the retained data, key->type_data.p[0] should be set to point to
+ the subtype chosen and key->type_data.p[1] should be set to point to a
+ copy of the key's identity string and 0 should be returned.
+
+ The key's identity string may be partially matched upon. For a public-key
+ algorithm such as RSA and DSA this will likely be a printable hex version
+ of the key's fingerprint.
+
+
+ int register_crypto_key_parser(struct crypto_key_parser *parser);
+ void unregister_crypto_key_parser(struct crypto_key_parser *subtype);
+
+Parsers may not have the same name. The names are only used for displaying in
+debugging messages.
+
+
+============================
+IMPLEMENTING CRYPTO SUBTYPES
+============================
+
+The parser selects the appropriate subtype directly and sets it on the key; the
+crypto key then retains a reference on the subtype module (which means the
+parser can be removed thereafter).
+
+
+ struct crypto_key_subtype {
+ struct module *owner;
+ const char *name;
+
+ void (*describe)(const struct key *key, struct seq_file *m);
+ void (*destroy)(void *payload);
+ };
+
+The owner and name fields should be set to the owning module and the name of
+the subtype.
+
+
+ (1) describe().
+
+ Mandatory. This allows the subtype to display something in /proc/keys
+ against the key. For instance the name of the public key algorithm type
+ could be displayed. The key type will display the tail of the key
+ identity string after this.
+
+ (2) destroy().
+
+ Mandatory. This should free the memory associated with the key. The
+ crypto key will look after freeing the fingerprint and releasing the
+ reference on the subtype module.
diff --git a/include/keys/crypto-subtype.h b/include/keys/crypto-subtype.h
new file mode 100644
index 0000000..fa87555
--- /dev/null
+++ b/include/keys/crypto-subtype.h
@@ -0,0 +1,56 @@
+/* Cryptographic key subtype
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ *
+ * See Documentation/security/keys-crypto.txt
+ */
+
+#ifndef _KEYS_CRYPTO_SUBTYPE_H
+#define _KEYS_CRYPTO_SUBTYPE_H
+
+#include <linux/seq_file.h>
+#include <keys/crypto-type.h>
+
+extern struct key_type key_type_crypto;
+
+/*
+ * Keys of this type declare a subtype that indicates the handlers and
+ * capabilities.
+ */
+struct crypto_key_subtype {
+ struct module *owner;
+ const char *name;
+ unsigned short name_len; /* length of name */
+
+ void (*describe)(const struct key *key, struct seq_file *m);
+
+ void (*destroy)(void *payload);
+};
+
+/*
+ * Data parser. Called during instantiation and signature verification
+ * initiation.
+ */
+struct crypto_key_parser {
+ struct list_head link;
+ struct module *owner;
+ const char *name;
+
+ /* Attempt to instantiate a key from the data blob passed to add_key()
+ * or keyctl_instantiate().
+ *
+ * Return EBADMSG if not recognised.
+ */
+ int (*instantiate)(struct key *key, const void *data, size_t datalen);
+};
+
+extern int register_crypto_key_parser(struct crypto_key_parser *);
+extern void unregister_crypto_key_parser(struct crypto_key_parser *);
+
+#endif /* _KEYS_CRYPTO_SUBTYPE_H */
diff --git a/include/keys/crypto-type.h b/include/keys/crypto-type.h
new file mode 100644
index 0000000..47c00c7
--- /dev/null
+++ b/include/keys/crypto-type.h
@@ -0,0 +1,25 @@
+/* Cryptographic key type interface
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ *
+ * See Documentation/security/keys-crypto.txt
+ */
+
+#ifndef _KEYS_CRYPTO_TYPE_H
+#define _KEYS_CRYPTO_TYPE_H
+
+#include <linux/key-type.h>
+
+extern struct key_type key_type_crypto;
+
+/*
+ * The payload is at the discretion of the subtype.
+ */
+
+#endif /* _KEYS_CRYPTO_TYPE_H */
diff --git a/security/keys/Kconfig b/security/keys/Kconfig
index a90d6d3..290c9d3 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -69,3 +69,11 @@ config KEYS_DEBUG_PROC_KEYS
the resulting table.
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.
diff --git a/security/keys/Makefile b/security/keys/Makefile
index 504aaa0..67fceaa 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -24,3 +24,6 @@ obj-$(CONFIG_SYSCTL) += sysctl.o
#
obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
+obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o
+
+crypto_keys-y := crypto_type.o
diff --git a/security/keys/crypto_keys.h b/security/keys/crypto_keys.h
new file mode 100644
index 0000000..a339ce0
--- /dev/null
+++ b/security/keys/crypto_keys.h
@@ -0,0 +1,28 @@
+/* Internal crypto type stuff
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+static inline
+struct crypto_key_subtype *crypto_key_subtype(const struct key *key)
+{
+ return key->type_data.p[0];
+}
+
+static inline char *crypto_key_id(const struct key *key)
+{
+ return key->type_data.p[1];
+}
+
+
+/*
+ * crypto_type.c
+ */
+extern struct list_head crypto_key_parsers;
+extern struct rw_semaphore crypto_key_parsers_sem;
diff --git a/security/keys/crypto_type.c b/security/keys/crypto_type.c
new file mode 100644
index 0000000..a06413a
--- /dev/null
+++ b/security/keys/crypto_type.c
@@ -0,0 +1,230 @@
+/* Cryptographic key type
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ *
+ * See Documentation/security/keys-crypto.txt
+ */
+#include <keys/crypto-subtype.h>
+#include <linux/seq_file.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "crypto_keys.h"
+
+MODULE_LICENSE("GPL");
+
+LIST_HEAD(crypto_key_parsers);
+DECLARE_RWSEM(crypto_key_parsers_sem);
+
+/*
+ * Match crypto_keys on (part of) their name
+ *
+ * "<desc>" - request a key by description
+ * "id:<id>" - request a key matching the ID
+ * "<subtype>:<id>" - request a key of a subtype
+ */
+static int crypto_key_match(const struct key *key, const void *description)
+{
+ const struct crypto_key_subtype *subtype = crypto_key_subtype(key);
+ const char *spec = description;
+ const char *id, *kid;
+ ptrdiff_t speclen;
+ size_t idlen, kidlen;
+
+ if (!subtype || !spec || !*spec)
+ return 0;
+
+ /* See if the full key description matches as is */
+ if (key->description && strcmp(key->description, description) == 0)
+ return 1;
+
+ /* All tests from here on break the criterion description into a
+ * specifier, a colon and then an identifier.
+ */
+ id = strchr(spec, ':');
+ if (!id)
+ return 0;
+
+ speclen = id - spec;
+ id++;
+
+ /* Anything after here requires a partial match on the ID string */
+ kid = crypto_key_id(key);
+ if (!kid)
+ return 0;
+
+ idlen = strlen(id);
+ kidlen = strlen(kid);
+ if (idlen > kidlen)
+ return 0;
+
+ kid += kidlen - idlen;
+ if (strcasecmp(id, kid) != 0)
+ return 0;
+
+ if (speclen == 2 &&
+ memcmp(spec, "id", 2) == 0)
+ return 1;
+
+ if (speclen == subtype->name_len &&
+ memcmp(spec, subtype->name, speclen) == 0)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Describe the crypto key
+ */
+static void crypto_key_describe(const struct key *key, struct seq_file *m)
+{
+ const struct crypto_key_subtype *subtype = crypto_key_subtype(key);
+ const char *kid = crypto_key_id(key);
+ size_t n;
+
+ seq_puts(m, key->description);
+
+ if (subtype) {
+ seq_puts(m, ": ");
+ subtype->describe(key, m);
+
+ if (kid) {
+ seq_putc(m, ' ');
+ n = strlen(kid);
+ if (n <= 8)
+ seq_puts(m, kid);
+ else
+ seq_puts(m, kid + n - 8);
+ }
+
+ seq_puts(m, " [");
+ /* put something here to indicate the key's capabilities */
+ seq_putc(m, ']');
+ }
+}
+
+/*
+ * Instantiate a crypto_key defined key
+ */
+static int crypto_key_instantiate(struct key *key,
+ const void *data, size_t datalen)
+{
+ struct crypto_key_parser *parser;
+ int ret;
+
+ pr_devel("==>%s()\n", __func__);
+
+ if (datalen == 0)
+ return -EINVAL;
+
+ down_read(&crypto_key_parsers_sem);
+
+ ret = -EBADMSG;
+ list_for_each_entry(parser, &crypto_key_parsers, link) {
+ pr_debug("Trying parser '%s'\n", parser->name);
+
+ ret = parser->instantiate(key, data, datalen);
+ if (ret != -EBADMSG) {
+ pr_debug("Parser recognised the format (ret %d)\n",
+ ret);
+ if (ret == 0)
+ module_get(crypto_key_subtype(key));
+ break;
+ }
+ }
+
+ up_read(&crypto_key_parsers_sem);
+ pr_devel("<==%s() = %d\n", __func__, ret);
+ return ret;
+}
+
+/*
+ * dispose of the data dangling from the corpse of a crypto key
+ */
+static void crypto_key_destroy(struct key *key)
+{
+ struct crypto_key_subtype *subtype = crypto_key_subtype(key);
+ if (subtype) {
+ subtype->destroy(key->payload.data);
+ module_put(subtype->owner);
+ key->type_data.p[0] = NULL;
+ }
+ kfree(key->type_data.p[1]);
+ key->type_data.p[1] = NULL;
+}
+
+struct key_type key_type_crypto = {
+ .name = "crypto",
+ .instantiate = crypto_key_instantiate,
+ .match = crypto_key_match,
+ .destroy = crypto_key_destroy,
+ .describe = crypto_key_describe,
+};
+EXPORT_SYMBOL_GPL(key_type_crypto);
+
+/**
+ * register_crypto_key_parser - Register a crypto key blob parser
+ */
+int register_crypto_key_parser(struct crypto_key_parser *parser)
+{
+ struct crypto_key_parser *cursor;
+ int ret;
+
+ down_write(&crypto_key_parsers_sem);
+
+ list_for_each_entry(cursor, &crypto_key_parsers, link) {
+ if (strcmp(cursor->name, parser->name) == 0) {
+ pr_err("Crypto key parser '%s' already registered\n",
+ parser->name);
+ ret = -EEXIST;
+ goto out;
+ }
+ }
+
+ list_add_tail(&parser->link, &crypto_key_parsers);
+
+ pr_notice("Crypto key parser '%s' registered\n", parser->name);
+ ret = 0;
+
+ up_write(&crypto_key_parsers_sem);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(register_crypto_key_parser);
+
+/**
+ * unregister_crypto_key_parser - Unregister a crypto key blob parser
+ */
+void unregister_crypto_key_parser(struct crypto_key_parser *parser)
+{
+ down_write(&crypto_key_parsers_sem);
+ list_del(&parser->link);
+ up_write(&crypto_key_parsers_sem);
+
+ pr_notice("Crypto key parser '%s' unregistered\n", parser->name);
+}
+EXPORT_SYMBOL_GPL(unregister_crypto_key_parser);
+
+/*
+ * Module stuff
+ */
+static int __init crypto_key_init(void)
+{
+ return register_key_type(&key_type_crypto);
+}
+
+static void __exit crypto_key_cleanup(void)
+{
+ unregister_key_type(&key_type_crypto);
+}
+
+module_init(crypto_key_init);
+module_exit(crypto_key_cleanup);
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2012-01-17 15:32:43 UTC
Permalink
Post by Mimi Zohar
Nice! Basically the 'crypto' key type ties crypto/ with security/keys.
Other than the posted pgp key parser used for verifying kernel module
signatures, I assume another use case could be to expose kernel
cryptography to userspace. As there was a submission
https://lkml.org/lkml/2010/8/20/103 to do just this, there must be
userspace apps that would benefit. This architecture would address a
number of concerns raised with the prior submission. (Refer to
http://lwn.net/Articles/401548/.)
:-)
Post by Mimi Zohar
You'd probably want to move the 'crypto' key type to its own directory.
Yeah.

I'd also like to see if Dmitry's work can be absorbes into this infrastructure.

David
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Kasatkin, Dmitry
2012-01-18 10:56:32 UTC
Permalink
Post by David Howells
Post by Mimi Zohar
Nice! Basically the 'crypto' key type ties crypto/ with security/keys.
Other than the posted pgp key parser used for verifying kernel module
signatures, I assume another use case could be to expose kernel
cryptography to userspace.  As there was a submission
https://lkml.org/lkml/2010/8/20/103 to do just this, there must be
userspace apps that would benefit.  This architecture would address a
number of concerns raised with the prior submission. (Refer to
http://lwn.net/Articles/401548/.)
:-)
Post by Mimi Zohar
You'd probably want to move the 'crypto' key type to its own directory.
Yeah.
I'd also like to see if Dmitry's work can be absorbes into this infrastructure.
Hi David,

Crypto keys is very nice idea.
We thought some time ago about having dedicated key type for handling
public key cryptography operations,
but did not go that far. Also I did not want to mess-up with GnuPG
formats and just made straight-forward RSA implementation,
which can be handled by any crypto library, such as openssl.
We can easily take GPG signing scheme into use for IMA/EVM when it
gets to upstream.

- Dmitry
Post by David Howells
David
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-02 18:44:26 UTC
Permalink
Add a facility whereby a key subtype may be asked to verify a signature against
the data it is purported to have signed.

This adds four routines:

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

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

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

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

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

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

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

Cancel the verification process.

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

Documentation/security/keys-crypto.txt | 101 +++++++++++++++++++++++++++++
include/keys/crypto-subtype.h | 21 ++++++
include/keys/crypto-type.h | 9 +++
security/keys/Makefile | 2 -
security/keys/crypto_verify.c | 111 ++++++++++++++++++++++++++++++++
5 files changed, 243 insertions(+), 1 deletions(-)
create mode 100644 security/keys/crypto_verify.c


diff --git a/Documentation/security/keys-crypto.txt b/Documentation/security/keys-crypto.txt
index 97dee80..a964717 100644
--- a/Documentation/security/keys-crypto.txt
+++ b/Documentation/security/keys-crypto.txt
@@ -7,6 +7,7 @@ Contents:
- Overview.
- Key identification.
- Accessing crypto keys.
+ - Signature verification.
- Implementing crypto parsers.
- Implementing crypto subtypes.

@@ -89,6 +90,65 @@ This gives access to the key type:
struct key_type key_type_crypto;


+SIGNATURE VERIFICATION
+----------------------
+
+The four operations that can perform cryptographic signature verification,
+using one of a set of keys to provide the public key:
+
+ (1) Begin verification procedure.
+
+ struct crypto_key_verify_context *
+ verify_sig_begin(struct key *keyring, const void *sig, size_t siglen);
+
+ This function sets up a verification context from the information in the
+ signature and looks for a suitable key in the keyring. The signature blob
+ must be presented again at the end of the procedure. The keys will be
+ checked against parameters in the signature, and if the matching one is
+ not found then -ENOKEY will be returned.
+
+ The hashing algorithm, if such a thing applies, will be determined from
+ information in the signature and the appropriate crypto module will be
+ used. -ENOPKG will be returned if the hash algorithm is unavailable.
+
+ The return value is an opaque pointer to be passed to the other functions,
+ or a negative error code.
+
+ (2) Indicate data to be verified.
+
+ int verify_sig_add_data(struct crypto_key_verify_context *ctx,
+ const void *data, size_t datalen);
+
+ This function is used to shovel data to the verification procedure so that
+ it can load it into the hash, pass it to hardware or whatever is
+ appropriate for the algorithm being employed.
+
+ The data is not canonicalised for the document type specified in the
+ signature. The caller must do that.
+
+ It will return 0 if successful and a negative error code if not.
+
+ (3) Complete the verification process.
+
+ int verify_sig_end(struct crypto_key_verify_context *ctx,
+ const void *sig, size_t siglen);
+
+ This function performs the actual signature verification step and cleans
+ up the resources allocated at the beginning. The signature must be
+ presented again as some of the data therein may need to be added to the
+ internal hash.
+
+ It will return -EKEYREJECTED if the signature didn't match, 0 if
+ successful and may return other errors as appropriate.
+
+ (4) Cancel the verification process.
+
+ void verify_sig_cancel(struct crypto_key_verify_context *ctx);
+
+ This function cleans up the resources allocated at the beginning. This is
+ not necessary if verify_sig_end() was called.
+
+
===========================
IMPLEMENTING CRYPTO PARSERS
===========================
@@ -96,6 +156,7 @@ IMPLEMENTING CRYPTO PARSERS
The crypto key type keeps a list of registered data parsers. An example of
such a parser is one that parses OpenPGP packet formatted data [RFC 4880].

+
During key instantiation each parser in the list is tried until one doesn't
return -EBADMSG.

@@ -107,6 +168,8 @@ The parser definition structure looks like the following:

int (*instantiate)(struct key *key,
const void *data, size_t datalen);
+ struct crypto_key_verify_context *(*verify_sig_begin)(
+ struct key *keyring, const u8 *sig, size_t siglen);
};

The owner and name fields should be set to the owning module and the name of
@@ -135,6 +198,44 @@ but it is expected that at least one will be defined.
algorithm such as RSA and DSA this will likely be a printable hex version
of the key's fingerprint.

+ (2) verify_sig_begin().
+
+ This is similar in concept to the instantiate() function, except that it
+ is given a signature blob to parse rather than a key data blob.
+
+ If the data format is not recognised, -EBADMSG should be returned. If it
+ is recognised, but the signature verification process cannot for some
+ reason be set up, some other negative error code should be returned.
+ -ENOKEY should be used to indicate that no matching key is available and
+ -ENOPKG should be returned if the hash algorithm or the verification
+ algorithm are unavailable.
+
+ If successful, the parser should allocate a verification context and embed
+ the following struct in it:
+
+ struct crypto_key_verify_context {
+ struct key *key;
+ int (*add_data)(struct crypto_key_verify_context *ctx,
+ const void *data, size_t datalen);
+ int (*end)(struct crypto_key_verify_context *ctx,
+ const u8 *sig, size_t siglen);
+ void (*cancel)(struct crypto_key_verify_context *ctx);
+ };
+
+ and return a pointer to this to the caller, who will then pass it to the
+ verification operation wrappers described in the "Signature Verification"
+ section. The three operation pointers here correspond exactly to those
+ wrappers and are all mandatory. container_of() should be used to retrieve
+ the actual context.
+
+ Note that the crypto key type retains a reference on the parser module for
+ the lifetime of this context, though the operation pointers need not point
+ into this module.
+
+ The parser should also record a pointer to the key selected and take a
+ reference on that key with key_get().
+
+
Functions are provided to register and unregister parsers:

int register_crypto_key_parser(struct crypto_key_parser *parser);
diff --git a/include/keys/crypto-subtype.h b/include/keys/crypto-subtype.h
index fa87555..f2b927a 100644
--- a/include/keys/crypto-subtype.h
+++ b/include/keys/crypto-subtype.h
@@ -20,6 +20,20 @@
extern struct key_type key_type_crypto;

/*
+ * Context base for signature verification methods. Allocated by the subtype
+ * and presumably embedded in something appropriate.
+ */
+struct crypto_key_verify_context {
+ struct key *key;
+ struct crypto_key_parser *parser;
+ int (*add_data)(struct crypto_key_verify_context *ctx,
+ const void *data, size_t datalen);
+ int (*end)(struct crypto_key_verify_context *ctx,
+ const u8 *sig, size_t siglen);
+ void (*cancel)(struct crypto_key_verify_context *ctx);
+};
+
+/*
* Keys of this type declare a subtype that indicates the handlers and
* capabilities.
*/
@@ -48,6 +62,13 @@ struct crypto_key_parser {
* Return EBADMSG if not recognised.
*/
int (*instantiate)(struct key *key, const void *data, size_t datalen);
+
+ /* Attempt to recognise a signature blob and find a matching key.
+ *
+ * Return EBADMSG if not recognised.
+ */
+ struct crypto_key_verify_context *(*verify_sig_begin)(
+ struct key *keyring, const u8 *sig, size_t siglen);
};

extern int register_crypto_key_parser(struct crypto_key_parser *);
diff --git a/include/keys/crypto-type.h b/include/keys/crypto-type.h
index 47c00c7..6b93366 100644
--- a/include/keys/crypto-type.h
+++ b/include/keys/crypto-type.h
@@ -18,6 +18,15 @@

extern struct key_type key_type_crypto;

+struct crypto_key_verify_context;
+extern struct crypto_key_verify_context *verify_sig_begin(
+ struct key *key, const void *sig, size_t siglen);
+extern int verify_sig_add_data(struct crypto_key_verify_context *ctx,
+ const void *data, size_t datalen);
+extern int verify_sig_end(struct crypto_key_verify_context *ctx,
+ const void *sig, size_t siglen);
+extern void verify_sig_cancel(struct crypto_key_verify_context *ctx);
+
/*
* The payload is at the discretion of the subtype.
*/
diff --git a/security/keys/Makefile b/security/keys/Makefile
index 67fceaa..8462904 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -26,4 +26,4 @@ obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o

-crypto_keys-y := crypto_type.o
+crypto_keys-y := crypto_type.o crypto_verify.o
diff --git a/security/keys/crypto_verify.c b/security/keys/crypto_verify.c
new file mode 100644
index 0000000..65f734c
--- /dev/null
+++ b/security/keys/crypto_verify.c
@@ -0,0 +1,111 @@
+/* Signature verification with a crypto key
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (***@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ *
+ * See Documentation/security/keys-crypto.txt
+ */
+
+#include <keys/crypto-subtype.h>
+#include <linux/module.h>
+#include "crypto_keys.h"
+
+/**
+ * verify_sig_begin - Initiate the use of a crypto key to verify a signature
+ * @keyring: The public keys to verify against
+ * @sig: The signature data
+ * @siglen: The signature length
+ *
+ * Returns a context or an error.
+ */
+struct crypto_key_verify_context *verify_sig_begin(
+ struct key *keyring, const void *sig, size_t siglen)
+{
+ struct crypto_key_verify_context *ret;
+ struct crypto_key_parser *parser;
+
+ pr_devel("==>%s()\n", __func__);
+
+ if (siglen == 0 || !sig)
+ return ERR_PTR(-EINVAL);
+
+ down_read(&crypto_key_parsers_sem);
+
+ ret = ERR_PTR(-EBADMSG);
+ list_for_each_entry(parser, &crypto_key_parsers, link) {
+ if (parser->verify_sig_begin) {
+ if (!try_module_get(parser->owner))
+ continue;
+
+ pr_debug("Trying parser '%s'\n", parser->name);
+
+ ret = parser->verify_sig_begin(keyring, sig, siglen);
+ if (IS_ERR(ret))
+ module_put(parser->owner);
+ else
+ ret->parser = parser;
+ if (ret != ERR_PTR(-EBADMSG)) {
+ pr_debug("Parser recognised the format"
+ " (ret %ld)\n",
+ PTR_ERR(ret));
+ break;
+ }
+ }
+ }
+
+ up_read(&crypto_key_parsers_sem);
+ pr_devel("<==%s() = %p\n", __func__, ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(verify_sig_begin);
+
+/**
+ * verify_sig_add_data - Incrementally provide data to be verified
+ * @ctx: The context from verify_sig_begin()
+ * @data: Data
+ * @datalen: The amount of @data
+ *
+ * This may be called multiple times.
+ */
+int verify_sig_add_data(struct crypto_key_verify_context *ctx,
+ const void *data, size_t datalen)
+{
+ return ctx->add_data(ctx, data, datalen);
+}
+EXPORT_SYMBOL_GPL(verify_sig_add_data);
+
+/**
+ * verify_sig_end - Finalise signature verification and return result
+ * @ctx: The context from verify_sig_begin()
+ * @sig: The signature data
+ * @siglen: The signature length
+ */
+int verify_sig_end(struct crypto_key_verify_context *ctx,
+ const void *sig, size_t siglen)
+{
+ struct crypto_key_parser *parser = ctx->parser;
+ int ret;
+
+ ret = ctx->end(ctx, sig, siglen);
+ module_put(parser->owner);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(verify_sig_end);
+
+/**
+ * verify_sig_end - Cancel signature verification
+ * @ctx: The context from verify_sig_begin()
+ */
+void verify_sig_cancel(struct crypto_key_verify_context *ctx)
+{
+ struct crypto_key_parser *parser = ctx->parser;
+
+ ctx->cancel(ctx);
+ module_put(parser->owner);
+}
+EXPORT_SYMBOL_GPL(verify_sig_cancel);

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Kasatkin, Dmitry
2012-01-18 11:21:07 UTC
Permalink
Post by David Howells
Add a facility whereby a key subtype may be asked to verify a signature against
the data it is purported to have signed.
 (1) struct crypto_key_verify_context *
    verify_sig_begin(struct key *keyring, const void *sig, size_t siglen);
    This sets up a verification context for the given signature using
    information in that signature to select a key from the specified keyring
    and to request a hash algorithm from the crypto layer.
 (2) int verify_sig_add_data(struct crypto_key_verify_context *ctx,
                            const void *data, size_t datalen);
    Incrementally supply data to be signed.  May be called multiple times.
Hello,

It would also nice to have an API to supply pre-computed data hash.
For example IMA uses the same functionality to compute the hash of the
file content,
and then, based on security.ima type decided either verify it using just hash,
or use digital signature.
We could pass a hash as data. But may be we do not want to have extra
operation and compute hash over hash.

- Dmitry
Post by David Howells
 (3) int verify_sig_end(struct crypto_key_verify_context *ctx,
                       const void *sig, size_t siglen);
    Complete the verification process and return the result.  -EKEYREJECTED
    will indicate that the verification failed and 0 will indicate success.
    Other errors are also possible.
 (4) void verify_sig_cancel(struct crypto_key_verify_context *ctx);
    Cancel the verification process.
---
 Documentation/security/keys-crypto.txt |  101 +++++++++++++++++++++++++++++
 include/keys/crypto-subtype.h          |   21 ++++++
 include/keys/crypto-type.h             |    9 +++
 security/keys/Makefile                 |    2 -
 security/keys/crypto_verify.c          |  111 ++++++++++++++++++++++++++++++++
 5 files changed, 243 insertions(+), 1 deletions(-)
 create mode 100644 security/keys/crypto_verify.c
diff --git a/Documentation/security/keys-crypto.txt b/Documentation/security/keys-crypto.txt
index 97dee80..a964717 100644
--- a/Documentation/security/keys-crypto.txt
+++ b/Documentation/security/keys-crypto.txt
  - Overview.
  - Key identification.
  - Accessing crypto keys.
+    - Signature verification.
  - Implementing crypto parsers.
  - Implementing crypto subtypes.
       struct key_type key_type_crypto;
+SIGNATURE VERIFICATION
+----------------------
+
+The four operations that can perform cryptographic signature verification,
+
+ (1) Begin verification procedure.
+
+       struct crypto_key_verify_context *
+       verify_sig_begin(struct key *keyring, const void *sig, size_t siglen);
+
+     This function sets up a verification context from the information in the
+     signature and looks for a suitable key in the keyring.  The signature blob
+     must be presented again at the end of the procedure.  The keys will be
+     checked against parameters in the signature, and if the matching one is
+     not found then -ENOKEY will be returned.
+
+     The hashing algorithm, if such a thing applies, will be determined from
+     information in the signature and the appropriate crypto module will be
+     used.  -ENOPKG will be returned if the hash algorithm is unavailable.
+
+     The return value is an opaque pointer to be passed to the other functions,
+     or a negative error code.
+
+ (2) Indicate data to be verified.
+
+       int verify_sig_add_data(struct crypto_key_verify_context *ctx,
+                               const void *data, size_t datalen);
+
+     This function is used to shovel data to the verification procedure so that
+     it can load it into the hash, pass it to hardware or whatever is
+     appropriate for the algorithm being employed.
+
+     The data is not canonicalised for the document type specified in the
+     signature.  The caller must do that.
+
+     It will return 0 if successful and a negative error code if not.
+
+ (3) Complete the verification process.
+
+       int verify_sig_end(struct crypto_key_verify_context *ctx,
+                          const void *sig, size_t siglen);
+
+     This function performs the actual signature verification step and cleans
+     up the resources allocated at the beginning.  The signature must be
+     presented again as some of the data therein may need to be added to the
+     internal hash.
+
+     It will return -EKEYREJECTED if the signature didn't match, 0 if
+     successful and may return other errors as appropriate.
+
+ (4) Cancel the verification process.
+
+       void verify_sig_cancel(struct crypto_key_verify_context *ctx);
+
+     This function cleans up the resources allocated at the beginning.  This is
+     not necessary if verify_sig_end() was called.
+
+
 ===========================
 IMPLEMENTING CRYPTO PARSERS
 ===========================
@@ -96,6 +156,7 @@ IMPLEMENTING CRYPTO PARSERS
 The crypto key type keeps a list of registered data parsers.  An example of
 such a parser is one that parses OpenPGP packet formatted data [RFC 4880].
+
 During key instantiation each parser in the list is tried until one doesn't
 return -EBADMSG.
               int (*instantiate)(struct key *key,
                                  const void *data, size_t datalen);
+               struct crypto_key_verify_context *(*verify_sig_begin)(
+                       struct key *keyring, const u8 *sig, size_t siglen);
       };
 The owner and name fields should be set to the owning module and the name of
@@ -135,6 +198,44 @@ but it is expected that at least one will be defined.
     algorithm such as RSA and DSA this will likely be a printable hex version
     of the key's fingerprint.
+ (2) verify_sig_begin().
+
+     This is similar in concept to the instantiate() function, except that it
+     is given a signature blob to parse rather than a key data blob.
+
+     If the data format is not recognised, -EBADMSG should be returned.  If it
+     is recognised, but the signature verification process cannot for some
+     reason be set up, some other negative error code should be returned.
+     -ENOKEY should be used to indicate that no matching key is available and
+     -ENOPKG should be returned if the hash algorithm or the verification
+     algorithm are unavailable.
+
+     If successful, the parser should allocate a verification context and embed
+
+       struct crypto_key_verify_context {
+               struct key *key;
+               int (*add_data)(struct crypto_key_verify_context *ctx,
+                               const void *data, size_t datalen);
+               int (*end)(struct crypto_key_verify_context *ctx,
+                          const u8 *sig, size_t siglen);
+               void (*cancel)(struct crypto_key_verify_context *ctx);
+       };
+
+     and return a pointer to this to the caller, who will then pass it to the
+     verification operation wrappers described in the "Signature Verification"
+     section.  The three operation pointers here correspond exactly to those
+     wrappers and are all mandatory.  container_of() should be used to retrieve
+     the actual context.
+
+     Note that the crypto key type retains a reference on the parser module for
+     the lifetime of this context, though the operation pointers need not point
+     into this module.
+
+     The parser should also record a pointer to the key selected and take a
+     reference on that key with key_get().
+
+
       int register_crypto_key_parser(struct crypto_key_parser *parser);
diff --git a/include/keys/crypto-subtype.h b/include/keys/crypto-subtype.h
index fa87555..f2b927a 100644
--- a/include/keys/crypto-subtype.h
+++ b/include/keys/crypto-subtype.h
@@ -20,6 +20,20 @@
 extern struct key_type key_type_crypto;
 /*
+ * Context base for signature verification methods.  Allocated by the subtype
+ * and presumably embedded in something appropriate.
+ */
+struct crypto_key_verify_context {
+       struct key *key;
+       struct crypto_key_parser *parser;
+       int (*add_data)(struct crypto_key_verify_context *ctx,
+                       const void *data, size_t datalen);
+       int (*end)(struct crypto_key_verify_context *ctx,
+                  const u8 *sig, size_t siglen);
+       void (*cancel)(struct crypto_key_verify_context *ctx);
+};
+
+/*
 * Keys of this type declare a subtype that indicates the handlers and
 * capabilities.
 */
@@ -48,6 +62,13 @@ struct crypto_key_parser {
        * Return EBADMSG if not recognised.
        */
       int (*instantiate)(struct key *key, const void *data, size_t datalen);
+
+       /* Attempt to recognise a signature blob and find a matching key.
+        *
+        * Return EBADMSG if not recognised.
+        */
+       struct crypto_key_verify_context *(*verify_sig_begin)(
+               struct key *keyring, const u8 *sig, size_t siglen);
 };
 extern int register_crypto_key_parser(struct crypto_key_parser *);
diff --git a/include/keys/crypto-type.h b/include/keys/crypto-type.h
index 47c00c7..6b93366 100644
--- a/include/keys/crypto-type.h
+++ b/include/keys/crypto-type.h
@@ -18,6 +18,15 @@
 extern struct key_type key_type_crypto;
+struct crypto_key_verify_context;
+extern struct crypto_key_verify_context *verify_sig_begin(
+       struct key *key, const void *sig, size_t siglen);
+extern int verify_sig_add_data(struct crypto_key_verify_context *ctx,
+                              const void *data, size_t datalen);
+extern int verify_sig_end(struct crypto_key_verify_context *ctx,
+                         const void *sig, size_t siglen);
+extern void verify_sig_cancel(struct crypto_key_verify_context *ctx);
+
 /*
 * The payload is at the discretion of the subtype.
 */
diff --git a/security/keys/Makefile b/security/keys/Makefile
index 67fceaa..8462904 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -26,4 +26,4 @@ obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
 obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
 obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o
-crypto_keys-y := crypto_type.o
+crypto_keys-y := crypto_type.o crypto_verify.o
diff --git a/security/keys/crypto_verify.c b/security/keys/crypto_verify.c
new file mode 100644
index 0000000..65f734c
--- /dev/null
+++ b/security/keys/crypto_verify.c
@@ -0,0 +1,111 @@
+/* Signature verification with a crypto key
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ *
+ * See Documentation/security/keys-crypto.txt
+ */
+
+#include <keys/crypto-subtype.h>
+#include <linux/module.h>
+#include "crypto_keys.h"
+
+/**
+ * verify_sig_begin - Initiate the use of a crypto key to verify a signature
+ *
+ * Returns a context or an error.
+ */
+struct crypto_key_verify_context *verify_sig_begin(
+       struct key *keyring, const void *sig, size_t siglen)
+{
+       struct crypto_key_verify_context *ret;
+       struct crypto_key_parser *parser;
+
+       pr_devel("==>%s()\n", __func__);
+
+       if (siglen == 0 || !sig)
+               return ERR_PTR(-EINVAL);
+
+       down_read(&crypto_key_parsers_sem);
+
+       ret = ERR_PTR(-EBADMSG);
+       list_for_each_entry(parser, &crypto_key_parsers, link) {
+               if (parser->verify_sig_begin) {
+                       if (!try_module_get(parser->owner))
+                               continue;
+
+                       pr_debug("Trying parser '%s'\n", parser->name);
+
+                       ret = parser->verify_sig_begin(keyring, sig, siglen);
+                       if (IS_ERR(ret))
+                               module_put(parser->owner);
+                       else
+                               ret->parser = parser;
+                       if (ret != ERR_PTR(-EBADMSG)) {
+                               pr_debug("Parser recognised the format"
+                                        " (ret %ld)\n",
+                                        PTR_ERR(ret));
+                               break;
+                       }
+               }
+       }
+
+       up_read(&crypto_key_parsers_sem);
+       pr_devel("<==%s() = %p\n", __func__, ret);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(verify_sig_begin);
+
+/**
+ * verify_sig_add_data - Incrementally provide data to be verified
+ *
+ * This may be called multiple times.
+ */
+int verify_sig_add_data(struct crypto_key_verify_context *ctx,
+                       const void *data, size_t datalen)
+{
+       return ctx->add_data(ctx, data, datalen);
+}
+EXPORT_SYMBOL_GPL(verify_sig_add_data);
+
+/**
+ * verify_sig_end - Finalise signature verification and return result
+ */
+int verify_sig_end(struct crypto_key_verify_context *ctx,
+                  const void *sig, size_t siglen)
+{
+       struct crypto_key_parser *parser = ctx->parser;
+       int ret;
+
+       ret = ctx->end(ctx, sig, siglen);
+       module_put(parser->owner);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(verify_sig_end);
+
+/**
+ * verify_sig_end - Cancel signature verification
+ */
+void verify_sig_cancel(struct crypto_key_verify_context *ctx)
+{
+       struct crypto_key_parser *parser = ctx->parser;
+
+       ctx->cancel(ctx);
+       module_put(parser->owner);
+}
+EXPORT_SYMBOL_GPL(verify_sig_cancel);
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2012-01-18 12:26:39 UTC
Permalink
It would also nice to have an API to supply pre-computed data hash. For
example IMA uses the same functionality to compute the hash of the file
content, and then, based on security.ima type decided either verify it using
just hash, or use digital signature. We could pass a hash as data. But may
be we do not want to have extra operation and compute hash over hash.
If I understand you correctly, you'd like to have the option to do the hashing
externally to this API? Would you supply the completed hash or just a hash
with the data in it, and require this API to complete it (ie. chuck metadata
into it)?

I don't think it should be hard. I could add an alternative to
verify_sig_add_data() perhaps. Either that or one function that does the lot
and takes the precomputed hash as input. There would be no need for the split
into four functions (begin, add_data, end, cancel) in such a case. The reason
for the split is so that the caller can invoke add_data several times with
non-contiguous bits of data.

It might even make sense to expose the crypto hash object for direct access
rather than use add_data - but that then makes it hard to use crypto hardware
where you would just shovel the raw data into it and it does all the hashing
and cryptography in a black box.

David
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Kasatkin, Dmitry
2012-01-18 13:26:42 UTC
Permalink
Post by David Howells
It would also nice to have an API to supply pre-computed data hash.  For
example IMA uses the same functionality to compute the hash of the file
content, and then, based on security.ima type decided either verify it using
just hash, or use digital signature.  We could pass a hash as data. But may
be we do not want to have extra operation and compute hash over hash.
If I understand you correctly, you'd like to have the option to do the hashing
externally to this API?  Would you supply the completed hash or just a hash
with the data in it, and require this API to complete it (ie. chuck metadata
into it)?
I meant just a hash of data..
Right, I remember, PGP finalizes hash with some additional metadata
pgp_pkey_digest_signature() seems does it...
Post by David Howells
I don't think it should be hard.  I could add an alternative to
verify_sig_add_data() perhaps.  Either that or one function that does the lot
and takes the precomputed hash as input.  There would be no need for the split
into four functions (begin, add_data, end, cancel) in such a case.  The reason
for the split is so that the caller can invoke add_data several times with
non-contiguous bits of data.
Yes. it is clear...
Would it be possible to have pass data (uncompleted) hash?
Post by David Howells
It might even make sense to expose the crypto hash object for direct access
rather than use add_data - but that then makes it hard to use crypto hardware
where you would just shovel the raw data into it and it does all the hashing
and cryptography in a black box.
David
--
To unsubscribe from this list: send the line "unsubscribe linux-crypto" in
More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2012-01-18 15:14:17 UTC
Permalink
Post by Kasatkin, Dmitry
Yes. it is clear...
Would it be possible to have pass data (uncompleted) hash?
Yes, provided it's not finalised. Also, with the current code it would have
to be a shash, but I think I should be able to support the passing in of an
async hash by exporting the state from that and importing the state into a
shash using the crypto layer export and import functions.

David
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Kasatkin, Dmitry
2012-01-18 15:20:47 UTC
Permalink
Post by Kasatkin, Dmitry
Yes. it is clear...
Would it be possible to have pass data (uncompleted) hash?
Yes, provided it's not finalised.  Also, with the current code it would have
to be a shash, but I think I should be able to support the passing in of an
async hash by exporting the state from that and importing the state into a
shash using the crypto layer export and import functions.
I thought about export/import.
But I think export/Import is hash driver implementation specific...
Cannot use ahash state for shash...

As you said in repose to later patch, you could provide some function to
build a final metadata buffer out of signature to finalize the hash...

- Dmitry
David
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2012-01-18 20:00:15 UTC
Permalink
Post by Kasatkin, Dmitry
I thought about export/import.
But I think export/Import is hash driver implementation specific...
Cannot use ahash state for shash...
Yeah... I'll have to consult Herbert Xu about that.
Post by Kasatkin, Dmitry
As you said in repose to later patch, you could provide some function to
build a final metadata buffer out of signature to finalize the hash...
That might be the only way to do it.

David
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Herbert Xu
2012-01-20 01:53:11 UTC
Permalink
Post by David Howells
Post by Kasatkin, Dmitry
I thought about export/import.
But I think export/Import is hash driver implementation specific...
Cannot use ahash state for shash...
Yeah... I'll have to consult Herbert Xu about that.
export/import is meant to be work across implementations.

Cheers,
--
Email: Herbert Xu <***@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-02 18:44:36 UTC
Permalink
Add a subtype for supporting asymmetric public-key encryption algorithms such
as DSA (FIPS-186) and RSA (PKCS#1 / RFC1337).

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

security/keys/Kconfig | 9 ++++
security/keys/Makefile | 1
security/keys/public_key.c | 55 ++++++++++++++++++++++
security/keys/public_key.h | 109 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 174 insertions(+), 0 deletions(-)
create mode 100644 security/keys/public_key.c
create mode 100644 security/keys/public_key.h


diff --git a/security/keys/Kconfig b/security/keys/Kconfig
index 290c9d3..07c7f3b 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -77,3 +77,12 @@ config CRYPTO_KEY_TYPE
This option provides support for a type of key that holds the keys
required for cryptographic operations such as encryption, decryption,
signature generation and signature verification.
+
+config CRYPTO_KEY_PUBLIC_KEY_SUBTYPE
+ tristate "Asymmetric public-key crypto algorithm subtype"
+ depends on CRYPTO_KEY_TYPE
+ help
+ This option provides support for asymmetric public key type handling.
+ If signature generation and/or verification are to be used,
+ appropriate hash algorithms (such as SHA-1) must be available.
+ ENOPKG will be reported if the requisite algorithm is unavailable.
diff --git a/security/keys/Makefile b/security/keys/Makefile
index 8462904..dc3281f 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -25,5 +25,6 @@ obj-$(CONFIG_SYSCTL) += sysctl.o
obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o
+obj-$(CONFIG_CRYPTO_KEY_PUBLIC_KEY_SUBTYPE) += public_key.o

crypto_keys-y := crypto_type.o crypto_verify.o
diff --git a/security/keys/public_key.c b/security/keys/public_key.c
new file mode 100644
index 0000000..c00ddac
--- /dev/null
+++ b/security/keys/public_key.c
@@ -0,0 +1,55 @@
+/* Asymmetric public key crypto subtype
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (***@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "PKEY: "fmt
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include "public_key.h"
+
+MODULE_LICENSE("GPL");
+
+/*
+ * Provide a part of a description of the key for /proc/keys.
+ */
+static void public_key_describe(const struct key *crypto_key,
+ struct seq_file *m)
+{
+ struct public_key *key = crypto_key->payload.data;
+
+ if (key)
+ seq_puts(m, key->algo->name);
+}
+
+/*
+ * Destroy a public key algorithm key
+ */
+static void public_key_destroy(void *payload)
+{
+ struct public_key *key = payload;
+ int i;
+
+ if (key) {
+ for (i = 0; i < ARRAY_SIZE(key->mpi); i++)
+ mpi_free(key->mpi[i]);
+ kfree(key);
+ }
+}
+
+/*
+ * Public key algorithm crypto key subtype
+ */
+struct crypto_key_subtype public_key_crypto_key_subtype = {
+ .owner = THIS_MODULE,
+ .name = "public_key",
+ .describe = public_key_describe,
+ .destroy = public_key_destroy,
+};
+EXPORT_SYMBOL_GPL(public_key_crypto_key_subtype);
diff --git a/security/keys/public_key.h b/security/keys/public_key.h
new file mode 100644
index 0000000..b5aab3d
--- /dev/null
+++ b/security/keys/public_key.h
@@ -0,0 +1,109 @@
+/* Asymmetric public-key algorithm definitions
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (***@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_PUBLIC_KEY_H
+#define _LINUX_PUBLIC_KEY_H
+
+#include <linux/mpi.h>
+#include <crypto/hash.h>
+#include <keys/crypto-subtype.h>
+
+struct public_key;
+struct public_key_signature;
+
+enum pkey_hash_algo {
+ PKEY_HASH_MD5,
+ PKEY_HASH_SHA1,
+ PKEY_HASH_RIPE_MD_160,
+ PKEY_HASH_SHA256,
+ PKEY_HASH_SHA384,
+ PKEY_HASH_SHA512,
+ PKEY_HASH_SHA224,
+ PKEY_HASH__LAST
+};
+
+/*
+ * Public key type definition
+ */
+struct public_key_algorithm {
+ const char *name;
+ u8 n_pub_mpi; /* Number of MPIs in public key */
+ u8 n_sec_mpi; /* Number of MPIs in secret key */
+ u8 n_sig_mpi; /* Number of MPIs in a signature */
+ int (*verify)(const struct public_key *key,
+ const struct public_key_signature *sig);
+};
+
+extern const struct public_key_algorithm DSA_public_key_algorithm;
+extern const struct public_key_algorithm RSA_public_key_algorithm;
+
+/*
+ * Asymmetric public key data
+ */
+struct public_key {
+ const struct public_key_algorithm *algo;
+ u8 capabilities;
+#define PKEY_CAN_ENCRYPT 0x01
+#define PKEY_CAN_DECRYPT 0x02
+#define PKEY_CAN_ENCDEC (PKEY_CAN_ENCRYPT | PKEY_CAN_DECRYPT)
+#define PKEY_CAN_SIGN 0x04
+#define PKEY_CAN_VERIFY 0x08
+#define PKEY_CAN_SIGVER (PKEY_CAN_SIGN | PKEY_CAN_VERIFY)
+ union {
+ MPI mpi[5];
+ struct {
+ MPI p; /* DSA prime */
+ MPI q; /* DSA group order */
+ MPI g; /* DSA group generator */
+ MPI y; /* DSA public-key value = g^x mod p */
+ MPI x; /* DSA secret exponent (if present) */
+ } dsa;
+ struct {
+ MPI n; /* RSA public modulus */
+ MPI e; /* RSA public encryption exponent */
+ MPI d; /* RSA secret encryption exponent (if present) */
+ MPI p; /* RSA secret prime (if present) */
+ MPI q; /* RSA secret prime (if present) */
+ } rsa;
+ };
+
+ u8 key_id[8]; /* ID of this key pair */
+ u8 key_id_size; /* Number of bytes in key_id */
+};
+
+/*
+ * Asymmetric public key algorithm signature data
+ */
+struct public_key_signature {
+ struct crypto_key_verify_context base;
+ u8 *digest;
+ enum pkey_hash_algo pkey_hash_algo : 8;
+ u8 signed_hash_msw[2];
+ u8 digest_size; /* Number of bytes in digest */
+ union {
+ MPI mpi[2];
+ struct {
+ MPI s; /* m^d mod n */
+ } rsa;
+ struct {
+ MPI r;
+ MPI s;
+ } dsa;
+ };
+ struct shash_desc hash; /* This must go last! */
+};
+
+extern struct crypto_key_verify_context *pgp_pkey_verify_sig_begin(
+ struct key *crypto_key, const u8 *sigdata, size_t siglen);
+
+extern struct crypto_key_subtype public_key_crypto_key_subtype;
+
+#endif /* _LINUX_PUBLIC_KEY_H */

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-02 18:44:46 UTC
Permalink
Implement the DSA algorithm (FIPS-186). At this time, only signature
verification is supported. This uses the asymmetric public key subtype to hold
its key data.

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

security/keys/Kconfig | 7 ++
security/keys/Makefile | 1
security/keys/crypto_dsa.c | 126 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 134 insertions(+), 0 deletions(-)
create mode 100644 security/keys/crypto_dsa.c


diff --git a/security/keys/Kconfig b/security/keys/Kconfig
index 07c7f3b..76de2ba 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -86,3 +86,10 @@ config CRYPTO_KEY_PUBLIC_KEY_SUBTYPE
If signature generation and/or verification are to be used,
appropriate hash algorithms (such as SHA-1) must be available.
ENOPKG will be reported if the requisite algorithm is unavailable.
+
+config CRYPTO_KEY_PKEY_ALGO_DSA
+ tristate "DSA public-key algorithm"
+ depends on CRYPTO_KEY_PUBLIC_KEY_SUBTYPE
+ help
+ This option enables support for the DSA public key algorithm
+ (FIPS-186).
diff --git a/security/keys/Makefile b/security/keys/Makefile
index dc3281f..5f1c627 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -26,5 +26,6 @@ obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o
obj-$(CONFIG_CRYPTO_KEY_PUBLIC_KEY_SUBTYPE) += public_key.o
+obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_DSA) += crypto_dsa.o

crypto_keys-y := crypto_type.o crypto_verify.o
diff --git a/security/keys/crypto_dsa.c b/security/keys/crypto_dsa.c
new file mode 100644
index 0000000..26b86f2
--- /dev/null
+++ b/security/keys/crypto_dsa.c
@@ -0,0 +1,126 @@
+/* DSA asymmetric public-key algorithm
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (***@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "DSA: "fmt
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include "public_key.h"
+
+MODULE_LICENSE("GPL");
+
+#define kenter(FMT, ...) \
+ pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
+#define kleave(FMT, ...) \
+ pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
+
+/*
+ * Perform the actual mathematical DSA signature verification.
+ */
+static int DSA_verify(const MPI datahash,
+ const struct public_key_signature *sig,
+ const struct public_key *key)
+{
+ MPI w = NULL, u1 = NULL, u2 = NULL, v = NULL;
+ MPI base[3];
+ MPI exp[3];
+ int rc;
+
+ kenter("");
+
+ if (!(mpi_cmp_ui(sig->dsa.r, 0) > 0 &&
+ mpi_cmp(sig->dsa.r, key->dsa.q) < 0)) {
+ pr_warning("Assertion failed [0 < r < q]\n");
+ return -EKEYREJECTED;
+ }
+
+ if (!(mpi_cmp_ui(sig->dsa.s, 0) > 0 &&
+ mpi_cmp(sig->dsa.s, key->dsa.q) < 0)) {
+ pr_warning("Assertion failed [0 < s < q]\n");
+ return -EKEYREJECTED;
+ }
+
+ rc = -ENOMEM;
+ w = mpi_alloc(mpi_get_nlimbs(key->dsa.q)); if (!w ) goto cleanup;
+ u1 = mpi_alloc(mpi_get_nlimbs(key->dsa.q)); if (!u1) goto cleanup;
+ u2 = mpi_alloc(mpi_get_nlimbs(key->dsa.q)); if (!u2) goto cleanup;
+ v = mpi_alloc(mpi_get_nlimbs(key->dsa.p)); if (!v ) goto cleanup;
+
+ /* w = s^(-1) mod q */
+ if (mpi_invm(w, sig->dsa.s, key->dsa.q) < 0)
+ goto cleanup;
+
+ /* u1 = (datahash * w) mod q */
+ if (mpi_mulm(u1, datahash, w, key->dsa.q) < 0)
+ goto cleanup;
+
+ /* u2 = r * w mod q */
+ if (mpi_mulm(u2, sig->dsa.r, w, key->dsa.q) < 0)
+ goto cleanup;
+
+ /* v = g^u1 * y^u2 mod p mod q */
+ base[0] = key->dsa.g; exp[0] = u1;
+ base[1] = key->dsa.y; exp[1] = u2;
+ base[2] = NULL; exp[2] = NULL;
+
+ if (mpi_mulpowm(v, base, exp, key->dsa.p) < 0)
+ goto cleanup;
+
+ if (mpi_fdiv_r(v, v, key->dsa.q) < 0)
+ goto cleanup;
+
+ rc = (mpi_cmp(v, sig->dsa.r) == 0) ? 0 : -EKEYREJECTED;
+
+cleanup:
+ mpi_free(w);
+ mpi_free(u1);
+ mpi_free(u2);
+ mpi_free(v);
+ kleave(" = %d", rc);
+ return rc;
+}
+
+/*
+ * Perform the verification step.
+ */
+static int DSA_verify_signature(const struct public_key *key,
+ const struct public_key_signature *sig)
+{
+ MPI datahash = NULL;
+ int ret;
+
+ kenter("");
+
+ ret = -ENOMEM;
+ datahash = mpi_alloc((sig->digest_size + BYTES_PER_MPI_LIMB - 1) /
+ BYTES_PER_MPI_LIMB);
+ if (!datahash)
+ goto error;
+
+ ret = mpi_set_buffer(datahash, sig->digest, sig->digest_size, 0);
+ if (ret < 0)
+ goto error;
+
+ ret = DSA_verify(datahash, sig, key);
+
+error:
+ mpi_free(datahash);
+ kleave(" = %d", ret);
+ return ret;
+}
+
+const struct public_key_algorithm DSA_public_key_algorithm = {
+ .name = "DSA",
+ .n_pub_mpi = 4,
+ .n_sec_mpi = 1,
+ .n_sig_mpi = 2,
+ .verify = DSA_verify_signature,
+};
+EXPORT_SYMBOL_GPL(DSA_public_key_algorithm);

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-02 18:45:01 UTC
Permalink
Implement the RSA algorithm (PKCS#1 / RFC3447). At this time, only signature
verification is supported. This uses the asymmetric public key subtype to hold
its key data.

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

security/keys/Kconfig | 6 +
security/keys/Makefile | 1
security/keys/crypto_rsa.c | 282 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 289 insertions(+), 0 deletions(-)
create mode 100644 security/keys/crypto_rsa.c


diff --git a/security/keys/Kconfig b/security/keys/Kconfig
index 76de2ba..0090cd5 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -93,3 +93,9 @@ config CRYPTO_KEY_PKEY_ALGO_DSA
help
This option enables support for the DSA public key algorithm
(FIPS-186).
+
+config CRYPTO_KEY_PKEY_ALGO_RSA
+ tristate "RSA public-key algorithm"
+ depends on CRYPTO_KEY_PUBLIC_KEY_SUBTYPE
+ help
+ This option enables support for the RSA algorithm (PKCS#1, RFC3447).
diff --git a/security/keys/Makefile b/security/keys/Makefile
index 5f1c627..7245a20 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -27,5 +27,6 @@ obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o
obj-$(CONFIG_CRYPTO_KEY_PUBLIC_KEY_SUBTYPE) += public_key.o
obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_DSA) += crypto_dsa.o
+obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) += crypto_rsa.o

crypto_keys-y := crypto_type.o crypto_verify.o
diff --git a/security/keys/crypto_rsa.c b/security/keys/crypto_rsa.c
new file mode 100644
index 0000000..beb5181
--- /dev/null
+++ b/security/keys/crypto_rsa.c
@@ -0,0 +1,282 @@
+/* RSA asymmetric public-key algorithm [RFC3447]
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (***@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "RSA: "fmt
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include "public_key.h"
+
+MODULE_LICENSE("GPL");
+
+#define kenter(FMT, ...) \
+ pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
+#define kleave(FMT, ...) \
+ pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
+
+/*
+ * Hash algorithm OIDs plus ASN.1 DER wrappings [RFC4880 sec 5.2.2].
+ */
+static const u8 RSA_digest_info_MD5[] = {
+ 0x30, 0x20, 0x30, 0x0C, 0x06, 0x08,
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, /* OID */
+ 0x05, 0x00, 0x04, 0x10
+};
+
+static const u8 RSA_digest_info_SHA1[] = {
+ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05,
+ 0x2B, 0x0E, 0x03, 0x02, 0x1A,
+ 0x05, 0x00, 0x04, 0x14
+};
+
+static const u8 RSA_digest_info_RIPE_MD_160[] = {
+ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05,
+ 0x2B, 0x24, 0x03, 0x02, 0x01,
+ 0x05, 0x00, 0x04, 0x14
+};
+
+static const u8 RSA_digest_info_SHA224[] = {
+ 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04,
+ 0x05, 0x00, 0x04, 0x1C
+};
+
+static const u8 RSA_digest_info_SHA256[] = {
+ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
+ 0x05, 0x00, 0x04, 0x20
+};
+
+static const u8 RSA_digest_info_SHA384[] = {
+ 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02,
+ 0x05, 0x00, 0x04, 0x30
+};
+
+static const u8 RSA_digest_info_SHA512[] = {
+ 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
+ 0x05, 0x00, 0x04, 0x40
+};
+
+static const struct {
+ const u8 const *data;
+ size_t size;
+} RSA_ASN1_templates[PKEY_HASH__LAST] = {
+#define _(X) { RSA_digest_info_##X, sizeof(RSA_digest_info_##X) }
+ [PKEY_HASH_MD5] = _(MD5),
+ [PKEY_HASH_SHA1] = _(SHA1),
+ [PKEY_HASH_RIPE_MD_160] = _(RIPE_MD_160),
+ [PKEY_HASH_SHA256] = _(SHA256),
+ [PKEY_HASH_SHA384] = _(SHA384),
+ [PKEY_HASH_SHA512] = _(SHA512),
+ [PKEY_HASH_SHA224] = _(SHA224),
+#undef _
+};
+
+/*
+ * RSAVP1() function [RFC3447 sec 5.2.2]
+ */
+static int RSAVP1(const struct public_key *key, MPI s, MPI *_m)
+{
+ MPI m;
+ int ret;
+
+ /* (1) Validate 0 <= s < n */
+ if (mpi_cmp_ui(s, 0) < 0) {
+ kleave(" = -EBADMSG [s < 0]");
+ return -EBADMSG;
+ }
+ if (mpi_cmp(s, key->rsa.n) >= 0) {
+ kleave(" = -EBADMSG [s >= n]");
+ return -EBADMSG;
+ }
+
+ m = mpi_alloc(0);
+ if (!m)
+ return -ENOMEM;
+
+ /* (2) m = s^e mod n */
+ ret = mpi_powm(m, s, key->rsa.e, key->rsa.n);
+ if (ret < 0) {
+ mpi_free(m);
+ return ret;
+ }
+
+ *_m = m;
+ return 0;
+}
+
+/*
+ * Integer to Octet String conversion [RFC3447 sec 4.1]
+ */
+static int RSA_I2OSP(MPI x, size_t xLen, u8 **_X)
+{
+ unsigned X_size, x_size;
+ int X_sign;
+ u8 *X;
+
+ /* Make sure the string is the right length. The number should begin
+ * with { 0x00, 0x01, ... } so we have to account for 15 leading zero
+ * bits not being reported by MPI.
+ */
+ x_size = mpi_get_nbits(x);
+ pr_devel("size(x)=%u xLen*8=%zu\n", x_size, xLen * 8);
+ if (x_size != xLen * 8 - 15)
+ return -ERANGE;
+
+ X = mpi_get_buffer(x, &X_size, &X_sign);
+ if (!X)
+ return -ENOMEM;
+ if (X_sign < 0) {
+ kfree(X);
+ return -EBADMSG;
+ }
+ if (X_size != xLen - 1) {
+ kfree(X);
+ return -EBADMSG;
+ }
+
+ *_X = X;
+ return 0;
+}
+
+/*
+ * Perform the RSA signature verification.
+ * @H: Value of hash of data and metadata
+ * @EM: The computed signature value
+ * @k: The size of EM (EM[0] is an invalid location but should hold 0x00)
+ * @hash_size: The size of H
+ * @asn1_template: The DigestInfo ASN.1 template
+ * @asn1_size: Size of asm1_template[]
+ */
+static int RSA_verify(const u8 *H, const u8 *EM, size_t k, size_t hash_size,
+ const u8 *asn1_template, size_t asn1_size)
+{
+ unsigned PS_end, T_offset, i;
+
+ kenter(",,%zu,%zu,%zu", k, hash_size, asn1_size);
+
+ if (k < 2 + 1 + asn1_size + hash_size)
+ return -EBADMSG;
+
+ /* Decode the EMSA-PKCS1-v1_5 */
+ if (EM[1] != 0x01) {
+ kleave(" = -EBADMSG [EM[1] == %02u]", EM[1]);
+ return -EBADMSG;
+ }
+
+ T_offset = k - (asn1_size + hash_size);
+ PS_end = T_offset - 1;
+ if (EM[PS_end] != 0x00) {
+ kleave(" = -EBADMSG [EM[T-1] == %02u]", EM[PS_end]);
+ return -EBADMSG;
+ }
+
+ for (i = 2; i < PS_end; i++) {
+ if (EM[i] != 0xff) {
+ kleave(" = -EBADMSG [EM[PS%x] == %02u]", i - 2, EM[i]);
+ return -EBADMSG;
+ }
+ }
+
+ if (memcmp(asn1_template, EM + T_offset, asn1_size) != 0) {
+ kleave(" = -EBADMSG [EM[T] ASN.1 mismatch]");
+ return -EBADMSG;
+ }
+
+ if (memcmp(H, EM + T_offset + asn1_size, hash_size) != 0) {
+ kleave(" = -EKEYREJECTED [EM[T] hash mismatch]");
+ return -EKEYREJECTED;
+ }
+
+ kleave(" = 0");
+ return 0;
+}
+
+/*
+ * Perform the verification step [RFC3447 sec 8.2.2].
+ */
+static int RSA_verify_signature(const struct public_key *key,
+ const struct public_key_signature *sig)
+{
+ size_t tsize;
+ int ret;
+
+ /* Variables as per RFC3447 sec 8.2.2 */
+ const u8 *H = sig->digest;
+ u8 *EM = NULL;
+ MPI m = NULL;
+ size_t k;
+
+ kenter("");
+
+ /* (1) Check the signature size against the public key modulus size */
+ k = (mpi_get_nbits(key->rsa.n) + 7) / 8;
+
+ tsize = (mpi_get_nbits(sig->rsa.s) + 7) / 8;
+ pr_devel("step 1: k=%zu size(S)=%zu\n", k, tsize);
+ if (tsize != k) {
+ ret = -EBADMSG;
+ goto error;
+ }
+
+ /* (2b) Apply the RSAVP1 verification primitive to the public key */
+ ret = RSAVP1(key, sig->rsa.s, &m);
+ if (ret < 0)
+ goto error;
+
+ /* (2c) Convert the message representative (m) to an encoded message
+ * (EM) of length k octets.
+ *
+ * NOTE! The leading zero byte is suppressed by MPI, so we pass a
+ * pointer to the _preceding_ byte to RSA_verify()!
+ */
+ ret = RSA_I2OSP(m, k, &EM);
+ if (ret < 0)
+ goto error;
+
+#if 0
+ {
+ int i;
+ printk("H: ");
+ for (i = 0; i < sig->digest_size; i++)
+ printk("%02x", H[i]);
+ printk("\n");
+ }
+
+ {
+ int i;
+ printk("EM: 00");
+ for (i = 0; i < k - 1; i++)
+ printk("%02x", EM[i]);
+ printk("\n");
+ }
+#endif
+
+ ret = RSA_verify(H, EM - 1, k, sig->digest_size,
+ RSA_ASN1_templates[sig->pkey_hash_algo].data,
+ RSA_ASN1_templates[sig->pkey_hash_algo].size);
+
+error:
+ kfree(EM);
+ mpi_free(m);
+ kleave(" = %d", ret);
+ return ret;
+}
+
+const struct public_key_algorithm RSA_public_key_algorithm = {
+ .name = "RSA",
+ .n_pub_mpi = 2,
+ .n_sec_mpi = 3,
+ .n_sig_mpi = 1,
+ .verify = RSA_verify_signature,
+};
+EXPORT_SYMBOL_GPL(RSA_public_key_algorithm);

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-02 18:45:19 UTC
Permalink
Provide some useful PGP definitions from RFC 4880. These describe details of
public key crypto as used by crypto keys for things like signature
verification.

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

include/linux/pgp.h | 206 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 206 insertions(+), 0 deletions(-)
create mode 100644 include/linux/pgp.h


diff --git a/include/linux/pgp.h b/include/linux/pgp.h
new file mode 100644
index 0000000..5d4b372
--- /dev/null
+++ b/include/linux/pgp.h
@@ -0,0 +1,206 @@
+/* PGP definitions (RFC 4880)
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (***@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_PGP_H
+#define _LINUX_PGP_H
+
+#include <linux/types.h>
+
+struct pgp_key_ID {
+ u8 id[8];
+};
+
+struct pgp_time {
+ u8 time[4];
+};
+
+/*
+ * PGP public-key algorithm identifiers [RFC4880: 9.1]
+ */
+enum pgp_pubkey_algo {
+ PGP_PUBKEY_RSA_ENC_OR_SIG = 1,
+ PGP_PUBKEY_RSA_ENC_ONLY = 2,
+ PGP_PUBKEY_RSA_SIG_ONLY = 3,
+ PGP_PUBKEY_ELGAMAL = 16,
+ PGP_PUBKEY_DSA = 17,
+ PGP_PUBKEY__LAST
+};
+
+/*
+ * PGP symmetric-key algorithm identifiers [RFC4880: 9.2]
+ */
+enum pgp_symkey_algo {
+ PGP_SYMKEY_PLAINTEXT = 0,
+ PGP_SYMKEY_IDEA = 1,
+ PGP_SYMKEY_3DES = 2,
+ PGP_SYMKEY_CAST5 = 3,
+ PGP_SYMKEY_BLOWFISH = 4,
+ PGP_SYMKEY_AES_128KEY = 7,
+ PGP_SYMKEY_AES_192KEY = 8,
+ PGP_SYMKEY_AES_256KEY = 9,
+ PGP_SYMKEY_TWOFISH_256KEY = 10,
+};
+
+/*
+ * PGP compression algorithm identifiers [RFC4880: 9.3]
+ */
+enum pgp_compr_algo {
+ PGP_COMPR_UNCOMPRESSED = 0,
+ PGP_COMPR_ZIP = 1,
+ PGP_COMPR_ZLIB = 2,
+ PGP_COMPR_BZIP2 = 3,
+};
+
+/*
+ * PGP hash algorithm identifiers [RFC4880: 9.4]
+ */
+enum pgp_hash_algo {
+ PGP_HASH_MD5 = 1,
+ PGP_HASH_SHA1 = 2,
+ PGP_HASH_RIPE_MD_160 = 3,
+ PGP_HASH_SHA256 = 8,
+ PGP_HASH_SHA384 = 9,
+ PGP_HASH_SHA512 = 10,
+ PGP_HASH_SHA224 = 11,
+ PGP_HASH__LAST
+};
+
+extern const char *const pgp_hash_algorithms[PGP_HASH__LAST];
+
+/*
+ * PGP packet type tags [RFC4880: 4.3].
+ */
+enum pgp_packet_tag {
+ PGP_PKT_RESERVED = 0,
+ PGP_PKT_PUBKEY_ENC_SESSION_KEY = 1,
+ PGP_PKT_SIGNATURE = 2,
+ PGP_PKT_SYMKEY_ENC_SESSION_KEY = 3,
+ PGP_PKT_ONEPASS_SIGNATURE = 4,
+ PGP_PKT_SECRET_KEY = 5,
+ PGP_PKT_PUBLIC_KEY = 6,
+ PGP_PKT_SECRET_SUBKEY = 7,
+ PGP_PKT_COMPRESSED_DATA = 8,
+ PGP_PKT_SYM_ENC_DATA = 9,
+ PGP_PKT_MARKER = 10,
+ PGP_PKT_LITERAL_DATA = 11,
+ PGP_PKT_TRUST = 12,
+ PGP_PKT_USER_ID = 13,
+ PGP_PKT_PUBLIC_SUBKEY = 14,
+ PGP_PKT_USER_ATTRIBUTE = 17,
+ PGP_PKT_SYM_ENC_AND_INTEG_DATA = 18,
+ PGP_PKT_MODIFY_DETECT_CODE = 19,
+ PGP_PKT_PRIVATE_0 = 60,
+ PGP_PKT_PRIVATE_3 = 63,
+ PGP_PKT__HIGHEST = 63
+};
+
+/*
+ * Signature (tag 2) packet [RFC4880: 5.2].
+ */
+enum pgp_signature_version {
+ PGP_SIG_VERSION_3 = 3,
+ PGP_SIG_VERSION_4 = 4,
+};
+
+enum pgp_signature_type {
+ PGP_SIG_BINARY_DOCUMENT_SIG = 0x00,
+ PGP_SIG_CANONICAL_TEXT_DOCUMENT_SIG = 0x01,
+ PGP_SIG_STANDALONE_SIG = 0x02,
+ PGP_SIG_GENERAL_CERT_OF_UID_PUBKEY = 0x10,
+ PGP_SIG_PERSONAL_CERT_OF_UID_PUBKEY = 0x11,
+ PGP_SIG_CASUAL_CERT_OF_UID_PUBKEY = 0x12,
+ PGP_SIG_POSTITIVE_CERT_OF_UID_PUBKEY = 0x13,
+ PGP_SIG_SUBKEY_BINDING_SIG = 0x18,
+ PGP_SIG_PRIMARY_KEY_BINDING_SIG = 0x19,
+ PGP_SIG_DIRECTLY_ON_KEY = 0x1F,
+ PGP_SIG_KEY_REVOCATION_SIG = 0x20,
+ PGP_SIG_SUBKEY_REVOCATION_SIG = 0x28,
+ PGP_SIG_CERT_REVOCATION_SIG = 0x30,
+ PGP_SIG_TIMESTAMP_SIG = 0x40,
+ PGP_SIG_THIRD_PARTY_CONFIRM_SIG = 0x50,
+};
+
+struct pgp_signature_v3_packet {
+ enum pgp_signature_version version : 8; /* == PGP_SIG_VERSION_3 */
+ u8 length_of_hashed; /* == 5 */
+ struct {
+ enum pgp_signature_type signature_type : 8;
+ struct pgp_time creation_time;
+ } hashed;
+ struct pgp_key_ID issuer;
+ enum pgp_pubkey_algo pubkey_algo : 8;
+ enum pgp_hash_algo hash_algo : 8;
+};
+
+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];
+};
+
+#endif /* _LINUX_PGP_H */

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-02 18:45:31 UTC
Permalink
Provide a simple parser that extracts the packets from a PGP packet blob and
passes the desirous ones to the given processor function:

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

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

This is configured on with CONFIG_PGP_LIBRARY.

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

include/linux/pgp.h | 25 ++++
security/keys/Kconfig | 6 +
security/keys/Makefile | 1
security/keys/pgp_library.c | 254 +++++++++++++++++++++++++++++++++++++++++++
4 files changed, 286 insertions(+), 0 deletions(-)
create mode 100644 security/keys/pgp_library.c


diff --git a/include/linux/pgp.h b/include/linux/pgp.h
index 5d4b372..06f5aaa 100644
--- a/include/linux/pgp.h
+++ b/include/linux/pgp.h
@@ -203,4 +203,29 @@ struct pgp_key_v4_packet {
u8 key_material[0];
};

+/*
+ * PGP library packet parser
+ */
+struct pgp_parse_context {
+ u64 types_of_interest;
+ int (*process_packet)(struct pgp_parse_context *context,
+ enum pgp_packet_tag type,
+ u8 headerlen,
+ const u8 *data,
+ size_t datalen);
+};
+
+extern int pgp_parse_packets(const u8 *data, size_t datalen,
+ struct pgp_parse_context *ctx);
+
+struct pgp_parse_pubkey {
+ enum pgp_key_version version : 8;
+ enum pgp_pubkey_algo pubkey_algo : 8;
+ time_t creation_time;
+ time_t expires_at;
+};
+
+extern int pgp_parse_public_key(const u8 **_data, size_t *_datalen,
+ struct pgp_parse_pubkey *pk);
+
#endif /* _LINUX_PGP_H */
diff --git a/security/keys/Kconfig b/security/keys/Kconfig
index 0090cd5..3196ba8 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -99,3 +99,9 @@ config CRYPTO_KEY_PKEY_ALGO_RSA
depends on CRYPTO_KEY_PUBLIC_KEY_SUBTYPE
help
This option enables support for the RSA algorithm (PKCS#1, RFC3447).
+
+config PGP_LIBRARY
+ tristate "PGP parsing library"
+ help
+ This option enables a library that provides a number of simple
+ utility functions for parsing PGP (RFC 4880) packet-based messages.
diff --git a/security/keys/Makefile b/security/keys/Makefile
index 7245a20..3c3918a 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -28,5 +28,6 @@ obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o
obj-$(CONFIG_CRYPTO_KEY_PUBLIC_KEY_SUBTYPE) += public_key.o
obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_DSA) += crypto_dsa.o
obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) += crypto_rsa.o
+obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o

crypto_keys-y := crypto_type.o crypto_verify.o
diff --git a/security/keys/pgp_library.c b/security/keys/pgp_library.c
new file mode 100644
index 0000000..685660f
--- /dev/null
+++ b/security/keys/pgp_library.c
@@ -0,0 +1,254 @@
+/* PGP packet parser (RFC 4880)
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (***@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#define pr_fmt(fmt) "PGP: "fmt
+#include <linux/pgp.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+MODULE_LICENSE("GPL");
+
+const char *const pgp_hash_algorithms[PGP_HASH__LAST] = {
+ [PGP_HASH_MD5] = "md5",
+ [PGP_HASH_SHA1] = "sha1",
+ [PGP_HASH_RIPE_MD_160] = "rmd160",
+ [PGP_HASH_SHA256] = "sha256",
+ [PGP_HASH_SHA384] = "sha384",
+ [PGP_HASH_SHA512] = "sha512",
+ [PGP_HASH_SHA224] = "sha224",
+};
+EXPORT_SYMBOL_GPL(pgp_hash_algorithms);
+
+/**
+ * pgp_parse_packet_header - Parse a PGP packet header
+ * @_data: Start of the PGP packet (updated to PGP packet data)
+ * @_datalen: Amount of data remaining in buffer (decreased)
+ * @_type: Where the packet type will be returned
+ * @_headerlen: Where the header length will be returned
+ *
+ * Parse a set of PGP packet header [RFC 4880: 4.2].
+ *
+ * Returns packet data size on success; non-zero on error. If successful,
+ * *_data and *_datalen will have been updated and *_headerlen will be set to
+ * hold the length of the packet header.
+ */
+ssize_t pgp_parse_packet_header(const u8 **_data, size_t *_datalen,
+ enum pgp_packet_tag *_type,
+ u8 *_headerlen)
+{
+ enum pgp_packet_tag type;
+ const u8 *data = *_data;
+ size_t size, datalen = *_datalen;
+
+ pr_devel("-->pgp_parse_packet_header(,%zu,,)", datalen);
+
+ if (datalen < 2)
+ goto short_packet;
+
+ pr_devel("pkthdr %02x, %02x\n", data[0], data[1]);
+
+ type = *data++;
+ datalen--;
+ if (!(type & 0x80)) {
+ pr_debug("Packet type does not have MSB set\n");
+ return -EBADMSG;
+ }
+ type &= ~0x80;
+
+ if (type & 0x40) {
+ /* New packet length format */
+ type &= ~0x40;
+ pr_devel("new format: t=%u\n", type);
+ switch (data[0]) {
+ case 0x00 ... 0xbf:
+ /* One-byte length */
+ size = data[0];
+ data++;
+ datalen--;
+ *_headerlen = 2;
+ break;
+ case 0xc0 ... 0xdf:
+ /* Two-byte length */
+ if (datalen < 2)
+ goto short_packet;
+ size = (data[0] - 192) * 256;
+ size += data[1] + 192;
+ data += 2;
+ datalen -= 2;
+ *_headerlen = 3;
+ break;
+ case 0xff:
+ pr_debug("Five-byte packet length not supported\n");
+ return -EBADMSG;
+ default:
+ pr_debug("Error parsing packet length\n");
+ return -EBADMSG;
+ }
+ } else {
+ /* Old packet length format */
+ u8 length_type = type & 0x03;
+ type >>= 2;
+ pr_devel("old format: t=%u lt=%u\n", type, length_type);
+
+ switch (length_type) {
+ case 0:
+ /* One-byte length */
+ size = data[0];
+ data++;
+ datalen--;
+ *_headerlen = 2;
+ break;
+ case 1:
+ /* Two-byte length */
+ if (datalen < 2)
+ goto short_packet;
+ size = data[0] << 8;
+ size |= data[1];
+ data += 2;
+ datalen -= 2;
+ *_headerlen = 3;
+ break;
+ case 2:
+ /* Four-byte length */
+ if (datalen < 4)
+ goto short_packet;
+ size = data[0] << 24;
+ size |= data[1] << 16;
+ size |= data[2] << 8;
+ size |= data[3];
+ data += 4;
+ datalen -= 4;
+ *_headerlen = 5;
+ break;
+ default:
+ pr_debug("Indefinite length packet not supported\n");
+ return -EBADMSG;
+ }
+ }
+
+ pr_devel("datalen=%zu size=%zu", datalen, size);
+ if (datalen < size)
+ goto short_packet;
+
+ *_data = data;
+ *_datalen = datalen;
+ *_type = type;
+ pr_devel("Found packet type=%u size=%zd\n", type, size);
+ return size;
+
+short_packet:
+ pr_debug("Attempt to parse short packet\n");
+ return -EBADMSG;
+}
+
+/**
+ * pgp_parse_packets - Parse a set of PGP packets
+ * @_data: Data to be parsed (updated)
+ * @_datalen: Amount of data (updated)
+ * @ctx: Parsing context
+ *
+ * Parse a set of PGP packets [RFC 4880: 4].
+ */
+int pgp_parse_packets(const u8 *data, size_t datalen,
+ struct pgp_parse_context *ctx)
+{
+ enum pgp_packet_tag type;
+ ssize_t pktlen;
+ u8 headerlen;
+ int ret;
+
+ while (datalen > 2) {
+ pktlen = pgp_parse_packet_header(&data, &datalen, &type,
+ &headerlen);
+ if (pktlen < 0)
+ return pktlen;
+
+ if ((ctx->types_of_interest >> type) & 1) {
+ ret = ctx->process_packet(ctx, type, headerlen,
+ data, pktlen);
+ if (ret < 0)
+ return ret;
+ }
+ data += pktlen;
+ datalen -= pktlen;
+ }
+
+ if (datalen != 0) {
+ pr_debug("Excess octets in packet stream\n");
+ return -EBADMSG;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pgp_parse_packets);
+
+/**
+ * pgp_parse_public_key - Parse the common part of a PGP pubkey packet
+ * @_data: Content of packet (updated)
+ * @_datalen: Length of packet remaining (updated)
+ * @pk: Public key data
+ *
+ * Parse the common data struct for a PGP pubkey packet [RFC 4880: 5.5.2].
+ */
+int pgp_parse_public_key(const u8 **_data, size_t *_datalen,
+ struct pgp_parse_pubkey *pk)
+{
+ const u8 *data = *_data;
+ size_t datalen = *_datalen;
+ __be32 tmp;
+
+ if (datalen < 12) {
+ pr_debug("Public key packet too short\n");
+ return -EBADMSG;
+ }
+
+ pk->version = *data++;
+ switch (pk->version) {
+ case PGP_KEY_VERSION_2:
+ case PGP_KEY_VERSION_3:
+ case PGP_KEY_VERSION_4:
+ break;
+ default:
+ pr_debug("Public key packet with unhandled version %d\n",
+ pk->version);
+ return -EBADMSG;
+ }
+
+ tmp = *data++ << 24;
+ tmp |= *data++ << 16;
+ tmp |= *data++ << 8;
+ tmp |= *data++;
+ pk->creation_time = tmp;
+ if (pk->version == PGP_KEY_VERSION_4) {
+ pk->expires_at = 0; /* Have to get it from the selfsignature */
+ } else {
+ unsigned short ndays;
+ ndays = *data++ << 8;
+ ndays |= *data++;
+ if (ndays)
+ pk->expires_at = pk->creation_time + ndays * 86400UL;
+ else
+ pk->expires_at = 0;
+ datalen -= 2;
+ }
+
+ pk->pubkey_algo = *data++;
+ datalen -= 6;
+
+ pr_devel("%x,%x,%lx,%lx",
+ pk->version, pk->pubkey_algo, pk->creation_time,
+ pk->expires_at);
+
+ *_data = data;
+ *_datalen = datalen;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pgp_parse_public_key);

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-02 18:45:48 UTC
Permalink
Provide some PGP signature parsing helpers:

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

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

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

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

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

include/linux/pgp.h | 24 ++++
security/keys/pgp_library.c | 277 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 301 insertions(+), 0 deletions(-)


diff --git a/include/linux/pgp.h b/include/linux/pgp.h
index 06f5aaa..8b24508 100644
--- a/include/linux/pgp.h
+++ b/include/linux/pgp.h
@@ -228,4 +228,28 @@ struct pgp_parse_pubkey {
extern int pgp_parse_public_key(const u8 **_data, size_t *_datalen,
struct pgp_parse_pubkey *pk);

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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-02 18:45:57 UTC
Permalink
Implement a PGP data parser for the crypto key type to use when instantiating a
key.

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

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

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

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


diff --git a/security/keys/Kconfig b/security/keys/Kconfig
index 3196ba8..8ffe822 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -105,3 +105,15 @@ config PGP_LIBRARY
help
This option enables a library that provides a number of simple
utility functions for parsing PGP (RFC 4880) packet-based messages.
+
+config CRYPTO_KEY_PGP_PARSER
+ tristate "PGP key blob parser"
+ depends on CRYPTO_KEY_TYPE
+ select CRYPTO_KEY_PUBLIC_KEY_SUBTYPE
+ select PGP_LIBRARY
+ select MD5 # V3 fingerprint generation
+ select SHA1 # V4 fingerprint generation
+ help
+ This option provides support for parsing PGP (RFC 4880) format blobs
+ for key data and provides the ability to instantiate a crypto key
+ from a public key packet found inside the blob.
diff --git a/security/keys/Makefile b/security/keys/Makefile
index 3c3918a..242a087 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -29,5 +29,9 @@ obj-$(CONFIG_CRYPTO_KEY_PUBLIC_KEY_SUBTYPE) += public_key.o
obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_DSA) += crypto_dsa.o
obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) += crypto_rsa.o
obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o
+obj-$(CONFIG_CRYPTO_KEY_PGP_PARSER) += pgp_parser.o

crypto_keys-y := crypto_type.o crypto_verify.o
+
+pgp_parser-y := \
+ pgp_key_parser.o
diff --git a/security/keys/pgp_key_parser.c b/security/keys/pgp_key_parser.c
new file mode 100644
index 0000000..d0d8a86
--- /dev/null
+++ b/security/keys/pgp_key_parser.c
@@ -0,0 +1,342 @@
+/* Parser for PGP format key data [RFC 4880]
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (***@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "PGP: "fmt
+#include <keys/crypto-subtype.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mpi.h>
+#include <linux/pgp.h>
+#include <crypto/hash.h>
+#include "public_key.h"
+#include "pgp_parser.h"
+
+MODULE_LICENSE("GPL");
+
+const
+struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST] = {
+#if defined(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) || \
+ defined(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA_MODULE)
+ [PGP_PUBKEY_RSA_ENC_OR_SIG] = &RSA_public_key_algorithm,
+ [PGP_PUBKEY_RSA_ENC_ONLY] = &RSA_public_key_algorithm,
+ [PGP_PUBKEY_RSA_SIG_ONLY] = &RSA_public_key_algorithm,
+#endif
+ [PGP_PUBKEY_ELGAMAL] = NULL,
+#if defined(CONFIG_CRYPTO_KEY_PKEY_ALGO_DSA) || \
+ defined(CONFIG_CRYPTO_KEY_PKEY_ALGO_DSA_MODULE)
+ [PGP_PUBKEY_DSA] = &DSA_public_key_algorithm,
+#endif
+};
+
+static const u8 pgp_public_key_capabilities[PGP_PUBKEY__LAST] = {
+ [PGP_PUBKEY_RSA_ENC_OR_SIG] = PKEY_CAN_ENCDEC | PKEY_CAN_SIGVER,
+ [PGP_PUBKEY_RSA_ENC_ONLY] = PKEY_CAN_ENCDEC,
+ [PGP_PUBKEY_RSA_SIG_ONLY] = PKEY_CAN_SIGVER,
+ [PGP_PUBKEY_ELGAMAL] = 0,
+ [PGP_PUBKEY_DSA] = PKEY_CAN_SIGVER,
+};
+
+static inline void digest_putc(struct shash_desc *digest, uint8_t ch)
+{
+ crypto_shash_update(digest, &ch, 1);
+}
+
+struct pgp_key_data_parse_context {
+ struct pgp_parse_context pgp;
+ struct crypto_key_subtype *subtype;
+ char *fingerprint;
+ void *payload;
+};
+
+/*
+ * Calculate the public key ID (RFC4880 12.2)
+ */
+static int pgp_calc_pkey_keyid(struct shash_desc *digest,
+ struct pgp_parse_pubkey *pgp,
+ struct public_key *key)
+{
+ unsigned nb[ARRAY_SIZE(key->mpi)];
+ unsigned nn[ARRAY_SIZE(key->mpi)];
+ unsigned n;
+ u8 *pp[ARRAY_SIZE(key->mpi)];
+ u32 a32;
+ int npkey = key->algo->n_pub_mpi;
+ int i, ret = -ENOMEM;
+
+ kenter("");
+
+ n = (pgp->version < PGP_KEY_VERSION_4) ? 8 : 6;
+ for (i = 0; i < npkey; i++) {
+ nb[i] = mpi_get_nbits(key->mpi[i]);
+ pp[i] = mpi_get_buffer(key->mpi[i], nn + i, NULL);
+ if (!pp[i])
+ goto error;
+ n += 2 + nn[i];
+ }
+
+ digest_putc(digest, 0x99); /* ctb */
+ digest_putc(digest, n >> 8); /* 16-bit header length */
+ digest_putc(digest, n);
+ digest_putc(digest, pgp->version);
+
+ a32 = pgp->creation_time;
+ digest_putc(digest, a32 >> 24);
+ digest_putc(digest, a32 >> 16);
+ digest_putc(digest, a32 >> 8);
+ digest_putc(digest, a32 >> 0);
+
+ if (pgp->version < PGP_KEY_VERSION_4) {
+ u16 a16;
+
+ if( pgp->expires_at)
+ a16 = (pgp->expires_at - pgp->creation_time) / 86400UL;
+ else
+ a16 = 0;
+ digest_putc(digest, a16 >> 8);
+ digest_putc(digest, a16 >> 0);
+ }
+
+ digest_putc(digest, pgp->pubkey_algo);
+
+ for (i = 0; i < npkey; i++) {
+ digest_putc(digest, nb[i] >> 8);
+ digest_putc(digest, nb[i]);
+ crypto_shash_update(digest, pp[i], nn[i]);
+ }
+ ret = 0;
+
+error:
+ for (i = 0; i < npkey; i++)
+ kfree(pp[i]);
+ kleave(" = %d", ret);
+ return ret;
+}
+
+/*
+ * Calculate the public key ID fingerprint
+ */
+static int pgp_generate_fingerprint(struct pgp_key_data_parse_context *ctx,
+ struct pgp_parse_pubkey *pgp,
+ struct public_key *key)
+{
+ struct crypto_shash *tfm;
+ struct shash_desc *digest;
+ char *fingerprint;
+ u8 *raw_fingerprint;
+ int digest_size, offset;
+ int ret, i;
+
+ ret = -ENOMEM;
+ tfm = crypto_alloc_shash(pgp->version < PGP_KEY_VERSION_4 ?
+ "md5" : "sha1", 0, 0);
+ if (!tfm)
+ goto cleanup;
+
+ digest = kmalloc(sizeof(*digest) + crypto_shash_descsize(tfm),
+ GFP_KERNEL);
+ if (!digest)
+ goto cleanup_tfm;
+
+ digest->tfm = tfm;
+ digest->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+ ret = crypto_shash_init(digest);
+ if (ret < 0)
+ goto cleanup_hash;
+
+ ret = pgp_calc_pkey_keyid(digest, pgp, key);
+ if (ret < 0)
+ goto cleanup_hash;
+
+ digest_size = crypto_shash_digestsize(tfm);
+
+ raw_fingerprint = kmalloc(digest_size, GFP_KERNEL);
+ if (!raw_fingerprint)
+ goto cleanup_hash;
+
+ ret = crypto_shash_final(digest, raw_fingerprint);
+ if (ret < 0)
+ goto cleanup_raw_fingerprint;
+
+ fingerprint = kmalloc(digest_size * 2 + 1, GFP_KERNEL);
+ if (!fingerprint)
+ goto cleanup_raw_fingerprint;
+
+ offset = digest_size - 8;
+ pr_debug("offset %u/%u\n", offset, digest_size);
+
+ for (i = 0; i < digest_size; i++)
+ sprintf(fingerprint + i * 2, "%02x", raw_fingerprint[i]);
+ pr_debug("fingerprint %s\n", fingerprint);
+
+ memcpy(&key->key_id, raw_fingerprint + offset, 8);
+ key->key_id_size = 8;
+
+ ctx->fingerprint = fingerprint;
+ ret = 0;
+cleanup_raw_fingerprint:
+ kfree(raw_fingerprint);
+cleanup_hash:
+ kfree(digest);
+cleanup_tfm:
+ crypto_free_shash(tfm);
+cleanup:
+ kleave(" = %d", ret);
+ return ret;
+}
+
+/*
+ * Extract a public key or public subkey from the PGP stream.
+ */
+static int pgp_process_public_key(struct pgp_parse_context *context,
+ enum pgp_packet_tag type,
+ u8 headerlen,
+ const u8 *data,
+ size_t datalen)
+{
+ const struct public_key_algorithm *algo;
+ struct pgp_key_data_parse_context *ctx =
+ container_of(context, struct pgp_key_data_parse_context, pgp);
+ struct pgp_parse_pubkey pgp;
+ struct public_key *key;
+ int i, ret;
+
+ kenter(",%u,%u,,%zu", type, headerlen, datalen);
+
+ if (ctx->subtype) {
+ kleave(" = -ENOKEY [already]");
+ return -EBADMSG;
+ }
+
+ key = kzalloc(sizeof(struct public_key), GFP_KERNEL);
+ if (!key)
+ return -ENOMEM;
+
+ ret = pgp_parse_public_key(&data, &datalen, &pgp);
+ if (ret < 0)
+ goto cleanup;
+
+ if (pgp.pubkey_algo >= PGP_PUBKEY__LAST ||
+ !pgp_public_key_algorithms[pgp.pubkey_algo]) {
+ pr_debug("Unsupported public key algorithm %u\n",
+ pgp.pubkey_algo);
+ ret = -ENOPKG;
+ goto cleanup;
+ }
+
+ algo = key->algo = pgp_public_key_algorithms[pgp.pubkey_algo];
+
+ /* It's a public key, so that only gives us encrypt and verify
+ * capabilities.
+ */
+ key->capabilities = pgp_public_key_capabilities[pgp.pubkey_algo] &
+ (PKEY_CAN_ENCRYPT | PKEY_CAN_VERIFY);
+
+ ret = -ENOMEM;
+ for (i = 0; i < algo->n_pub_mpi; i++) {
+ unsigned int remaining = datalen;
+ ret = -EBADMSG;
+ if (remaining == 0) {
+ pr_debug("short %zu mpi %d\n", datalen, i);
+ goto cleanup;
+ }
+ key->mpi[i] = mpi_read_from_buffer(data, &remaining);
+ if (!key->mpi[i])
+ goto cleanup;
+ data += remaining;
+ datalen -= remaining;
+ }
+
+ ret = -EBADMSG;
+ if (datalen != 0) {
+ pr_debug("excess %zu\n", datalen);
+ goto cleanup;
+ }
+
+ ret = pgp_generate_fingerprint(ctx, &pgp, key);
+ if (ret < 0)
+ goto cleanup;
+
+ ctx->subtype = &public_key_crypto_key_subtype;
+ ctx->payload = key;
+ kleave(" = 0 [use]");
+ return 0;
+
+cleanup:
+ pr_devel("cleanup");
+ if (key) {
+ for (i = 0; i < ARRAY_SIZE(key->mpi); i++)
+ mpi_free(key->mpi[i]);
+ kfree(key);
+ }
+ kleave(" = %d", ret);
+ return ret;
+}
+
+/*
+ * Attempt to parse the instantiation data blob for a key as a PGP packet
+ * message holding a key.
+ */
+static int pgp_key_instantiate(struct key *key,
+ const void *data, size_t datalen)
+{
+ struct pgp_key_data_parse_context ctx;
+ int ret;
+
+ kenter("");
+
+ ret = key_payload_reserve(key, datalen);
+ if (ret < 0)
+ return ret;
+
+ ctx.pgp.types_of_interest =
+ (1 << PGP_PKT_PUBLIC_KEY) | (1 << PGP_PKT_PUBLIC_SUBKEY);
+ ctx.pgp.process_packet = pgp_process_public_key;
+ ctx.subtype = NULL;
+ ctx.fingerprint = NULL;
+ ctx.payload = NULL;
+
+ ret = pgp_parse_packets(data, datalen, &ctx.pgp);
+ if (ret < 0) {
+ if (ctx.payload)
+ ctx.subtype->destroy(ctx.payload);
+ kfree(ctx.fingerprint);
+ key_payload_reserve(key, 0);
+ return ret;
+ }
+
+ key->type_data.p[0] = ctx.subtype;
+ key->type_data.p[1] = ctx.fingerprint;
+ key->payload.data = ctx.payload;
+ return 0;
+}
+
+static struct crypto_key_parser pgp_key_parser = {
+ .owner = THIS_MODULE,
+ .name = "pgp",
+ .instantiate = pgp_key_instantiate,
+};
+
+/*
+ * Module stuff
+ */
+static int __init pgp_key_init(void)
+{
+ return register_crypto_key_parser(&pgp_key_parser);
+}
+
+static void __exit pgp_key_exit(void)
+{
+ unregister_crypto_key_parser(&pgp_key_parser);
+}
+
+module_init(pgp_key_init);
+module_exit(pgp_key_exit);
diff --git a/security/keys/pgp_parser.h b/security/keys/pgp_parser.h
new file mode 100644
index 0000000..1cda231
--- /dev/null
+++ b/security/keys/pgp_parser.h
@@ -0,0 +1,23 @@
+/* PGP crypto data parser internal definitions
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (***@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/pgp.h>
+
+#define kenter(FMT, ...) \
+ pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
+#define kleave(FMT, ...) \
+ pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
+
+/*
+ * pgp_key_parser.c
+ */
+extern const
+struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST];

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-02 18:46:12 UTC
Permalink
Provide handlers for PGP-based public-key algorithm signature verification.
This does most of the work involved in signature verification as most of it is
public-key algorithm agnostic. The public-key verification algorithm itself
is just the last little bit and is supplied the complete hash data to process.

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

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

security/keys/Makefile | 3
security/keys/pgp_parser.h | 6 +
security/keys/pgp_pubkey_sig.c | 323 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 331 insertions(+), 1 deletions(-)
create mode 100644 security/keys/pgp_pubkey_sig.c


diff --git a/security/keys/Makefile b/security/keys/Makefile
index 242a087..fc1968e 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -34,4 +34,5 @@ obj-$(CONFIG_CRYPTO_KEY_PGP_PARSER) += pgp_parser.o
crypto_keys-y := crypto_type.o crypto_verify.o

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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Kasatkin, Dmitry
2012-01-18 11:36:34 UTC
Permalink
Post by David Howells
Provide handlers for PGP-based public-key algorithm signature verification.
This does most of the work involved in signature verification as most of it is
public-key algorithm agnostic.  The public-key verification algorithm itself
is just the last little bit and is supplied the complete hash data to process.
This requires glue logic putting on top to make use of it - something the next
patch provides.
---
 security/keys/Makefile         |    3
 security/keys/pgp_parser.h     |    6 +
 security/keys/pgp_pubkey_sig.c |  323 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 331 insertions(+), 1 deletions(-)
 create mode 100644 security/keys/pgp_pubkey_sig.c
diff --git a/security/keys/Makefile b/security/keys/Makefile
index 242a087..fc1968e 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -34,4 +34,5 @@ obj-$(CONFIG_CRYPTO_KEY_PGP_PARSER) += pgp_parser.o
 crypto_keys-y := crypto_type.o crypto_verify.o
 pgp_parser-y := \
-       pgp_key_parser.o
+       pgp_key_parser.o \
+       pgp_pubkey_sig.o
diff --git a/security/keys/pgp_parser.h b/security/keys/pgp_parser.h
index 1cda231..a6192ce 100644
--- a/security/keys/pgp_parser.h
+++ b/security/keys/pgp_parser.h
@@ -21,3 +21,9 @@
 */
 extern const
 struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST];
+
+/*
+ * pgp_pubkey_sig.c
+ */
+extern struct crypto_key_verify_context *pgp_pkey_verify_sig_begin(
+       struct key *crypto_key, const u8 *sigdata, size_t siglen);
diff --git a/security/keys/pgp_pubkey_sig.c b/security/keys/pgp_pubkey_sig.c
new file mode 100644
index 0000000..b4b7cb0
--- /dev/null
+++ b/security/keys/pgp_pubkey_sig.c
@@ -0,0 +1,323 @@
+/* Handling for PGP public key signature data [RFC 4880]
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "PGPSIG: "fmt
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/pgp.h>
+#include "public_key.h"
+#include "pgp_parser.h"
+
+const struct {
+       enum pkey_hash_algo algo : 8;
+} pgp_pubkey_hash[PGP_HASH__LAST] = {
+       [PGP_HASH_MD5].algo             = PKEY_HASH_MD5,
+       [PGP_HASH_SHA1].algo            = PKEY_HASH_SHA1,
+       [PGP_HASH_RIPE_MD_160].algo     = PKEY_HASH_RIPE_MD_160,
+       [PGP_HASH_SHA256].algo          = PKEY_HASH_SHA256,
+       [PGP_HASH_SHA384].algo          = PKEY_HASH_SHA384,
+       [PGP_HASH_SHA512].algo          = PKEY_HASH_SHA512,
+       [PGP_HASH_SHA224].algo          = PKEY_HASH_SHA224,
+};
+
+static int pgp_pkey_verify_sig_add_data(struct crypto_key_verify_context *ctx,
+                                       const void *data, size_t datalen);
+static int pgp_pkey_verify_sig_end(struct crypto_key_verify_context *ctx,
+                                  const u8 *sig, size_t siglen);
+static void pgp_pkey_verify_sig_cancel(struct crypto_key_verify_context *ctx);
+
+struct pgp_pkey_sig_parse_context {
+       struct pgp_parse_context pgp;
+       struct pgp_sig_parameters params;
+};
+
+static int pgp_pkey_parse_signature(struct pgp_parse_context *context,
+                                   enum pgp_packet_tag type,
+                                   u8 headerlen,
+                                   const u8 *data,
+                                   size_t datalen)
+{
+       struct pgp_pkey_sig_parse_context *ctx =
+               container_of(context, struct pgp_pkey_sig_parse_context, pgp);
+
+       return pgp_parse_sig_params(&data, &datalen, &ctx->params);
+}
+
+/*
+ * Begin the process of verifying a DSA signature.
+ *
+ * This involves allocating the hash into which first the data and then the
+ * metadata will be put, and parsing the signature to check that it matches the
+ * key.
+ */
+struct crypto_key_verify_context *pgp_pkey_verify_sig_begin(
+       struct key *crypto_key, const u8 *sigdata, size_t siglen)
+{
+       struct pgp_pkey_sig_parse_context p;
+       struct public_key_signature *sig;
+       struct crypto_shash *tfm;
+       const struct public_key *key = crypto_key->payload.data;
+       size_t digest_size, desc_size;
+       int ret;
+
+       kenter("{%d},,%zu", key_serial(crypto_key), siglen);
+
+       if (!key) {
+               kleave(" = -ENOKEY [no public key]");
+               return ERR_PTR(-ENOKEY);
+       }
+
+       p.pgp.types_of_interest = (1 << PGP_PKT_SIGNATURE);
+       p.pgp.process_packet = pgp_pkey_parse_signature;
+       ret = pgp_parse_packets(sigdata, siglen, &p.pgp);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       if (p.params.pubkey_algo >= PGP_PUBKEY__LAST ||
+           !pgp_public_key_algorithms[p.params.pubkey_algo]) {
+               pr_debug("Unsupported public key algorithm %u\n",
+                        p.params.pubkey_algo);
+               return ERR_PTR(-ENOKEY);
+       }
+
+       if (pgp_public_key_algorithms[p.params.pubkey_algo] != key->algo) {
+               kleave(" = -ENOKEY [wrong pk algo]");
+               return ERR_PTR(-ENOKEY);
+       }
+
+       if (!(key->capabilities & PKEY_CAN_VERIFY)) {
+               kleave(" = -EKEYREJECTED [key can't verify]");
+               return ERR_PTR(-EKEYREJECTED);
+       }
+
+       if (p.params.hash_algo >= PGP_HASH__LAST ||
+           !pgp_hash_algorithms[p.params.hash_algo]) {
+               kleave(" = -ENOPKG [hash]");
+               return ERR_PTR(-ENOPKG);
+       }
+
+       pr_debug("Signature generated with %s hash\n",
+                pgp_hash_algorithms[p.params.hash_algo]);
+
+       if (memcmp(&p.params.issuer, key->key_id, 8) != 0) {
+               kleave(" = -ENOKEY [wrong key ID]");
+               return ERR_PTR(-ENOKEY);
+       }
+
+       if (p.params.signature_type != PGP_SIG_BINARY_DOCUMENT_SIG &&
+           p.params.signature_type != PGP_SIG_STANDALONE_SIG) {
+               /* We don't want to canonicalise */
+               kleave(" = -EOPNOTSUPP [canon]");
+               return ERR_PTR(-EOPNOTSUPP);
+       }
+
+       /* Allocate the hashing algorithm we're going to need and find out how
+        * big the hash operational data will be.
+        */
+       tfm = crypto_alloc_shash(pgp_hash_algorithms[p.params.hash_algo], 0, 0);
+       if (IS_ERR(tfm))
+               return PTR_ERR(tfm) == -ENOENT ?
+                       ERR_PTR(-ENOPKG) : ERR_CAST(tfm);
+
+       desc_size = crypto_shash_descsize(tfm);
+       digest_size = crypto_shash_digestsize(tfm);
+
+       /* We allocate the hash operational data storage on the end of our
+        * context data.
+        */
+       sig = kzalloc(sizeof(*sig) + desc_size + digest_size, GFP_KERNEL);
+       if (!sig) {
+               crypto_free_shash(tfm);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       sig->base.key           = crypto_key;
+       sig->base.add_data      = pgp_pkey_verify_sig_add_data;
+       sig->base.end           = pgp_pkey_verify_sig_end;
+       sig->base.cancel        = pgp_pkey_verify_sig_cancel;
+       sig->pkey_hash_algo     = pgp_pubkey_hash[p.params.hash_algo].algo;
+       sig->digest             = (u8 *)sig + sizeof(*sig) + desc_size;
+       sig->digest_size        = digest_size;
+       sig->hash.tfm           = tfm;
+       sig->hash.flags         = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+       ret = crypto_shash_init(&sig->hash);
+       if (ret < 0) {
+               crypto_free_shash(sig->hash.tfm);
+               kfree(sig);
+               return ERR_PTR(ret);
+       }
+
+       key_get(sig->base.key);
+       kleave(" = %p", sig);
+       return &sig->base;
+}
+
+/*
+ * Load data into the hash
+ */
+static int pgp_pkey_verify_sig_add_data(struct crypto_key_verify_context *ctx,
+                                       const void *data, size_t datalen)
+{
+       struct public_key_signature *sig =
+               container_of(ctx, struct public_key_signature, base);
+
+       return crypto_shash_update(&sig->hash, data, datalen);
+}
Hello,

Synchronous hash SHASH is used only for software hash implementation...
HW acceleration is not supported by this hash.
It is good for short data.
But when calculating a hash over long data as files can be,
async hash AHASH is a preferred choice as enables HW acceleration.

As in my response to
[PATCH 08/21] KEYS: Add signature verification facility [ver #3]
It would be nice to have API to pass pre-computed hash,
then client might tackle async peculiarities by itself...

- Dmitry
Post by David Howells
+
+struct pgp_pkey_sig_digest_context {
+       struct pgp_parse_context pgp;
+       const struct public_key *key;
+       struct public_key_signature *sig;
+};
+
+/*
+ * Extract required metadata from the signature packet and add what we need to
+ * to the hash.
+ */
+static int pgp_pkey_digest_signature(struct pgp_parse_context *context,
+                                    enum pgp_packet_tag type,
+                                    u8 headerlen,
+                                    const u8 *data,
+                                    size_t datalen)
+{
+       struct pgp_pkey_sig_digest_context *ctx =
+               container_of(context, struct pgp_pkey_sig_digest_context, pgp);
+       enum pgp_signature_version version;
+       int i;
+
+       kenter(",%u,%u,,%zu", type, headerlen, datalen);
+
+       version = *data;
+       if (version == PGP_SIG_VERSION_3) {
+               /* We just include an excerpt of the metadata from a V3
+                * signature.
+                */
+               crypto_shash_update(&ctx->sig->hash, data + 1, 5);
+               data += sizeof(struct pgp_signature_v3_packet);
+               datalen -= sizeof(struct pgp_signature_v3_packet);
+       } else if (version == PGP_SIG_VERSION_4) {
+               /* We add the whole metadata header and some of the hashed data
+                * for a V4 signature, plus a trailer.
+                */
+               size_t hashedsz, unhashedsz;
+               u8 trailer[6];
+
+               hashedsz = 4 + 2 + (data[4] << 8) + data[5];
+               crypto_shash_update(&ctx->sig->hash, data, hashedsz);
+
+               trailer[0] = version;
+               trailer[1] = 0xffU;
+               trailer[2] = hashedsz >> 24;
+               trailer[3] = hashedsz >> 16;
+               trailer[4] = hashedsz >> 8;
+               trailer[5] = hashedsz;
+
+               crypto_shash_update(&ctx->sig->hash, trailer, 6);
+               data += hashedsz;
+               datalen -= hashedsz;
+
+               unhashedsz = 2 + (data[0] << 8) + data[1];
+               data += unhashedsz;
+               datalen -= unhashedsz;
+       }
+
+       if (datalen <= 2) {
+               kleave(" = -EBADMSG");
+               return -EBADMSG;
+       }
+
+       /* There's a quick check on the hash available. */
+       ctx->sig->signed_hash_msw[0] = *data++;
+       ctx->sig->signed_hash_msw[1] = *data++;
+       datalen -= 2;
+
+       /* And then the cryptographic data, which we'll need for the
+        * algorithm.
+        */
+       for (i = 0; i < ctx->key->algo->n_sig_mpi; i++) {
+               unsigned int remaining = datalen;
+               if (remaining == 0) {
+                       pr_debug("short %zu mpi %d\n", datalen, i);
+                       return -EBADMSG;
+               }
+               ctx->sig->mpi[i] = mpi_read_from_buffer(data, &remaining);
+               if (!ctx->sig->mpi[i])
+                       return -ENOMEM;
+               data += remaining;
+               datalen -= remaining;
+       }
+
+       if (datalen != 0) {
+               kleave(" = -EBADMSG [trailer %zu]", datalen);
+               return -EBADMSG;
+       }
+
+       kleave(" = 0");
+       return 0;
+}
+
+/*
+ * The data is now all loaded into the hash; load the metadata, finalise the
+ * hash and perform the verification step.
+ */
+static int pgp_pkey_verify_sig_end(struct crypto_key_verify_context *ctx,
+                                  const u8 *sigdata, size_t siglen)
+{
+       struct public_key_signature *sig =
+               container_of(ctx, struct public_key_signature, base);
+       const struct public_key *key = sig->base.key->payload.data;
+       struct pgp_pkey_sig_digest_context p;
+       int ret;
+
+       kenter("");
+
+       /* Firstly we add metadata, starting with some of the data from the
+        * signature packet */
+       p.pgp.types_of_interest = (1 << PGP_PKT_SIGNATURE);
+       p.pgp.process_packet = pgp_pkey_digest_signature;
+       p.key = key;
+       p.sig = sig;
+       ret = pgp_parse_packets(sigdata, siglen, &p.pgp);
+       if (ret < 0)
+               goto error_free_ctx;
+
+       crypto_shash_final(&sig->hash, sig->digest);
+
+       ret = key->algo->verify(key, sig);
+
+       pgp_pkey_verify_sig_cancel(ctx);
+       kleave(" = %d", ret);
+       return ret;
+}
+
+/*
+ * Cancel an in-progress data loading
+ */
+static void pgp_pkey_verify_sig_cancel(struct crypto_key_verify_context *ctx)
+{
+       struct public_key_signature *sig =
+               container_of(ctx, struct public_key_signature, base);
+       int i;
+
+       kenter("");
+
+       /* !!! Do we need to tell the crypto layer to cancel too? */
+       crypto_free_shash(sig->hash.tfm);
+       key_put(sig->base.key);
+       for (i = 0; i < ARRAY_SIZE(sig->mpi); i++)
+               mpi_free(sig->mpi[i]);
+       kfree(sig);
+
+       kleave("");
+}
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2012-01-18 12:50:19 UTC
Permalink
Post by Kasatkin, Dmitry
Synchronous hash SHASH is used only for software hash implementation...
HW acceleration is not supported by this hash.
It is good for short data.
But when calculating a hash over long data as files can be,
async hash AHASH is a preferred choice as enables HW acceleration.
Indeed. The asynchronous hash is a pain to use in the kernel, though, for a
couple of reasons: kernel addresses don't necessarily correspond to addresses
the h/w accel will see and you have to handle the h/w not signalling
completion. Herbert created shash to make it easier, and for module signing,
they're perfectly sufficient.
Post by Kasatkin, Dmitry
As in my response to [PATCH 08/21] KEYS: Add signature verification facility
[ver #3] It would be nice to have API to pass pre-computed hash, then client
might tackle async peculiarities by itself...
True. If you can give me the completed hash data, then I don't need to care
how you managed it. If you give me an uncompleted hash, I then have to deal
with the async hash in the kernel.

It might make sense for me to provide an API call to give you the postamble you
need to add to the hash to complete it. That call could also indicate which
hash you require and could also be combined with the call to find the
appropriate key.

David
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Kasatkin, Dmitry
2012-01-18 13:35:22 UTC
Permalink
Post by Kasatkin, Dmitry
Synchronous hash SHASH is used only for software hash implementation...
HW acceleration is not supported by this hash.
It is good for short data.
But when calculating a hash over long data as files can be,
async hash AHASH is a preferred choice as enables HW acceleration.
Indeed.  The asynchronous hash is a pain to use in the kernel, though, for a
couple of reasons: kernel addresses don't necessarily correspond to addresses
the h/w accel will see and you have to handle the h/w not signalling
completion.  Herbert created shash to make it easier, and for module signing,
they're perfectly sufficient.
Well, from client side, API is not that more complicate.
It is just about scatterlist. Rest is handled by particular driver/HW.

I agree, modules are not that big and SHASH is perfect choice for that...
Post by Kasatkin, Dmitry
As in my response to [PATCH 08/21] KEYS: Add signature verification facility
[ver #3] It would be nice to have API to pass pre-computed hash, then client
might tackle async peculiarities by itself...
True.  If you can give me the completed hash data, then I don't need to care
how you managed it.  If you give me an uncompleted hash, I then have to deal
with the async hash in the kernel.
It might make sense for me to provide an API call to give you the postamble you
need to add to the hash to complete it.  That call could also indicate which
hash you require and could also be combined with the call to find the
appropriate key.
Indeed, some blob with metadata to update before closing the hash
would work well.

PS.. As I understand, it is PGP spec which requires such processing.
Otherwise, plain data hash could be used to produce another hash for signing,
similar to what has been done in digsig project.... I have used the
same approach for IMA.

Thanks!
David
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-02 18:46:22 UTC
Permalink
Implement a signature parser that will attempt to parse a signature blob as a
PGP packet format message. If it can, it will find an appropriate crypto key
and set the public-key algorithm according to the data in the signature.

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

security/keys/Makefile | 1
security/keys/pgp_key_parser.c | 1
security/keys/pgp_parser.h | 6 ++
security/keys/pgp_sig_parser.c | 104 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 112 insertions(+), 0 deletions(-)
create mode 100644 security/keys/pgp_sig_parser.c


diff --git a/security/keys/Makefile b/security/keys/Makefile
index fc1968e..c7fa7ea 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -35,4 +35,5 @@ crypto_keys-y := crypto_type.o crypto_verify.o

pgp_parser-y := \
pgp_key_parser.o \
+ pgp_sig_parser.o \
pgp_pubkey_sig.o
diff --git a/security/keys/pgp_key_parser.c b/security/keys/pgp_key_parser.c
index d0d8a86..12a7644 100644
--- a/security/keys/pgp_key_parser.c
+++ b/security/keys/pgp_key_parser.c
@@ -323,6 +323,7 @@ static struct crypto_key_parser pgp_key_parser = {
.owner = THIS_MODULE,
.name = "pgp",
.instantiate = pgp_key_instantiate,
+ .verify_sig_begin = pgp_verify_sig_begin,
};

/*
diff --git a/security/keys/pgp_parser.h b/security/keys/pgp_parser.h
index a6192ce..73c900e 100644
--- a/security/keys/pgp_parser.h
+++ b/security/keys/pgp_parser.h
@@ -23,6 +23,12 @@ extern const
struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST];

/*
+ * pgp_sig_parser.c
+ */
+extern struct crypto_key_verify_context *pgp_verify_sig_begin(
+ struct key *keyring, const u8 *sig, size_t siglen);
+
+/*
* pgp_pubkey_sig.c
*/
extern struct crypto_key_verify_context *pgp_pkey_verify_sig_begin(
diff --git a/security/keys/pgp_sig_parser.c b/security/keys/pgp_sig_parser.c
new file mode 100644
index 0000000..b72c505
--- /dev/null
+++ b/security/keys/pgp_sig_parser.c
@@ -0,0 +1,104 @@
+/* Handling for PGP public key signature data [RFC 4880]
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (***@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "PGPSIG: "fmt
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pgp.h>
+#include "public_key.h"
+#include "pgp_parser.h"
+
+struct PGP_sig_parse_context {
+ struct pgp_parse_context pgp;
+ struct pgp_sig_parameters params;
+ bool found_sig;
+};
+
+/*
+ * Look inside signature sections for a key ID
+ */
+static int pgp_process_signature(struct pgp_parse_context *context,
+ enum pgp_packet_tag type,
+ u8 headerlen,
+ const u8 *data,
+ size_t datalen)
+{
+ struct PGP_sig_parse_context *ctx =
+ container_of(context, struct PGP_sig_parse_context, pgp);
+
+ ctx->found_sig = true;
+ return pgp_parse_sig_params(&data, &datalen, &ctx->params);
+}
+
+/*
+ * Attempt to find a key to use for PGP signature verification, starting off by
+ * looking in the supplied keyring.
+ *
+ * The function may also look for other key sources such as a TPM. If an
+ * alternative key is found it can be added to the keyring for future
+ * reference.
+ */
+static struct key *find_key_for_pgp_sig(struct key *keyring,
+ const u8 *sig, size_t siglen)
+{
+ struct PGP_sig_parse_context p;
+ key_ref_t key;
+ char criterion[3 + 8 * 2 + 1];
+ int ret;
+
+ if (!keyring)
+ return ERR_PTR(-ENOKEY);
+
+ /* Need to find the key ID */
+ p.pgp.types_of_interest = (1 << PGP_PKT_SIGNATURE);
+ p.pgp.process_packet = pgp_process_signature;
+ p.found_sig = false;
+ ret = pgp_parse_packets(sig, siglen, &p.pgp);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ if (!p.found_sig)
+ return ERR_PTR(-EINVAL);
+
+ sprintf(criterion, "id:%08x%08x",
+ be32_to_cpu(p.params.issuer32[0]),
+ be32_to_cpu(p.params.issuer32[1]));
+
+ pr_debug("Look up: %s\n", criterion);
+
+ key = keyring_search(make_key_ref(keyring, 1),
+ &key_type_crypto, criterion);
+ if (IS_ERR(key))
+ return ERR_CAST(key);
+
+ pr_debug("Found key %x\n", key_serial(key_ref_to_ptr(key)));
+ return key_ref_to_ptr(key);
+}
+
+/*
+ * Attempt to parse a signature as a PGP packet format blob and find a
+ * matching key.
+ */
+struct crypto_key_verify_context *pgp_verify_sig_begin(
+ struct key *keyring, const u8 *sig, size_t siglen)
+{
+ struct crypto_key_verify_context *ctx;
+ struct key *key;
+
+ key = find_key_for_pgp_sig(keyring, sig, siglen);
+ if (IS_ERR(key))
+ return ERR_CAST(key);
+
+ /* We only handle in-kernel public key signatures for the moment */
+ ctx = pgp_pkey_verify_sig_begin(key, sig, siglen);
+ key_put(key);
+ return ctx;
+}

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-02 18:46:32 UTC
Permalink
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 <***@redhat.com>
---

Documentation/security/keys-crypto.txt | 20 +++++++
include/keys/crypto-type.h | 3 +
security/keys/Kconfig | 9 +++
security/keys/Makefile | 1
security/keys/pgp_preload.c | 90 ++++++++++++++++++++++++++++++++
5 files changed, 123 insertions(+), 0 deletions(-)
create mode 100644 security/keys/pgp_preload.c


diff --git a/Documentation/security/keys-crypto.txt b/Documentation/security/keys-crypto.txt
index a964717..ba2ab55 100644
--- a/Documentation/security/keys-crypto.txt
+++ b/Documentation/security/keys-crypto.txt
@@ -10,6 +10,7 @@ Contents:
- Signature verification.
- Implementing crypto parsers.
- Implementing crypto subtypes.
+ - Initial PGP key preloading.


========
@@ -280,3 +281,22 @@ There are a number of operations defined by the subtype:
Mandatory. This should free the memory associated with the key. The
crypto key will look after freeing the fingerprint and releasing the
reference on the subtype module.
+
+
+=======================
+INITIAL PGP KEY LOADING
+=======================
+
+A function is provided to perform an initial load of a set of public keys bound
+into a PGP packet format blob:
+
+ int preload_pgp_keys(const u8 *pgpdata, size_t pgpdatalen,
+ struct key *keyring, const char *descprefix);
+
+This takes the blob of data defined by pgpdata and pgpdatalen, extracts keys
+from them and adds them to the specified keyring. The keys are labelled with
+descprefix plus a simple uniquifier - it is not expected that the description
+will be used to identify the key. The description is required to prevent all
+but the last key being discarded when the keys are linked into the keyring.
+
+This function is only available during initial kernel set up.
diff --git a/include/keys/crypto-type.h b/include/keys/crypto-type.h
index 6b93366..710e77f 100644
--- a/include/keys/crypto-type.h
+++ b/include/keys/crypto-type.h
@@ -31,4 +31,7 @@ extern void verify_sig_cancel(struct crypto_key_verify_context *ctx);
* The payload is at the discretion of the subtype.
*/

+extern __init int preload_pgp_keys(const u8 *pgpdata, size_t pgpdatalen,
+ struct key *keyring, const char *descprefix);
+
#endif /* _KEYS_CRYPTO_TYPE_H */
diff --git a/security/keys/Kconfig b/security/keys/Kconfig
index 8ffe822..14f9683 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -117,3 +117,12 @@ config CRYPTO_KEY_PGP_PARSER
This option provides support for parsing PGP (RFC 4880) format blobs
for key data and provides the ability to instantiate a crypto key
from a public key packet found inside the blob.
+
+config PGP_PRELOAD
+ bool "PGP public key preloading facility"
+ select PGP_LIBRARY
+ select CRYPTO_KEY_PGP_PARSER
+ help
+ This option provides a facility for the kernel to preload PGP-wrapped
+ bundles of keys during boot. It is used by module signing to load
+ the module signing keys for example.
diff --git a/security/keys/Makefile b/security/keys/Makefile
index c7fa7ea..a25bf48 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_CRYPTO_KEY_PUBLIC_KEY_SUBTYPE) += public_key.o
obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_DSA) += crypto_dsa.o
obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) += crypto_rsa.o
obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o
+obj-$(CONFIG_PGP_PRELOAD) += pgp_preload.o
obj-$(CONFIG_CRYPTO_KEY_PGP_PARSER) += pgp_parser.o

crypto_keys-y := crypto_type.o crypto_verify.o
diff --git a/security/keys/pgp_preload.c b/security/keys/pgp_preload.c
new file mode 100644
index 0000000..25154e3
--- /dev/null
+++ b/security/keys/pgp_preload.c
@@ -0,0 +1,90 @@
+/* Cryptographic key request handling
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (***@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ *
+ * See Documentation/security/keys-crypto.txt
+ */
+
+#include <linux/module.h>
+#include <linux/key.h>
+#include <linux/pgp.h>
+#include "crypto_keys.h"
+
+struct preload_pgp_keys_context {
+ struct pgp_parse_context pgp;
+ key_ref_t keyring;
+ char descbuf[20];
+ u8 key_n;
+ u8 dsize;
+};
+
+/*
+ * Extract a public key or subkey from the PGP stream.
+ */
+static int __init found_pgp_key(struct pgp_parse_context *context,
+ enum pgp_packet_tag type, u8 headerlen,
+ const u8 *data, size_t datalen)
+{
+ struct preload_pgp_keys_context *ctx =
+ container_of(context, struct preload_pgp_keys_context, pgp);
+ key_ref_t key;
+
+ sprintf(ctx->descbuf + ctx->dsize, "%d", ctx->key_n++);
+
+ key = key_create_or_update(ctx->keyring, "crypto", ctx->descbuf,
+ data - headerlen, datalen + headerlen,
+ KEY_POS_ALL | KEY_USR_VIEW,
+ KEY_ALLOC_NOT_IN_QUOTA);
+
+ if (IS_ERR(key))
+ return PTR_ERR(key);
+
+ pr_notice("Loaded %s key: %s\n",
+ key_ref_to_ptr(key)->description,
+ crypto_key_id(key_ref_to_ptr(key)));
+
+ key_ref_put(key);
+ return 0;
+}
+
+/**
+ * preload_pgp_keys - Load keys from a PGP keyring blob
+ * @pgpdata: The PGP keyring blob containing the keys.
+ * @pgpdatalen: The size of the @pgpdata blob.
+ * @keyring: The keyring to add the new keys to.
+ * @descprefix: The key description prefix.
+ *
+ * Preload a pack of keys from a PGP keyring blob.
+ *
+ * The keys are given description of @descprefix + the number of the key in the
+ * list. Since keys can be matched on their key IDs independently of the key
+ * description, the description is mostly irrelevant apart from the fact that
+ * keys of the same description displace one another from a keyring.
+ *
+ * The caller should override the current creds if they want the keys to be
+ * owned by someone other than the current process's owner. Keys will not be
+ * accounted towards the owner's quota.
+ *
+ * This function may only be called whilst the kernel is booting.
+ */
+int __init preload_pgp_keys(const u8 *pgpdata, size_t pgpdatalen,
+ struct key *keyring, const char *descprefix)
+{
+ struct preload_pgp_keys_context ctx;
+
+ ctx.pgp.types_of_interest =
+ (1 << PGP_PKT_PUBLIC_KEY) | (1 << PGP_PKT_PUBLIC_SUBKEY);
+ ctx.pgp.process_packet = found_pgp_key;
+ ctx.keyring = make_key_ref(keyring, 1);
+ ctx.key_n = 0;
+ ctx.dsize = strlen(descprefix);
+ strcpy(ctx.descbuf, descprefix);
+
+ return pgp_parse_packets(pgpdata, pgpdatalen, &ctx.pgp);
+}

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-02 18:46:43 UTC
Permalink
Add per-arch indications of module ELF types and relocation table entry types.

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

arch/alpha/include/asm/module.h | 3 +++
arch/arm/include/asm/module.h | 5 +++++
arch/cris/include/asm/module.h | 5 +++++
arch/h8300/include/asm/module.h | 5 +++++
arch/ia64/include/asm/module.h | 5 +++++
arch/m32r/include/asm/module.h | 5 +++++
arch/m68k/include/asm/module.h | 5 +++++
arch/mips/include/asm/module.h | 12 ++++++++++--
arch/parisc/include/asm/module.h | 8 ++++++++
arch/powerpc/include/asm/module.h | 10 ++++++++++
arch/s390/include/asm/module.h | 3 +++
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 */

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-02 18:46:56 UTC
Permalink
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 <***@redhat.com>
---

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 (***@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <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 (***@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <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 (***@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <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;

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-02 18:47:15 UTC
Permalink
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 <***@redhat.com>
---

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


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

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

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

#endif /* _LINUX_ELFNOTE_H */
diff --git a/include/linux/modsign.h b/include/linux/modsign.h
new file mode 100644
index 0000000..c5ac87a
--- /dev/null
+++ b/include/linux/modsign.h
@@ -0,0 +1,27 @@
+/* Module signing definitions
+ *
+ * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (***@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_MODSIGN_H
+#define _LINUX_MODSIGN_H
+
+#ifdef CONFIG_MODULE_SIG
+
+#include <linux/elfnote.h>
+
+/*
+ * The parameters of the ELF note used to carry the signature
+ */
+#define MODSIGN_NOTE_NAME module.sig
+#define MODSIGN_NOTE_TYPE 100
+
+#endif
+
+#endif /* _LINUX_MODSIGN_H */
diff --git a/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..7de546a 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1389,10 +1389,64 @@ 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_PKEY_ALGO_DSA
+ select CRYPTO_KEY_PKEY_ALGO_RSA
+ select PGP_PARSER
+ select PGP_PRELOAD
+ select MODULE_VERIFY_ELF
+ help
+ Check modules for valid signatures upon load. For more information
+ see:
+
+ Documentation/module-signing.txt
+
+choice
+ prompt "Which hash algorithm should modules be signed with?"
+ depends on MODULE_SIG
+ help
+ This determines which sort of hashing algorithm will be used during
+ signature generation. This algorithm _must_ be built into the kernel
+ directly so that signature verification can take place. It is not
+ possible to load a signed module containing the algorithm to check
+ the signature on that module.
+
+config MODULE_SIG_SHA1
+ bool "Sign modules with SHA-1"
+ select CRYPTO_SHA1
+
+config MODULE_SIG_SHA224
+ bool "Sign modules with SHA-224"
+ select CRYPTO_SHA224
+
+config MODULE_SIG_SHA256
+ bool "Sign modules with SHA-256"
+ select CRYPTO_SHA256
+
+config MODULE_SIG_SHA384
+ bool "Sign modules with SHA-384"
+ select CRYPTO_SHA384
+
+config MODULE_SIG_SHA512
+ bool "Sign modules with SHA-512"
+ select CRYPTO_SHA512
+
+endchoice
+
+config MODULE_SIG_FORCE
+ bool "Required modules to be validly signed (EXPERIMENTAL)"
+ depends on MODULE_SIG
+ help
+ Reject unsigned modules or signed modules for which we don't have a
+ key.
+
config MODULE_VERIFY
bool
depends on MODULES
- default y if MODULE_VERIFY_ELF
+ default y if MODULE_VERIFY_ELF || MODULE_SIG

endif # MODULES

diff --git a/kernel/Makefile b/kernel/Makefile
index 3c34fab..1d1c91c 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -53,6 +53,8 @@ obj-$(CONFIG_UID16) += uid16.o
obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_MODULE_VERIFY) += module-verify.o
obj-$(CONFIG_MODULE_VERIFY_ELF) += module-verify-elf.o
+obj-$(CONFIG_MODULE_SIG) += module-verify-sig.o modsign-pubkey.o
+kernel/modsign-pubkey.o: kernel.pub
obj-$(CONFIG_KALLSYMS) += kallsyms.o
obj-$(CONFIG_PM) += power/
obj-$(CONFIG_FREEZER) += power/
diff --git a/kernel/modsign-pubkey.c b/kernel/modsign-pubkey.c
new file mode 100644
index 0000000..df55565
--- /dev/null
+++ b/kernel/modsign-pubkey.c
@@ -0,0 +1,44 @@
+/* Public keys for module signature verification
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (***@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <keys/crypto-type.h>
+#include "module-verify.h"
+
+extern __initdata const u8 modsign_public_keys[];
+extern __initdata const u8 modsign_public_keys_end[];
+asm(".section .init.data,\"aw\"\n"
+ "modsign_public_keys:\n"
+ ".incbin \"kernel.pub\"\n"
+ "modsign_public_keys_end:"
+ );
+
+/*
+ * We need to make sure ccache doesn't cache the .o file as it doesn't notice
+ * if kernel.pub changes.
+ */
+static __initdata const char annoy_ccache[] = __TIME__ "foo";
+
+/*
+ * Load the compiled-in keys
+ */
+static __init int modsign_pubkey_init(void)
+{
+ pr_notice("Load module verification keys\n");
+
+ if (preload_pgp_keys(modsign_public_keys,
+ modsign_public_keys_end - modsign_public_keys,
+ modsign_keyring, "modsign.") < 0)
+ panic("Can't load module signing keys\n");
+
+ return 0;
+}
+late_initcall(modsign_pubkey_init);
diff --git a/kernel/module-verify-sig.c b/kernel/module-verify-sig.c
new file mode 100644
index 0000000..ced5681
--- /dev/null
+++ b/kernel/module-verify-sig.c
@@ -0,0 +1,526 @@
+/* Module signature checker
+ *
+ * Copyright (C) 2004,2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (***@redhat.com)
+ * - Derived from GregKH's RSA module signer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#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;
+ 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 required hash algorithm module,
+ * we will deadlock!!!
+ */
+ mod_sig = verify_sig_begin(modsign_keyring, sig, sig_size);
+ if (IS_ERR(mod_sig)) {
+ pr_err("Couldn't initiate module signature verification: %ld\n",
+ PTR_ERR(mod_sig));
+ return PTR_ERR(mod_sig);
+ }
+
+ mvdata->mod_sig = mod_sig;
+
+#ifdef MODSIGN_DEBUG
+ mvdata->xcsum = 0;
+#endif
+
+ /* load data from each relevant section into the digest */
+ for (loop = 0; loop < mvdata->ncanon; loop++) {
+ int sect = mvdata->canonlist[loop];
+ unsigned long sh_type = sechdrs[sect].sh_type;
+ unsigned long sh_info = sechdrs[sect].sh_info;
+ unsigned long sh_size = sechdrs[sect].sh_size;
+ unsigned long sh_flags = sechdrs[sect].sh_flags;
+ const char *sh_name = secstrings + sechdrs[sect].sh_name;
+ const void *data = mvdata->buffer + sechdrs[sect].sh_offset;
+
+#ifdef MODSIGN_DEBUG
+ mvdata->csum = 0;
+#endif
+
+ /* it would be nice to include relocation sections, but the act
+ * of adding a signature to the module seems changes their
+ * contents, because the symtab gets changed when sections are
+ * added or removed */
+ if (sh_type == SHT_REL || sh_type == SHT_RELA) {
+ uint32_t xsh_info = mvdata->canonmap[sh_info];
+
+ crypto_digest_update_data(mvdata, sh_name, strlen(sh_name));
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_type);
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_flags);
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_size);
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_addralign);
+ crypto_digest_update_val(mvdata, xsh_info);
+
+ if (sh_type == SHT_RELA)
+ ret = extract_elf_rela(
+ mvdata, sect,
+ data,
+ sh_size / sizeof(Elf_Rela),
+ sh_name);
+ else
+ ret = extract_elf_rel(
+ mvdata, sect,
+ data,
+ sh_size / sizeof(Elf_Rel),
+ sh_name);
+
+ if (ret < 0)
+ goto format_error;
+ continue;
+ }
+
+ /* include the headers of BSS sections */
+ if (sh_type == SHT_NOBITS && sh_flags & SHF_ALLOC) {
+ crypto_digest_update_data(mvdata, sh_name, strlen(sh_name));
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_type);
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_flags);
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_size);
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_addralign);
+ goto digested;
+ }
+
+ /* include allocatable loadable sections */
+ if (sh_type != SHT_NOBITS && sh_flags & SHF_ALLOC)
+ goto include_section;
+
+ continue;
+
+ include_section:
+ crypto_digest_update_data(mvdata, sh_name, strlen(sh_name));
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_type);
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_flags);
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_size);
+ crypto_digest_update_val(mvdata, sechdrs[sect].sh_addralign);
+
+ crypto_digest_update_data(mvdata, data, sh_size);
+
+ digested:
+ _debug("%08zx %02x digested the %s section, size %ld\n",
+ mvdata->signed_size, mvdata->csum, sh_name, sh_size);
+ }
+
+ _debug("Contributed %zu bytes to the digest (csum 0x%02x)\n",
+ mvdata->signed_size, mvdata->xcsum);
+
+ /* do the actual signature verification */
+ ret = verify_sig_end(mvdata->mod_sig, sig, sig_size);
+ mvdata->mod_sig = NULL;
+
+ _debug("verify-sig : %d\n", ret);
+
+ switch (ret) {
+ case 0: /* good signature */
+ *_gpgsig_ok = 1;
+ break;
+ case -EKEYREJECTED: /* signature mismatch or number format error */
+ pr_err("Module signature verification failed\n");
+ break;
+ case -ENOKEY: /* signed, but we don't have the public key */
+ pr_err("Module signed with unknown public key\n");
+ break;
+ default: /* other error (probably ENOMEM) */
+ break;
+ }
+
+ return ret;
+
+format_error:
+ verify_sig_cancel(mvdata->mod_sig);
+ mvdata->mod_sig = NULL;
+format_error_no_free:
+ pr_err("Module format error encountered\n");
+ return -ELIBBAD;
+
+ /* deal with the case of an unsigned module */
+no_signature:
+ _debug("no signature found\n");
+ if (!signedonly)
+ return 0;
+ pr_err("An attempt to load unsigned module was rejected\n");
+ return -EKEYREJECTED;
+}
+
+/*
+ * canonicalise the section table index numbers
+ */
+static int module_verify_canonicalise(struct module_verify_data *mvdata)
+{
+ int canon, loop, changed, tmp;
+
+ /* produce a list of index numbers of sections that contribute
+ * to the kernel's module image
+ */
+ mvdata->canonlist =
+ kmalloc(sizeof(int) * mvdata->nsects * 2, GFP_KERNEL);
+ if (!mvdata->canonlist)
+ return -ENOMEM;
+
+ mvdata->canonmap = mvdata->canonlist + mvdata->nsects;
+ canon = 0;
+
+ for (loop = 1; loop < mvdata->nsects; loop++) {
+ const Elf_Shdr *section = mvdata->sections + loop;
+
+ if (loop == mvdata->sig_index)
+ continue;
+
+ /* we only need to canonicalise allocatable sections */
+ if (section->sh_flags & SHF_ALLOC)
+ mvdata->canonlist[canon++] = loop;
+ else if ((section->sh_type == SHT_REL ||
+ section->sh_type == SHT_RELA) &&
+ mvdata->sections[section->sh_info].sh_flags & SHF_ALLOC)
+ mvdata->canonlist[canon++] = loop;
+ }
+
+ /* canonicalise the index numbers of the contributing section */
+ do {
+ changed = 0;
+
+ for (loop = 0; loop < canon - 1; loop++) {
+ const char *x, *y;
+
+ x = mvdata->secstrings +
+ mvdata->sections[mvdata->canonlist[loop + 0]].sh_name;
+ y = mvdata->secstrings +
+ mvdata->sections[mvdata->canonlist[loop + 1]].sh_name;
+
+ if (strcmp(x, y) > 0) {
+ tmp = mvdata->canonlist[loop + 0];
+ mvdata->canonlist[loop + 0] =
+ mvdata->canonlist[loop + 1];
+ mvdata->canonlist[loop + 1] = tmp;
+ changed = 1;
+ }
+ }
+
+ } while (changed);
+
+ for (loop = 0; loop < canon; loop++)
+ mvdata->canonmap[mvdata->canonlist[loop]] = loop + 1;
+ mvdata->ncanon = canon;
+ return 0;
+}
+
+/*
+ * extract an ELF RELA table
+ * - need to canonicalise the entries in case section addition/removal has
+ * rearranged the symbol table and the section table
+ */
+static int extract_elf_rela(struct module_verify_data *mvdata,
+ int secix,
+ const Elf_Rela *relatab, size_t nrels,
+ const char *sh_name)
+{
+ struct {
+#if defined(MODULES_ARE_ELF32)
+ uint32_t r_offset;
+ uint32_t r_addend;
+ uint32_t st_value;
+ uint32_t st_size;
+ uint16_t st_shndx;
+ uint8_t r_type;
+ uint8_t st_info;
+ uint8_t st_other;
+#elif defined(MODULES_ARE_ELF64)
+ uint64_t r_offset;
+ uint64_t r_addend;
+ uint64_t st_value;
+ uint64_t st_size;
+ uint32_t r_type;
+ uint16_t st_shndx;
+ uint8_t st_info;
+ uint8_t st_other;
+#else
+#error unsupported module type
+#endif
+ } __attribute__((packed)) relocation;
+
+ const Elf_Rela *reloc;
+ const Elf_Sym *symbol;
+ size_t loop;
+
+ /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
+ for (loop = 0; loop < nrels; loop++) {
+ int st_shndx;
+
+ reloc = &relatab[loop];
+
+ /* decode the relocation */
+ relocation.r_offset = reloc->r_offset;
+ relocation.r_addend = reloc->r_addend;
+ relocation.r_type = ELF_R_TYPE(reloc->r_info);
+
+ /* decode the symbol referenced by the relocation */
+ symbol = &mvdata->symbols[ELF_R_SYM(reloc->r_info)];
+ relocation.st_info = symbol->st_info;
+ relocation.st_other = symbol->st_other;
+ relocation.st_value = symbol->st_value;
+ relocation.st_size = symbol->st_size;
+ relocation.st_shndx = symbol->st_shndx;
+ st_shndx = symbol->st_shndx;
+
+ /* canonicalise the section used by the symbol */
+ if (st_shndx > SHN_UNDEF && st_shndx < mvdata->nsects)
+ relocation.st_shndx = mvdata->canonmap[st_shndx];
+
+ crypto_digest_update_val(mvdata, relocation);
+
+ /* undefined symbols must be named if referenced */
+ if (st_shndx == SHN_UNDEF) {
+ const char *name = mvdata->strings + symbol->st_name;
+ crypto_digest_update_data(mvdata,
+ name, strlen(name) + 1);
+ }
+ }
+
+ _debug("%08zx %02x digested the %s section, nrels %zu\n",
+ mvdata->signed_size, mvdata->csum, sh_name, nrels);
+
+ return 0;
+}
+
+/*
+ * extract an ELF REL table
+ * - need to canonicalise the entries in case section addition/removal has
+ * rearranged the symbol table and the section table
+ */
+static int extract_elf_rel(struct module_verify_data *mvdata,
+ int secix,
+ const Elf_Rel *reltab, size_t nrels,
+ const char *sh_name)
+{
+ struct {
+#if defined(MODULES_ARE_ELF32)
+ uint32_t r_offset;
+ uint32_t st_value;
+ uint32_t st_size;
+ uint16_t st_shndx;
+ uint8_t r_type;
+ uint8_t st_info;
+ uint8_t st_other;
+#elif defined(MODULES_ARE_ELF64)
+ uint64_t r_offset;
+ uint64_t st_value;
+ uint64_t st_size;
+ uint32_t r_type;
+ uint16_t st_shndx;
+ uint8_t st_info;
+ uint8_t st_other;
+#else
+#error unsupported module type
+#endif
+ } __attribute__((packed)) relocation;
+
+ const Elf_Rel *reloc;
+ const Elf_Sym *symbol;
+ size_t loop;
+
+ /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
+ for (loop = 0; loop < nrels; loop++) {
+ int st_shndx;
+
+ reloc = &reltab[loop];
+
+ /* decode the relocation */
+ relocation.r_offset = reloc->r_offset;
+ relocation.r_type = ELF_R_TYPE(reloc->r_info);
+
+ /* decode the symbol referenced by the relocation */
+ symbol = &mvdata->symbols[ELF_R_SYM(reloc->r_info)];
+ relocation.st_info = symbol->st_info;
+ relocation.st_other = symbol->st_other;
+ relocation.st_value = symbol->st_value;
+ relocation.st_size = symbol->st_size;
+ relocation.st_shndx = symbol->st_shndx;
+ st_shndx = symbol->st_shndx;
+
+ /* canonicalise the section used by the symbol */
+ if (st_shndx > SHN_UNDEF && st_shndx < mvdata->nsects)
+ relocation.st_shndx = mvdata->canonmap[st_shndx];
+
+ crypto_digest_update_val(mvdata, relocation);
+
+ /* undefined symbols must be named if referenced */
+ if (st_shndx == SHN_UNDEF) {
+ const char *name = mvdata->strings + symbol->st_name;
+ crypto_digest_update_data(mvdata,
+ name, strlen(name) + 1);
+ }
+ }
+
+ _debug("%08zx %02x digested the %s section, nrels %zu\n",
+ mvdata->signed_size, mvdata->csum, sh_name, nrels);
+
+ return 0;
+}
+
+/*
+ * Load the compiled-in keys
+ */
+static __init int module_verify_init(void)
+{
+ pr_notice("Initialise module verification\n");
+
+ modsign_keyring = key_alloc(&key_type_keyring, ".module_sign",
+ 0, 0, current_cred(),
+ (KEY_POS_ALL & ~KEY_POS_SETATTR) |
+ KEY_USR_VIEW | KEY_USR_READ,
+ KEY_ALLOC_NOT_IN_QUOTA);
+ if (IS_ERR(modsign_keyring))
+ panic("Can't allocate module signing keyring\n");
+
+ if (key_instantiate_and_link(modsign_keyring, NULL, 0, NULL, NULL) < 0)
+ panic("Can't instantiate module signing keyring\n");
+
+ return 0;
+}
+
+/*
+ * Must be initialised before we try and load the keys into the keyring.
+ */
+device_initcall(module_verify_init);
diff --git a/kernel/module-verify.c b/kernel/module-verify.c
index 875279f..64c5813 100644
--- a/kernel/module-verify.c
+++ b/kernel/module-verify.c
@@ -16,8 +16,9 @@
/*
* verify a module's integrity
* - check the ELF is viable
+ * - check the module's signature
*/
-int module_verify(const Elf_Ehdr *hdr, size_t size)
+int module_verify(const Elf_Ehdr *hdr, size_t size, int *_gpgsig_ok)
{
struct module_verify_data mvdata;
int ret;
@@ -34,6 +35,8 @@ int module_verify(const Elf_Ehdr *hdr, size_t size)
goto error;
}

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

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

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

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

+/*
+ * module-verify-sig.c
+ */
+#ifdef CONFIG_MODULE_SIG
+extern struct key *modsign_keyring;
+
+extern int module_verify_signature(struct module_verify_data *mvdata,
+ int *_gpgsig_ok);
+#else
+#define module_verify_signature(m, g) (0)
+#endif
+
#else
-#define module_verify(h, s) (0)
+#define module_verify(h, s, g) (0)
#endif
diff --git a/kernel/module.c b/kernel/module.c
index 8309389..9672d8a 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2333,7 +2333,8 @@ static inline void kmemleak_load_module(const struct module *mod,
/* Sets info->hdr and info->len. */
static int copy_and_check(struct load_info *info,
const void __user *umod, unsigned long len,
- const char __user *uargs)
+ const char __user *uargs,
+ int *_gpgsig_ok)
{
int err;
Elf_Ehdr *hdr;
@@ -2367,7 +2368,7 @@ static int copy_and_check(struct load_info *info,
}

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

@@ -2713,7 +2714,8 @@ int __weak module_frob_arch_sections(Elf_Ehdr *hdr,
return 0;
}

-static struct module *layout_and_allocate(struct load_info *info)
+static struct module *layout_and_allocate(struct load_info *info,
+ int gpgsig_ok)
{
/* Module within temporary copy. */
struct module *mod;
@@ -2723,6 +2725,7 @@ static struct module *layout_and_allocate(struct load_info *info)
mod = setup_load_info(info);
if (IS_ERR(mod))
return mod;
+ mod->gpgsig_ok = gpgsig_ok;

err = check_modinfo(mod, info);
if (err)
@@ -2816,17 +2819,18 @@ static struct module *load_module(void __user *umod,
struct load_info info = { NULL, };
struct module *mod;
long err;
+ int gpgsig_ok;

DEBUGP("load_module: umod=%p, len=%lu, uargs=%p\n",
umod, len, uargs);

/* Copy in the blobs from userspace, check they are vaguely sane. */
- err = copy_and_check(&info, umod, len, uargs);
+ err = copy_and_check(&info, umod, len, uargs, &gpgsig_ok);
if (err)
return ERR_PTR(err);

/* Figure out module layout, and allocate all the memory. */
- mod = layout_and_allocate(&info);
+ mod = layout_and_allocate(&info, gpgsig_ok);
if (IS_ERR(mod)) {
err = PTR_ERR(mod);
goto free_copy;
@@ -3476,8 +3480,13 @@ void print_modules(void)
printk(KERN_DEFAULT "Modules linked in:");
/* Most callers should already have preempt disabled, but make sure */
preempt_disable();
- list_for_each_entry_rcu(mod, &modules, list)
+ list_for_each_entry_rcu(mod, &modules, list) {
printk(" %s%s", mod->name, module_flags(mod, buf));
+#ifdef CONFIG_MODULE_SIG
+ if (!mod->gpgsig_ok)
+ printk("(U)");
+#endif
+ }
preempt_enable();
if (last_unloaded_module[0])
printk(" [last unloaded: %s]", last_unloaded_module);
diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost
index 08dce14..b436b04 100644
--- a/scripts/Makefile.modpost
+++ b/scripts/Makefile.modpost
@@ -14,7 +14,8 @@
# 3) create one <module>.mod.c file pr. module
# 4) create one Module.symvers file with CRC for all exported symbols
# 5) compile all <module>.mod.c files
-# 6) final link of the module to a <module.ko> file
+# 6) final link of the module to a <module.ko> (or <module.unsigned>) file
+# 7) signs the modules to a <module.ko> file

# Step 3 is used to place certain information in the module's ELF
# section, including information such as:
@@ -32,6 +33,8 @@
# Step 4 is solely used to allow module versioning in external modules,
# where the CRC of each module is retrieved from the Module.symvers file.

+# Step 7 is dependent on CONFIG_MODULE_SIG being enabled.
+
# KBUILD_MODPOST_WARN can be set to avoid error out in case of undefined
# symbols in the final module linking stage
# KBUILD_MODPOST_NOFINAL can be set to skip the final link of modules.
@@ -116,6 +119,7 @@ $(modules:.ko=.mod.o): %.mod.o: %.mod.c FORCE
targets += $(modules:.ko=.mod.o)

# Step 6), final link of the modules
+ifneq ($(CONFIG_MODULE_SIG),y)
quiet_cmd_ld_ko_o = LD [M] $@
cmd_ld_ko_o = $(LD) -r $(LDFLAGS) \
$(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \
@@ -125,7 +129,86 @@ $(modules): %.ko :%.o %.mod.o FORCE
$(call if_changed,ld_ko_o)

targets += $(modules)
+else
+quiet_cmd_ld_ko_unsigned_o = LD [M] $@
+ cmd_ld_ko_unsigned_o = \
+ $(LD) -r $(LDFLAGS) \
+ $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \
+ -o $@ $(filter-out FORCE,$^) \
+ $(if $(AFTER_LINK),; $(AFTER_LINK))
+
+$(modules:.ko=.ko.unsigned): %.ko.unsigned :%.o %.mod.o FORCE
+ $(call if_changed,ld_ko_unsigned_o)
+
+targets += $(modules:.ko=.ko.unsigned)
+
+# Step 7), sign the modules
+MODSECKEY = ./kernel.sec
+MODPUBKEY = ./kernel.pub
+KEYFLAGS = --no-default-keyring --secret-keyring $(MODSECKEY) --keyring $(MODPUBKEY) --no-default-keyring --homedir . --no-options --no-auto-check-trustdb --no-permission-warning
+
+ifdef CONFIG_MODULE_SIG_SHA1
+KEYFLAGS += --digest-algo=SHA1
+else
+ifdef CONFIG_MODULE_SIG_SHA224
+KEYFLAGS += --digest-algo=SHA224
+else
+ifdef CONFIG_MODULE_SIG_SHA256
+KEYFLAGS += --digest-algo=SHA256
+else
+ifdef CONFIG_MODULE_SIG_SHA384
+KEYFLAGS += --digest-algo=SHA384
+else
+ifdef CONFIG_MODULE_SIG_SHA512
+KEYFLAGS += --digest-algo=SHA512
+else
+endif
+endif
+endif
+endif
+endif
+
+ifdef MODKEYNAME
+KEYFLAGS += --default-key $(MODKEYNAME)
+endif

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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Rusty Russell
2011-12-09 12:06:31 UTC
Permalink
Post by David Howells
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.
And adds a great deal of code in a supposedly security-sensitive path to
achieve it.

How about simply append a signature to the module? That'd be about 20
lines of code to carefully check the bounds of the module to figure out
where the signature is. You could even allow multiple signatures, then
have one for stripped, and one for non-stripped versions.

Sure, you now need to re-append that after stripping, but that's not the
kernel's problem.

Cheers,
Rusty.
PS. Yay for finding out about module patches via LWN! How would you
get this in without my ack, FFS?


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-09 18:43:53 UTC
Permalink
Post by Rusty Russell
And adds a great deal of code in a supposedly security-sensitive path to
achieve it.
How about simply append a signature to the module? That'd be about 20 lines
of code to carefully check the bounds of the module to figure out where the
signature is. You could even allow multiple signatures, then have one for
stripped, and one for non-stripped versions.
A big chunk of the code is dealing with the cryptographic bits - and you need
those anyway - and if it's done right it can be shared with other things
(eCryptfs for example; maybe CIFS from what Steve French said) and auxiliary
keys can be stored in places other than the kernel (the TPM for example).
Post by Rusty Russell
Sure, you now need to re-append that after stripping, but that's not the
kernel's problem.
You may also have to remove the signature before passing it to any binutils
tool lest it malfunction on the trailer - and would you also have to modify
insmod and modprobe? I suspect they parse the ELF to find out about parameters
and things.

I've found that rpmbuild and mkinitrd alter the module files at various times,
so you'd need a bunch of signatures, one for each (may just be two, but I can't
guarantee that). This means the kernel build process needs to know what
transformations are going to be applied to a module - something that has
changed occasionally within the distribution I use and may vary between
distributions (or even just someone building for themselves).

David
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Rusty Russell
2011-12-10 07:05:41 UTC
Permalink
Post by David Howells
Post by Rusty Russell
And adds a great deal of code in a supposedly security-sensitive path to
achieve it.
How about simply append a signature to the module? That'd be about 20 lines
of code to carefully check the bounds of the module to figure out where the
signature is. You could even allow multiple signatures, then have one for
stripped, and one for non-stripped versions.
A big chunk of the code is dealing with the cryptographic bits - and you need
those anyway - and if it's done right it can be shared with other things
(eCryptfs for example; maybe CIFS from what Steve French said) and auxiliary
keys can be stored in places other than the kernel (the TPM for example).
Sure, I was only commenting on the module parts, which are my
responsibility.
Post by David Howells
Post by Rusty Russell
Sure, you now need to re-append that after stripping, but that's not the
kernel's problem.
You may also have to remove the signature before passing it to any
binutils tool lest it malfunction on the trailer
Well, you're already on your own if you're using non-module-init-tools
tools on modules. But objdump doesn't care about appended data.
objcopy and strip will remove it, of course, but without complaining.
Post by David Howells
- and would you also
have to modify insmod and modprobe? I suspect they parse the ELF to
find out about parameters and things.
No. modprobe needs to read .modinfo for --force, and modinfo does too.
depmod is the worst: it actually looks through symtab and strtab, etc.
And again, they're all fine.

But even if they weren't, it wouldn't be an argument for putting all
this in the kernel!

(We'll want to enhance modinfo, at least, to show the signatures).
Post by David Howells
I've found that rpmbuild and mkinitrd alter the module files at
various times, so you'd need a bunch of signatures, one for each (may
just be two, but I can't guarantee that). This means the kernel build
process needs to know what transformations are going to be applied to
a module - something that has changed occasionally within the
distribution I use and may vary between distributions (or even just
someone building for themselves).
Yes, there may be more than stripped and unstripped. You may need to
do fancy things. But now, adding a signature is so easy that it's not a
real problem. And we can always have a hook, like:

if VARIANTS=`make-module-variants $MOD`; then
for m in $VARIANTS; do sign $m >> $MOD; rm $m; done
fi

Cheers,
Rusty.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Arjan van de Ven
2011-12-10 18:36:57 UTC
Permalink
Post by Rusty Russell
Yes, there may be more than stripped and unstripped. You may need to
do fancy things. But now, adding a signature is so easy that it's
if VARIANTS=`make-module-variants $MOD`; then
for m in $VARIANTS; do sign $m >> $MOD; rm $m; done
fi
but that requires you to keep the key around.
the most simple and common deployment of this is to generate a key,
build the public key into the kernel, sign the modules as you build the
kernel, and then destroy the key.
And THEN it gets deployed.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Rusty Russell
2011-12-11 05:11:58 UTC
Permalink
Post by Arjan van de Ven
Post by Rusty Russell
Yes, there may be more than stripped and unstripped. You may need to
do fancy things. But now, adding a signature is so easy that it's
if VARIANTS=`make-module-variants $MOD`; then
for m in $VARIANTS; do sign $m >> $MOD; rm $m; done
fi
but that requires you to keep the key around.
No, that's the point. This was proposed as part of the kernel build.

make-module-variants does one or more transforms on the module
(eg. strip) and outputs the names of the results. If they're not
reproducable, you have to keep them somewhere, yes, rather than remove
them as here.
Post by Arjan van de Ven
the most simple and common deployment of this is to generate a key,
build the public key into the kernel, sign the modules as you build the
kernel, and then destroy the key.
And THEN it gets deployed.
Indeed.

You have to demonstrate why the simplest way won't work, before putting
450 lines of extra checking into the kernel. Particularly since it's
supposed to protect against malicious modules, so I need to *really*
carefully review it.

Thanks,
Rusty.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-10 14:09:01 UTC
Permalink
Post by Rusty Russell
Post by David Howells
Post by Rusty Russell
Sure, you now need to re-append that after stripping, but that's not the
kernel's problem.
You may also have to remove the signature before passing it to any
binutils tool lest it malfunction on the trailer
Well, you're already on your own if you're using non-module-init-tools
tools on modules.
The distributions packaging tools and initramfs tools are things I have to
contend with, and others will have to contend with.
Post by Rusty Russell
(We'll want to enhance modinfo, at least, to show the signatures).
Ummm... To show what exactly? And why? The modinfo has to go into the
signature hash. Further to find the modinfo, you have to parse the ELF - which
brings us right back to how do you know you can trust it?
Post by Rusty Russell
Post by David Howells
I've found that rpmbuild and mkinitrd alter the module files at
various times, so you'd need a bunch of signatures, one for each (may
just be two, but I can't guarantee that). This means the kernel build
process needs to know what transformations are going to be applied to
a module - something that has changed occasionally within the
distribution I use and may vary between distributions (or even just
someone building for themselves).
Yes, there may be more than stripped and unstripped. You may need to
do fancy things. But now, adding a signature is so easy that it's not a
if VARIANTS=`make-module-variants $MOD`; then
for m in $VARIANTS; do sign $m >> $MOD; rm $m; done
fi
That's not very practical. That spreads the what-do-we-need-to-calculate
question over a whole bunch of packages: the kernel, rpmbuild (if RPM-based),
mkinitramfs and maybe others. I presume you're thinking of trying all the
possible strip combinations and generating a signature for each? However, if
someone upgrades their binutils, but not their kernel, say, and then their
initramfs gets rebuilt for some reason, this may invalidate all their module
signatures.

So the number of signatures is:

strip_combos × binutils_variations × other_factors

and you have to generate all these at kernel build time because the secret key
cannot be disseminated to the installed machines that might be affected by
this.

(Note that binutils may rearrange the symbol, relocation and section tables in
different ways for different versions.)

David
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Rusty Russell
2011-12-11 05:11:59 UTC
Permalink
Post by David Howells
Post by Rusty Russell
Post by David Howells
Post by Rusty Russell
Sure, you now need to re-append that after stripping, but that's not the
kernel's problem.
You may also have to remove the signature before passing it to any
binutils tool lest it malfunction on the trailer
Well, you're already on your own if you're using non-module-init-tools
tools on modules.
The distributions packaging tools and initramfs tools are things I have to
contend with, and others will have to contend with.
If they're doing weird things to modules, yep, they'll break. They're
not supposed to, but I know these things happen. But I'll need more
than speculation, we'll need examples.
Post by David Howells
Post by Rusty Russell
(We'll want to enhance modinfo, at least, to show the signatures).
Ummm... To show what exactly? And why? The modinfo has to go into the
signature hash. Further to find the modinfo, you have to parse the ELF - which
brings us right back to how do you know you can trust it?
I think you misunderstand, I'm talking about the modinfo command, not
the .modinfo section.
Post by David Howells
Post by Rusty Russell
Post by David Howells
I've found that rpmbuild and mkinitrd alter the module files at
various times, so you'd need a bunch of signatures, one for each (may
just be two, but I can't guarantee that). This means the kernel build
process needs to know what transformations are going to be applied to
a module - something that has changed occasionally within the
distribution I use and may vary between distributions (or even just
someone building for themselves).
Yes, there may be more than stripped and unstripped. You may need to
do fancy things. But now, adding a signature is so easy that it's not a
if VARIANTS=`make-module-variants $MOD`; then
for m in $VARIANTS; do sign $m >> $MOD; rm $m; done
fi
That's not very practical. That spreads the what-do-we-need-to-calculate
question over a whole bunch of packages: the kernel, rpmbuild (if RPM-based),
mkinitramfs and maybe others. I presume you're thinking of trying all the
possible strip combinations and generating a signature for each? However, if
someone upgrades their binutils, but not their kernel, say, and then their
initramfs gets rebuilt for some reason, this may invalidate all their module
signatures.
I'm assuming you either guarantee that strip produces identical results,
or you generate all altered versions during the build (perhaps
make-module-variants has a place it can stash them in the kernel tree
where it gets rolled into your rpm).

But I need to know exactly what these version-dependent mangling of
modules is. Is it real? Is it more than strip? Is it so hard to fix
that it makes sense to add 450 lines of dense kernel code to allow
alteration of a module after signing?

The answer may be "yes". But I need to know that before accepting your
code.

Thanks,
Rusty.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-12 01:22:08 UTC
Permalink
Post by Rusty Russell
I think you misunderstand, I'm talking about the modinfo command, not
the .modinfo section.
Sorry, yes. But why do you need to enhance modinfo?
Post by Rusty Russell
But I need to know exactly what these version-dependent mangling of
modules is. Is it real? Is it more than strip? Is it so hard to fix
that it makes sense to add 450 lines of dense kernel code to allow
alteration of a module after signing?
The strip program (as far as I know that's the only binutil that we need worry
about) rearranges and reorders the section, symbol and relocation tables when
it discards stuff, and different versions of strip have done it differently.

There's GNU build ID. gcc/binutils was changed at some point to insert an ELF
note with the time at which the binary was built (something to do with
debuginfo matching, I think), and strip would update this when run on the
binary.

I haven't encountered many other things introducing breakage that wasn't the
fault of the tool doing the breaking - which usually got fixed pretty quickly.


However, you said it should be fairly easy to jump over the ELF parcel to get
to the signature. How do you plan on doing that? I presume you would just
parse sufficient of the ELF to find the theoretical ELF EOF and then look there
for a whole string of signatures - and hope they haven't got removed by some
unanticipated tool before you see the module.

David
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Rusty Russell
2011-12-12 09:11:14 UTC
Permalink
Post by David Howells
Post by Rusty Russell
I think you misunderstand, I'm talking about the modinfo command, not
the .modinfo section.
Sorry, yes. But why do you need to enhance modinfo?
I was suggesting that you want it to print the signatures, or at least
indicate their existence. Maybe check them too, but that might be a bit
too heavy for modinfo.
Post by David Howells
Post by Rusty Russell
But I need to know exactly what these version-dependent mangling of
modules is. Is it real? Is it more than strip? Is it so hard to fix
that it makes sense to add 450 lines of dense kernel code to allow
alteration of a module after signing?
The strip program (as far as I know that's the only binutil that we need worry
about) rearranges and reorders the section, symbol and relocation tables when
it discards stuff, and different versions of strip have done it differently.
OK, then you need to generate stripped modules as part of the build,
too. It's a bit of a pain, sure, but hardly a showstopper.
Post by David Howells
However, you said it should be fairly easy to jump over the ELF parcel to get
to the signature. How do you plan on doing that?
I presume you would just parse sufficient of the ELF to find the
theoretical ELF EOF and then look there for a whole string of
signatures
You could do that. But there's an easier way. Took me longer to figure
out the damn crypto API than actually write the module part :(

Subject: module: simple signature support.

A signature contains a magic marker: it signs everything up to the
magic marker (ie. just append them):
SUM=`md5sum drivers/block/loop.ko | cut -d\ -f1`; echo "@Module signature:$SUM" >> drivers/block/loop.ko

We can have false positives, but at worst that make us report EINVAL
(bad signature) instead of ENOENT (no signature).

diff --git a/kernel/module.c b/kernel/module.c
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2374,6 +2374,98 @@ free_hdr:
return err;
}

+/* CONFIG_MODULE_SIGN implies we don't trust modules: verify signature
+ * before we interpret (almost) anything. */
+#define MOD_SIGNATURE "@Module signature:"
+
+#include <linux/ctype.h>
+#include <crypto/hash.h>
+#include <crypto/md5.h>
+
+static int from_hex(char c)
+{
+ if (isdigit(c))
+ return c - '0';
+ if (isupper(c))
+ return c - 'A' + 10;
+ return c - 'a' + 10;
+}
+
+/* A signature signs everything before it. */
+static int try_signature(void *data, void *sig, unsigned long max_sig)
+{
+ unsigned long data_len = sig - data;
+
+ sig += strlen(MOD_SIGNATURE);
+ max_sig -= strlen(MOD_SIGNATURE);
+
+ /* Dummy: accept md5 as signature. */
+ {
+ struct crypto_api_blows {
+ struct shash_desc md5;
+ char morestuff[100];
+ } m;
+ u8 digest[MD5_DIGEST_SIZE], expected[MD5_DIGEST_SIZE];
+ char *s = sig;
+ int i;
+
+ /* Not a signature? */
+ if (max_sig < MD5_DIGEST_SIZE * 2) {
+ printk("Too close to end (%lu)\n", max_sig);
+ return -ENOENT;
+ }
+
+ for (i = 0; i < MD5_DIGEST_SIZE * 2; i += 2) {
+ /* Not a signature? */
+ if (!isxdigit(s[i]) || !isxdigit(s[i+1])) {
+ printk("Not hex digit (%c)\n", s[i]);
+ return -ENOENT;
+ }
+ digest[i/2] = (from_hex(s[i])<<4) | from_hex(s[i+1]);
+ }
+
+ m.md5.tfm = crypto_alloc_shash("md5", 0, 0);
+ if (IS_ERR(m.md5.tfm))
+ return PTR_ERR(m.md5.tfm);
+ m.md5.flags = 0;
+
+ crypto_shash_digest(&m.md5, data, data_len, expected);
+ crypto_free_shash(m.md5.tfm);
+
+ if (memcmp(digest, expected, sizeof(digest)) != 0) {
+ printk("Mismatch: given %02x%02x%02x...,"
+ " expect %02x%02x%02x...\n",
+ digest[0], digest[1], digest[2],
+ expected[0], expected[1], expected[2]);
+ return -EINVAL;
+ }
+ printk("Found valid signature!\n");
+ return 0;
+ }
+}
+
+/* -ENOENT if no signature found, -EINVAL if invalid, 0 if good. */
+static int find_and_check_signatures(struct load_info *info)
+{
+ void *p = info->hdr, *end = p + info->len;
+ const size_t sigsize = strlen(MOD_SIGNATURE);
+ int err = -ENOENT;
+
+ /* Poor man's memmem. len > sigsize */
+ while ((p = memchr(p, MOD_SIGNATURE[0], end - p))) {
+ if (p + sigsize > end)
+ break;
+
+ if (memcmp(p, MOD_SIGNATURE, sigsize) == 0) {
+ err = try_signature(info->hdr, p, end - p);
+ if (!err)
+ break;
+ }
+ p++;
+ }
+ return err;
+}
+
static void free_copy(struct load_info *info)
{
vfree(info->hdr);
@@ -2819,6 +2911,11 @@ static struct module *load_module(void _
if (err)
return ERR_PTR(err);

+ /* Before we trust it, carefully check signatures. */
+ err = find_and_check_signatures(&info);
+ if (err)
+ goto free_copy;
+
/* Figure out module layout, and allocate all the memory. */
mod = layout_and_allocate(&info);
if (IS_ERR(mod)) {


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-12 16:11:51 UTC
Permalink
Post by Rusty Russell
OK, then you need to generate stripped modules as part of the build,
too. It's a bit of a pain, sure, but hardly a showstopper.
They'd have to be maximally stripped so that mkinitrd doesn't do anything to
them, but you'd then get the debuginfo from them into the packaging if you're
on some distribution or other. And you also provide an option to not strip
them if whoever wants them unstripped.
Post by Rusty Russell
A signature contains a magic marker
I don't like this particularly - you can't guarantee that this won't be
generated by the assembler quite by accident. You should find the end of the
ELF and work from there. It should be a simple matter of parsing the header
and the section table only, right? Then you can look at the file offset +
length of the last section in the file. At that point, assuming this isn't
coincident with the actual end of the file, you can try parsing what's
thereafter as a signature. If it is actual PGP, then an RFC4880 parser should
recognise it as valid, and a signature packet should be seen.
Post by Rusty Russell
A signature contains a magic marker: it signs everything up to the
That's not a useful signature, but I suspect you're just showing this as an
example.
Post by Rusty Russell
We can have false positives, but at worst that make us report EINVAL
(bad signature) instead of ENOENT (no signature).
EKEYREJECTED please; that way it's the same as RHEL does now.
Post by Rusty Russell
Took me longer to figure out the damn crypto API
You don't actually need to use that. The crypto API for the moment doesn't do
crytographic signature verification.

Look at:

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

From tag cryptokeys-2011-12-07. That's my crypto signature verification stuff
in the kernel. You can build on that. The last patch in the sequence has
some of the bits you need for actually generating the signature, though rather
than editing the ELF, you'd just append all the signatures you actually need.

The following file:

http://git.kernel.org/?p=linux/kernel/git/dhowells/linux-modsign.git;a=blob;f=kernel/module-verify-sig.c;h=ced56816b2c47307a36cce0c6f829ea23fc5f2e6;hb=d71374f23e3a8c88c3ae20f62f04fb07a641f805

has an example of how it can be used, but it's basically:

struct crypto_key_verify_context *mod_sig;
u8 *sig = <signature data>;
size_t sig_size = <signature data size>;

/* Find the crypto key for the module signature
* - !!! if this tries to load the required hash algorithm module,
* we will deadlock!!!
*/
mod_sig = verify_sig_begin(modsign_keyring, sig, sig_size);
if (IS_ERR(mod_sig))
...

returns -EBADMSG if none of its parsers recognise the signature, -ENOPKG if
the signature is recognised, but we can't handle it (for instance if it's an
unsupported hash algorithm), -ENOKEY if we recognise it, but there's no key
available or -EKEYREJECTED if we recognised it, found the matching key, but
the key couldn't be used to verify the signature for some reason.

/* Call repeatedly to shovel data into the crypto hash */
verify_sig_add_data(mod_sig, dataptr, datasize);

/* Call to finalise and actually perform the verification */
ret = verify_sig_end(mod_sig, sig, sig_size);

or:

/* Call to cancel the verification */
verify_sig_cancel(mod_sig);

This does all the work for you.

David
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Rusty Russell
2011-12-14 23:54:25 UTC
Permalink
Post by David Howells
Post by Rusty Russell
OK, then you need to generate stripped modules as part of the build,
too. It's a bit of a pain, sure, but hardly a showstopper.
They'd have to be maximally stripped so that mkinitrd doesn't do anything to
them, but you'd then get the debuginfo from them into the packaging if you're
on some distribution or other. And you also provide an option to not strip
them if whoever wants them unstripped.
I was thinking we generate multiple modules; people will definitely want
unstripped versions as well.
Post by David Howells
Post by Rusty Russell
A signature contains a magic marker
I don't like this particularly - you can't guarantee that this won't be
generated by the assembler quite by accident.
We don't care, in practice. It can't generate a valid key by accident.
The only difference is that you might get "bad key" instead of "no key"
if no sigature matches. But a validly parsable signature won't happen
in practice.
Post by David Howells
You should find the end of the
ELF and work from there. It should be a simple matter of parsing the header
and the section table only, right? Then you can look at the file offset +
length of the last section in the file.
Tried that first. It's more lines (which need to be carefully
scrutinized)...
Post by David Howells
At that point, assuming this isn't
coincident with the actual end of the file, you can try parsing what's
thereafter as a signature. If it is actual PGP, then an RFC4880 parser should
recognise it as valid, and a signature packet should be seen.
..and you can still have padding. At which point, I realised that we
might as well just scan the whole thing and be done.

It's a bit cheeky, but I *know* it works, and can be verified by any
reader without knowing anything about ELF or trusting the format at all.
Post by David Howells
Post by Rusty Russell
A signature contains a magic marker: it signs everything up to the
That's not a useful signature, but I suspect you're just showing this as an
example.
Yeah, it was supposed to be a dumb example...
Post by David Howells
Post by Rusty Russell
We can have false positives, but at worst that make us report EINVAL
(bad signature) instead of ENOENT (no signature).
EKEYREJECTED please; that way it's the same as RHEL does now.
OK, sure (who knew that was there?).
Post by David Howells
Post by Rusty Russell
Took me longer to figure out the damn crypto API
You don't actually need to use that. The crypto API for the moment doesn't do
crytographic signature verification.
Sure, I was working on mainline, which is why I chose md5 (plus using
md5sum is easy).
Post by David Howells
returns -EBADMSG if none of its parsers recognise the signature, -ENOPKG if
the signature is recognised, but we can't handle it (for instance if it's an
unsupported hash algorithm), -ENOKEY if we recognise it, but there's no key
available or -EKEYREJECTED if we recognised it, found the matching key, but
the key couldn't be used to verify the signature for some reason.
/* Call repeatedly to shovel data into the crypto hash */
verify_sig_add_data(mod_sig, dataptr, datasize);
/* Call to finalise and actually perform the verification */
ret = verify_sig_end(mod_sig, sig, sig_size);
/* Call to cancel the verification */
verify_sig_cancel(mod_sig);
This does all the work for you.
That looks really nice, actually!

Thanks,
Rusty.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
David Howells
2011-12-15 00:16:59 UTC
Permalink
Post by Rusty Russell
Post by David Howells
Post by Rusty Russell
We can have false positives, but at worst that make us report EINVAL
(bad signature) instead of ENOENT (no signature).
EKEYREJECTED please; that way it's the same as RHEL does now.
OK, sure (who knew that was there?).
Second paragraph in the description of patch #21:

These patches have been in use by RHEL and Fedora kernels for years,
and so have been thoroughly tested.

Further down the description:

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

David
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Rusty Russell
2011-12-16 03:57:47 UTC
Permalink
Post by Rusty Russell
Post by David Howells
Post by Rusty Russell
We can have false positives, but at worst that make us report EINVAL
(bad signature) instead of ENOENT (no signature).
EKEYREJECTED please; that way it's the same as RHEL does now.
OK, sure (who knew that was there?).
Oh yes, I read these, but I didn't appreciate that those errnos had
existed for over 6 years.

Cheers,
Rusty.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Mimi Zohar
2012-01-08 22:05:14 UTC
Permalink
Post by David Howells
Here are a set of patches that create a framework for using cryptographic keys
http://git.kernel.org/?p=linux/kernel/git/dhowells/linux-modsign.git;a=shortlog;h=refs/heads/devel
Patches 2, 4, 5, and 6 are simple, straight forward Key cleanup. Please
add my Acks.

thanks,

Mimi

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Loading...