From 734114f8782f6c3398762f2353fe9101d87b6d06 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 3 Apr 2017 16:07:24 +0100 Subject: KEYS: Add a system blacklist keyring Add the following: (1) A new system keyring that is used to store information about blacklisted certificates and signatures. (2) A new key type (called 'blacklist') that is used to store a blacklisted hash in its description as a hex string. The key accepts no payload. (3) The ability to configure a list of blacklisted hashes into the kernel at build time. This is done by setting CONFIG_SYSTEM_BLACKLIST_HASH_LIST to the filename of a list of hashes that are in the form: "", "", ..., "" where each is a hex string representation of the hash and must include all necessary leading zeros to pad the hash to the right size. The above are enabled with CONFIG_SYSTEM_BLACKLIST_KEYRING. Once the kernel is booted, the blacklist keyring can be listed: root@andromeda ~]# keyctl show %:.blacklist Keyring 723359729 ---lswrv 0 0 keyring: .blacklist 676257228 ---lswrv 0 0 \_ blacklist: 123412341234c55c1dcc601ab8e172917706aa32fb5eaf826813547fdf02dd46 The blacklist cannot currently be modified by userspace, but it will be possible to load it, for example, from the UEFI blacklist database. A later commit will make it possible to load blacklisted asymmetric keys in here too. Signed-off-by: David Howells --- certs/Kconfig | 18 +++++ certs/Makefile | 6 ++ certs/blacklist.c | 174 ++++++++++++++++++++++++++++++++++++++++++ certs/blacklist.h | 3 + certs/blacklist_hashes.c | 6 ++ certs/blacklist_nohashes.c | 5 ++ include/keys/system_keyring.h | 12 +++ 7 files changed, 224 insertions(+) create mode 100644 certs/blacklist.c create mode 100644 certs/blacklist.h create mode 100644 certs/blacklist_hashes.c create mode 100644 certs/blacklist_nohashes.c diff --git a/certs/Kconfig b/certs/Kconfig index fc5955f5fc8a..6ce51ede9e9b 100644 --- a/certs/Kconfig +++ b/certs/Kconfig @@ -64,4 +64,22 @@ config SECONDARY_TRUSTED_KEYRING those keys are not blacklisted and are vouched for by a key built into the kernel or already in the secondary trusted keyring. +config SYSTEM_BLACKLIST_KEYRING + bool "Provide system-wide ring of blacklisted keys" + depends on KEYS + help + Provide a system keyring to which blacklisted keys can be added. + Keys in the keyring are considered entirely untrusted. Keys in this + keyring are used by the module signature checking to reject loading + of modules signed with a blacklisted key. + +config SYSTEM_BLACKLIST_HASH_LIST + string "Hashes to be preloaded into the system blacklist keyring" + depends on SYSTEM_BLACKLIST_KEYRING + help + If set, this option should be the filename of a list of hashes in the + form "", "", ... . This will be included into a C + wrapper to incorporate the list into the kernel. Each should + be a string of hex digits. + endmenu diff --git a/certs/Makefile b/certs/Makefile index 2773c4afa24c..4119bb376ea1 100644 --- a/certs/Makefile +++ b/certs/Makefile @@ -3,6 +3,12 @@ # obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o +obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist.o +ifneq ($(CONFIG_SYSTEM_BLACKLIST_HASH_LIST),"") +obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_hashes.o +else +obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_nohashes.o +endif ifeq ($(CONFIG_SYSTEM_TRUSTED_KEYRING),y) diff --git a/certs/blacklist.c b/certs/blacklist.c new file mode 100644 index 000000000000..3eddce0e307a --- /dev/null +++ b/certs/blacklist.c @@ -0,0 +1,174 @@ +/* System hash blacklist. + * + * Copyright (C) 2016 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) "blacklist: "fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "blacklist.h" + +static struct key *blacklist_keyring; + +/* + * The description must be a type prefix, a colon and then an even number of + * hex digits. The hash is kept in the description. + */ +static int blacklist_vet_description(const char *desc) +{ + int n = 0; + + if (*desc == ':') + return -EINVAL; + for (; *desc; desc++) + if (*desc == ':') + goto found_colon; + return -EINVAL; + +found_colon: + desc++; + for (; *desc; desc++) { + if (!isxdigit(*desc)) + return -EINVAL; + n++; + } + + if (n == 0 || n & 1) + return -EINVAL; + return 0; +} + +/* + * The hash to be blacklisted is expected to be in the description. There will + * be no payload. + */ +static int blacklist_preparse(struct key_preparsed_payload *prep) +{ + if (prep->datalen > 0) + return -EINVAL; + return 0; +} + +static void blacklist_free_preparse(struct key_preparsed_payload *prep) +{ +} + +static void blacklist_describe(const struct key *key, struct seq_file *m) +{ + seq_puts(m, key->description); +} + +static struct key_type key_type_blacklist = { + .name = "blacklist", + .vet_description = blacklist_vet_description, + .preparse = blacklist_preparse, + .free_preparse = blacklist_free_preparse, + .instantiate = generic_key_instantiate, + .describe = blacklist_describe, +}; + +/** + * mark_hash_blacklisted - Add a hash to the system blacklist + * @hash - The hash as a hex string with a type prefix (eg. "tbs:23aa429783") + */ +int mark_hash_blacklisted(const char *hash) +{ + key_ref_t key; + + key = key_create_or_update(make_key_ref(blacklist_keyring, true), + "blacklist", + hash, + NULL, + 0, + ((KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW), + KEY_ALLOC_NOT_IN_QUOTA | + KEY_ALLOC_BUILT_IN); + if (IS_ERR(key)) { + pr_err("Problem blacklisting hash (%ld)\n", PTR_ERR(key)); + return PTR_ERR(key); + } + return 0; +} + +/** + * is_hash_blacklisted - Determine if a hash is blacklisted + * @hash: The hash to be checked as a binary blob + * @hash_len: The length of the binary hash + * @type: Type of hash + */ +int is_hash_blacklisted(const u8 *hash, size_t hash_len, const char *type) +{ + key_ref_t kref; + size_t type_len = strlen(type); + char *buffer, *p; + int ret = 0; + + buffer = kmalloc(type_len + 1 + hash_len * 2 + 1, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + p = memcpy(buffer, type, type_len); + p += type_len; + *p++ = ':'; + bin2hex(p, hash, hash_len); + p += hash_len * 2; + *p = 0; + + kref = keyring_search(make_key_ref(blacklist_keyring, true), + &key_type_blacklist, buffer); + if (!IS_ERR(kref)) { + key_ref_put(kref); + ret = -EKEYREJECTED; + } + + kfree(buffer); + return ret; +} +EXPORT_SYMBOL_GPL(is_hash_blacklisted); + +/* + * Intialise the blacklist + */ +static int __init blacklist_init(void) +{ + const char *const *bl; + + if (register_key_type(&key_type_blacklist) < 0) + panic("Can't allocate system blacklist key type\n"); + + blacklist_keyring = + keyring_alloc(".blacklist", + KUIDT_INIT(0), KGIDT_INIT(0), + current_cred(), + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ | + KEY_USR_SEARCH, + KEY_ALLOC_NOT_IN_QUOTA | + KEY_FLAG_KEEP, + NULL, NULL); + if (IS_ERR(blacklist_keyring)) + panic("Can't allocate system blacklist keyring\n"); + + for (bl = blacklist_hashes; *bl; bl++) + if (mark_hash_blacklisted(*bl) < 0) + pr_err("- blacklisting failed\n"); + return 0; +} + +/* + * Must be initialised before we try and load the keys into the keyring. + */ +device_initcall(blacklist_init); diff --git a/certs/blacklist.h b/certs/blacklist.h new file mode 100644 index 000000000000..150d82da8e99 --- /dev/null +++ b/certs/blacklist.h @@ -0,0 +1,3 @@ +#include + +extern const char __initdata *const blacklist_hashes[]; diff --git a/certs/blacklist_hashes.c b/certs/blacklist_hashes.c new file mode 100644 index 000000000000..5bd449f7db17 --- /dev/null +++ b/certs/blacklist_hashes.c @@ -0,0 +1,6 @@ +#include "blacklist.h" + +const char __initdata *const blacklist_hashes[] = { +#include CONFIG_SYSTEM_BLACKLIST_HASH_LIST + , NULL +}; diff --git a/certs/blacklist_nohashes.c b/certs/blacklist_nohashes.c new file mode 100644 index 000000000000..851de10706a5 --- /dev/null +++ b/certs/blacklist_nohashes.c @@ -0,0 +1,5 @@ +#include "blacklist.h" + +const char __initdata *const blacklist_hashes[] = { + NULL +}; diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h index fbd4647767e9..0d8762622ab9 100644 --- a/include/keys/system_keyring.h +++ b/include/keys/system_keyring.h @@ -33,6 +33,18 @@ extern int restrict_link_by_builtin_and_secondary_trusted( #define restrict_link_by_builtin_and_secondary_trusted restrict_link_by_builtin_trusted #endif +#ifdef CONFIG_SYSTEM_BLACKLIST_KEYRING +extern int mark_hash_blacklisted(const char *hash); +extern int is_hash_blacklisted(const u8 *hash, size_t hash_len, + const char *type); +#else +static inline int is_hash_blacklisted(const u8 *hash, size_t hash_len, + const char *type) +{ + return 0; +} +#endif + #ifdef CONFIG_IMA_BLACKLIST_KEYRING extern struct key *ima_blacklist_keyring; -- cgit v1.2.3 From 436529562df2748fd9918f578205b22cf8ced277 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 3 Apr 2017 16:07:25 +0100 Subject: X.509: Allow X.509 certs to be blacklisted Allow X.509 certs to be blacklisted based on their TBSCertificate hash. This is convenient since we have to determine this anyway to be able to check the signature on an X.509 certificate. This is also what UEFI uses in its blacklist. If a certificate built into the kernel is blacklisted, something like the following might then be seen during boot: X.509: Cert 123412341234c55c1dcc601ab8e172917706aa32fb5eaf826813547fdf02dd46 is blacklisted Problem loading in-kernel X.509 certificate (-129) where the hex string shown is the blacklisted hash. Signed-off-by: David Howells --- crypto/asymmetric_keys/x509_parser.h | 1 + crypto/asymmetric_keys/x509_public_key.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h index 05eef1c68881..e373e7483812 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -42,6 +42,7 @@ struct x509_certificate { bool self_signed; /* T if self-signed (check unsupported_sig too) */ bool unsupported_key; /* T if key uses unsupported crypto */ bool unsupported_sig; /* T if signature uses unsupported crypto */ + bool blacklisted; }; /* diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index fb732296cd36..eea71dc9686c 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -84,6 +84,16 @@ int x509_get_sig_params(struct x509_certificate *cert) goto error_2; might_sleep(); ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, sig->digest); + if (ret < 0) + goto error_2; + + ret = is_hash_blacklisted(sig->digest, sig->digest_size, "tbs"); + if (ret == -EKEYREJECTED) { + pr_err("Cert %*phN is blacklisted\n", + sig->digest_size, sig->digest); + cert->blacklisted = true; + ret = 0; + } error_2: kfree(desc); @@ -186,6 +196,11 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) cert->sig->pkey_algo, cert->sig->hash_algo); } + /* Don't permit addition of blacklisted keys */ + ret = -EKEYREJECTED; + if (cert->blacklisted) + goto error_free_cert; + /* Propose a description */ sulen = strlen(cert->subject); if (cert->raw_skid) { -- cgit v1.2.3 From 03bb79315ddc8972b1af71539799450acbc1be4f Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 3 Apr 2017 16:07:25 +0100 Subject: PKCS#7: Handle blacklisted certificates PKCS#7: Handle certificates that are blacklisted when verifying the chain of trust on the signatures on a PKCS#7 message. Signed-off-by: David Howells --- crypto/asymmetric_keys/pkcs7_parser.h | 1 + crypto/asymmetric_keys/pkcs7_verify.c | 32 ++++++++++++++++++++++++-------- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h index f4e81074f5e0..ac341e19e530 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.h +++ b/crypto/asymmetric_keys/pkcs7_parser.h @@ -23,6 +23,7 @@ struct pkcs7_signed_info { struct x509_certificate *signer; /* Signing certificate (in msg->certs) */ unsigned index; bool unsupported_crypto; /* T if not usable due to missing crypto */ + bool blacklisted; /* Message digest - the digest of the Content Data (or NULL) */ const void *msgdigest; diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c index 2ffd69769466..2d93d9eccb4d 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -190,6 +190,18 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, x509->subject, x509->raw_serial_size, x509->raw_serial); x509->seen = true; + + if (x509->blacklisted) { + /* If this cert is blacklisted, then mark everything + * that depends on this as blacklisted too. + */ + sinfo->blacklisted = true; + for (p = sinfo->signer; p != x509; p = p->signer) + p->blacklisted = true; + pr_debug("- blacklisted\n"); + return 0; + } + if (x509->unsupported_key) goto unsupported_crypto_in_x509; @@ -357,17 +369,19 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7, * * (*) -EBADMSG if some part of the message was invalid, or: * - * (*) -ENOPKG if none of the signature chains are verifiable because suitable - * crypto modules couldn't be found, or: + * (*) 0 if no signature chains were found to be blacklisted or to contain + * unsupported crypto, or: * - * (*) 0 if all the signature chains that don't incur -ENOPKG can be verified - * (note that a signature chain may be of zero length), or: + * (*) -EKEYREJECTED if a blacklisted key was encountered, or: + * + * (*) -ENOPKG if none of the signature chains are verifiable because suitable + * crypto modules couldn't be found. */ int pkcs7_verify(struct pkcs7_message *pkcs7, enum key_being_used_for usage) { struct pkcs7_signed_info *sinfo; - int enopkg = -ENOPKG; + int actual_ret = -ENOPKG; int ret; kenter(""); @@ -412,6 +426,8 @@ int pkcs7_verify(struct pkcs7_message *pkcs7, for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { ret = pkcs7_verify_one(pkcs7, sinfo); + if (sinfo->blacklisted && actual_ret == -ENOPKG) + actual_ret = -EKEYREJECTED; if (ret < 0) { if (ret == -ENOPKG) { sinfo->unsupported_crypto = true; @@ -420,11 +436,11 @@ int pkcs7_verify(struct pkcs7_message *pkcs7, kleave(" = %d", ret); return ret; } - enopkg = 0; + actual_ret = 0; } - kleave(" = %d", enopkg); - return enopkg; + kleave(" = %d", actual_ret); + return actual_ret; } EXPORT_SYMBOL_GPL(pkcs7_verify); -- cgit v1.2.3 From 469ff8f7d46d75b36de68a0411a2ce80109ad00b Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Mon, 25 Apr 2016 11:30:39 -0700 Subject: KEYS: Use a typedef for restrict_link function pointers This pointer type needs to be returned from a lookup function, and without a typedef the syntax gets cumbersome. Signed-off-by: Mat Martineau --- Documentation/security/keys.txt | 5 +---- include/linux/key.h | 16 +++++++--------- security/keys/key.c | 8 ++------ security/keys/keyring.c | 4 +--- 4 files changed, 11 insertions(+), 22 deletions(-) diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index 0e03baf271bd..4502237b12a7 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -1032,10 +1032,7 @@ payload contents" for more information. struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, const struct cred *cred, key_perm_t perm, - int (*restrict_link)(struct key *, - const struct key_type *, - unsigned long, - const union key_payload *), + key_restrict_link_func_t restrict_link, unsigned long flags, struct key *dest); diff --git a/include/linux/key.h b/include/linux/key.h index 9d9fac583dd3..3bb327043869 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -127,6 +127,10 @@ static inline bool is_key_possessed(const key_ref_t key_ref) return (unsigned long) key_ref & 1UL; } +typedef int (*key_restrict_link_func_t)(struct key *keyring, + const struct key_type *type, + const union key_payload *payload); + /*****************************************************************************/ /* * authentication token / access credential / keyring @@ -215,9 +219,7 @@ struct key { * overrides this, allowing the kernel to add extra keys without * restriction. */ - int (*restrict_link)(struct key *keyring, - const struct key_type *type, - const union key_payload *payload); + key_restrict_link_func_t restrict_link; }; extern struct key *key_alloc(struct key_type *type, @@ -226,9 +228,7 @@ extern struct key *key_alloc(struct key_type *type, const struct cred *cred, key_perm_t perm, unsigned long flags, - int (*restrict_link)(struct key *, - const struct key_type *, - const union key_payload *)); + key_restrict_link_func_t restrict_link); #define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */ @@ -304,9 +304,7 @@ extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid const struct cred *cred, key_perm_t perm, unsigned long flags, - int (*restrict_link)(struct key *, - const struct key_type *, - const union key_payload *), + key_restrict_link_func_t restrict_link, struct key *dest); extern int restrict_link_reject(struct key *keyring, diff --git a/security/keys/key.c b/security/keys/key.c index b4958b36fa27..08dfa13f6a85 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -225,9 +225,7 @@ serial_exists: struct key *key_alloc(struct key_type *type, const char *desc, kuid_t uid, kgid_t gid, const struct cred *cred, key_perm_t perm, unsigned long flags, - int (*restrict_link)(struct key *, - const struct key_type *, - const union key_payload *)) + key_restrict_link_func_t restrict_link) { struct key_user *user = NULL; struct key *key; @@ -806,9 +804,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, struct key *keyring, *key = NULL; key_ref_t key_ref; int ret; - int (*restrict_link)(struct key *, - const struct key_type *, - const union key_payload *) = NULL; + key_restrict_link_func_t restrict_link = NULL; /* look up the key type to see if it's one of the registered kernel * types */ diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 3d95f7d02ba1..1b29ac759bf7 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -492,9 +492,7 @@ static long keyring_read(const struct key *keyring, struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, const struct cred *cred, key_perm_t perm, unsigned long flags, - int (*restrict_link)(struct key *, - const struct key_type *, - const union key_payload *), + key_restrict_link_func_t restrict_link, struct key *dest) { struct key *keyring; -- cgit v1.2.3 From aaf66c883813f0078e3dafe7d20d1461321ac14f Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 30 Aug 2016 11:33:13 -0700 Subject: KEYS: Split role of the keyring pointer for keyring restrict functions The first argument to the restrict_link_func_t functions was a keyring pointer. These functions are called by the key subsystem with this argument set to the destination keyring, but restrict_link_by_signature expects a pointer to the relevant trusted keyring. Restrict functions may need something other than a single struct key pointer to allow or reject key linkage, so the data used to make that decision (such as the trust keyring) is moved to a new, fourth argument. The first argument is now always the destination keyring. Signed-off-by: Mat Martineau --- Documentation/security/keys.txt | 8 ++++---- certs/system_keyring.c | 18 +++++++++++------- crypto/asymmetric_keys/restrict.c | 8 +++++--- include/crypto/public_key.h | 5 +++-- include/keys/system_keyring.h | 6 ++++-- include/linux/key.h | 8 +++++--- security/keys/key.c | 5 +++-- security/keys/keyring.c | 6 ++++-- 8 files changed, 39 insertions(+), 25 deletions(-) diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index 4502237b12a7..bb575ab80207 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -1054,10 +1054,10 @@ payload contents" for more information. can be verified by a key the kernel already has. When called, the restriction function will be passed the keyring being - added to, the key flags value and the type and payload of the key being - added. Note that when a new key is being created, this is called between - payload preparsing and actual key creation. The function should return 0 - to allow the link or an error to reject it. + added to, the key type, the payload of the key being added, and data to be + used in the restriction check. Note that when a new key is being created, + this is called between payload preparsing and actual key creation. The + function should return 0 to allow the link or an error to reject it. A convenience function, restrict_link_reject, exists to always return -EPERM to in this case. diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 50979d6dcecd..e39cce68dcfa 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -32,11 +32,13 @@ extern __initconst const unsigned long system_certificate_list_size; * Restrict the addition of keys into a keyring based on the key-to-be-added * being vouched for by a key in the built in system keyring. */ -int restrict_link_by_builtin_trusted(struct key *keyring, +int restrict_link_by_builtin_trusted(struct key *dest_keyring, const struct key_type *type, - const union key_payload *payload) + const union key_payload *payload, + struct key *restriction_key) { - return restrict_link_by_signature(builtin_trusted_keys, type, payload); + return restrict_link_by_signature(dest_keyring, type, payload, + builtin_trusted_keys); } #ifdef CONFIG_SECONDARY_TRUSTED_KEYRING @@ -49,20 +51,22 @@ int restrict_link_by_builtin_trusted(struct key *keyring, * keyrings. */ int restrict_link_by_builtin_and_secondary_trusted( - struct key *keyring, + struct key *dest_keyring, const struct key_type *type, - const union key_payload *payload) + const union key_payload *payload, + struct key *restrict_key) { /* If we have a secondary trusted keyring, then that contains a link * through to the builtin keyring and the search will follow that link. */ if (type == &key_type_keyring && - keyring == secondary_trusted_keys && + dest_keyring == secondary_trusted_keys && payload == &builtin_trusted_keys->payload) /* Allow the builtin keyring to be added to the secondary */ return 0; - return restrict_link_by_signature(secondary_trusted_keys, type, payload); + return restrict_link_by_signature(dest_keyring, type, payload, + secondary_trusted_keys); } #endif diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c index 19d1afb9890f..a3afbf783255 100644 --- a/crypto/asymmetric_keys/restrict.c +++ b/crypto/asymmetric_keys/restrict.c @@ -56,9 +56,10 @@ __setup("ca_keys=", ca_keys_setup); /** * restrict_link_by_signature - Restrict additions to a ring of public keys - * @trust_keyring: A ring of keys that can be used to vouch for the new cert. + * @dest_keyring: Keyring being linked to. * @type: The type of key being added. * @payload: The payload of the new key. + * @trust_keyring: A ring of keys that can be used to vouch for the new cert. * * Check the new certificate against the ones in the trust keyring. If one of * those is the signing key and validates the new certificate, then mark the @@ -69,9 +70,10 @@ __setup("ca_keys=", ca_keys_setup); * signature check fails or the key is blacklisted and some other error if * there is a matching certificate but the signature check cannot be performed. */ -int restrict_link_by_signature(struct key *trust_keyring, +int restrict_link_by_signature(struct key *dest_keyring, const struct key_type *type, - const union key_payload *payload) + const union key_payload *payload, + struct key *trust_keyring) { const struct public_key_signature *sig; struct key *key; diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 882ca0e1e7a5..ec0262fa08f8 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -50,9 +50,10 @@ struct key; struct key_type; union key_payload; -extern int restrict_link_by_signature(struct key *trust_keyring, +extern int restrict_link_by_signature(struct key *dest_keyring, const struct key_type *type, - const union key_payload *payload); + const union key_payload *payload, + struct key *trust_keyring); extern int verify_signature(const struct key *key, const struct public_key_signature *sig); diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h index 0d8762622ab9..359c2f936004 100644 --- a/include/keys/system_keyring.h +++ b/include/keys/system_keyring.h @@ -18,7 +18,8 @@ extern int restrict_link_by_builtin_trusted(struct key *keyring, const struct key_type *type, - const union key_payload *payload); + const union key_payload *payload, + struct key *restriction_key); #else #define restrict_link_by_builtin_trusted restrict_link_reject @@ -28,7 +29,8 @@ extern int restrict_link_by_builtin_trusted(struct key *keyring, extern int restrict_link_by_builtin_and_secondary_trusted( struct key *keyring, const struct key_type *type, - const union key_payload *payload); + const union key_payload *payload, + struct key *restriction_key); #else #define restrict_link_by_builtin_and_secondary_trusted restrict_link_by_builtin_trusted #endif diff --git a/include/linux/key.h b/include/linux/key.h index 3bb327043869..c59d1008c4fc 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -127,9 +127,10 @@ static inline bool is_key_possessed(const key_ref_t key_ref) return (unsigned long) key_ref & 1UL; } -typedef int (*key_restrict_link_func_t)(struct key *keyring, +typedef int (*key_restrict_link_func_t)(struct key *dest_keyring, const struct key_type *type, - const union key_payload *payload); + const union key_payload *payload, + struct key *restriction_key); /*****************************************************************************/ /* @@ -309,7 +310,8 @@ extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid extern int restrict_link_reject(struct key *keyring, const struct key_type *type, - const union key_payload *payload); + const union key_payload *payload, + struct key *restriction_key); extern int keyring_clear(struct key *keyring); diff --git a/security/keys/key.c b/security/keys/key.c index 08dfa13f6a85..27fc1bb40034 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -499,7 +499,7 @@ int key_instantiate_and_link(struct key *key, if (keyring) { if (keyring->restrict_link) { ret = keyring->restrict_link(keyring, key->type, - &prep.payload); + &prep.payload, NULL); if (ret < 0) goto error; } @@ -851,7 +851,8 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, index_key.desc_len = strlen(index_key.description); if (restrict_link) { - ret = restrict_link(keyring, index_key.type, &prep.payload); + ret = restrict_link(keyring, index_key.type, &prep.payload, + NULL); if (ret < 0) { key_ref = ERR_PTR(ret); goto error_free_prep; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 1b29ac759bf7..2ccc66ec35d7 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -517,6 +517,7 @@ EXPORT_SYMBOL(keyring_alloc); * @keyring: The keyring being added to. * @type: The type of key being added. * @payload: The payload of the key intended to be added. + * @data: Additional data for evaluating restriction. * * Reject the addition of any links to a keyring. It can be overridden by * passing KEY_ALLOC_BYPASS_RESTRICTION to key_instantiate_and_link() when @@ -527,7 +528,8 @@ EXPORT_SYMBOL(keyring_alloc); */ int restrict_link_reject(struct key *keyring, const struct key_type *type, - const union key_payload *payload) + const union key_payload *payload, + struct key *restriction_key) { return -EPERM; } @@ -1220,7 +1222,7 @@ static int __key_link_check_restriction(struct key *keyring, struct key *key) { if (!keyring->restrict_link) return 0; - return keyring->restrict_link(keyring, key->type, &key->payload); + return keyring->restrict_link(keyring, key->type, &key->payload, NULL); } /** -- cgit v1.2.3 From e9cc0f689a7c0c9be6fed6861b3a3f49ad0e7a52 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Mon, 27 Jun 2016 16:10:59 -0700 Subject: KEYS: Add a key restriction struct Key link restrictions require restriction-specific data as well as a restriction-specific function pointer. As a first step toward replacing the restrict_link pointer in struct key, define a more general key_restriction structure that captures the required function, key, and key type pointers. Key type modules should not be pinned on account of this key type pointer because the pointer will be cleared by the garbage collector if the key type is unregistered. Signed-off-by: Mat Martineau --- include/linux/key.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/linux/key.h b/include/linux/key.h index c59d1008c4fc..a06649f3223d 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -132,6 +132,12 @@ typedef int (*key_restrict_link_func_t)(struct key *dest_keyring, const union key_payload *payload, struct key *restriction_key); +struct key_restriction { + key_restrict_link_func_t check; + struct key *key; + struct key_type *keytype; +}; + /*****************************************************************************/ /* * authentication token / access credential / keyring -- cgit v1.2.3 From 2b6aa412ff23a02ac777ad307249c60a839cfd25 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Wed, 31 Aug 2016 16:05:43 -0700 Subject: KEYS: Use structure to capture key restriction function and data Replace struct key's restrict_link function pointer with a pointer to the new struct key_restriction. The structure contains pointers to the restriction function as well as relevant data for evaluating the restriction. The garbage collector checks restrict_link->keytype when key types are unregistered. Restrictions involving a removed key type are converted to use restrict_link_reject so that restrictions cannot be removed by unregistering key types. Signed-off-by: Mat Martineau --- Documentation/security/keys.txt | 21 +++++++------ certs/system_keyring.c | 21 ++++++++++++- include/linux/key.h | 8 ++--- security/integrity/digsig.c | 9 +++++- security/integrity/ima/ima_mok.c | 11 ++++++- security/keys/gc.c | 11 +++++++ security/keys/internal.h | 2 ++ security/keys/key.c | 23 ++++++++------ security/keys/keyring.c | 68 +++++++++++++++++++++++++++++++++++++--- 9 files changed, 144 insertions(+), 30 deletions(-) diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index bb575ab80207..e35de987fc48 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -1032,7 +1032,7 @@ payload contents" for more information. struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, const struct cred *cred, key_perm_t perm, - key_restrict_link_func_t restrict_link, + struct key_restriction *restrict_link, unsigned long flags, struct key *dest); @@ -1044,14 +1044,17 @@ payload contents" for more information. KEY_ALLOC_NOT_IN_QUOTA in flags if the keyring shouldn't be accounted towards the user's quota). Error ENOMEM can also be returned. - If restrict_link not NULL, it should point to a function that will be - called each time an attempt is made to link a key into the new keyring. - This function is called to check whether a key may be added into the keying - or not. Callers of key_create_or_update() within the kernel can pass - KEY_ALLOC_BYPASS_RESTRICTION to suppress the check. An example of using - this is to manage rings of cryptographic keys that are set up when the - kernel boots where userspace is also permitted to add keys - provided they - can be verified by a key the kernel already has. + If restrict_link is not NULL, it should point to a structure that contains + the function that will be called each time an attempt is made to link a + key into the new keyring. The structure may also contain a key pointer + and an associated key type. The function is called to check whether a key + may be added into the keyring or not. The key type is used by the garbage + collector to clean up function or data pointers in this structure if the + given key type is unregistered. Callers of key_create_or_update() within + the kernel can pass KEY_ALLOC_BYPASS_RESTRICTION to suppress the check. + An example of using this is to manage rings of cryptographic keys that are + set up when the kernel boots where userspace is also permitted to add keys + - provided they can be verified by a key the kernel already has. When called, the restriction function will be passed the keyring being added to, the key type, the payload of the key being added, and data to be diff --git a/certs/system_keyring.c b/certs/system_keyring.c index e39cce68dcfa..6251d1b27f0c 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -68,6 +69,24 @@ int restrict_link_by_builtin_and_secondary_trusted( return restrict_link_by_signature(dest_keyring, type, payload, secondary_trusted_keys); } + +/** + * Allocate a struct key_restriction for the "builtin and secondary trust" + * keyring. Only for use in system_trusted_keyring_init(). + */ +static __init struct key_restriction *get_builtin_and_secondary_restriction(void) +{ + struct key_restriction *restriction; + + restriction = kzalloc(sizeof(struct key_restriction), GFP_KERNEL); + + if (!restriction) + panic("Can't allocate secondary trusted keyring restriction\n"); + + restriction->check = restrict_link_by_builtin_and_secondary_trusted; + + return restriction; +} #endif /* @@ -95,7 +114,7 @@ static __init int system_trusted_keyring_init(void) KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH | KEY_USR_WRITE), KEY_ALLOC_NOT_IN_QUOTA, - restrict_link_by_builtin_and_secondary_trusted, + get_builtin_and_secondary_restriction(), NULL); if (IS_ERR(secondary_trusted_keys)) panic("Can't allocate secondary trusted keyring\n"); diff --git a/include/linux/key.h b/include/linux/key.h index a06649f3223d..d2916363689c 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -217,7 +217,7 @@ struct key { }; /* This is set on a keyring to restrict the addition of a link to a key - * to it. If this method isn't provided then it is assumed that the + * to it. If this structure isn't provided then it is assumed that the * keyring is open to any addition. It is ignored for non-keyring * keys. * @@ -226,7 +226,7 @@ struct key { * overrides this, allowing the kernel to add extra keys without * restriction. */ - key_restrict_link_func_t restrict_link; + struct key_restriction *restrict_link; }; extern struct key *key_alloc(struct key_type *type, @@ -235,7 +235,7 @@ extern struct key *key_alloc(struct key_type *type, const struct cred *cred, key_perm_t perm, unsigned long flags, - key_restrict_link_func_t restrict_link); + struct key_restriction *restrict_link); #define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */ @@ -311,7 +311,7 @@ extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid const struct cred *cred, key_perm_t perm, unsigned long flags, - key_restrict_link_func_t restrict_link, + struct key_restriction *restrict_link, struct key *dest); extern int restrict_link_reject(struct key *keyring, diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index 106e855e2d9d..06554c448dce 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -81,18 +81,25 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, int __init integrity_init_keyring(const unsigned int id) { const struct cred *cred = current_cred(); + struct key_restriction *restriction; int err = 0; if (!init_keyring) return 0; + restriction = kzalloc(sizeof(struct key_restriction), GFP_KERNEL); + if (!restriction) + return -ENOMEM; + + restriction->check = restrict_link_to_ima; + keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0), KGIDT_INIT(0), cred, ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE | KEY_USR_SEARCH), KEY_ALLOC_NOT_IN_QUOTA, - restrict_link_to_ima, NULL); + restriction, NULL); if (IS_ERR(keyring[id])) { err = PTR_ERR(keyring[id]); pr_info("Can't allocate %s keyring (%d)\n", diff --git a/security/integrity/ima/ima_mok.c b/security/integrity/ima/ima_mok.c index 74a279957464..073ddc9bce5b 100644 --- a/security/integrity/ima/ima_mok.c +++ b/security/integrity/ima/ima_mok.c @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -27,15 +28,23 @@ struct key *ima_blacklist_keyring; */ __init int ima_mok_init(void) { + struct key_restriction *restriction; + pr_notice("Allocating IMA blacklist keyring.\n"); + restriction = kzalloc(sizeof(struct key_restriction), GFP_KERNEL); + if (!restriction) + panic("Can't allocate IMA blacklist restriction."); + + restriction->check = restrict_link_by_builtin_trusted; + ima_blacklist_keyring = keyring_alloc(".ima_blacklist", KUIDT_INIT(0), KGIDT_INIT(0), current_cred(), (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE | KEY_USR_SEARCH, KEY_ALLOC_NOT_IN_QUOTA, - restrict_link_by_builtin_trusted, NULL); + restriction, NULL); if (IS_ERR(ima_blacklist_keyring)) panic("Can't allocate IMA blacklist keyring."); diff --git a/security/keys/gc.c b/security/keys/gc.c index 44789256c88c..15b9ddf510e4 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -229,6 +229,9 @@ continue_scanning: set_bit(KEY_FLAG_DEAD, &key->flags); key->perm = 0; goto skip_dead_key; + } else if (key->type == &key_type_keyring && + key->restrict_link) { + goto found_restricted_keyring; } } @@ -334,6 +337,14 @@ found_unreferenced_key: gc_state |= KEY_GC_REAP_AGAIN; goto maybe_resched; + /* We found a restricted keyring and need to update the restriction if + * it is associated with the dead key type. + */ +found_restricted_keyring: + spin_unlock(&key_serial_lock); + keyring_restriction_gc(key, key_gc_dead_keytype); + goto maybe_resched; + /* We found a keyring and we need to check the payload for links to * dead or expired keys. We don't flag another reap immediately as we * have to wait for the old payload to be destroyed by RCU before we diff --git a/security/keys/internal.h b/security/keys/internal.h index 6bee06ae026d..24762ae9a198 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -168,6 +168,8 @@ extern void key_change_session_keyring(struct callback_head *twork); extern struct work_struct key_gc_work; extern unsigned key_gc_delay; extern void keyring_gc(struct key *keyring, time_t limit); +extern void keyring_restriction_gc(struct key *keyring, + struct key_type *dead_type); extern void key_schedule_gc(time_t gc_at); extern void key_schedule_gc_links(void); extern void key_gc_keytype(struct key_type *ktype); diff --git a/security/keys/key.c b/security/keys/key.c index 27fc1bb40034..2ea5967121de 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -201,12 +201,15 @@ serial_exists: * @cred: The credentials specifying UID namespace. * @perm: The permissions mask of the new key. * @flags: Flags specifying quota properties. - * @restrict_link: Optional link restriction method for new keyrings. + * @restrict_link: Optional link restriction for new keyrings. * * Allocate a key of the specified type with the attributes given. The key is * returned in an uninstantiated state and the caller needs to instantiate the * key before returning. * + * The restrict_link structure (if not NULL) will be freed when the + * keyring is destroyed, so it must be dynamically allocated. + * * The user's key count quota is updated to reflect the creation of the key and * the user's key data quota has the default for the key type reserved. The * instantiation function should amend this as necessary. If insufficient @@ -225,7 +228,7 @@ serial_exists: struct key *key_alloc(struct key_type *type, const char *desc, kuid_t uid, kgid_t gid, const struct cred *cred, key_perm_t perm, unsigned long flags, - key_restrict_link_func_t restrict_link) + struct key_restriction *restrict_link) { struct key_user *user = NULL; struct key *key; @@ -497,9 +500,11 @@ int key_instantiate_and_link(struct key *key, } if (keyring) { - if (keyring->restrict_link) { - ret = keyring->restrict_link(keyring, key->type, - &prep.payload, NULL); + if (keyring->restrict_link && keyring->restrict_link->check) { + struct key_restriction *keyres = keyring->restrict_link; + + ret = keyres->check(keyring, key->type, &prep.payload, + keyres->key); if (ret < 0) goto error; } @@ -804,7 +809,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, struct key *keyring, *key = NULL; key_ref_t key_ref; int ret; - key_restrict_link_func_t restrict_link = NULL; + struct key_restriction *restrict_link = NULL; /* look up the key type to see if it's one of the registered kernel * types */ @@ -850,9 +855,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, } index_key.desc_len = strlen(index_key.description); - if (restrict_link) { - ret = restrict_link(keyring, index_key.type, &prep.payload, - NULL); + if (restrict_link && restrict_link->check) { + ret = restrict_link->check(keyring, index_key.type, + &prep.payload, restrict_link->key); if (ret < 0) { key_ref = ERR_PTR(ret); goto error_free_prep; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 2ccc66ec35d7..838334fec6ce 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -394,6 +394,13 @@ static void keyring_destroy(struct key *keyring) write_unlock(&keyring_name_lock); } + if (keyring->restrict_link) { + struct key_restriction *keyres = keyring->restrict_link; + + key_put(keyres->key); + kfree(keyres); + } + assoc_array_destroy(&keyring->keys, &keyring_assoc_array_ops); } @@ -492,7 +499,7 @@ static long keyring_read(const struct key *keyring, struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, const struct cred *cred, key_perm_t perm, unsigned long flags, - key_restrict_link_func_t restrict_link, + struct key_restriction *restrict_link, struct key *dest) { struct key *keyring; @@ -523,8 +530,8 @@ EXPORT_SYMBOL(keyring_alloc); * passing KEY_ALLOC_BYPASS_RESTRICTION to key_instantiate_and_link() when * adding a key to a keyring. * - * This is meant to be passed as the restrict_link parameter to - * keyring_alloc(). + * This is meant to be stored in a key_restriction structure which is passed + * in the restrict_link parameter to keyring_alloc(). */ int restrict_link_reject(struct key *keyring, const struct key_type *type, @@ -1220,9 +1227,10 @@ void __key_link_end(struct key *keyring, */ static int __key_link_check_restriction(struct key *keyring, struct key *key) { - if (!keyring->restrict_link) + if (!keyring->restrict_link || !keyring->restrict_link->check) return 0; - return keyring->restrict_link(keyring, key->type, &key->payload, NULL); + return keyring->restrict_link->check(keyring, key->type, &key->payload, + keyring->restrict_link->key); } /** @@ -1426,3 +1434,53 @@ do_gc: up_write(&keyring->sem); kleave(" [gc]"); } + +/* + * Garbage collect restriction pointers from a keyring. + * + * Keyring restrictions are associated with a key type, and must be cleaned + * up if the key type is unregistered. The restriction is altered to always + * reject additional keys so a keyring cannot be opened up by unregistering + * a key type. + * + * Not called with any keyring locks held. The keyring's key struct will not + * be deallocated under us as only our caller may deallocate it. + * + * The caller is required to hold key_types_sem and dead_type->sem. This is + * fulfilled by key_gc_keytype() holding the locks on behalf of + * key_garbage_collector(), which it invokes on a workqueue. + */ +void keyring_restriction_gc(struct key *keyring, struct key_type *dead_type) +{ + struct key_restriction *keyres; + + kenter("%x{%s}", keyring->serial, keyring->description ?: ""); + + /* + * keyring->restrict_link is only assigned at key allocation time + * or with the key type locked, so the only values that could be + * concurrently assigned to keyring->restrict_link are for key + * types other than dead_type. Given this, it's ok to check + * the key type before acquiring keyring->sem. + */ + if (!dead_type || !keyring->restrict_link || + keyring->restrict_link->keytype != dead_type) { + kleave(" [no restriction gc]"); + return; + } + + /* Lock the keyring to ensure that a link is not in progress */ + down_write(&keyring->sem); + + keyres = keyring->restrict_link; + + keyres->check = restrict_link_reject; + + key_put(keyres->key); + keyres->key = NULL; + keyres->keytype = NULL; + + up_write(&keyring->sem); + + kleave(" [restriction gc]"); +} -- cgit v1.2.3 From efba797b977c99bc6e0c301299272c80fb8b287f Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Fri, 6 May 2016 15:38:17 -0700 Subject: KEYS: Add an optional lookup_restriction hook to key_type The restrict_link functions used to validate keys as they are linked to a keyring can be associated with specific key types. Each key type may be loaded (or not) at runtime, so lookup of restrict_link functions needs to be part of the key type implementation to ensure that the requested keys can be examined. Signed-off-by: Mat Martineau --- Documentation/security/keys.txt | 9 +++++++++ include/linux/key-type.h | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index e35de987fc48..5fe04a7cc03d 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -1445,6 +1445,15 @@ The structure has a number of fields, some of which are mandatory: The authorisation key. + (*) struct key_restriction *(*lookup_restriction)(const char *params); + + This optional method is used to enable userspace configuration of keyring + restrictions. The restriction parameter string (not including the key type + name) is passed in, and this method returns a pointer to a key_restriction + structure containing the relevant functions and data to evaluate each + attempted key link operation. If there is no match, -EINVAL is returned. + + ============================ REQUEST-KEY CALLBACK SERVICE ============================ diff --git a/include/linux/key-type.h b/include/linux/key-type.h index eaee981c5558..8496cf64575c 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -147,6 +147,14 @@ struct key_type { */ request_key_actor_t request_key; + /* Look up a keyring access restriction (optional) + * + * - NULL is a valid return value (meaning the requested restriction + * is known but will never block addition of a key) + * - should return -EINVAL if the restriction is unknown + */ + struct key_restriction *(*lookup_restriction)(const char *params); + /* internal fields */ struct list_head link; /* link in types list */ struct lock_class_key lock_class; /* key->sem lock class */ -- cgit v1.2.3 From 4a420896f12d2d043602f134ae18ad6be5b9d9dd Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 4 Oct 2016 16:27:32 -0700 Subject: KEYS: Consistent ordering for __key_link_begin and restrict check The keyring restrict callback was sometimes called before __key_link_begin and sometimes after, which meant that the keyring semaphores were not always held during the restrict callback. If the semaphores are consistently acquired before checking link restrictions, keyring contents cannot be changed after the restrict check is complete but before the evaluated key is linked to the keyring. Signed-off-by: Mat Martineau --- security/keys/key.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/security/keys/key.c b/security/keys/key.c index 2ea5967121de..455c04d80bbb 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -500,21 +500,23 @@ int key_instantiate_and_link(struct key *key, } if (keyring) { + ret = __key_link_begin(keyring, &key->index_key, &edit); + if (ret < 0) + goto error; + if (keyring->restrict_link && keyring->restrict_link->check) { struct key_restriction *keyres = keyring->restrict_link; ret = keyres->check(keyring, key->type, &prep.payload, keyres->key); if (ret < 0) - goto error; + goto error_link_end; } - ret = __key_link_begin(keyring, &key->index_key, &edit); - if (ret < 0) - goto error; } ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &edit); +error_link_end: if (keyring) __key_link_end(keyring, &key->index_key, edit); @@ -855,21 +857,21 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, } index_key.desc_len = strlen(index_key.description); + ret = __key_link_begin(keyring, &index_key, &edit); + if (ret < 0) { + key_ref = ERR_PTR(ret); + goto error_free_prep; + } + if (restrict_link && restrict_link->check) { ret = restrict_link->check(keyring, index_key.type, &prep.payload, restrict_link->key); if (ret < 0) { key_ref = ERR_PTR(ret); - goto error_free_prep; + goto error_link_end; } } - ret = __key_link_begin(keyring, &index_key, &edit); - if (ret < 0) { - key_ref = ERR_PTR(ret); - goto error_free_prep; - } - /* if we're going to allocate a new key, we're going to have * to modify the keyring */ ret = key_permission(keyring_ref, KEY_NEED_WRITE); -- cgit v1.2.3 From 6563c91fd645556c7801748f15bc727c77fcd311 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Wed, 1 Mar 2017 16:44:09 -0800 Subject: KEYS: Add KEYCTL_RESTRICT_KEYRING Keyrings recently gained restrict_link capabilities that allow individual keys to be validated prior to linking. This functionality was only available using internal kernel APIs. With the KEYCTL_RESTRICT_KEYRING command existing keyrings can be configured to check the content of keys before they are linked, and then allow or disallow linkage of that key to the keyring. To restrict a keyring, call: keyctl(KEYCTL_RESTRICT_KEYRING, key_serial_t keyring, const char *type, const char *restriction) where 'type' is the name of a registered key type and 'restriction' is a string describing how key linkage is to be restricted. The restriction option syntax is specific to each key type. Signed-off-by: Mat Martineau --- Documentation/security/keys.txt | 25 ++++++++++ include/linux/key.h | 6 ++- include/uapi/linux/keyctl.h | 1 + security/keys/compat.c | 4 ++ security/keys/internal.h | 3 ++ security/keys/keyctl.c | 58 ++++++++++++++++++++++ security/keys/keyring.c | 105 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 201 insertions(+), 1 deletion(-) diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index 5fe04a7cc03d..5f554aab8751 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -857,6 +857,31 @@ The keyctl syscall functions are: supported, error ENOKEY if the key could not be found, or error EACCES if the key is not readable by the caller. + (*) Restrict keyring linkage + + long keyctl(KEYCTL_RESTRICT_KEYRING, key_serial_t keyring, + const char *type, const char *restriction); + + An existing keyring can restrict linkage of additional keys by evaluating + the contents of the key according to a restriction scheme. + + "keyring" is the key ID for an existing keyring to apply a restriction + to. It may be empty or may already have keys linked. Existing linked keys + will remain in the keyring even if the new restriction would reject them. + + "type" is a registered key type. + + "restriction" is a string describing how key linkage is to be restricted. + The format varies depending on the key type, and the string is passed to + the lookup_restriction() function for the requested type. It may specify + a method and relevant data for the restriction such as signature + verification or constraints on key payload. If the requested key type is + later unregistered, no keys may be added to the keyring after the key type + is removed. + + To apply a keyring restriction the process must have Set Attribute + permission and the keyring must not be previously restricted. + =============== KERNEL SERVICES =============== diff --git a/include/linux/key.h b/include/linux/key.h index d2916363689c..0c9b93b0d1f7 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -219,7 +219,8 @@ struct key { /* This is set on a keyring to restrict the addition of a link to a key * to it. If this structure isn't provided then it is assumed that the * keyring is open to any addition. It is ignored for non-keyring - * keys. + * keys. Only set this value using keyring_restrict(), keyring_alloc(), + * or key_alloc(). * * This is intended for use with rings of trusted keys whereby addition * to the keyring needs to be controlled. KEY_ALLOC_BYPASS_RESTRICTION @@ -328,6 +329,9 @@ extern key_ref_t keyring_search(key_ref_t keyring, extern int keyring_add_key(struct key *keyring, struct key *key); +extern int keyring_restrict(key_ref_t keyring, const char *type, + const char *restriction); + extern struct key *key_lookup(key_serial_t id); static inline key_serial_t key_serial(const struct key *key) diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h index 86eddd6241f3..ff79c44e49a3 100644 --- a/include/uapi/linux/keyctl.h +++ b/include/uapi/linux/keyctl.h @@ -60,6 +60,7 @@ #define KEYCTL_INVALIDATE 21 /* invalidate a key */ #define KEYCTL_GET_PERSISTENT 22 /* get a user's persistent keyring */ #define KEYCTL_DH_COMPUTE 23 /* Compute Diffie-Hellman values */ +#define KEYCTL_RESTRICT_KEYRING 29 /* Restrict keys allowed to link to a keyring */ /* keyctl structures */ struct keyctl_dh_params { diff --git a/security/keys/compat.c b/security/keys/compat.c index 36c80bf5b89c..bb98f2b8dd7d 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -136,6 +136,10 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option, return keyctl_dh_compute(compat_ptr(arg2), compat_ptr(arg3), arg4, compat_ptr(arg5)); + case KEYCTL_RESTRICT_KEYRING: + return keyctl_restrict_keyring(arg2, compat_ptr(arg3), + compat_ptr(arg4)); + default: return -EOPNOTSUPP; } diff --git a/security/keys/internal.h b/security/keys/internal.h index 24762ae9a198..6ce016314897 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -252,6 +252,9 @@ struct iov_iter; extern long keyctl_instantiate_key_common(key_serial_t, struct iov_iter *, key_serial_t); +extern long keyctl_restrict_keyring(key_serial_t id, + const char __user *_type, + const char __user *_restriction); #ifdef CONFIG_PERSISTENT_KEYRINGS extern long keyctl_get_persistent(uid_t, key_serial_t); extern unsigned persistent_keyring_expiry; diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 52c34532c785..6ee2826a2d06 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1582,6 +1582,59 @@ error_keyring: return ret; } +/* + * Apply a restriction to a given keyring. + * + * The caller must have Setattr permission to change keyring restrictions. + * + * The requested type name may be a NULL pointer to reject all attempts + * to link to the keyring. If _type is non-NULL, _restriction can be + * NULL or a pointer to a string describing the restriction. If _type is + * NULL, _restriction must also be NULL. + * + * Returns 0 if successful. + */ +long keyctl_restrict_keyring(key_serial_t id, const char __user *_type, + const char __user *_restriction) +{ + key_ref_t key_ref; + bool link_reject = !_type; + char type[32]; + char *restriction = NULL; + long ret; + + key_ref = lookup_user_key(id, 0, KEY_NEED_SETATTR); + if (IS_ERR(key_ref)) + return PTR_ERR(key_ref); + + if (_type) { + ret = key_get_type_from_user(type, _type, sizeof(type)); + if (ret < 0) + goto error; + } + + if (_restriction) { + if (!_type) { + ret = -EINVAL; + goto error; + } + + restriction = strndup_user(_restriction, PAGE_SIZE); + if (IS_ERR(restriction)) { + ret = PTR_ERR(restriction); + goto error; + } + } + + ret = keyring_restrict(key_ref, link_reject ? NULL : type, restriction); + kfree(restriction); + +error: + key_ref_put(key_ref); + + return ret; +} + /* * The key control system call */ @@ -1693,6 +1746,11 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, (char __user *) arg3, (size_t) arg4, (void __user *) arg5); + case KEYCTL_RESTRICT_KEYRING: + return keyctl_restrict_keyring((key_serial_t) arg2, + (const char __user *) arg3, + (const char __user *) arg4); + default: return -EOPNOTSUPP; } diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 838334fec6ce..4d1678e4586f 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -947,6 +947,111 @@ key_ref_t keyring_search(key_ref_t keyring, } EXPORT_SYMBOL(keyring_search); +static struct key_restriction *keyring_restriction_alloc( + key_restrict_link_func_t check) +{ + struct key_restriction *keyres = + kzalloc(sizeof(struct key_restriction), GFP_KERNEL); + + if (!keyres) + return ERR_PTR(-ENOMEM); + + keyres->check = check; + + return keyres; +} + +/* + * Semaphore to serialise restriction setup to prevent reference count + * cycles through restriction key pointers. + */ +static DECLARE_RWSEM(keyring_serialise_restrict_sem); + +/* + * Check for restriction cycles that would prevent keyring garbage collection. + * keyring_serialise_restrict_sem must be held. + */ +static bool keyring_detect_restriction_cycle(const struct key *dest_keyring, + struct key_restriction *keyres) +{ + while (keyres && keyres->key && + keyres->key->type == &key_type_keyring) { + if (keyres->key == dest_keyring) + return true; + + keyres = keyres->key->restrict_link; + } + + return false; +} + +/** + * keyring_restrict - Look up and apply a restriction to a keyring + * + * @keyring: The keyring to be restricted + * @restriction: The restriction options to apply to the keyring + */ +int keyring_restrict(key_ref_t keyring_ref, const char *type, + const char *restriction) +{ + struct key *keyring; + struct key_type *restrict_type = NULL; + struct key_restriction *restrict_link; + int ret = 0; + + keyring = key_ref_to_ptr(keyring_ref); + key_check(keyring); + + if (keyring->type != &key_type_keyring) + return -ENOTDIR; + + if (!type) { + restrict_link = keyring_restriction_alloc(restrict_link_reject); + } else { + restrict_type = key_type_lookup(type); + + if (IS_ERR(restrict_type)) + return PTR_ERR(restrict_type); + + if (!restrict_type->lookup_restriction) { + ret = -ENOENT; + goto error; + } + + restrict_link = restrict_type->lookup_restriction(restriction); + } + + if (IS_ERR(restrict_link)) { + ret = PTR_ERR(restrict_link); + goto error; + } + + down_write(&keyring->sem); + down_write(&keyring_serialise_restrict_sem); + + if (keyring->restrict_link) + ret = -EEXIST; + else if (keyring_detect_restriction_cycle(keyring, restrict_link)) + ret = -EDEADLK; + else + keyring->restrict_link = restrict_link; + + up_write(&keyring_serialise_restrict_sem); + up_write(&keyring->sem); + + if (ret < 0) { + key_put(restrict_link->key); + kfree(restrict_link); + } + +error: + if (restrict_type) + key_type_put(restrict_type); + + return ret; +} +EXPORT_SYMBOL(keyring_restrict); + /* * Search the given keyring for a key that might be updated. * -- cgit v1.2.3 From 97d3aa0f313435a24440e7157c9c9115c58ca463 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Fri, 6 May 2016 14:25:39 -0700 Subject: KEYS: Add a lookup_restriction function for the asymmetric key type Look up asymmetric keyring restriction information using the key-type lookup_restrict hook. Signed-off-by: Mat Martineau --- Documentation/crypto/asymmetric-keys.txt | 35 +++++++++++++++++++++ crypto/asymmetric_keys/asymmetric_type.c | 52 +++++++++++++++++++++++++++----- 2 files changed, 79 insertions(+), 8 deletions(-) diff --git a/Documentation/crypto/asymmetric-keys.txt b/Documentation/crypto/asymmetric-keys.txt index 2b7816dea370..4373e7d86c6a 100644 --- a/Documentation/crypto/asymmetric-keys.txt +++ b/Documentation/crypto/asymmetric-keys.txt @@ -311,3 +311,38 @@ Functions are provided to register and unregister parsers: Parsers may not have the same name. The names are otherwise only used for displaying in debugging messages. + + +========================= +KEYRING LINK RESTRICTIONS +========================= + +Keyrings created from userspace using add_key can be configured to check the +signature of the key being linked. + +Several restriction methods are available: + + (1) Restrict using the kernel builtin trusted keyring + + - Option string used with KEYCTL_RESTRICT_KEYRING: + - "builtin_trusted" + + The kernel builtin trusted keyring will be searched for the signing + key. The ca_keys kernel parameter also affects which keys are used for + signature verification. + + (2) Restrict using the kernel builtin and secondary trusted keyrings + + - Option string used with KEYCTL_RESTRICT_KEYRING: + - "builtin_and_secondary_trusted" + + The kernel builtin and secondary trusted keyrings will be searched for the + signing key. The ca_keys kernel parameter also affects which keys are used + for signature verification. + +In all of these cases, if the signing key is found the signature of the key to +be linked will be verified using the signing key. The requested key is added +to the keyring only if the signature is successfully verified. -ENOKEY is +returned if the parent certificate could not be found, or -EKEYREJECTED is +returned if the signature check fails or the key is blacklisted. Other errors +may be returned if the signature check could not be performed. diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 6600181d5d01..2e3380d09631 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "asymmetric_keys.h" MODULE_LICENSE("GPL"); @@ -451,15 +452,50 @@ static void asymmetric_key_destroy(struct key *key) asymmetric_key_free_kids(kids); } +static struct key_restriction *asymmetric_restriction_alloc( + key_restrict_link_func_t check, + struct key *key) +{ + struct key_restriction *keyres = + kzalloc(sizeof(struct key_restriction), GFP_KERNEL); + + if (!keyres) + return ERR_PTR(-ENOMEM); + + keyres->check = check; + keyres->key = key; + keyres->keytype = &key_type_asymmetric; + + return keyres; +} + +/* + * look up keyring restrict functions for asymmetric keys + */ +static struct key_restriction *asymmetric_lookup_restriction( + const char *restriction) +{ + if (strcmp("builtin_trusted", restriction) == 0) + return asymmetric_restriction_alloc( + restrict_link_by_builtin_trusted, NULL); + + if (strcmp("builtin_and_secondary_trusted", restriction) == 0) + return asymmetric_restriction_alloc( + restrict_link_by_builtin_and_secondary_trusted, NULL); + + return ERR_PTR(-EINVAL); +} + struct key_type key_type_asymmetric = { - .name = "asymmetric", - .preparse = asymmetric_key_preparse, - .free_preparse = asymmetric_key_free_preparse, - .instantiate = generic_key_instantiate, - .match_preparse = asymmetric_key_match_preparse, - .match_free = asymmetric_key_match_free, - .destroy = asymmetric_key_destroy, - .describe = asymmetric_key_describe, + .name = "asymmetric", + .preparse = asymmetric_key_preparse, + .free_preparse = asymmetric_key_free_preparse, + .instantiate = generic_key_instantiate, + .match_preparse = asymmetric_key_match_preparse, + .match_free = asymmetric_key_match_free, + .destroy = asymmetric_key_destroy, + .describe = asymmetric_key_describe, + .lookup_restriction = asymmetric_lookup_restriction, }; EXPORT_SYMBOL_GPL(key_type_asymmetric); -- cgit v1.2.3 From 7e3c4d22083f6e7316c5229b6197ca2d5335aa35 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Mon, 27 Jun 2016 16:45:16 -0700 Subject: KEYS: Restrict asymmetric key linkage using a specific keychain Adds restrict_link_by_signature_keyring(), which uses the restrict_key member of the provided destination_keyring data structure as the key or keyring to search for signing keys. Signed-off-by: Mat Martineau --- Documentation/crypto/asymmetric-keys.txt | 11 +++++ crypto/asymmetric_keys/asymmetric_type.c | 35 +++++++++++++++- crypto/asymmetric_keys/restrict.c | 71 ++++++++++++++++++++++++++++++++ include/crypto/public_key.h | 5 +++ 4 files changed, 121 insertions(+), 1 deletion(-) diff --git a/Documentation/crypto/asymmetric-keys.txt b/Documentation/crypto/asymmetric-keys.txt index 4373e7d86c6a..9814722f4b6b 100644 --- a/Documentation/crypto/asymmetric-keys.txt +++ b/Documentation/crypto/asymmetric-keys.txt @@ -340,6 +340,17 @@ Several restriction methods are available: signing key. The ca_keys kernel parameter also affects which keys are used for signature verification. + (3) Restrict using a separate key or keyring + + - Option string used with KEYCTL_RESTRICT_KEYRING: + - "key_or_keyring:" + + Whenever a key link is requested, the link will only succeed if the key + being linked is signed by one of the designated keys. This key may be + specified directly by providing a serial number for one asymmetric key, or + a group of keys may be searched for the signing key by providing the + serial number for a keyring. + In all of these cases, if the signing key is found the signature of the key to be linked will be verified using the signing key. The requested key is added to the keyring only if the signature is successfully verified. -ENOKEY is diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 2e3380d09631..72700ed81594 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -475,6 +475,11 @@ static struct key_restriction *asymmetric_restriction_alloc( static struct key_restriction *asymmetric_lookup_restriction( const char *restriction) { + char *restrict_method; + char *parse_buf; + char *next; + struct key_restriction *ret = ERR_PTR(-EINVAL); + if (strcmp("builtin_trusted", restriction) == 0) return asymmetric_restriction_alloc( restrict_link_by_builtin_trusted, NULL); @@ -483,7 +488,35 @@ static struct key_restriction *asymmetric_lookup_restriction( return asymmetric_restriction_alloc( restrict_link_by_builtin_and_secondary_trusted, NULL); - return ERR_PTR(-EINVAL); + parse_buf = kstrndup(restriction, PAGE_SIZE, GFP_KERNEL); + if (!parse_buf) + return ERR_PTR(-ENOMEM); + + next = parse_buf; + restrict_method = strsep(&next, ":"); + + if ((strcmp(restrict_method, "key_or_keyring") == 0) && next) { + key_serial_t serial; + struct key *key; + + if (kstrtos32(next, 0, &serial) < 0) + goto out; + + key = key_lookup(serial); + if (IS_ERR(key)) { + ret = ERR_CAST(key); + goto out; + } + + ret = asymmetric_restriction_alloc( + restrict_link_by_key_or_keyring, key); + if (IS_ERR(ret)) + key_put(key); + } + +out: + kfree(parse_buf); + return ret; } struct key_type key_type_asymmetric = { diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c index a3afbf783255..183cb642580e 100644 --- a/crypto/asymmetric_keys/restrict.c +++ b/crypto/asymmetric_keys/restrict.c @@ -108,3 +108,74 @@ int restrict_link_by_signature(struct key *dest_keyring, key_put(key); return ret; } + +/** + * restrict_link_by_key_or_keyring - Restrict additions to a ring of public + * keys using the restrict_key information stored in the ring. + * @dest_keyring: Keyring being linked to. + * @type: The type of key being added. + * @payload: The payload of the new key. + * @trusted: A key or ring of keys that can be used to vouch for the new cert. + * + * Check the new certificate only against the key or keys passed in the data + * parameter. If one of those is the signing key and validates the new + * certificate, then mark the new certificate as being ok to link. + * + * Returns 0 if the new certificate was accepted, -ENOKEY if we + * couldn't find a matching parent certificate in the trusted list, + * -EKEYREJECTED if the signature check fails, and some other error if + * there is a matching certificate but the signature check cannot be + * performed. + */ +int restrict_link_by_key_or_keyring(struct key *dest_keyring, + const struct key_type *type, + const union key_payload *payload, + struct key *trusted) +{ + const struct public_key_signature *sig; + struct key *key; + int ret; + + pr_devel("==>%s()\n", __func__); + + if (!dest_keyring) + return -ENOKEY; + else if (dest_keyring->type != &key_type_keyring) + return -EOPNOTSUPP; + + if (!trusted) + return -ENOKEY; + + if (type != &key_type_asymmetric) + return -EOPNOTSUPP; + + sig = payload->data[asym_auth]; + if (!sig->auth_ids[0] && !sig->auth_ids[1]) + return -ENOKEY; + + if (trusted->type == &key_type_keyring) { + /* See if we have a key that signed this one. */ + key = find_asymmetric_key(trusted, sig->auth_ids[0], + sig->auth_ids[1], false); + if (IS_ERR(key)) + return -ENOKEY; + } else if (trusted->type == &key_type_asymmetric) { + const struct asymmetric_key_ids *kids; + + kids = asymmetric_key_ids(trusted); + + if (!asymmetric_key_id_same(kids->id[1], sig->auth_ids[0])) + return -ENOKEY; + + key = __key_get(trusted); + } else { + return -EOPNOTSUPP; + } + + ret = key_validate(key); + if (ret == 0) + ret = verify_signature(key, sig); + + key_put(key); + return ret; +} diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index ec0262fa08f8..bb6a884352a5 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -55,6 +55,11 @@ extern int restrict_link_by_signature(struct key *dest_keyring, const union key_payload *payload, struct key *trust_keyring); +extern int restrict_link_by_key_or_keyring(struct key *dest_keyring, + const struct key_type *type, + const union key_payload *payload, + struct key *trusted); + extern int verify_signature(const struct key *key, const struct public_key_signature *sig); -- cgit v1.2.3 From 8e323a02e866014091180443ccb186fee1e3d30d Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 4 Oct 2016 16:42:45 -0700 Subject: KEYS: Keyring asymmetric key restrict method with chaining Add a restrict_link_by_key_or_keyring_chain link restriction that searches for signing keys in the destination keyring in addition to the signing key or keyring designated when the destination keyring was created. Userspace enables this behavior by including the "chain" option in the keyring restriction: keyctl(KEYCTL_RESTRICT_KEYRING, keyring, "asymmetric", "key_or_keyring::chain"); Signed-off-by: Mat Martineau --- Documentation/crypto/asymmetric-keys.txt | 7 +- crypto/asymmetric_keys/asymmetric_type.c | 31 ++++-- crypto/asymmetric_keys/restrict.c | 158 +++++++++++++++++++++++-------- include/crypto/public_key.h | 5 + 4 files changed, 155 insertions(+), 46 deletions(-) diff --git a/Documentation/crypto/asymmetric-keys.txt b/Documentation/crypto/asymmetric-keys.txt index 9814722f4b6b..5ad6480e3fb9 100644 --- a/Documentation/crypto/asymmetric-keys.txt +++ b/Documentation/crypto/asymmetric-keys.txt @@ -343,7 +343,7 @@ Several restriction methods are available: (3) Restrict using a separate key or keyring - Option string used with KEYCTL_RESTRICT_KEYRING: - - "key_or_keyring:" + - "key_or_keyring:[:chain]" Whenever a key link is requested, the link will only succeed if the key being linked is signed by one of the designated keys. This key may be @@ -351,6 +351,11 @@ Several restriction methods are available: a group of keys may be searched for the signing key by providing the serial number for a keyring. + When the "chain" option is provided at the end of the string, the keys + within the destination keyring will also be searched for signing keys. + This allows for verification of certificate chains by adding each + cert in order (starting closest to the root) to one keyring. + In all of these cases, if the signing key is found the signature of the key to be linked will be verified using the signing key. The requested key is added to the keyring only if the signature is successfully verified. -ENOKEY is diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 72700ed81594..e4b0ed386bc8 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -496,20 +496,37 @@ static struct key_restriction *asymmetric_lookup_restriction( restrict_method = strsep(&next, ":"); if ((strcmp(restrict_method, "key_or_keyring") == 0) && next) { + char *key_text; key_serial_t serial; struct key *key; + key_restrict_link_func_t link_fn = + restrict_link_by_key_or_keyring; + bool allow_null_key = false; - if (kstrtos32(next, 0, &serial) < 0) - goto out; + key_text = strsep(&next, ":"); + + if (next) { + if (strcmp(next, "chain") != 0) + goto out; + + link_fn = restrict_link_by_key_or_keyring_chain; + allow_null_key = true; + } - key = key_lookup(serial); - if (IS_ERR(key)) { - ret = ERR_CAST(key); + if (kstrtos32(key_text, 0, &serial) < 0) goto out; + + if ((serial == 0) && allow_null_key) { + key = NULL; + } else { + key = key_lookup(serial); + if (IS_ERR(key)) { + ret = ERR_CAST(key); + goto out; + } } - ret = asymmetric_restriction_alloc( - restrict_link_by_key_or_keyring, key); + ret = asymmetric_restriction_alloc(link_fn, key); if (IS_ERR(ret)) key_put(key); } diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c index 183cb642580e..86fb68508952 100644 --- a/crypto/asymmetric_keys/restrict.c +++ b/crypto/asymmetric_keys/restrict.c @@ -109,31 +109,20 @@ int restrict_link_by_signature(struct key *dest_keyring, return ret; } -/** - * restrict_link_by_key_or_keyring - Restrict additions to a ring of public - * keys using the restrict_key information stored in the ring. - * @dest_keyring: Keyring being linked to. - * @type: The type of key being added. - * @payload: The payload of the new key. - * @trusted: A key or ring of keys that can be used to vouch for the new cert. - * - * Check the new certificate only against the key or keys passed in the data - * parameter. If one of those is the signing key and validates the new - * certificate, then mark the new certificate as being ok to link. - * - * Returns 0 if the new certificate was accepted, -ENOKEY if we - * couldn't find a matching parent certificate in the trusted list, - * -EKEYREJECTED if the signature check fails, and some other error if - * there is a matching certificate but the signature check cannot be - * performed. - */ -int restrict_link_by_key_or_keyring(struct key *dest_keyring, - const struct key_type *type, - const union key_payload *payload, - struct key *trusted) +static bool match_either_id(const struct asymmetric_key_ids *pair, + const struct asymmetric_key_id *single) +{ + return (asymmetric_key_id_same(pair->id[0], single) || + asymmetric_key_id_same(pair->id[1], single)); +} + +static int key_or_keyring_common(struct key *dest_keyring, + const struct key_type *type, + const union key_payload *payload, + struct key *trusted, bool check_dest) { const struct public_key_signature *sig; - struct key *key; + struct key *key = NULL; int ret; pr_devel("==>%s()\n", __func__); @@ -143,7 +132,7 @@ int restrict_link_by_key_or_keyring(struct key *dest_keyring, else if (dest_keyring->type != &key_type_keyring) return -EOPNOTSUPP; - if (!trusted) + if (!trusted && !check_dest) return -ENOKEY; if (type != &key_type_asymmetric) @@ -153,25 +142,64 @@ int restrict_link_by_key_or_keyring(struct key *dest_keyring, if (!sig->auth_ids[0] && !sig->auth_ids[1]) return -ENOKEY; - if (trusted->type == &key_type_keyring) { - /* See if we have a key that signed this one. */ - key = find_asymmetric_key(trusted, sig->auth_ids[0], - sig->auth_ids[1], false); - if (IS_ERR(key)) - return -ENOKEY; - } else if (trusted->type == &key_type_asymmetric) { - const struct asymmetric_key_ids *kids; + if (trusted) { + if (trusted->type == &key_type_keyring) { + /* See if we have a key that signed this one. */ + key = find_asymmetric_key(trusted, sig->auth_ids[0], + sig->auth_ids[1], false); + if (IS_ERR(key)) + key = NULL; + } else if (trusted->type == &key_type_asymmetric) { + const struct asymmetric_key_ids *signer_ids; - kids = asymmetric_key_ids(trusted); + signer_ids = asymmetric_key_ids(trusted); - if (!asymmetric_key_id_same(kids->id[1], sig->auth_ids[0])) - return -ENOKEY; + /* + * The auth_ids come from the candidate key (the + * one that is being considered for addition to + * dest_keyring) and identify the key that was + * used to sign. + * + * The signer_ids are identifiers for the + * signing key specified for dest_keyring. + * + * The first auth_id is the preferred id, and + * the second is the fallback. If only one + * auth_id is present, it may match against + * either signer_id. If two auth_ids are + * present, the first auth_id must match one + * signer_id and the second auth_id must match + * the second signer_id. + */ + if (!sig->auth_ids[0] || !sig->auth_ids[1]) { + const struct asymmetric_key_id *auth_id; - key = __key_get(trusted); - } else { - return -EOPNOTSUPP; + auth_id = sig->auth_ids[0] ?: sig->auth_ids[1]; + if (match_either_id(signer_ids, auth_id)) + key = __key_get(trusted); + + } else if (asymmetric_key_id_same(signer_ids->id[1], + sig->auth_ids[1]) && + match_either_id(signer_ids, + sig->auth_ids[0])) { + key = __key_get(trusted); + } + } else { + return -EOPNOTSUPP; + } } + if (check_dest && !key) { + /* See if the destination has a key that signed this one. */ + key = find_asymmetric_key(dest_keyring, sig->auth_ids[0], + sig->auth_ids[1], false); + if (IS_ERR(key)) + key = NULL; + } + + if (!key) + return -ENOKEY; + ret = key_validate(key); if (ret == 0) ret = verify_signature(key, sig); @@ -179,3 +207,57 @@ int restrict_link_by_key_or_keyring(struct key *dest_keyring, key_put(key); return ret; } + +/** + * restrict_link_by_key_or_keyring - Restrict additions to a ring of public + * keys using the restrict_key information stored in the ring. + * @dest_keyring: Keyring being linked to. + * @type: The type of key being added. + * @payload: The payload of the new key. + * @trusted: A key or ring of keys that can be used to vouch for the new cert. + * + * Check the new certificate only against the key or keys passed in the data + * parameter. If one of those is the signing key and validates the new + * certificate, then mark the new certificate as being ok to link. + * + * Returns 0 if the new certificate was accepted, -ENOKEY if we + * couldn't find a matching parent certificate in the trusted list, + * -EKEYREJECTED if the signature check fails, and some other error if + * there is a matching certificate but the signature check cannot be + * performed. + */ +int restrict_link_by_key_or_keyring(struct key *dest_keyring, + const struct key_type *type, + const union key_payload *payload, + struct key *trusted) +{ + return key_or_keyring_common(dest_keyring, type, payload, trusted, + false); +} + +/** + * restrict_link_by_key_or_keyring_chain - Restrict additions to a ring of + * public keys using the restrict_key information stored in the ring. + * @dest_keyring: Keyring being linked to. + * @type: The type of key being added. + * @payload: The payload of the new key. + * @trusted: A key or ring of keys that can be used to vouch for the new cert. + * + * Check the new certificate only against the key or keys passed in the data + * parameter. If one of those is the signing key and validates the new + * certificate, then mark the new certificate as being ok to link. + * + * Returns 0 if the new certificate was accepted, -ENOKEY if we + * couldn't find a matching parent certificate in the trusted list, + * -EKEYREJECTED if the signature check fails, and some other error if + * there is a matching certificate but the signature check cannot be + * performed. + */ +int restrict_link_by_key_or_keyring_chain(struct key *dest_keyring, + const struct key_type *type, + const union key_payload *payload, + struct key *trusted) +{ + return key_or_keyring_common(dest_keyring, type, payload, trusted, + true); +} diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index bb6a884352a5..e0b681a717ba 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -60,6 +60,11 @@ extern int restrict_link_by_key_or_keyring(struct key *dest_keyring, const union key_payload *payload, struct key *trusted); +extern int restrict_link_by_key_or_keyring_chain(struct key *trust_keyring, + const struct key_type *type, + const union key_payload *payload, + struct key *trusted); + extern int verify_signature(const struct key *key, const struct public_key_signature *sig); -- cgit v1.2.3 From f1c316a3ab9d24df6022682422fe897492f2c0c8 Mon Sep 17 00:00:00 2001 From: Stephan Mueller Date: Fri, 19 Aug 2016 20:39:09 +0200 Subject: KEYS: add SP800-56A KDF support for DH SP800-56A defines the use of DH with key derivation function based on a counter. The input to the KDF is defined as (DH shared secret || other information). The value for the "other information" is to be provided by the caller. The KDF is implemented using the hash support from the kernel crypto API. The implementation uses the symmetric hash support as the input to the hash operation is usually very small. The caller is allowed to specify the hash name that he wants to use to derive the key material allowing the use of all supported hashes provided with the kernel crypto API. As the KDF implements the proper truncation of the DH shared secret to the requested size, this patch fills the caller buffer up to its size. The patch is tested with a new test added to the keyutils user space code which uses a CAVS test vector testing the compliance with SP800-56A. Signed-off-by: Stephan Mueller Signed-off-by: David Howells --- Documentation/security/keys.txt | 34 +++++-- include/linux/compat.h | 7 ++ include/uapi/linux/keyctl.h | 7 ++ security/keys/Kconfig | 1 + security/keys/Makefile | 3 +- security/keys/compat.c | 5 +- security/keys/compat_dh.c | 38 +++++++ security/keys/dh.c | 220 +++++++++++++++++++++++++++++++++++++--- security/keys/internal.h | 24 ++++- security/keys/keyctl.c | 2 +- 10 files changed, 315 insertions(+), 26 deletions(-) create mode 100644 security/keys/compat_dh.c diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index 5f554aab8751..cd5019934d7f 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -827,7 +827,7 @@ The keyctl syscall functions are: long keyctl(KEYCTL_DH_COMPUTE, struct keyctl_dh_params *params, char *buffer, size_t buflen, - void *reserved); + struct keyctl_kdf_params *kdf); The params struct contains serial numbers for three keys: @@ -844,18 +844,36 @@ The keyctl syscall functions are: public key. If the base is the remote public key, the result is the shared secret. - The reserved argument must be set to NULL. + If the parameter kdf is NULL, the following applies: - The buffer length must be at least the length of the prime, or zero. + - The buffer length must be at least the length of the prime, or zero. - If the buffer length is nonzero, the length of the result is - returned when it is successfully calculated and copied in to the - buffer. When the buffer length is zero, the minimum required - buffer length is returned. + - If the buffer length is nonzero, the length of the result is + returned when it is successfully calculated and copied in to the + buffer. When the buffer length is zero, the minimum required + buffer length is returned. + + The kdf parameter allows the caller to apply a key derivation function + (KDF) on the Diffie-Hellman computation where only the result + of the KDF is returned to the caller. The KDF is characterized with + struct keyctl_kdf_params as follows: + + - char *hashname specifies the NUL terminated string identifying + the hash used from the kernel crypto API and applied for the KDF + operation. The KDF implemenation complies with SP800-56A as well + as with SP800-108 (the counter KDF). + + - char *otherinfo specifies the OtherInfo data as documented in + SP800-56A section 5.8.1.2. The length of the buffer is given with + otherinfolen. The format of OtherInfo is defined by the caller. + The otherinfo pointer may be NULL if no OtherInfo shall be used. This function will return error EOPNOTSUPP if the key type is not supported, error ENOKEY if the key could not be found, or error - EACCES if the key is not readable by the caller. + EACCES if the key is not readable by the caller. In addition, the + function will return EMSGSIZE when the parameter kdf is non-NULL + and either the buffer length or the OtherInfo length exceeds the + allowed length. (*) Restrict keyring linkage diff --git a/include/linux/compat.h b/include/linux/compat.h index aef47be2a5c1..993c87182e02 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -295,6 +295,13 @@ struct compat_old_sigaction { }; #endif +struct compat_keyctl_kdf_params { + compat_uptr_t hashname; + compat_uptr_t otherinfo; + __u32 otherinfolen; + __u32 __spare[8]; +}; + struct compat_statfs; struct compat_statfs64; struct compat_old_linux_dirent; diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h index ff79c44e49a3..201c6644b237 100644 --- a/include/uapi/linux/keyctl.h +++ b/include/uapi/linux/keyctl.h @@ -69,4 +69,11 @@ struct keyctl_dh_params { __s32 base; }; +struct keyctl_kdf_params { + char *hashname; + char *otherinfo; + __u32 otherinfolen; + __u32 __spare[8]; +}; + #endif /* _LINUX_KEYCTL_H */ diff --git a/security/keys/Kconfig b/security/keys/Kconfig index d942c7c2bc0a..4ac1b83a23f8 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -90,6 +90,7 @@ config KEY_DH_OPERATIONS bool "Diffie-Hellman operations on retained keys" depends on KEYS select MPILIB + select CRYPTO_HASH help This option provides support for calculating Diffie-Hellman public keys and shared secrets using values stored as keys diff --git a/security/keys/Makefile b/security/keys/Makefile index 1fd4a16e6daf..57dff0c15809 100644 --- a/security/keys/Makefile +++ b/security/keys/Makefile @@ -15,7 +15,8 @@ obj-y := \ request_key.o \ request_key_auth.o \ user_defined.o -obj-$(CONFIG_KEYS_COMPAT) += compat.o +compat-obj-$(CONFIG_KEY_DH_OPERATIONS) += compat_dh.o +obj-$(CONFIG_KEYS_COMPAT) += compat.o $(compat-obj-y) obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_SYSCTL) += sysctl.o obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o diff --git a/security/keys/compat.c b/security/keys/compat.c index bb98f2b8dd7d..e87c89c0177c 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -133,8 +133,9 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option, return keyctl_get_persistent(arg2, arg3); case KEYCTL_DH_COMPUTE: - return keyctl_dh_compute(compat_ptr(arg2), compat_ptr(arg3), - arg4, compat_ptr(arg5)); + return compat_keyctl_dh_compute(compat_ptr(arg2), + compat_ptr(arg3), + arg4, compat_ptr(arg5)); case KEYCTL_RESTRICT_KEYRING: return keyctl_restrict_keyring(arg2, compat_ptr(arg3), diff --git a/security/keys/compat_dh.c b/security/keys/compat_dh.c new file mode 100644 index 000000000000..a6a659b6bcb6 --- /dev/null +++ b/security/keys/compat_dh.c @@ -0,0 +1,38 @@ +/* 32-bit compatibility syscall for 64-bit systems for DH operations + * + * Copyright (C) 2016 Stephan Mueller + * + * 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 + +#include "internal.h" + +/* + * Perform the DH computation or DH based key derivation. + * + * If successful, 0 will be returned. + */ +long compat_keyctl_dh_compute(struct keyctl_dh_params __user *params, + char __user *buffer, size_t buflen, + struct compat_keyctl_kdf_params __user *kdf) +{ + struct keyctl_kdf_params kdfcopy; + struct compat_keyctl_kdf_params compat_kdfcopy; + + if (!kdf) + return __keyctl_dh_compute(params, buffer, buflen, NULL); + + if (copy_from_user(&compat_kdfcopy, kdf, sizeof(compat_kdfcopy)) != 0) + return -EFAULT; + + kdfcopy.hashname = compat_ptr(compat_kdfcopy.hashname); + kdfcopy.otherinfo = compat_ptr(compat_kdfcopy.otherinfo); + kdfcopy.otherinfolen = compat_kdfcopy.otherinfolen; + + return __keyctl_dh_compute(params, buffer, buflen, &kdfcopy); +} diff --git a/security/keys/dh.c b/security/keys/dh.c index 893af4c45038..e603bd912e4c 100644 --- a/security/keys/dh.c +++ b/security/keys/dh.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include "internal.h" @@ -77,9 +79,146 @@ error: return ret; } -long keyctl_dh_compute(struct keyctl_dh_params __user *params, - char __user *buffer, size_t buflen, - void __user *reserved) +struct kdf_sdesc { + struct shash_desc shash; + char ctx[]; +}; + +static int kdf_alloc(struct kdf_sdesc **sdesc_ret, char *hashname) +{ + struct crypto_shash *tfm; + struct kdf_sdesc *sdesc; + int size; + + /* allocate synchronous hash */ + tfm = crypto_alloc_shash(hashname, 0, 0); + if (IS_ERR(tfm)) { + pr_info("could not allocate digest TFM handle %s\n", hashname); + return PTR_ERR(tfm); + } + + size = sizeof(struct shash_desc) + crypto_shash_descsize(tfm); + sdesc = kmalloc(size, GFP_KERNEL); + if (!sdesc) + return -ENOMEM; + sdesc->shash.tfm = tfm; + sdesc->shash.flags = 0x0; + + *sdesc_ret = sdesc; + + return 0; +} + +static void kdf_dealloc(struct kdf_sdesc *sdesc) +{ + if (!sdesc) + return; + + if (sdesc->shash.tfm) + crypto_free_shash(sdesc->shash.tfm); + + kzfree(sdesc); +} + +/* convert 32 bit integer into its string representation */ +static inline void crypto_kw_cpu_to_be32(u32 val, u8 *buf) +{ + __be32 *a = (__be32 *)buf; + + *a = cpu_to_be32(val); +} + +/* + * Implementation of the KDF in counter mode according to SP800-108 section 5.1 + * as well as SP800-56A section 5.8.1 (Single-step KDF). + * + * SP800-56A: + * The src pointer is defined as Z || other info where Z is the shared secret + * from DH and other info is an arbitrary string (see SP800-56A section + * 5.8.1.2). + */ +static int kdf_ctr(struct kdf_sdesc *sdesc, const u8 *src, unsigned int slen, + u8 *dst, unsigned int dlen) +{ + struct shash_desc *desc = &sdesc->shash; + unsigned int h = crypto_shash_digestsize(desc->tfm); + int err = 0; + u8 *dst_orig = dst; + u32 i = 1; + u8 iteration[sizeof(u32)]; + + while (dlen) { + err = crypto_shash_init(desc); + if (err) + goto err; + + crypto_kw_cpu_to_be32(i, iteration); + err = crypto_shash_update(desc, iteration, sizeof(u32)); + if (err) + goto err; + + if (src && slen) { + err = crypto_shash_update(desc, src, slen); + if (err) + goto err; + } + + if (dlen < h) { + u8 tmpbuffer[h]; + + err = crypto_shash_final(desc, tmpbuffer); + if (err) + goto err; + memcpy(dst, tmpbuffer, dlen); + memzero_explicit(tmpbuffer, h); + return 0; + } else { + err = crypto_shash_final(desc, dst); + if (err) + goto err; + + dlen -= h; + dst += h; + i++; + } + } + + return 0; + +err: + memzero_explicit(dst_orig, dlen); + return err; +} + +static int keyctl_dh_compute_kdf(struct kdf_sdesc *sdesc, + char __user *buffer, size_t buflen, + uint8_t *kbuf, size_t kbuflen) +{ + uint8_t *outbuf = NULL; + int ret; + + outbuf = kmalloc(buflen, GFP_KERNEL); + if (!outbuf) { + ret = -ENOMEM; + goto err; + } + + ret = kdf_ctr(sdesc, kbuf, kbuflen, outbuf, buflen); + if (ret) + goto err; + + ret = buflen; + if (copy_to_user(buffer, outbuf, buflen) != 0) + ret = -EFAULT; + +err: + kzfree(outbuf); + return ret; +} + +long __keyctl_dh_compute(struct keyctl_dh_params __user *params, + char __user *buffer, size_t buflen, + struct keyctl_kdf_params *kdfcopy) { long ret; MPI base, private, prime, result; @@ -88,6 +227,7 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params, uint8_t *kbuf; ssize_t keylen; size_t resultlen; + struct kdf_sdesc *sdesc = NULL; if (!params || (!buffer && buflen)) { ret = -EINVAL; @@ -98,12 +238,34 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params, goto out; } - if (reserved) { - ret = -EINVAL; - goto out; + if (kdfcopy) { + char *hashname; + + if (buflen > KEYCTL_KDF_MAX_OUTPUT_LEN || + kdfcopy->otherinfolen > KEYCTL_KDF_MAX_OI_LEN) { + ret = -EMSGSIZE; + goto out; + } + + /* get KDF name string */ + hashname = strndup_user(kdfcopy->hashname, CRYPTO_MAX_ALG_NAME); + if (IS_ERR(hashname)) { + ret = PTR_ERR(hashname); + goto out; + } + + /* allocate KDF from the kernel crypto API */ + ret = kdf_alloc(&sdesc, hashname); + kfree(hashname); + if (ret) + goto out; } - keylen = mpi_from_key(pcopy.prime, buflen, &prime); + /* + * If the caller requests postprocessing with a KDF, allow an + * arbitrary output buffer size since the KDF ensures proper truncation. + */ + keylen = mpi_from_key(pcopy.prime, kdfcopy ? SIZE_MAX : buflen, &prime); if (keylen < 0 || !prime) { /* buflen == 0 may be used to query the required buffer size, * which is the prime key length. @@ -133,12 +295,25 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params, goto error3; } - kbuf = kmalloc(resultlen, GFP_KERNEL); + /* allocate space for DH shared secret and SP800-56A otherinfo */ + kbuf = kmalloc(kdfcopy ? (resultlen + kdfcopy->otherinfolen) : resultlen, + GFP_KERNEL); if (!kbuf) { ret = -ENOMEM; goto error4; } + /* + * Concatenate SP800-56A otherinfo past DH shared secret -- the + * input to the KDF is (DH shared secret || otherinfo) + */ + if (kdfcopy && kdfcopy->otherinfo && + copy_from_user(kbuf + resultlen, kdfcopy->otherinfo, + kdfcopy->otherinfolen) != 0) { + ret = -EFAULT; + goto error5; + } + ret = do_dh(result, base, private, prime); if (ret) goto error5; @@ -147,12 +322,17 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params, if (ret != 0) goto error5; - ret = nbytes; - if (copy_to_user(buffer, kbuf, nbytes) != 0) - ret = -EFAULT; + if (kdfcopy) { + ret = keyctl_dh_compute_kdf(sdesc, buffer, buflen, kbuf, + resultlen + kdfcopy->otherinfolen); + } else { + ret = nbytes; + if (copy_to_user(buffer, kbuf, nbytes) != 0) + ret = -EFAULT; + } error5: - kfree(kbuf); + kzfree(kbuf); error4: mpi_free(result); error3: @@ -162,5 +342,21 @@ error2: error1: mpi_free(prime); out: + kdf_dealloc(sdesc); return ret; } + +long keyctl_dh_compute(struct keyctl_dh_params __user *params, + char __user *buffer, size_t buflen, + struct keyctl_kdf_params __user *kdf) +{ + struct keyctl_kdf_params kdfcopy; + + if (!kdf) + return __keyctl_dh_compute(params, buffer, buflen, NULL); + + if (copy_from_user(&kdfcopy, kdf, sizeof(kdfcopy)) != 0) + return -EFAULT; + + return __keyctl_dh_compute(params, buffer, buflen, &kdfcopy); +} diff --git a/security/keys/internal.h b/security/keys/internal.h index 6ce016314897..c0f8682eba69 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -18,6 +18,7 @@ #include #include #include +#include struct iovec; @@ -267,15 +268,34 @@ static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring) #ifdef CONFIG_KEY_DH_OPERATIONS extern long keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *, - size_t, void __user *); + size_t, struct keyctl_kdf_params __user *); +extern long __keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *, + size_t, struct keyctl_kdf_params *); +#ifdef CONFIG_KEYS_COMPAT +extern long compat_keyctl_dh_compute(struct keyctl_dh_params __user *params, + char __user *buffer, size_t buflen, + struct compat_keyctl_kdf_params __user *kdf); +#endif +#define KEYCTL_KDF_MAX_OUTPUT_LEN 1024 /* max length of KDF output */ +#define KEYCTL_KDF_MAX_OI_LEN 64 /* max length of otherinfo */ #else static inline long keyctl_dh_compute(struct keyctl_dh_params __user *params, char __user *buffer, size_t buflen, - void __user *reserved) + struct keyctl_kdf_params __user *kdf) +{ + return -EOPNOTSUPP; +} + +#ifdef CONFIG_KEYS_COMPAT +static inline long compat_keyctl_dh_compute( + struct keyctl_dh_params __user *params, + char __user *buffer, size_t buflen, + struct keyctl_kdf_params __user *kdf) { return -EOPNOTSUPP; } #endif +#endif /* * Debugging key validation diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 6ee2826a2d06..10fcea154c0f 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1744,7 +1744,7 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, case KEYCTL_DH_COMPUTE: return keyctl_dh_compute((struct keyctl_dh_params __user *) arg2, (char __user *) arg3, (size_t) arg4, - (void __user *) arg5); + (struct keyctl_kdf_params __user *) arg5); case KEYCTL_RESTRICT_KEYRING: return keyctl_restrict_keyring((key_serial_t) arg2, -- cgit v1.2.3 From 4cd4ca7cc848bedc70b5d0acac9d1ae33d73513a Mon Sep 17 00:00:00 2001 From: Stephan Müller Date: Tue, 11 Apr 2017 13:07:07 +0200 Subject: keys: select CONFIG_CRYPTO when selecting DH / KDF Select CONFIG_CRYPTO in addition to CONFIG_HASH to ensure that also CONFIG_HASH2 is selected. Both are needed for the shash cipher support required for the KDF operation. Signed-off-by: Stephan Mueller Signed-off-by: David Howells --- security/keys/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/security/keys/Kconfig b/security/keys/Kconfig index 4ac1b83a23f8..6fd95f76bfae 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -90,6 +90,7 @@ config KEY_DH_OPERATIONS bool "Diffie-Hellman operations on retained keys" depends on KEYS select MPILIB + select CRYPTO select CRYPTO_HASH help This option provides support for calculating Diffie-Hellman -- cgit v1.2.3