diff options
author | James Morris <james.l.morris@oracle.com> | 2017-04-18 07:37:51 +1000 |
---|---|---|
committer | James Morris <james.l.morris@oracle.com> | 2017-04-18 07:37:51 +1000 |
commit | 30a83251dd8b7e3566be9ea8c4921bafc21bee8f (patch) | |
tree | 1d86ec5f4e8aaeea0cf9c5a5c19003e6cd0730a8 | |
parent | 622f6e3265707ebf02ba776ac6e68003bcc31213 (diff) | |
parent | 4cd4ca7cc848bedc70b5d0acac9d1ae33d73513a (diff) | |
download | linux-30a83251dd8b7e3566be9ea8c4921bafc21bee8f.tar.bz2 |
Merge tag 'keys-next-20170412' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs into next
33 files changed, 1305 insertions, 116 deletions
diff --git a/Documentation/crypto/asymmetric-keys.txt b/Documentation/crypto/asymmetric-keys.txt index 2b7816dea370..5ad6480e3fb9 100644 --- a/Documentation/crypto/asymmetric-keys.txt +++ b/Documentation/crypto/asymmetric-keys.txt @@ -311,3 +311,54 @@ 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. + + (3) Restrict using a separate key or keyring + + - Option string used with KEYCTL_RESTRICT_KEYRING: + - "key_or_keyring:<key or keyring serial number>[: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 + 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. + + 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 +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/Documentation/security/keys.txt b/Documentation/security/keys.txt index 0e03baf271bd..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,61 @@ 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 + + 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 @@ -1032,10 +1075,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 *), + struct key_restriction *restrict_link, unsigned long flags, struct key *dest); @@ -1047,20 +1087,23 @@ 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 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. @@ -1445,6 +1488,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/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 "<hash>", "<hash>", ... . This will be included into a C + wrapper to incorporate the list into the kernel. Each <hash> 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 <linux/module.h> +#include <linux/slab.h> +#include <linux/key.h> +#include <linux/key-type.h> +#include <linux/sched.h> +#include <linux/ctype.h> +#include <linux/err.h> +#include <linux/seq_file.h> +#include <keys/system_keyring.h> +#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 <linux/kernel.h> + +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/certs/system_keyring.c b/certs/system_keyring.c index 50979d6dcecd..6251d1b27f0c 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -14,6 +14,7 @@ #include <linux/sched.h> #include <linux/cred.h> #include <linux/err.h> +#include <linux/slab.h> #include <keys/asymmetric-type.h> #include <keys/system_keyring.h> #include <crypto/pkcs7.h> @@ -32,11 +33,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 +52,40 @@ 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); +} + +/** + * 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 @@ -91,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/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 6600181d5d01..e4b0ed386bc8 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -17,6 +17,7 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/ctype.h> +#include <keys/system_keyring.h> #include "asymmetric_keys.h" MODULE_LICENSE("GPL"); @@ -451,15 +452,100 @@ 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) +{ + 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); + + if (strcmp("builtin_and_secondary_trusted", restriction) == 0) + return asymmetric_restriction_alloc( + restrict_link_by_builtin_and_secondary_trusted, NULL); + + 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) { + 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; + + 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; + } + + 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(link_fn, key); + if (IS_ERR(ret)) + key_put(key); + } + +out: + kfree(parse_buf); + return ret; +} + 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); 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); diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c index 19d1afb9890f..86fb68508952 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; @@ -106,3 +108,156 @@ int restrict_link_by_signature(struct key *trust_keyring, key_put(key); return ret; } + +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 = NULL; + int ret; + + pr_devel("==>%s()\n", __func__); + + if (!dest_keyring) + return -ENOKEY; + else if (dest_keyring->type != &key_type_keyring) + return -EOPNOTSUPP; + + if (!trusted && !check_dest) + 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) { + 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; + + signer_ids = asymmetric_key_ids(trusted); + + /* + * 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; + + 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); + + 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/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) { diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 882ca0e1e7a5..e0b681a717ba 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -50,9 +50,20 @@ 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 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 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); diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h index fbd4647767e9..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,11 +29,24 @@ 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 +#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; 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/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 */ diff --git a/include/linux/key.h b/include/linux/key.h index 9d9fac583dd3..0c9b93b0d1f7 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -127,6 +127,17 @@ 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 *dest_keyring, + const struct key_type *type, + 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 @@ -206,18 +217,17 @@ 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. + * 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 * 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); + struct key_restriction *restrict_link; }; extern struct key *key_alloc(struct key_type *type, @@ -226,9 +236,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 *)); + struct key_restriction *restrict_link); #define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */ @@ -304,14 +312,13 @@ 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 *), + struct key_restriction *restrict_link, struct key *dest); 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); @@ -322,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..201c6644b237 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 { @@ -68,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/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 <linux/cred.h> #include <linux/err.h> #include <linux/init.h> +#include <linux/slab.h> #include <keys/system_keyring.h> @@ -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/Kconfig b/security/keys/Kconfig index d942c7c2bc0a..6fd95f76bfae 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -90,6 +90,8 @@ 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 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 36c80bf5b89c..e87c89c0177c 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -133,8 +133,13 @@ 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), + compat_ptr(arg4)); default: return -EOPNOTSUPP; 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 <smueller@chronox.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/uaccess.h> + +#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 <linux/mpi.h> #include <linux/slab.h> #include <linux/uaccess.h> +#include <linux/crypto.h> +#include <crypto/hash.h> #include <keys/user-type.h> #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/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..c0f8682eba69 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -18,6 +18,7 @@ #include <linux/task_work.h> #include <linux/keyctl.h> #include <linux/refcount.h> +#include <linux/compat.h> struct iovec; @@ -168,6 +169,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); @@ -250,6 +253,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; @@ -262,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/key.c b/security/keys/key.c index b4958b36fa27..455c04d80bbb 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,9 +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, - int (*restrict_link)(struct key *, - const struct key_type *, - const union key_payload *)) + struct key_restriction *restrict_link) { struct key_user *user = NULL; struct key *key; @@ -499,19 +500,23 @@ int key_instantiate_and_link(struct key *key, } if (keyring) { - if (keyring->restrict_link) { - ret = keyring->restrict_link(keyring, key->type, - &prep.payload); - if (ret < 0) - goto error; - } 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_link_end; + } } ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &edit); +error_link_end: if (keyring) __key_link_end(keyring, &key->index_key, edit); @@ -806,9 +811,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; + struct key_restriction *restrict_link = NULL; /* look up the key type to see if it's one of the registered kernel * types */ @@ -854,20 +857,21 @@ 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); - if (ret < 0) { - key_ref = ERR_PTR(ret); - goto error_free_prep; - } - } - 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_link_end; + } + } + /* 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); diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 52c34532c785..10fcea154c0f 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1583,6 +1583,59 @@ error_keyring: } /* + * 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 */ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, @@ -1691,7 +1744,12 @@ 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, + (const char __user *) arg3, + (const char __user *) arg4); default: return -EOPNOTSUPP; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 3d95f7d02ba1..4d1678e4586f 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,9 +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, - int (*restrict_link)(struct key *, - const struct key_type *, - const union key_payload *), + struct key_restriction *restrict_link, struct key *dest) { struct key *keyring; @@ -519,17 +524,19 @@ 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 * 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, - const union key_payload *payload) + const union key_payload *payload, + struct key *restriction_key) { return -EPERM; } @@ -940,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. * @@ -1220,9 +1332,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); + return keyring->restrict_link->check(keyring, key->type, &key->payload, + keyring->restrict_link->key); } /** @@ -1426,3 +1539,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]"); +} |