Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756770Ab2HPBfF (ORCPT ); Wed, 15 Aug 2012 21:35:05 -0400 Received: from mx1.redhat.com ([209.132.183.28]:57512 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756511Ab2HPBez (ORCPT ); Wed, 15 Aug 2012 21:34:55 -0400 From: David Howells Subject: [PATCH 03/25] KEYS: Create a key type that can be used for general cryptographic operations 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 Date: Thu, 16 Aug 2012 02:34:45 +0100 Message-ID: <20120816013445.872.34394.stgit@warthog.procyon.org.uk> In-Reply-To: <20120816013405.872.42381.stgit@warthog.procyon.org.uk> References: <20120816013405.872.42381.stgit@warthog.procyon.org.uk> User-Agent: StGIT/0.14.3 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 20137 Lines: 672 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 --- Documentation/security/keys-crypto.txt | 181 +++++++++++++++++++++ include/keys/crypto-subtype.h | 57 +++++++ include/keys/crypto-type.h | 25 +++ security/keys/Kconfig | 2 security/keys/Makefile | 1 security/keys/crypto/Kconfig | 7 + security/keys/crypto/Makefile | 7 + security/keys/crypto/crypto_keys.h | 28 +++ security/keys/crypto/crypto_type.c | 272 ++++++++++++++++++++++++++++++++ 9 files changed, 580 insertions(+) create mode 100644 Documentation/security/keys-crypto.txt create mode 100644 include/keys/crypto-subtype.h create mode 100644 include/keys/crypto-type.h create mode 100644 security/keys/crypto/Kconfig create mode 100644 security/keys/crypto/Makefile create mode 100644 security/keys/crypto/crypto_keys.h create mode 100644 security/keys/crypto/crypto_type.c diff --git a/Documentation/security/keys-crypto.txt b/Documentation/security/keys-crypto.txt new file mode 100644 index 0000000..97dee80 --- /dev/null +++ b/Documentation/security/keys-crypto.txt @@ -0,0 +1,181 @@ + ====================== + CRYPTOGRAPHIC KEY TYPE + ====================== + +Contents: + + - Overview. + - Key identification. + - Accessing crypto keys. + - Implementing crypto parsers. + - Implementing crypto subtypes. + + +======== +OVERVIEW +======== + +The "crypto" key type is designed to be a container for cryptographic keys, +without imposing any particular restrictions on the form of the cryptography or +the key. + +The crypto key is given a subtype that defines what sort of data is associated +with the key and provides operations to describe and destroy it. However, no +requirement is made that the key data actually be loaded into the key. + +The crypto key also has a number of data parsers registered with it. The data +parsers are responsible for extracing information the blobs of data passed to +the instantiator function. The first data parser that recognises the blob gets +to set the subtype of the key and define the operations that can be done on +that key. + +Completely in-kernel key retention and operation subtypes and parsers can be +defined, but it would also be possible to provide access to cryptographic +hardware (such as a TPM) that might be used to both retain the relevant key and +perform operations using that key. In such a case, the crypto key would then +merely be an interface to the TPM driver. + + +================== +KEY IDENTIFICATION +================== + +Because the identity of a key is not necessarily known and may not be easily +calculated when a crypto key is allocated, it may not be a simple matter to set +a key description to something that's useful for determining whether this is +the key you're looking for. Furthermore, it may be necessary to perform a +partial match upon the key identity. + +To help with this, when a key is loaded, the parser calculates the key +fingerprint and stores a copy in the key structure. + +The crypto key type's key matching function then performs more checks than just +the straightforward comparison of the description with the criterion string: + + (1) If the criterion string is of the form "id:" 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 ":" 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 + +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..1f546e6 --- /dev/null +++ b/include/keys/crypto-subtype.h @@ -0,0 +1,57 @@ +/* Cryptographic key subtype + * + * 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. + * + * See Documentation/security/keys-crypto.txt + */ + +#ifndef _KEYS_CRYPTO_SUBTYPE_H +#define _KEYS_CRYPTO_SUBTYPE_H + +#include +#include + +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 parse a key from the data blob passed to add_key() or + * keyctl_instantiate(). Should also generate a proposed description + * that the caller can optionally use for the key. + * + * Return EBADMSG if not recognised. + */ + int (*preparse)(struct key_preparsed_payload *prep); +}; + +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 (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. + * + * See Documentation/security/keys-crypto.txt + */ + +#ifndef _KEYS_CRYPTO_TYPE_H +#define _KEYS_CRYPTO_TYPE_H + +#include + +extern struct key_type key_type_crypto; + +/* + * The payload is at the discretion of the subtype. + */ + +#endif /* _KEYS_CRYPTO_TYPE_H */ diff --git a/security/keys/Kconfig b/security/keys/Kconfig index a90d6d3..992fe52 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -69,3 +69,5 @@ config KEYS_DEBUG_PROC_KEYS the resulting table. If you are unsure as to whether this is required, answer N. + +source security/keys/crypto/Kconfig diff --git a/security/keys/Makefile b/security/keys/Makefile index 504aaa0..67dae73 100644 --- a/security/keys/Makefile +++ b/security/keys/Makefile @@ -24,3 +24,4 @@ obj-$(CONFIG_SYSCTL) += sysctl.o # obj-$(CONFIG_TRUSTED_KEYS) += trusted.o obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/ +obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto/ diff --git a/security/keys/crypto/Kconfig b/security/keys/crypto/Kconfig new file mode 100644 index 0000000..3d15710 --- /dev/null +++ b/security/keys/crypto/Kconfig @@ -0,0 +1,7 @@ +config CRYPTO_KEY_TYPE + tristate "Cryptographic key type" + depends on KEYS + help + This option provides support for a type of key that holds the keys + required for cryptographic operations such as encryption, decryption, + signature generation and signature verification. diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile new file mode 100644 index 0000000..36db1d5 --- /dev/null +++ b/security/keys/crypto/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for cryptographic keys +# + +obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o + +crypto_keys-y := crypto_type.o diff --git a/security/keys/crypto/crypto_keys.h b/security/keys/crypto/crypto_keys.h new file mode 100644 index 0000000..eb11788 --- /dev/null +++ b/security/keys/crypto/crypto_keys.h @@ -0,0 +1,28 @@ +/* Internal crypto type stuff + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (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. + */ + +static inline +struct crypto_key_subtype *crypto_key_subtype(const struct key *key) +{ + return key->type_data.p[0]; +} + +static inline const char *crypto_key_id(const struct key *key) +{ + return key->type_data.p[1]; +} + + +/* + * crypto_type.c + */ +extern struct list_head crypto_key_parsers; +extern struct rw_semaphore crypto_key_parsers_sem; diff --git a/security/keys/crypto/crypto_type.c b/security/keys/crypto/crypto_type.c new file mode 100644 index 0000000..e8f83a6 --- /dev/null +++ b/security/keys/crypto/crypto_type.c @@ -0,0 +1,272 @@ +/* Cryptographic key type + * + * 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. + * + * See Documentation/security/keys-crypto.txt + */ +#include +#include +#include +#include +#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: + * + * "" - request a key by description + * "id:" - request a key matching the 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, ']'); + } +} + +/* + * Preparse a crypto payload to get format the contents appropriately for the + * internal payload to cut down on the number of scans of the data performed. + * + * We also generate a proposed description from the contents of the key that + * can be used to name the key if the user doesn't want to provide one. + */ +static int crypto_key_preparse(struct key_preparsed_payload *prep) +{ + struct crypto_key_parser *parser; + int ret; + + pr_devel("==>%s()\n", __func__); + + if (prep->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->preparse(prep); + if (ret != -EBADMSG) { + pr_debug("Parser recognised the format (ret %d)\n", + ret); + break; + } + } + + up_read(&crypto_key_parsers_sem); + pr_devel("<==%s() = %d\n", __func__, ret); + return ret; +} + +/* + * Clean up the preparse data + */ +static void crypto_key_free_preparse(struct key_preparsed_payload *prep) +{ + struct crypto_key_subtype *subtype = prep->type_data[0]; + + pr_devel("==>%s()\n", __func__); + + if (subtype) { + subtype->destroy(prep->payload); + module_put(subtype->owner); + } + kfree(prep->type_data[1]); + kfree(prep->description); +} + +/* + * Instantiate a crypto_key defined key + */ +static int crypto_key_instantiate(struct key *key, struct key_preparsed_payload *prep) +{ + int ret; + + pr_devel("==>%s()\n", __func__); + + ret = key_payload_reserve(key, prep->quotalen); + if (ret == 0) { + key->type_data.p[0] = prep->type_data[0]; + key->type_data.p[1] = prep->type_data[1]; + key->payload.data = prep->payload; + prep->type_data[0] = NULL; + prep->type_data[1] = NULL; + prep->payload = NULL; + } + 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", + .preparse = crypto_key_preparse, + .free_preparse = crypto_key_free_preparse, + .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 majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/