From: David Howells <dhowells@redhat.com>
To: rusty@rustcorp.com.au
Cc: dhowells@redhat.com, dmitry.kasatkin@intel.com,
zohar@linux.vnet.ibm.com, jmorris@namei.org,
keyrings@linux-nfs.org, linux-security-module@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: [PATCH 12/25] KEYS: PGP data parser
Date: Thu, 16 Aug 2012 02:36:23 +0100 [thread overview]
Message-ID: <20120816013623.872.94338.stgit@warthog.procyon.org.uk> (raw)
In-Reply-To: <20120816013405.872.42381.stgit@warthog.procyon.org.uk>
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.
Thanks to Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> for pointing out
some errors.
Signed-off-by: David Howells <dhowells@redhat.com>
---
security/keys/crypto/Kconfig | 12 +
security/keys/crypto/Makefile | 4
security/keys/crypto/pgp_parser.h | 23 ++
security/keys/crypto/pgp_public_key.c | 344 +++++++++++++++++++++++++++++++++
4 files changed, 383 insertions(+)
create mode 100644 security/keys/crypto/pgp_parser.h
create mode 100644 security/keys/crypto/pgp_public_key.c
diff --git a/security/keys/crypto/Kconfig b/security/keys/crypto/Kconfig
index 88ce0e2..1c2ae55 100644
--- a/security/keys/crypto/Kconfig
+++ b/security/keys/crypto/Kconfig
@@ -28,3 +28,15 @@ config PGP_LIBRARY
help
This option enables a library that provides a number of simple
utility functions for parsing PGP (RFC 4880) packet-based messages.
+
+config CRYPTO_KEY_PGP_PARSER
+ tristate "PGP key blob parser"
+ depends on CRYPTO_KEY_TYPE
+ select CRYPTO_KEY_PUBLIC_KEY_SUBTYPE
+ select PGP_LIBRARY
+ select MD5 # V3 fingerprint generation
+ select SHA1 # V4 fingerprint generation
+ help
+ This option provides support for parsing PGP (RFC 4880) format blobs
+ for key data and provides the ability to instantiate a crypto key
+ from a public key packet found inside the blob.
diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile
index 5fbe54e..35733fc 100644
--- a/security/keys/crypto/Makefile
+++ b/security/keys/crypto/Makefile
@@ -8,3 +8,7 @@ crypto_keys-y := crypto_type.o crypto_verify.o
obj-$(CONFIG_CRYPTO_KEY_PUBLIC_KEY_SUBTYPE) += public_key.o
obj-$(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) += crypto_rsa.o
obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o
+
+obj-$(CONFIG_CRYPTO_KEY_PGP_PARSER) += pgp_key_parser.o
+pgp_key_parser-y := \
+ pgp_public_key.o
diff --git a/security/keys/crypto/pgp_parser.h b/security/keys/crypto/pgp_parser.h
new file mode 100644
index 0000000..1cda231
--- /dev/null
+++ b/security/keys/crypto/pgp_parser.h
@@ -0,0 +1,23 @@
+/* PGP crypto data parser internal definitions
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/pgp.h>
+
+#define kenter(FMT, ...) \
+ pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
+#define kleave(FMT, ...) \
+ pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
+
+/*
+ * pgp_key_parser.c
+ */
+extern const
+struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST];
diff --git a/security/keys/crypto/pgp_public_key.c b/security/keys/crypto/pgp_public_key.c
new file mode 100644
index 0000000..c260e02
--- /dev/null
+++ b/security/keys/crypto/pgp_public_key.c
@@ -0,0 +1,344 @@
+/* Instantiate a public key crypto key from PGP format data [RFC 4880]
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "PGP: "fmt
+#include <keys/crypto-subtype.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mpi.h>
+#include <linux/pgplib.h>
+#include <crypto/hash.h>
+#include "public_key.h"
+#include "pgp_parser.h"
+
+MODULE_LICENSE("GPL");
+
+const
+struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST] = {
+#if defined(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA) || \
+ defined(CONFIG_CRYPTO_KEY_PKEY_ALGO_RSA_MODULE)
+ [PGP_PUBKEY_RSA_ENC_OR_SIG] = &RSA_public_key_algorithm,
+ [PGP_PUBKEY_RSA_ENC_ONLY] = &RSA_public_key_algorithm,
+ [PGP_PUBKEY_RSA_SIG_ONLY] = &RSA_public_key_algorithm,
+#endif
+ [PGP_PUBKEY_ELGAMAL] = NULL,
+ [PGP_PUBKEY_DSA] = NULL,
+};
+
+static const u8 pgp_public_key_capabilities[PGP_PUBKEY__LAST] = {
+ [PGP_PUBKEY_RSA_ENC_OR_SIG] = PKEY_CAN_ENCDEC | PKEY_CAN_SIGVER,
+ [PGP_PUBKEY_RSA_ENC_ONLY] = PKEY_CAN_ENCDEC,
+ [PGP_PUBKEY_RSA_SIG_ONLY] = PKEY_CAN_SIGVER,
+ [PGP_PUBKEY_ELGAMAL] = 0,
+ [PGP_PUBKEY_DSA] = 0,
+};
+
+static inline void digest_putc(struct shash_desc *digest, uint8_t ch)
+{
+ crypto_shash_update(digest, &ch, 1);
+}
+
+struct pgp_key_data_parse_context {
+ struct pgp_parse_context pgp;
+ struct crypto_key_subtype *subtype;
+ char *fingerprint;
+ void *payload;
+};
+
+/*
+ * Calculate the public key ID (RFC4880 12.2)
+ */
+static int pgp_calc_pkey_keyid(struct shash_desc *digest,
+ struct pgp_parse_pubkey *pgp,
+ struct public_key *key)
+{
+ unsigned nb[ARRAY_SIZE(key->mpi)];
+ unsigned nn[ARRAY_SIZE(key->mpi)];
+ unsigned n;
+ u8 *pp[ARRAY_SIZE(key->mpi)];
+ u32 a32;
+ int npkey = key->algo->n_pub_mpi;
+ int i, ret = -ENOMEM;
+
+ kenter("");
+
+ for (i = 0; i < ARRAY_SIZE(pp); i++)
+ pp[i] = NULL;
+
+ n = (pgp->version < PGP_KEY_VERSION_4) ? 8 : 6;
+ for (i = 0; i < npkey; i++) {
+ nb[i] = mpi_get_nbits(key->mpi[i]);
+ pp[i] = mpi_get_buffer(key->mpi[i], nn + i, NULL);
+ if (!pp[i])
+ goto error;
+ n += 2 + nn[i];
+ }
+
+ digest_putc(digest, 0x99); /* ctb */
+ digest_putc(digest, n >> 8); /* 16-bit header length */
+ digest_putc(digest, n);
+ digest_putc(digest, pgp->version);
+
+ a32 = pgp->creation_time;
+ digest_putc(digest, a32 >> 24);
+ digest_putc(digest, a32 >> 16);
+ digest_putc(digest, a32 >> 8);
+ digest_putc(digest, a32 >> 0);
+
+ if (pgp->version < PGP_KEY_VERSION_4) {
+ u16 a16;
+
+ if (pgp->expires_at)
+ a16 = (pgp->expires_at - pgp->creation_time) / 86400UL;
+ else
+ a16 = 0;
+ digest_putc(digest, a16 >> 8);
+ digest_putc(digest, a16 >> 0);
+ }
+
+ digest_putc(digest, pgp->pubkey_algo);
+
+ for (i = 0; i < npkey; i++) {
+ digest_putc(digest, nb[i] >> 8);
+ digest_putc(digest, nb[i]);
+ crypto_shash_update(digest, pp[i], nn[i]);
+ }
+ ret = 0;
+
+error:
+ for (i = 0; i < npkey; i++)
+ kfree(pp[i]);
+ kleave(" = %d", ret);
+ return ret;
+}
+
+/*
+ * Calculate the public key ID fingerprint
+ */
+static int pgp_generate_fingerprint(struct pgp_key_data_parse_context *ctx,
+ struct pgp_parse_pubkey *pgp,
+ struct public_key *key)
+{
+ struct crypto_shash *tfm;
+ struct shash_desc *digest;
+ char *fingerprint;
+ u8 *raw_fingerprint;
+ int digest_size, offset;
+ int ret, i;
+
+ ret = -ENOMEM;
+ tfm = crypto_alloc_shash(pgp->version < PGP_KEY_VERSION_4 ?
+ "md5" : "sha1", 0, 0);
+ if (!tfm)
+ goto cleanup;
+
+ digest = kmalloc(sizeof(*digest) + crypto_shash_descsize(tfm),
+ GFP_KERNEL);
+ if (!digest)
+ goto cleanup_tfm;
+
+ digest->tfm = tfm;
+ digest->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+ ret = crypto_shash_init(digest);
+ if (ret < 0)
+ goto cleanup_hash;
+
+ ret = pgp_calc_pkey_keyid(digest, pgp, key);
+ if (ret < 0)
+ goto cleanup_hash;
+
+ digest_size = crypto_shash_digestsize(tfm);
+
+ raw_fingerprint = kmalloc(digest_size, GFP_KERNEL);
+ if (!raw_fingerprint)
+ goto cleanup_hash;
+
+ ret = crypto_shash_final(digest, raw_fingerprint);
+ if (ret < 0)
+ goto cleanup_raw_fingerprint;
+
+ fingerprint = kmalloc(digest_size * 2 + 1, GFP_KERNEL);
+ if (!fingerprint)
+ goto cleanup_raw_fingerprint;
+
+ offset = digest_size - 8;
+ pr_debug("offset %u/%u\n", offset, digest_size);
+
+ for (i = 0; i < digest_size; i++)
+ sprintf(fingerprint + i * 2, "%02x", raw_fingerprint[i]);
+ pr_debug("fingerprint %s\n", fingerprint);
+
+ memcpy(&key->key_id, raw_fingerprint + offset, 8);
+ key->key_id_size = 8;
+ key->id_type = PKEY_ID_PGP;
+
+ 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);
+
+ for (i = 0; i < algo->n_pub_mpi; i++) {
+ unsigned int remaining = datalen;
+ if (remaining == 0) {
+ pr_debug("short %zu mpi %d\n", datalen, i);
+ goto cleanup_badmsg;
+ }
+ key->mpi[i] = mpi_read_from_buffer(data, &remaining);
+ if (!key->mpi[i])
+ goto cleanup_nomem;
+ data += remaining;
+ datalen -= remaining;
+ }
+
+ if (datalen != 0) {
+ pr_debug("excess %zu\n", datalen);
+ goto cleanup_badmsg;
+ }
+
+ ret = pgp_generate_fingerprint(ctx, &pgp, key);
+ if (ret < 0)
+ goto cleanup;
+
+ /* We're pinning the module by being linked against it */
+ __module_get(public_key_crypto_key_subtype.owner);
+ ctx->subtype = &public_key_crypto_key_subtype;
+ ctx->payload = key;
+ kleave(" = 0 [use]");
+ return 0;
+
+cleanup_nomem:
+ ret = -ENOMEM;
+ goto cleanup;
+cleanup_badmsg:
+ ret = -EBADMSG;
+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_preparse(struct key_preparsed_payload *prep)
+{
+ struct pgp_key_data_parse_context ctx;
+ int ret;
+
+ kenter("");
+
+ 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(prep->data, prep->datalen, &ctx.pgp);
+ if (ret < 0) {
+ if (ctx.payload)
+ ctx.subtype->destroy(ctx.payload);
+ if (ctx.subtype)
+ module_put(ctx.subtype->owner);
+ kfree(ctx.fingerprint);
+ return ret;
+ }
+
+ prep->type_data[0] = ctx.subtype;
+ prep->type_data[1] = ctx.fingerprint;
+ prep->payload = ctx.payload;
+ prep->quotalen = prep->datalen;
+ return 0;
+}
+
+static struct crypto_key_parser pgp_key_parser = {
+ .owner = THIS_MODULE,
+ .name = "pgp",
+ .preparse = pgp_key_preparse,
+};
+
+/*
+ * 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);
next prev parent reply other threads:[~2012-08-16 1:36 UTC|newest]
Thread overview: 48+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-08-16 1:34 [PATCH 00/25] Crypto keys and module signing David Howells
2012-08-16 1:34 ` [PATCH 01/25] KEYS: Add payload preparsing opportunity prior to key instantiate or update David Howells
2012-08-16 1:34 ` [PATCH 02/25] MPILIB: Provide count_leading/trailing_zeros() based on arch functions David Howells
2012-09-10 7:13 ` Kasatkin, Dmitry
2012-09-13 5:14 ` James Morris
2012-09-13 14:09 ` Kasatkin, Dmitry
2012-08-16 1:34 ` [PATCH 03/25] KEYS: Create a key type that can be used for general cryptographic operations David Howells
2012-08-16 1:34 ` [PATCH 04/25] KEYS: Add signature verification facility David Howells
2012-08-16 1:35 ` [PATCH 05/25] KEYS: Asymmetric public-key algorithm crypto key subtype David Howells
2012-08-16 1:35 ` [PATCH 06/25] MPILIB: Reinstate mpi_cmp[_ui]() and export for RSA signature verification David Howells
2012-08-16 1:35 ` [PATCH 07/25] KEYS: RSA: Implement signature verification algorithm [PKCS#1 / RFC3447] David Howells
2012-08-16 1:35 ` [PATCH 08/25] KEYS: RSA: Fix signature verification for shorter signatures David Howells
2012-08-16 1:35 ` [PATCH 09/25] PGPLIB: PGP definitions (RFC 4880) David Howells
2012-08-16 1:36 ` [PATCH 10/25] PGPLIB: Basic packet parser David Howells
2012-08-16 1:36 ` [PATCH 11/25] PGPLIB: Signature parser David Howells
2012-08-16 1:36 ` David Howells [this message]
2012-08-16 1:36 ` [PATCH 13/25] KEYS: PGP-based public key signature verification David Howells
2012-08-16 1:36 ` [PATCH 14/25] KEYS: PGP format signature parser David Howells
2012-08-16 1:36 ` [PATCH 15/25] KEYS: Provide PGP key description autogeneration David Howells
2012-08-16 1:37 ` [PATCH 16/25] KEYS: Provide a function to load keys from a PGP keyring blob David Howells
2012-08-16 1:37 ` [PATCH 17/25] MODSIGN: Provide gitignore and make clean rules for extra files David Howells
2012-08-16 1:37 ` [PATCH 18/25] MODSIGN: Provide Documentation and Kconfig options David Howells
2012-08-16 1:37 ` [PATCH 19/25] MODSIGN: Sign modules during the build process David Howells
2012-08-16 1:37 ` [PATCH 20/25] MODSIGN: Provide module signing public keys to the kernel David Howells
2012-08-31 14:33 ` Michal Marek
2012-08-16 1:38 ` [PATCH 21/25] MODSIGN: Module signature verification David Howells
2012-08-16 1:38 ` [PATCH 22/25] MODSIGN: Automatically generate module signing keys if missing David Howells
2012-08-16 1:38 ` [PATCH 23/25] MODSIGN: Panic the kernel if FIPS is enabled upon module signing failure David Howells
2012-08-16 1:38 ` [PATCH 24/25] MODSIGN: Allow modules to be signed with an unknown key unless enforcing David Howells
2012-08-16 1:38 ` [PATCH 25/25] MODSIGN: Fix documentation of signed-nokey behavior when not enforcing David Howells
2012-08-21 5:04 ` [PATCH 00/25] Crypto keys and module signing Rusty Russell
2012-08-22 10:50 ` David Howells
2012-08-22 11:52 ` Mimi Zohar
2012-08-22 16:07 ` Kasatkin, Dmitry
2012-09-04 5:55 ` [RFC] module: signature infrastructure Rusty Russell
2012-09-04 12:07 ` Kasatkin, Dmitry
2012-09-04 12:21 ` Kasatkin, Dmitry
2012-09-04 13:40 ` Mimi Zohar
2012-09-05 0:29 ` Rusty Russell
2012-09-05 13:34 ` Mimi Zohar
2012-09-06 2:05 ` Rusty Russell
2012-09-04 14:25 ` Lucas De Marchi
2012-09-04 15:04 ` Kasatkin, Dmitry
2012-09-05 0:19 ` Rusty Russell
2012-09-05 23:41 ` Lucas De Marchi
2012-09-06 7:55 ` Rusty Russell
2012-09-04 22:51 ` David Howells
2012-09-04 23:17 ` Kasatkin, Dmitry
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20120816013623.872.94338.stgit@warthog.procyon.org.uk \
--to=dhowells@redhat.com \
--cc=dmitry.kasatkin@intel.com \
--cc=jmorris@namei.org \
--cc=keyrings@linux-nfs.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=rusty@rustcorp.com.au \
--cc=zohar@linux.vnet.ibm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Powered by JetHome