diff options
author | James Morris <james.l.morris@oracle.com> | 2016-05-06 01:29:00 +0200 |
---|---|---|
committer | James Morris <james.l.morris@oracle.com> | 2016-05-06 01:29:00 +0200 |
commit | 0250abcd726b4eba8a6175f09656fe544ed6491a (patch) | |
tree | 43ded3d5f9b8b5684879c61ff6d03effdb7ea7c0 | |
parent | Yama: use atomic allocations when reporting (diff) | |
parent | Merge branch 'keys-trust' into keys-next (diff) | |
download | linux-0250abcd726b4eba8a6175f09656fe544ed6491a.tar.xz linux-0250abcd726b4eba8a6175f09656fe544ed6491a.zip |
Merge tag 'keys-next-20160505' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs into next
56 files changed, 1341 insertions, 717 deletions
diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index 8c183873b2b7..20d05719bceb 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -823,6 +823,36 @@ The keyctl syscall functions are: A process must have search permission on the key for this function to be successful. + (*) Compute a Diffie-Hellman shared secret or public key + + long keyctl(KEYCTL_DH_COMPUTE, struct keyctl_dh_params *params, + char *buffer, size_t buflen); + + The params struct contains serial numbers for three keys: + + - The prime, p, known to both parties + - The local private key + - The base integer, which is either a shared generator or the + remote public key + + The value computed is: + + result = base ^ private (mod prime) + + If the base is the shared generator, the result is the local + public key. If the base is the remote public key, the result is + the shared secret. + + 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. + + 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. =============== KERNEL SERVICES @@ -999,6 +1029,10 @@ 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 *), unsigned long flags, struct key *dest); @@ -1010,6 +1044,24 @@ 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. + + 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. + + A convenience function, restrict_link_reject, exists to always return + -EPERM to in this case. + (*) To check the validity of a key, this function can be called: diff --git a/arch/x86/kernel/kexec-bzimage64.c b/arch/x86/kernel/kexec-bzimage64.c index 2af478e3fd4e..f2356bda2b05 100644 --- a/arch/x86/kernel/kexec-bzimage64.c +++ b/arch/x86/kernel/kexec-bzimage64.c @@ -19,8 +19,7 @@ #include <linux/kernel.h> #include <linux/mm.h> #include <linux/efi.h> -#include <linux/verify_pefile.h> -#include <keys/system_keyring.h> +#include <linux/verification.h> #include <asm/bootparam.h> #include <asm/setup.h> @@ -529,18 +528,9 @@ static int bzImage64_cleanup(void *loader_data) #ifdef CONFIG_KEXEC_BZIMAGE_VERIFY_SIG static int bzImage64_verify_sig(const char *kernel, unsigned long kernel_len) { - bool trusted; - int ret; - - ret = verify_pefile_signature(kernel, kernel_len, - system_trusted_keyring, - VERIFYING_KEXEC_PE_SIGNATURE, - &trusted); - if (ret < 0) - return ret; - if (!trusted) - return -EKEYREJECTED; - return 0; + return verify_pefile_signature(kernel, kernel_len, + NULL, + VERIFYING_KEXEC_PE_SIGNATURE); } #endif diff --git a/certs/Kconfig b/certs/Kconfig index f0f8a4433685..fc5955f5fc8a 100644 --- a/certs/Kconfig +++ b/certs/Kconfig @@ -17,6 +17,7 @@ config MODULE_SIG_KEY config SYSTEM_TRUSTED_KEYRING bool "Provide system-wide ring of trusted keys" depends on KEYS + depends on ASYMMETRIC_KEY_TYPE help Provide a system keyring to which trusted keys can be added. Keys in the keyring are considered to be trusted. Keys may be added at will @@ -55,4 +56,12 @@ config SYSTEM_EXTRA_CERTIFICATE_SIZE This is the number of bytes reserved in the kernel image for a certificate to be inserted. +config SECONDARY_TRUSTED_KEYRING + bool "Provide a keyring to which extra trustable keys may be added" + depends on SYSTEM_TRUSTED_KEYRING + help + If set, provide a keyring to which extra keys may be added, provided + those keys are not blacklisted and are vouched for by a key built + into the kernel or already in the secondary trusted keyring. + endmenu diff --git a/certs/system_keyring.c b/certs/system_keyring.c index f4180326c2e1..50979d6dcecd 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -18,29 +18,88 @@ #include <keys/system_keyring.h> #include <crypto/pkcs7.h> -struct key *system_trusted_keyring; -EXPORT_SYMBOL_GPL(system_trusted_keyring); +static struct key *builtin_trusted_keys; +#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING +static struct key *secondary_trusted_keys; +#endif extern __initconst const u8 system_certificate_list[]; extern __initconst const unsigned long system_certificate_list_size; +/** + * restrict_link_to_builtin_trusted - Restrict keyring addition by built in CA + * + * 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, + const struct key_type *type, + const union key_payload *payload) +{ + return restrict_link_by_signature(builtin_trusted_keys, type, payload); +} + +#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING +/** + * restrict_link_by_builtin_and_secondary_trusted - Restrict keyring + * addition by both builtin and secondary keyrings + * + * Restrict the addition of keys into a keyring based on the key-to-be-added + * being vouched for by a key in either the built-in or the secondary system + * keyrings. + */ +int restrict_link_by_builtin_and_secondary_trusted( + struct key *keyring, + const struct key_type *type, + const union key_payload *payload) +{ + /* 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 && + 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); +} +#endif + /* - * Load the compiled-in keys + * Create the trusted keyrings */ static __init int system_trusted_keyring_init(void) { - pr_notice("Initialise system trusted keyring\n"); + pr_notice("Initialise system trusted keyrings\n"); - system_trusted_keyring = - keyring_alloc(".system_keyring", + builtin_trusted_keys = + keyring_alloc(".builtin_trusted_keys", 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, NULL); - if (IS_ERR(system_trusted_keyring)) - panic("Can't allocate system trusted keyring\n"); + KEY_ALLOC_NOT_IN_QUOTA, + NULL, NULL); + if (IS_ERR(builtin_trusted_keys)) + panic("Can't allocate builtin trusted keyring\n"); + +#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING + secondary_trusted_keys = + keyring_alloc(".secondary_trusted_keys", + KUIDT_INIT(0), KGIDT_INIT(0), current_cred(), + ((KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH | + KEY_USR_WRITE), + KEY_ALLOC_NOT_IN_QUOTA, + restrict_link_by_builtin_and_secondary_trusted, + NULL); + if (IS_ERR(secondary_trusted_keys)) + panic("Can't allocate secondary trusted keyring\n"); + + if (key_link(secondary_trusted_keys, builtin_trusted_keys) < 0) + panic("Can't link trusted keyrings\n"); +#endif - set_bit(KEY_FLAG_TRUSTED_ONLY, &system_trusted_keyring->flags); return 0; } @@ -76,7 +135,7 @@ static __init int load_system_certificate_list(void) if (plen > end - p) goto dodgy_cert; - key = key_create_or_update(make_key_ref(system_trusted_keyring, 1), + key = key_create_or_update(make_key_ref(builtin_trusted_keys, 1), "asymmetric", NULL, p, @@ -84,8 +143,8 @@ static __init int load_system_certificate_list(void) ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ), KEY_ALLOC_NOT_IN_QUOTA | - KEY_ALLOC_TRUSTED | - KEY_ALLOC_BUILT_IN); + KEY_ALLOC_BUILT_IN | + KEY_ALLOC_BYPASS_RESTRICTION); if (IS_ERR(key)) { pr_err("Problem loading in-kernel X.509 certificate (%ld)\n", PTR_ERR(key)); @@ -108,19 +167,27 @@ late_initcall(load_system_certificate_list); #ifdef CONFIG_SYSTEM_DATA_VERIFICATION /** - * Verify a PKCS#7-based signature on system data. - * @data: The data to be verified. + * verify_pkcs7_signature - Verify a PKCS#7-based signature on system data. + * @data: The data to be verified (NULL if expecting internal data). * @len: Size of @data. * @raw_pkcs7: The PKCS#7 message that is the signature. * @pkcs7_len: The size of @raw_pkcs7. + * @trusted_keys: Trusted keys to use (NULL for builtin trusted keys only, + * (void *)1UL for all trusted keys). * @usage: The use to which the key is being put. + * @view_content: Callback to gain access to content. + * @ctx: Context for callback. */ -int system_verify_data(const void *data, unsigned long len, - const void *raw_pkcs7, size_t pkcs7_len, - enum key_being_used_for usage) +int verify_pkcs7_signature(const void *data, size_t len, + const void *raw_pkcs7, size_t pkcs7_len, + struct key *trusted_keys, + enum key_being_used_for usage, + int (*view_content)(void *ctx, + const void *data, size_t len, + size_t asn1hdrlen), + void *ctx) { struct pkcs7_message *pkcs7; - bool trusted; int ret; pkcs7 = pkcs7_parse_message(raw_pkcs7, pkcs7_len); @@ -128,7 +195,7 @@ int system_verify_data(const void *data, unsigned long len, return PTR_ERR(pkcs7); /* The data should be detached - so we need to supply it. */ - if (pkcs7_supply_detached_data(pkcs7, data, len) < 0) { + if (data && pkcs7_supply_detached_data(pkcs7, data, len) < 0) { pr_err("PKCS#7 signature with non-detached data\n"); ret = -EBADMSG; goto error; @@ -138,13 +205,33 @@ int system_verify_data(const void *data, unsigned long len, if (ret < 0) goto error; - ret = pkcs7_validate_trust(pkcs7, system_trusted_keyring, &trusted); - if (ret < 0) + if (!trusted_keys) { + trusted_keys = builtin_trusted_keys; + } else if (trusted_keys == (void *)1UL) { +#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING + trusted_keys = secondary_trusted_keys; +#else + trusted_keys = builtin_trusted_keys; +#endif + } + ret = pkcs7_validate_trust(pkcs7, trusted_keys); + if (ret < 0) { + if (ret == -ENOKEY) + pr_err("PKCS#7 signature not signed with a trusted key\n"); goto error; + } + + if (view_content) { + size_t asn1hdrlen; + + ret = pkcs7_get_content_data(pkcs7, &data, &len, &asn1hdrlen); + if (ret < 0) { + if (ret == -ENODATA) + pr_devel("PKCS#7 message does not contain data\n"); + goto error; + } - if (!trusted) { - pr_err("PKCS#7 signature not signed with a trusted key\n"); - ret = -ENOKEY; + ret = view_content(ctx, data, len, asn1hdrlen); } error: @@ -152,6 +239,6 @@ error: pr_devel("<==%s() = %d\n", __func__, ret); return ret; } -EXPORT_SYMBOL_GPL(system_verify_data); +EXPORT_SYMBOL_GPL(verify_pkcs7_signature); #endif /* CONFIG_SYSTEM_DATA_VERIFICATION */ diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 91a7e047a765..e28e912000a7 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -1,5 +1,5 @@ menuconfig ASYMMETRIC_KEY_TYPE - tristate "Asymmetric (public-key cryptographic) key type" + bool "Asymmetric (public-key cryptographic) key type" depends on KEYS help This option provides support for a key type that holds the data for @@ -40,8 +40,7 @@ config PKCS7_MESSAGE_PARSER config PKCS7_TEST_KEY tristate "PKCS#7 testing key type" - depends on PKCS7_MESSAGE_PARSER - select SYSTEM_TRUSTED_KEYRING + depends on SYSTEM_DATA_VERIFICATION help This option provides a type of key that can be loaded up from a PKCS#7 message - provided the message is signed by a trusted key. If @@ -54,6 +53,7 @@ config PKCS7_TEST_KEY config SIGNED_PE_FILE_VERIFICATION bool "Support for PE file signature verification" depends on PKCS7_MESSAGE_PARSER=y + depends on SYSTEM_DATA_VERIFICATION select ASN1 select OID_REGISTRY help diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index f90486256f01..6516855bec18 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -4,7 +4,10 @@ obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o -asymmetric_keys-y := asymmetric_type.o signature.o +asymmetric_keys-y := \ + asymmetric_type.o \ + restrict.o \ + signature.o obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o diff --git a/crypto/asymmetric_keys/asymmetric_keys.h b/crypto/asymmetric_keys/asymmetric_keys.h index 1d450b580245..ca8e9ac34ce6 100644 --- a/crypto/asymmetric_keys/asymmetric_keys.h +++ b/crypto/asymmetric_keys/asymmetric_keys.h @@ -9,6 +9,8 @@ * 2 of the Licence, or (at your option) any later version. */ +#include <keys/asymmetric-type.h> + extern struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id); extern int __asymmetric_key_hex_to_key_id(const char *id, diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 9f2165b27d52..6600181d5d01 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -35,6 +35,95 @@ static LIST_HEAD(asymmetric_key_parsers); static DECLARE_RWSEM(asymmetric_key_parsers_sem); /** + * find_asymmetric_key - Find a key by ID. + * @keyring: The keys to search. + * @id_0: The first ID to look for or NULL. + * @id_1: The second ID to look for or NULL. + * @partial: Use partial match if true, exact if false. + * + * Find a key in the given keyring by identifier. The preferred identifier is + * the id_0 and the fallback identifier is the id_1. If both are given, the + * lookup is by the former, but the latter must also match. + */ +struct key *find_asymmetric_key(struct key *keyring, + const struct asymmetric_key_id *id_0, + const struct asymmetric_key_id *id_1, + bool partial) +{ + struct key *key; + key_ref_t ref; + const char *lookup; + char *req, *p; + int len; + + if (id_0) { + lookup = id_0->data; + len = id_0->len; + } else { + lookup = id_1->data; + len = id_1->len; + } + + /* Construct an identifier "id:<keyid>". */ + p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL); + if (!req) + return ERR_PTR(-ENOMEM); + + if (partial) { + *p++ = 'i'; + *p++ = 'd'; + } else { + *p++ = 'e'; + *p++ = 'x'; + } + *p++ = ':'; + p = bin2hex(p, lookup, len); + *p = 0; + + pr_debug("Look up: \"%s\"\n", req); + + ref = keyring_search(make_key_ref(keyring, 1), + &key_type_asymmetric, req); + if (IS_ERR(ref)) + pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref)); + kfree(req); + + if (IS_ERR(ref)) { + switch (PTR_ERR(ref)) { + /* Hide some search errors */ + case -EACCES: + case -ENOTDIR: + case -EAGAIN: + return ERR_PTR(-ENOKEY); + default: + return ERR_CAST(ref); + } + } + + key = key_ref_to_ptr(ref); + if (id_0 && id_1) { + const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); + + if (!kids->id[0]) { + pr_debug("First ID matches, but second is missing\n"); + goto reject; + } + if (!asymmetric_key_id_same(id_1, kids->id[1])) { + pr_debug("First ID matches, but second does not\n"); + goto reject; + } + } + + pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key)); + return key; + +reject: + key_put(key); + return ERR_PTR(-EKEYREJECTED); +} +EXPORT_SYMBOL_GPL(find_asymmetric_key); + +/** * asymmetric_key_generate_id: Construct an asymmetric key ID * @val_1: First binary blob * @len_1: Length of first binary blob @@ -331,7 +420,8 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) pr_devel("==>%s()\n", __func__); if (subtype) { - subtype->destroy(prep->payload.data[asym_crypto]); + subtype->destroy(prep->payload.data[asym_crypto], + prep->payload.data[asym_auth]); module_put(subtype->owner); } asymmetric_key_free_kids(kids); @@ -346,13 +436,15 @@ static void asymmetric_key_destroy(struct key *key) struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); struct asymmetric_key_ids *kids = key->payload.data[asym_key_ids]; void *data = key->payload.data[asym_crypto]; + void *auth = key->payload.data[asym_auth]; key->payload.data[asym_crypto] = NULL; key->payload.data[asym_subtype] = NULL; key->payload.data[asym_key_ids] = NULL; + key->payload.data[asym_auth] = NULL; if (subtype) { - subtype->destroy(data); + subtype->destroy(data, auth); module_put(subtype->owner); } diff --git a/crypto/asymmetric_keys/mscode_parser.c b/crypto/asymmetric_keys/mscode_parser.c index 3242cbfaeaa2..6a76d5c70ef6 100644 --- a/crypto/asymmetric_keys/mscode_parser.c +++ b/crypto/asymmetric_keys/mscode_parser.c @@ -21,19 +21,13 @@ /* * Parse a Microsoft Individual Code Signing blob */ -int mscode_parse(struct pefile_context *ctx) +int mscode_parse(void *_ctx, const void *content_data, size_t data_len, + size_t asn1hdrlen) { - const void *content_data; - size_t data_len; - int ret; - - ret = pkcs7_get_content_data(ctx->pkcs7, &content_data, &data_len, 1); - - if (ret) { - pr_debug("PKCS#7 message does not contain data\n"); - return ret; - } + struct pefile_context *ctx = _ctx; + content_data -= asn1hdrlen; + data_len += asn1hdrlen; pr_devel("Data: %zu [%*ph]\n", data_len, (unsigned)(data_len), content_data); @@ -129,7 +123,6 @@ int mscode_note_digest(void *context, size_t hdrlen, { struct pefile_context *ctx = context; - ctx->digest = value; - ctx->digest_len = vlen; - return 0; + ctx->digest = kmemdup(value, vlen, GFP_KERNEL); + return ctx->digest ? 0 : -ENOMEM; } diff --git a/crypto/asymmetric_keys/pkcs7_key_type.c b/crypto/asymmetric_keys/pkcs7_key_type.c index e2d0edbbc71a..3b92523882e5 100644 --- a/crypto/asymmetric_keys/pkcs7_key_type.c +++ b/crypto/asymmetric_keys/pkcs7_key_type.c @@ -13,12 +13,9 @@ #include <linux/key.h> #include <linux/err.h> #include <linux/module.h> +#include <linux/verification.h> #include <linux/key-type.h> -#include <keys/asymmetric-type.h> -#include <crypto/pkcs7.h> #include <keys/user-type.h> -#include <keys/system_keyring.h> -#include "pkcs7_parser.h" MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("PKCS#7 testing key type"); @@ -29,60 +26,47 @@ MODULE_PARM_DESC(pkcs7_usage, "Usage to specify when verifying the PKCS#7 message"); /* - * Preparse a PKCS#7 wrapped and validated data blob. + * Retrieve the PKCS#7 message content. */ -static int pkcs7_preparse(struct key_preparsed_payload *prep) +static int pkcs7_view_content(void *ctx, const void *data, size_t len, + size_t asn1hdrlen) { - enum key_being_used_for usage = pkcs7_usage; - struct pkcs7_message *pkcs7; - const void *data, *saved_prep_data; - size_t datalen, saved_prep_datalen; - bool trusted; + struct key_preparsed_payload *prep = ctx; + const void *saved_prep_data; + size_t saved_prep_datalen; int ret; - kenter(""); - - if (usage >= NR__KEY_BEING_USED_FOR) { - pr_err("Invalid usage type %d\n", usage); - return -EINVAL; - } - saved_prep_data = prep->data; saved_prep_datalen = prep->datalen; - pkcs7 = pkcs7_parse_message(saved_prep_data, saved_prep_datalen); - if (IS_ERR(pkcs7)) { - ret = PTR_ERR(pkcs7); - goto error; - } - - ret = pkcs7_verify(pkcs7, usage); - if (ret < 0) - goto error_free; - - ret = pkcs7_validate_trust(pkcs7, system_trusted_keyring, &trusted); - if (ret < 0) - goto error_free; - if (!trusted) - pr_warn("PKCS#7 message doesn't chain back to a trusted key\n"); - - ret = pkcs7_get_content_data(pkcs7, &data, &datalen, false); - if (ret < 0) - goto error_free; - prep->data = data; - prep->datalen = datalen; + prep->datalen = len; + ret = user_preparse(prep); + prep->data = saved_prep_data; prep->datalen = saved_prep_datalen; - -error_free: - pkcs7_free_message(pkcs7); -error: - kleave(" = %d", ret); return ret; } /* + * Preparse a PKCS#7 wrapped and validated data blob. + */ +static int pkcs7_preparse(struct key_preparsed_payload *prep) +{ + enum key_being_used_for usage = pkcs7_usage; + + if (usage >= NR__KEY_BEING_USED_FOR) { + pr_err("Invalid usage type %d\n", usage); + return -EINVAL; + } + + return verify_pkcs7_signature(NULL, 0, + prep->data, prep->datalen, + NULL, usage, + pkcs7_view_content, prep); +} + +/* * user defined keys take an arbitrary string as the description and an * arbitrary blob of data as the payload */ diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c index 40de03f49ff8..af4cd8649117 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.c +++ b/crypto/asymmetric_keys/pkcs7_parser.c @@ -44,9 +44,7 @@ struct pkcs7_parse_context { static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo) { if (sinfo) { - kfree(sinfo->sig.s); - kfree(sinfo->sig.digest); - kfree(sinfo->signing_cert_id); + public_key_signature_free(sinfo->sig); kfree(sinfo); } } @@ -125,6 +123,10 @@ struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen) ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL); if (!ctx->sinfo) goto out_no_sinfo; + ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature), + GFP_KERNEL); + if (!ctx->sinfo->sig) + goto out_no_sig; ctx->data = (unsigned long)data; ctx->ppcerts = &ctx->certs; @@ -150,6 +152,7 @@ out: ctx->certs = cert->next; x509_free_certificate(cert); } +out_no_sig: pkcs7_free_signed_info(ctx->sinfo); out_no_sinfo: pkcs7_free_message(ctx->msg); @@ -165,24 +168,25 @@ EXPORT_SYMBOL_GPL(pkcs7_parse_message); * @pkcs7: The preparsed PKCS#7 message to access * @_data: Place to return a pointer to the data * @_data_len: Place to return the data length - * @want_wrapper: True if the ASN.1 object header should be included in the data + * @_headerlen: Size of ASN.1 header not included in _data * - * Get access to the data content of the PKCS#7 message, including, optionally, - * the header of the ASN.1 object that contains it. Returns -ENODATA if the - * data object was missing from the message. + * Get access to the data content of the PKCS#7 message. The size of the + * header of the ASN.1 object that contains it is also provided and can be used + * to adjust *_data and *_data_len to get the entire object. + * + * Returns -ENODATA if the data object was missing from the message. */ int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, const void **_data, size_t *_data_len, - bool want_wrapper) + size_t *_headerlen) { - size_t wrapper; - if (!pkcs7->data) return -ENODATA; - wrapper = want_wrapper ? pkcs7->data_hdrlen : 0; - *_data = pkcs7->data - wrapper; - *_data_len = pkcs7->data_len + wrapper; + *_data = pkcs7->data; + *_data_len = pkcs7->data_len; + if (_headerlen) + *_headerlen = pkcs7->data_hdrlen; return 0; } EXPORT_SYMBOL_GPL(pkcs7_get_content_data); @@ -218,25 +222,26 @@ int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen, switch (ctx->last_oid) { case OID_md4: - ctx->sinfo->sig.hash_algo = "md4"; + ctx->sinfo->sig->hash_algo = "md4"; break; case OID_md5: - ctx->sinfo->sig.hash_algo = "md5"; + ctx->sinfo->sig->hash_algo = "md5"; break; case OID_sha1: - ctx->sinfo->sig.hash_algo = "sha1"; + ctx->sinfo->sig->hash_algo = "sha1"; break; case OID_sha256: - ctx->sinfo->sig.hash_algo = "sha256"; + ctx->sinfo->sig->hash_algo = "sha256"; break; case OID_sha384: - ctx->sinfo->sig.hash_algo = "sha384"; + ctx->sinfo->sig->hash_algo = "sha384"; break; case OID_sha512: - ctx->sinfo->sig.hash_algo = "sha512"; + ctx->sinfo->sig->hash_algo = "sha512"; break; case OID_sha224: - ctx->sinfo->sig.hash_algo = "sha224"; + ctx->sinfo->sig->hash_algo = "sha224"; + break; default: printk("Unsupported digest algo: %u\n", ctx->last_oid); return -ENOPKG; @@ -255,7 +260,7 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen, switch (ctx->last_oid) { case OID_rsaEncryption: - ctx->sinfo->sig.pkey_algo = "rsa"; + ctx->sinfo->sig->pkey_algo = "rsa"; break; default: printk("Unsupported pkey algo: %u\n", ctx->last_oid); @@ -615,11 +620,11 @@ int pkcs7_sig_note_signature(void *context, size_t hdrlen, { struct pkcs7_parse_context *ctx = context; - ctx->sinfo->sig.s = kmemdup(value, vlen, GFP_KERNEL); - if (!ctx->sinfo->sig.s) + ctx->sinfo->sig->s = kmemdup(value, vlen, GFP_KERNEL); + if (!ctx->sinfo->sig->s) return -ENOMEM; - ctx->sinfo->sig.s_size = vlen; + ctx->sinfo->sig->s_size = vlen; return 0; } @@ -655,12 +660,16 @@ int pkcs7_note_signed_info(void *context, size_t hdrlen, pr_devel("SINFO KID: %u [%*phN]\n", kid->len, kid->len, kid->data); - sinfo->signing_cert_id = kid; + sinfo->sig->auth_ids[0] = kid; sinfo->index = ++ctx->sinfo_index; *ctx->ppsinfo = sinfo; ctx->ppsinfo = &sinfo->next; ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL); if (!ctx->sinfo) return -ENOMEM; + ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature), + GFP_KERNEL); + if (!ctx->sinfo->sig) + return -ENOMEM; return 0; } diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h index a66b19ebcf47..f4e81074f5e0 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.h +++ b/crypto/asymmetric_keys/pkcs7_parser.h @@ -22,7 +22,6 @@ struct pkcs7_signed_info { struct pkcs7_signed_info *next; struct x509_certificate *signer; /* Signing certificate (in msg->certs) */ unsigned index; - bool trusted; bool unsupported_crypto; /* T if not usable due to missing crypto */ /* Message digest - the digest of the Content Data (or NULL) */ @@ -41,19 +40,17 @@ struct pkcs7_signed_info { #define sinfo_has_ms_statement_type 5 time64_t signing_time; - /* Issuing cert serial number and issuer's name [PKCS#7 or CMS ver 1] - * or issuing cert's SKID [CMS ver 3]. - */ - struct asymmetric_key_id *signing_cert_id; - /* Message signature. * * This contains the generated digest of _either_ the Content Data or * the Authenticated Attributes [RFC2315 9.3]. If the latter, one of * the attributes contains the digest of the the Content Data within * it. + * + * THis also contains the issuing cert serial number and issuer's name + * [PKCS#7 or CMS ver 1] or issuing cert's SKID [CMS ver 3]. */ - struct public_key_signature sig; + struct public_key_signature *sig; }; struct pkcs7_message { diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index 7d7a39b47c62..f6a009d88a33 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -27,10 +27,9 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo, struct key *trust_keyring) { - struct public_key_signature *sig = &sinfo->sig; + struct public_key_signature *sig = sinfo->sig; struct x509_certificate *x509, *last = NULL, *p; struct key *key; - bool trusted; int ret; kenter(",%u,", sinfo->index); @@ -42,10 +41,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, for (x509 = sinfo->signer; x509; x509 = x509->signer) { if (x509->seen) { - if (x509->verified) { - trusted = x509->trusted; + if (x509->verified) goto verified; - } kleave(" = -ENOKEY [cached]"); return -ENOKEY; } @@ -54,9 +51,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, /* Look to see if this certificate is present in the trusted * keys. */ - key = x509_request_asymmetric_key(trust_keyring, - x509->id, x509->skid, - false); + key = find_asymmetric_key(trust_keyring, + x509->id, x509->skid, false); if (!IS_ERR(key)) { /* One of the X.509 certificates in the PKCS#7 message * is apparently the same as one we already trust. @@ -80,17 +76,17 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, might_sleep(); last = x509; - sig = &last->sig; + sig = last->sig; } /* No match - see if the root certificate has a signer amongst the * trusted keys. */ - if (last && (last->akid_id || last->akid_skid)) { - key = x509_request_asymmetric_key(trust_keyring, - last->akid_id, - last->akid_skid, - false); + if (last && (last->sig->auth_ids[0] || last->sig->auth_ids[1])) { + key = find_asymmetric_key(trust_keyring, + last->sig->auth_ids[0], + last->sig->auth_ids[1], + false); if (!IS_ERR(key)) { x509 = last; pr_devel("sinfo %u: Root cert %u signer is key %x\n", @@ -104,10 +100,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, /* As a last resort, see if we have a trusted public key that matches * the signed info directly. */ - key = x509_request_asymmetric_key(trust_keyring, - sinfo->signing_cert_id, - NULL, - false); + key = find_asymmetric_key(trust_keyring, + sinfo->sig->auth_ids[0], NULL, false); if (!IS_ERR(key)) { pr_devel("sinfo %u: Direct signer is key %x\n", sinfo->index, key_serial(key)); @@ -122,7 +116,6 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, matched: ret = verify_signature(key, sig); - trusted = test_bit(KEY_FLAG_TRUSTED, &key->flags); key_put(key); if (ret < 0) { if (ret == -ENOMEM) @@ -134,12 +127,9 @@ matched: verified: if (x509) { x509->verified = true; - for (p = sinfo->signer; p != x509; p = p->signer) { + for (p = sinfo->signer; p != x509; p = p->signer) p->verified = true; - p->trusted = trusted; - } } - sinfo->trusted = trusted; kleave(" = 0"); return 0; } @@ -148,7 +138,6 @@ verified: * pkcs7_validate_trust - Validate PKCS#7 trust chain * @pkcs7: The PKCS#7 certificate to validate * @trust_keyring: Signing certificates to use as starting points - * @_trusted: Set to true if trustworth, false otherwise * * Validate that the certificate chain inside the PKCS#7 message intersects * keys we already know and trust. @@ -170,16 +159,13 @@ verified: * May also return -ENOMEM. */ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, - struct key *trust_keyring, - bool *_trusted) + struct key *trust_keyring) { struct pkcs7_signed_info *sinfo; struct x509_certificate *p; int cached_ret = -ENOKEY; int ret; - *_trusted = false; - for (p = pkcs7->certs; p; p = p->next) p->seen = false; @@ -193,7 +179,6 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, cached_ret = -ENOPKG; continue; case 0: - *_trusted |= sinfo->trusted; cached_ret = 0; continue; default: diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c index 50be2a15e531..44b746e9df1b 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -25,34 +25,36 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo) { + struct public_key_signature *sig = sinfo->sig; struct crypto_shash *tfm; struct shash_desc *desc; - size_t digest_size, desc_size; - void *digest; + size_t desc_size; int ret; - kenter(",%u,%s", sinfo->index, sinfo->sig.hash_algo); + kenter(",%u,%s", sinfo->index, sinfo->sig->hash_algo); - if (!sinfo->sig.hash_algo) + if (!sinfo->sig->hash_algo) return -ENOPKG; /* Allocate the hashing algorithm we're going to need and find out how * big the hash operational data will be. */ - tfm = crypto_alloc_shash(sinfo->sig.hash_algo, 0, 0); + tfm = crypto_alloc_shash(sinfo->sig->hash_algo, 0, 0); if (IS_ERR(tfm)) return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); - sinfo->sig.digest_size = digest_size = crypto_shash_digestsize(tfm); + sig->digest_size = crypto_shash_digestsize(tfm); ret = -ENOMEM; - digest = kzalloc(ALIGN(digest_size, __alignof__(*desc)) + desc_size, - GFP_KERNEL); - if (!digest) + sig->digest = kmalloc(sig->digest_size, GFP_KERNEL); + if (!sig->digest) + goto error_no_desc; + + desc = kzalloc(desc_size, GFP_KERNEL); + if (!desc) goto error_no_desc; - desc = PTR_ALIGN(digest + digest_size, __alignof__(*desc)); desc->tfm = tfm; desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; @@ -60,10 +62,11 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, ret = crypto_shash_init(desc); if (ret < 0) goto error; - ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len, digest); + ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len, + sig->digest); if (ret < 0) goto error; - pr_devel("MsgDigest = [%*ph]\n", 8, digest); + pr_devel("MsgDigest = [%*ph]\n", 8, sig->digest); /* However, if there are authenticated attributes, there must be a * message digest attribute amongst them which corresponds to the @@ -78,14 +81,15 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, goto error; } - if (sinfo->msgdigest_len != sinfo->sig.digest_size) { + if (sinfo->msgdigest_len != sig->digest_size) { pr_debug("Sig %u: Invalid digest size (%u)\n", sinfo->index, sinfo->msgdigest_len); ret = -EBADMSG; goto error; } - if (memcmp(digest, sinfo->msgdigest, sinfo->msgdigest_len) != 0) { + if (memcmp(sig->digest, sinfo->msgdigest, + sinfo->msgdigest_len) != 0) { pr_debug("Sig %u: Message digest doesn't match\n", sinfo->index); ret = -EKEYREJECTED; @@ -97,7 +101,7 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, * convert the attributes from a CONT.0 into a SET before we * hash it. */ - memset(digest, 0, sinfo->sig.digest_size); + memset(sig->digest, 0, sig->digest_size); ret = crypto_shash_init(desc); if (ret < 0) @@ -107,17 +111,14 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, if (ret < 0) goto error; ret = crypto_shash_finup(desc, sinfo->authattrs, - sinfo->authattrs_len, digest); + sinfo->authattrs_len, sig->digest); if (ret < 0) goto error; - pr_devel("AADigest = [%*ph]\n", 8, digest); + pr_devel("AADigest = [%*ph]\n", 8, sig->digest); } - sinfo->sig.digest = digest; - digest = NULL; - error: - kfree(digest); + kfree(desc); error_no_desc: crypto_free_shash(tfm); kleave(" = %d", ret); @@ -144,12 +145,12 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7, * PKCS#7 message - but I can't be 100% sure of that. It's * possible this will need element-by-element comparison. */ - if (!asymmetric_key_id_same(x509->id, sinfo->signing_cert_id)) + if (!asymmetric_key_id_same(x509->id, sinfo->sig->auth_ids[0])) continue; pr_devel("Sig %u: Found cert serial match X.509[%u]\n", sinfo->index, certix); - if (x509->pub->pkey_algo != sinfo->sig.pkey_algo) { + if (x509->pub->pkey_algo != sinfo->sig->pkey_algo) { pr_warn("Sig %u: X.509 algo and PKCS#7 sig algo don't match\n", sinfo->index); continue; @@ -164,7 +165,7 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7, */ pr_debug("Sig %u: Issuing X.509 cert not found (#%*phN)\n", sinfo->index, - sinfo->signing_cert_id->len, sinfo->signing_cert_id->data); + sinfo->sig->auth_ids[0]->len, sinfo->sig->auth_ids[0]->data); return 0; } @@ -174,6 +175,7 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7, static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo) { + struct public_key_signature *sig; struct x509_certificate *x509 = sinfo->signer, *p; struct asymmetric_key_id *auth; int ret; @@ -188,34 +190,26 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, x509->subject, x509->raw_serial_size, x509->raw_serial); x509->seen = true; - ret = x509_get_sig_params(x509); - if (ret < 0) - goto maybe_missing_crypto_in_x509; + if (x509->unsupported_key) + goto unsupported_crypto_in_x509; pr_debug("- issuer %s\n", x509->issuer); - if (x509->akid_id) + sig = x509->sig; + if (sig->auth_ids[0]) pr_debug("- authkeyid.id %*phN\n", - x509->akid_id->len, x509->akid_id->data); - if (x509->akid_skid) + sig->auth_ids[0]->len, sig->auth_ids[0]->data); + if (sig->auth_ids[1]) pr_debug("- authkeyid.skid %*phN\n", - x509->akid_skid->len, x509->akid_skid->data); + sig->auth_ids[1]->len, sig->auth_ids[1]->data); - if ((!x509->akid_id && !x509->akid_skid) || - strcmp(x509->subject, x509->issuer) == 0) { + if (x509->self_signed) { /* If there's no authority certificate specified, then * the certificate must be self-signed and is the root * of the chain. Likewise if the cert is its own * authority. */ - pr_debug("- no auth?\n"); - if (x509->raw_subject_size != x509->raw_issuer_size || - memcmp(x509->raw_subject, x509->raw_issuer, - x509->raw_issuer_size) != 0) - return 0; - - ret = x509_check_signature(x509->pub, x509); - if (ret < 0) - goto maybe_missing_crypto_in_x509; + if (x509->unsupported_sig) + goto unsupported_crypto_in_x509; x509->signer = x509; pr_debug("- self-signed\n"); return 0; @@ -224,7 +218,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, /* Look through the X.509 certificates in the PKCS#7 message's * list to see if the next one is there. */ - auth = x509->akid_id; + auth = sig->auth_ids[0]; if (auth) { pr_debug("- want %*phN\n", auth->len, auth->data); for (p = pkcs7->certs; p; p = p->next) { @@ -234,7 +228,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, goto found_issuer_check_skid; } } else { - auth = x509->akid_skid; + auth = sig->auth_ids[1]; pr_debug("- want %*phN\n", auth->len, auth->data); for (p = pkcs7->certs; p; p = p->next) { if (!p->skid) @@ -254,8 +248,8 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, /* We matched issuer + serialNumber, but if there's an * authKeyId.keyId, that must match the CA subjKeyId also. */ - if (x509->akid_skid && - !asymmetric_key_id_same(p->skid, x509->akid_skid)) { + if (sig->auth_ids[1] && + !asymmetric_key_id_same(p->skid, sig->auth_ids[1])) { pr_warn("Sig %u: X.509 chain contains auth-skid nonmatch (%u->%u)\n", sinfo->index, x509->index, p->index); return -EKEYREJECTED; @@ -267,7 +261,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, sinfo->index); return 0; } - ret = x509_check_signature(p->pub, x509); + ret = public_key_verify_signature(p->pub, p->sig); if (ret < 0) return ret; x509->signer = p; @@ -279,16 +273,14 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, might_sleep(); } -maybe_missing_crypto_in_x509: +unsupported_crypto_in_x509: /* Just prune the certificate chain at this point if we lack some * crypto module to go further. Note, however, we don't want to set - * sinfo->missing_crypto as the signed info block may still be + * sinfo->unsupported_crypto as the signed info block may still be * validatable against an X.509 cert lower in the chain that we have a * trusted copy of. */ - if (ret == -ENOPKG) - return 0; - return ret; + return 0; } /* @@ -332,7 +324,7 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7, } /* Verify the PKCS#7 binary against the key */ - ret = public_key_verify_signature(sinfo->signer->pub, &sinfo->sig); + ret = public_key_verify_signature(sinfo->signer->pub, sinfo->sig); if (ret < 0) return ret; @@ -375,9 +367,8 @@ int pkcs7_verify(struct pkcs7_message *pkcs7, enum key_being_used_for usage) { struct pkcs7_signed_info *sinfo; - struct x509_certificate *x509; int enopkg = -ENOPKG; - int ret, n; + int ret; kenter(""); @@ -419,12 +410,6 @@ int pkcs7_verify(struct pkcs7_message *pkcs7, return -EINVAL; } - for (n = 0, x509 = pkcs7->certs; x509; x509 = x509->next, n++) { - ret = x509_get_sig_params(x509); - if (ret < 0) - return ret; - } - for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { ret = pkcs7_verify_one(pkcs7, sinfo); if (ret < 0) { diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c index 0f8b264b3961..fd76b5fc3b3a 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c @@ -39,15 +39,23 @@ static void public_key_describe(const struct key *asymmetric_key, /* * Destroy a public key algorithm key. */ -void public_key_destroy(void *payload) +void public_key_free(struct public_key *key) { - struct public_key *key = payload; - - if (key) + if (key) { kfree(key->key); - kfree(key); + kfree(key); + } +} +EXPORT_SYMBOL_GPL(public_key_free); + +/* + * Destroy a public key algorithm key. + */ +static void public_key_destroy(void *payload0, void *payload3) +{ + public_key_free(payload0); + public_key_signature_free(payload3); } -EXPORT_SYMBOL_GPL(public_key_destroy); struct public_key_completion { struct completion completion; diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c new file mode 100644 index 000000000000..ac4bddf669de --- /dev/null +++ b/crypto/asymmetric_keys/restrict.c @@ -0,0 +1,108 @@ +/* Instantiate a public key crypto key from an X.509 Certificate + * + * Copyright (C) 2012, 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) "ASYM: "fmt +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <crypto/public_key.h> +#include "asymmetric_keys.h" + +static bool use_builtin_keys; +static struct asymmetric_key_id *ca_keyid; + +#ifndef MODULE +static struct { + struct asymmetric_key_id id; + unsigned char data[10]; +} cakey; + +static int __init ca_keys_setup(char *str) +{ + if (!str) /* default system keyring */ + return 1; + + if (strncmp(str, "id:", 3) == 0) { + struct asymmetric_key_id *p = &cakey.id; + size_t hexlen = (strlen(str) - 3) / 2; + int ret; + + if (hexlen == 0 || hexlen > sizeof(cakey.data)) { + pr_err("Missing or invalid ca_keys id\n"); + return 1; + } + + ret = __asymmetric_key_hex_to_key_id(str + 3, p, hexlen); + if (ret < 0) + pr_err("Unparsable ca_keys id hex string\n"); + else + ca_keyid = p; /* owner key 'id:xxxxxx' */ + } else if (strcmp(str, "builtin") == 0) { + use_builtin_keys = true; + } + + return 1; +} +__setup("ca_keys=", ca_keys_setup); +#endif + +/** + * 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. + * @type: The type of key being added. + * @payload: The payload of the new key. + * + * 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 + * new certificate as being trusted. + * + * 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 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, + const struct key_type *type, + const union key_payload *payload) +{ + const struct public_key_signature *sig; + struct key *key; + int ret; + + pr_devel("==>%s()\n", __func__); + + if (!trust_keyring) + return -ENOKEY; + + if (type != &key_type_asymmetric) + return -EOPNOTSUPP; + + sig = payload->data[asym_auth]; + if (!sig->auth_ids[0] && !sig->auth_ids[1]) + return 0; + + if (ca_keyid && !asymmetric_key_id_partial(sig->auth_ids[1], ca_keyid)) + return -EPERM; + + /* See if we have a key that signed this one. */ + key = find_asymmetric_key(trust_keyring, + sig->auth_ids[0], sig->auth_ids[1], + false); + if (IS_ERR(key)) + return -ENOKEY; + + if (use_builtin_keys && !test_bit(KEY_FLAG_BUILTIN, &key->flags)) + ret = -ENOKEY; + else + ret = verify_signature(key, sig); + key_put(key); + return ret; +} diff --git a/crypto/asymmetric_keys/signature.c b/crypto/asymmetric_keys/signature.c index 004d5fc8e56b..11b7ba170904 100644 --- a/crypto/asymmetric_keys/signature.c +++ b/crypto/asymmetric_keys/signature.c @@ -15,9 +15,27 @@ #include <keys/asymmetric-subtype.h> #include <linux/export.h> #include <linux/err.h> +#include <linux/slab.h> #include <crypto/public_key.h> #include "asymmetric_keys.h" +/* + * Destroy a public key signature. + */ +void public_key_signature_free(struct public_key_signature *sig) +{ + int i; + + if (sig) { + for (i = 0; i < ARRAY_SIZE(sig->auth_ids); i++) + kfree(sig->auth_ids[i]); + kfree(sig->s); + kfree(sig->digest); + kfree(sig); + } +} +EXPORT_SYMBOL_GPL(public_key_signature_free); + /** * verify_signature - Initiate the use of an asymmetric key to verify a signature * @key: The asymmetric key to verify against diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c index 7e8c2338ae25..672a94c2c3ff 100644 --- a/crypto/asymmetric_keys/verify_pefile.c +++ b/crypto/asymmetric_keys/verify_pefile.c @@ -16,7 +16,7 @@ #include <linux/err.h> #include <linux/pe.h> #include <linux/asn1.h> -#include <crypto/pkcs7.h> +#include <linux/verification.h> #include <crypto/hash.h> #include "verify_pefile.h" @@ -392,9 +392,8 @@ error_no_desc: * verify_pefile_signature - Verify the signature on a PE binary image * @pebuf: Buffer containing the PE binary image * @pelen: Length of the binary image - * @trust_keyring: Signing certificates to use as starting points + * @trust_keys: Signing certificate(s) to use as starting points * @usage: The use to which the key is being put. - * @_trusted: Set to true if trustworth, false otherwise * * Validate that the certificate chain inside the PKCS#7 message inside the PE * binary image intersects keys we already know and trust. @@ -418,14 +417,10 @@ error_no_desc: * May also return -ENOMEM. */ int verify_pefile_signature(const void *pebuf, unsigned pelen, - struct key *trusted_keyring, - enum key_being_used_for usage, - bool *_trusted) + struct key *trusted_keys, + enum key_being_used_for usage) { - struct pkcs7_message *pkcs7; struct pefile_context ctx; - const void *data; - size_t datalen; int ret; kenter(""); @@ -439,19 +434,10 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen, if (ret < 0) return ret; - pkcs7 = pkcs7_parse_message(pebuf + ctx.sig_offset, ctx.sig_len); - if (IS_ERR(pkcs7)) - return PTR_ERR(pkcs7); - ctx.pkcs7 = pkcs7; - - ret = pkcs7_get_content_data(ctx.pkcs7, &data, &datalen, false); - if (ret < 0 || datalen == 0) { - pr_devel("PKCS#7 message does not contain data\n"); - ret = -EBADMSG; - goto error; - } - - ret = mscode_parse(&ctx); + ret = verify_pkcs7_signature(NULL, 0, + pebuf + ctx.sig_offset, ctx.sig_len, + trusted_keys, usage, + mscode_parse, &ctx); if (ret < 0) goto error; @@ -462,16 +448,8 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen, * contents. */ ret = pefile_digest_pe(pebuf, pelen, &ctx); - if (ret < 0) - goto error; - - ret = pkcs7_verify(pkcs7, usage); - if (ret < 0) - goto error; - - ret = pkcs7_validate_trust(pkcs7, trusted_keyring, _trusted); error: - pkcs7_free_message(ctx.pkcs7); + kfree(ctx.digest); return ret; } diff --git a/crypto/asymmetric_keys/verify_pefile.h b/crypto/asymmetric_keys/verify_pefile.h index a133eb81a492..cd4d20930728 100644 --- a/crypto/asymmetric_keys/verify_pefile.h +++ b/crypto/asymmetric_keys/verify_pefile.h @@ -9,7 +9,6 @@ * 2 of the Licence, or (at your option) any later version. */ -#include <linux/verify_pefile.h> #include <crypto/pkcs7.h> #include <crypto/hash_info.h> @@ -23,7 +22,6 @@ struct pefile_context { unsigned sig_offset; unsigned sig_len; const struct section_header *secs; - struct pkcs7_message *pkcs7; /* PKCS#7 MS Individual Code Signing content */ const void *digest; /* Digest */ @@ -39,4 +37,5 @@ struct pefile_context { /* * mscode_parser.c */ -extern int mscode_parse(struct pefile_context *ctx); +extern int mscode_parse(void *_ctx, const void *content_data, size_t data_len, + size_t asn1hdrlen); diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 4a29bac70060..865f46ea724f 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -47,15 +47,12 @@ struct x509_parse_context { void x509_free_certificate(struct x509_certificate *cert) { if (cert) { - public_key_destroy(cert->pub); + public_key_free(cert->pub); + public_key_signature_free(cert->sig); kfree(cert->issuer); kfree(cert->subject); kfree(cert->id); kfree(cert->skid); - kfree(cert->akid_id); - kfree(cert->akid_skid); - kfree(cert->sig.digest); - kfree(cert->sig.s); kfree(cert); } } @@ -78,6 +75,9 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) cert->pub = kzalloc(sizeof(struct public_key), GFP_KERNEL); if (!cert->pub) goto error_no_ctx; + cert->sig = kzalloc(sizeof(struct public_key_signature), GFP_KERNEL); + if (!cert->sig) + goto error_no_ctx; ctx = kzalloc(sizeof(struct x509_parse_context), GFP_KERNEL); if (!ctx) goto error_no_ctx; @@ -108,6 +108,11 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) cert->pub->keylen = ctx->key_size; + /* Grab the signature bits */ + ret = x509_get_sig_params(cert); + if (ret < 0) + goto error_decode; + /* Generate cert issuer + serial number key ID */ kid = asymmetric_key_generate_id(cert->raw_serial, cert->raw_serial_size, @@ -119,6 +124,11 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) } cert->id = kid; + /* Detect self-signed certificates */ + ret = x509_check_for_self_signed(cert); + if (ret < 0) + goto error_decode; + kfree(ctx); return cert; @@ -188,33 +198,33 @@ int x509_note_pkey_algo(void *context, size_t hdrlen, return -ENOPKG; /* Unsupported combination */ case OID_md4WithRSAEncryption: - ctx->cert->sig.hash_algo = "md4"; - ctx->cert->sig.pkey_algo = "rsa"; + ctx->cert->sig->hash_algo = "md4"; + ctx->cert->sig->pkey_algo = "rsa"; break; case OID_sha1WithRSAEncryption: - ctx->cert->sig.hash_algo = "sha1"; - ctx->cert->sig.pkey_algo = "rsa"; + ctx->cert->sig->hash_algo = "sha1"; + ctx->cert->sig->pkey_algo = "rsa"; break; case OID_sha256WithRSAEncryption: - ctx->cert->sig.hash_algo = "sha256"; - ctx->cert->sig.pkey_algo = "rsa"; + ctx->cert->sig->hash_algo = "sha256"; + ctx->cert->sig->pkey_algo = "rsa"; break; case OID_sha384WithRSAEncryption: - ctx->cert->sig.hash_algo = "sha384"; - ctx->cert->sig.pkey_algo = "rsa"; + ctx->cert->sig->hash_algo = "sha384"; + ctx->cert->sig->pkey_algo = "rsa"; break; case OID_sha512WithRSAEncryption: - ctx->cert->sig.hash_algo = "sha512"; - ctx->cert->sig.pkey_algo = "rsa"; + ctx->cert->sig->hash_algo = "sha512"; + ctx->cert->sig->pkey_algo = "rsa"; break; case OID_sha224WithRSAEncryption: - ctx->cert->sig.hash_algo = "sha224"; - ctx->cert->sig.pkey_algo = "rsa"; + ctx->cert->sig->hash_algo = "sha224"; + ctx->cert->sig->pkey_algo = "rsa"; break; } @@ -572,14 +582,14 @@ int x509_akid_note_kid(void *context, size_t hdrlen, pr_debug("AKID: keyid: %*phN\n", (int)vlen, value); - if (ctx->cert->akid_skid) + if (ctx->cert->sig->auth_ids[1]) return 0; kid = asymmetric_key_generate_id(value, vlen, "", 0); if (IS_ERR(kid)) return PTR_ERR(kid); pr_debug("authkeyid %*phN\n", kid->len, kid->data); - ctx->cert->akid_skid = kid; + ctx->cert->sig->auth_ids[1] = kid; return 0; } @@ -611,7 +621,7 @@ int x509_akid_note_serial(void *context, size_t hdrlen, pr_debug("AKID: serial: %*phN\n", (int)vlen, value); - if (!ctx->akid_raw_issuer || ctx->cert->akid_id) + if (!ctx->akid_raw_issuer || ctx->cert->sig->auth_ids[0]) return 0; kid = asymmetric_key_generate_id(value, @@ -622,6 +632,6 @@ int x509_akid_note_serial(void *context, size_t hdrlen, return PTR_ERR(kid); pr_debug("authkeyid %*phN\n", kid->len, kid->data); - ctx->cert->akid_id = kid; + ctx->cert->sig->auth_ids[0] = kid; return 0; } diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h index dbeed6018e63..05eef1c68881 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -17,13 +17,11 @@ struct x509_certificate { struct x509_certificate *next; struct x509_certificate *signer; /* Certificate that signed this one */ struct public_key *pub; /* Public key details */ - struct public_key_signature sig; /* Signature parameters */ + struct public_key_signature *sig; /* Signature parameters */ char *issuer; /* Name of certificate issuer */ char *subject; /* Name of certificate subject */ struct asymmetric_key_id *id; /* Issuer + Serial number */ struct asymmetric_key_id *skid; /* Subject + subjectKeyId (optional) */ - struct asymmetric_key_id *akid_id; /* CA AuthKeyId matching ->id (optional) */ - struct asymmetric_key_id *akid_skid; /* CA AuthKeyId matching ->skid (optional) */ time64_t valid_from; time64_t valid_to; const void *tbs; /* Signed data */ @@ -41,8 +39,9 @@ struct x509_certificate { unsigned index; bool seen; /* Infinite recursion prevention */ bool verified; - bool trusted; - bool unsupported_crypto; /* T if can't be verified due to missing crypto */ + 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 */ }; /* @@ -58,5 +57,4 @@ extern int x509_decode_time(time64_t *_t, size_t hdrlen, * x509_public_key.c */ extern int x509_get_sig_params(struct x509_certificate *cert); -extern int x509_check_signature(const struct public_key *pub, - struct x509_certificate *cert); +extern int x509_check_for_self_signed(struct x509_certificate *cert); diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 733c046aacc6..fb732296cd36 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -20,256 +20,133 @@ #include "asymmetric_keys.h" #include "x509_parser.h" -static bool use_builtin_keys; -static struct asymmetric_key_id *ca_keyid; - -#ifndef MODULE -static struct { - struct asymmetric_key_id id; - unsigned char data[10]; -} cakey; - -static int __init ca_keys_setup(char *str) -{ - if (!str) /* default system keyring */ - return 1; - - if (strncmp(str, "id:", 3) == 0) { - struct asymmetric_key_id *p = &cakey.id; - size_t hexlen = (strlen(str) - 3) / 2; - int ret; - - if (hexlen == 0 || hexlen > sizeof(cakey.data)) { - pr_err("Missing or invalid ca_keys id\n"); - return 1; - } - - ret = __asymmetric_key_hex_to_key_id(str + 3, p, hexlen); - if (ret < 0) - pr_err("Unparsable ca_keys id hex string\n"); - else - ca_keyid = p; /* owner key 'id:xxxxxx' */ - } else if (strcmp(str, "builtin") == 0) { - use_builtin_keys = true; - } - - return 1; -} -__setup("ca_keys=", ca_keys_setup); -#endif - -/** - * x509_request_asymmetric_key - Request a key by X.509 certificate params. - * @keyring: The keys to search. - * @id: The issuer & serialNumber to look for or NULL. - * @skid: The subjectKeyIdentifier to look for or NULL. - * @partial: Use partial match if true, exact if false. - * - * Find a key in the given keyring by identifier. The preferred identifier is - * the issuer + serialNumber and the fallback identifier is the - * subjectKeyIdentifier. If both are given, the lookup is by the former, but - * the latter must also match. - */ -struct key *x509_request_asymmetric_key(struct key *keyring, - const struct asymmetric_key_id *id, - const struct asymmetric_key_id *skid, - bool partial) -{ - struct key *key; - key_ref_t ref; - const char *lookup; - char *req, *p; - int len; - - if (id) { - lookup = id->data; - len = id->len; - } else { - lookup = skid->data; - len = skid->len; - } - - /* Construct an identifier "id:<keyid>". */ - p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL); - if (!req) - return ERR_PTR(-ENOMEM); - - if (partial) { - *p++ = 'i'; - *p++ = 'd'; - } else { - *p++ = 'e'; - *p++ = 'x'; - } - *p++ = ':'; - p = bin2hex(p, lookup, len); - *p = 0; - - pr_debug("Look up: \"%s\"\n", req); - - ref = keyring_search(make_key_ref(keyring, 1), - &key_type_asymmetric, req); - if (IS_ERR(ref)) - pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref)); - kfree(req); - - if (IS_ERR(ref)) { - switch (PTR_ERR(ref)) { - /* Hide some search errors */ - case -EACCES: - case -ENOTDIR: - case -EAGAIN: - return ERR_PTR(-ENOKEY); - default: - return ERR_CAST(ref); - } - } - - key = key_ref_to_ptr(ref); - if (id && skid) { - const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); - if (!kids->id[1]) { - pr_debug("issuer+serial match, but expected SKID missing\n"); - goto reject; - } - if (!asymmetric_key_id_same(skid, kids->id[1])) { - pr_debug("issuer+serial match, but SKID does not\n"); - goto reject; - } - } - - pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key)); - return key; - -reject: - key_put(key); - return ERR_PTR(-EKEYREJECTED); -} -EXPORT_SYMBOL_GPL(x509_request_asymmetric_key); - /* * Set up the signature parameters in an X.509 certificate. This involves * digesting the signed data and extracting the signature. */ int x509_get_sig_params(struct x509_certificate *cert) { + struct public_key_signature *sig = cert->sig; struct crypto_shash *tfm; struct shash_desc *desc; - size_t digest_size, desc_size; - void *digest; + size_t desc_size; int ret; pr_devel("==>%s()\n", __func__); - if (cert->unsupported_crypto) - return -ENOPKG; - if (cert->sig.s) + if (!cert->pub->pkey_algo) + cert->unsupported_key = true; + + if (!sig->pkey_algo) + cert->unsupported_sig = true; + + /* We check the hash if we can - even if we can't then verify it */ + if (!sig->hash_algo) { + cert->unsupported_sig = true; return 0; + } - cert->sig.s = kmemdup(cert->raw_sig, cert->raw_sig_size, - GFP_KERNEL); - if (!cert->sig.s) + sig->s = kmemdup(cert->raw_sig, cert->raw_sig_size, GFP_KERNEL); + if (!sig->s) return -ENOMEM; - cert->sig.s_size = cert->raw_sig_size; + sig->s_size = cert->raw_sig_size; /* Allocate the hashing algorithm we're going to need and find out how * big the hash operational data will be. */ - tfm = crypto_alloc_shash(cert->sig.hash_algo, 0, 0); + tfm = crypto_alloc_shash(sig->hash_algo, 0, 0); if (IS_ERR(tfm)) { if (PTR_ERR(tfm) == -ENOENT) { - cert->unsupported_crypto = true; - return -ENOPKG; + cert->unsupported_sig = true; + return 0; } return PTR_ERR(tfm); } desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); - digest_size = crypto_shash_digestsize(tfm); + sig->digest_size = crypto_shash_digestsize(tfm); - /* We allocate the hash operational data storage on the end of the - * digest storage space. - */ ret = -ENOMEM; - digest = kzalloc(ALIGN(digest_size, __alignof__(*desc)) + desc_size, - GFP_KERNEL); - if (!digest) + sig->digest = kmalloc(sig->digest_size, GFP_KERNEL); + if (!sig->digest) goto error; - cert->sig.digest = digest; - cert->sig.digest_size = digest_size; + desc = kzalloc(desc_size, GFP_KERNEL); + if (!desc) + goto error; - desc = PTR_ALIGN(digest + digest_size, __alignof__(*desc)); desc->tfm = tfm; desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; ret = crypto_shash_init(desc); if (ret < 0) - goto error; + goto error_2; might_sleep(); - ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, digest); + ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, sig->digest); + +error_2: + kfree(desc); error: crypto_free_shash(tfm); pr_devel("<==%s() = %d\n", __func__, ret); return ret; } -EXPORT_SYMBOL_GPL(x509_get_sig_params); /* - * Check the signature on a certificate using the provided public key + * Check for self-signedness in an X.509 cert and if found, check the signature + * immediately if we can. */ -int x509_check_signature(const struct public_key *pub, - struct x509_certificate *cert) +int x509_check_for_self_signed(struct x509_certificate *cert) { - int ret; + int ret = 0; pr_devel("==>%s()\n", __func__); - ret = x509_get_sig_params(cert); - if (ret < 0) - return ret; + if (cert->raw_subject_size != cert->raw_issuer_size || + memcmp(cert->raw_subject, cert->raw_issuer, + cert->raw_issuer_size) != 0) + goto not_self_signed; + + if (cert->sig->auth_ids[0] || cert->sig->auth_ids[1]) { + /* If the AKID is present it may have one or two parts. If + * both are supplied, both must match. + */ + bool a = asymmetric_key_id_same(cert->skid, cert->sig->auth_ids[1]); + bool b = asymmetric_key_id_same(cert->id, cert->sig->auth_ids[0]); + + if (!a && !b) + goto not_self_signed; + + ret = -EKEYREJECTED; + if (((a && !b) || (b && !a)) && + cert->sig->auth_ids[0] && cert->sig->auth_ids[1]) + goto out; + } - ret = public_key_verify_signature(pub, &cert->sig); - if (ret == -ENOPKG) - cert->unsupported_crypto = true; - pr_debug("Cert Verification: %d\n", ret); - return ret; -} -EXPORT_SYMBOL_GPL(x509_check_signature); + ret = -EKEYREJECTED; + if (cert->pub->pkey_algo != cert->sig->pkey_algo) + goto out; -/* - * 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 - * new certificate as being trusted. - * - * Return 0 if the new certificate was successfully validated, 1 if we couldn't - * find a matching parent certificate in the trusted list and an error if there - * is a matching certificate but the signature check fails. - */ -static int x509_validate_trust(struct x509_certificate *cert, - struct key *trust_keyring) -{ - struct key *key; - int ret = 1; - - if (!trust_keyring) - return -EOPNOTSUPP; - - if (ca_keyid && !asymmetric_key_id_partial(cert->akid_skid, ca_keyid)) - return -EPERM; - - key = x509_request_asymmetric_key(trust_keyring, - cert->akid_id, cert->akid_skid, - false); - if (!IS_ERR(key)) { - if (!use_builtin_keys - || test_bit(KEY_FLAG_BUILTIN, &key->flags)) - ret = x509_check_signature(key->payload.data[asym_crypto], - cert); - key_put(key); + ret = public_key_verify_signature(cert->pub, cert->sig); + if (ret < 0) { + if (ret == -ENOPKG) { + cert->unsupported_sig = true; + ret = 0; + } + goto out; } + + pr_devel("Cert Self-signature verified"); + cert->self_signed = true; + +out: + pr_devel("<==%s() = %d\n", __func__, ret); return ret; + +not_self_signed: + pr_devel("<==%s() = 0 [not]\n", __func__); + return 0; } /* @@ -291,34 +168,22 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) pr_devel("Cert Issuer: %s\n", cert->issuer); pr_devel("Cert Subject: %s\n", cert->subject); - if (!cert->pub->pkey_algo || - !cert->sig.pkey_algo || - !cert->sig.hash_algo) { + if (cert->unsupported_key) { ret = -ENOPKG; goto error_free_cert; } pr_devel("Cert Key Algo: %s\n", cert->pub->pkey_algo); pr_devel("Cert Valid period: %lld-%lld\n", cert->valid_from, cert->valid_to); - pr_devel("Cert Signature: %s + %s\n", - cert->sig.pkey_algo, - cert->sig.hash_algo); cert->pub->id_type = "X509"; - /* Check the signature on the key if it appears to be self-signed */ - if ((!cert->akid_skid && !cert->akid_id) || - asymmetric_key_id_same(cert->skid, cert->akid_skid) || - asymmetric_key_id_same(cert->id, cert->akid_id)) { - ret = x509_check_signature(cert->pub, cert); /* self-signed */ - if (ret < 0) - goto error_free_cert; - } else if (!prep->trusted) { - ret = x509_validate_trust(cert, get_system_trusted_keyring()); - if (ret) - ret = x509_validate_trust(cert, get_ima_mok_keyring()); - if (!ret) - prep->trusted = 1; + if (cert->unsupported_sig) { + public_key_signature_free(cert->sig); + cert->sig = NULL; + } else { + pr_devel("Cert Signature: %s + %s\n", + cert->sig->pkey_algo, cert->sig->hash_algo); } /* Propose a description */ @@ -353,6 +218,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) prep->payload.data[asym_subtype] = &public_key_subtype; prep->payload.data[asym_key_ids] = kids; prep->payload.data[asym_crypto] = cert->pub; + prep->payload.data[asym_auth] = cert->sig; prep->description = desc; prep->quotalen = 100; @@ -360,6 +226,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) cert->pub = NULL; cert->id = NULL; cert->skid = NULL; + cert->sig = NULL; desc = NULL; ret = 0; diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 3f93125916bf..71e8a56e9479 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -360,7 +360,7 @@ init_cifs_idmap(void) GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_NOT_IN_QUOTA, NULL); + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto failed_put_cred; diff --git a/fs/nfs/nfs4idmap.c b/fs/nfs/nfs4idmap.c index 5ba22c6b0ffa..c444285bb1b1 100644 --- a/fs/nfs/nfs4idmap.c +++ b/fs/nfs/nfs4idmap.c @@ -201,7 +201,7 @@ int nfs_idmap_init(void) GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_NOT_IN_QUOTA, NULL); + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto failed_put_cred; diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h index 441aff9b5aa7..583f199400a3 100644 --- a/include/crypto/pkcs7.h +++ b/include/crypto/pkcs7.h @@ -12,6 +12,7 @@ #ifndef _CRYPTO_PKCS7_H #define _CRYPTO_PKCS7_H +#include <linux/verification.h> #include <crypto/public_key.h> struct key; @@ -26,14 +27,13 @@ extern void pkcs7_free_message(struct pkcs7_message *pkcs7); extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, const void **_data, size_t *_datalen, - bool want_wrapper); + size_t *_headerlen); /* * pkcs7_trust.c */ extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7, - struct key *trust_keyring, - bool *_trusted); + struct key *trust_keyring); /* * pkcs7_verify.c diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index aa730ea7faf8..882ca0e1e7a5 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -15,20 +15,6 @@ #define _LINUX_PUBLIC_KEY_H /* - * The use to which an asymmetric key is being put. - */ -enum key_being_used_for { - VERIFYING_MODULE_SIGNATURE, - VERIFYING_FIRMWARE_SIGNATURE, - VERIFYING_KEXEC_PE_SIGNATURE, - VERIFYING_KEY_SIGNATURE, - VERIFYING_KEY_SELF_SIGNATURE, - VERIFYING_UNSPECIFIED_SIGNATURE, - NR__KEY_BEING_USED_FOR -}; -extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR]; - -/* * Cryptographic data for the public-key subtype of the asymmetric key type. * * Note that this may include private part of the key as well as the public @@ -41,12 +27,13 @@ struct public_key { const char *pkey_algo; }; -extern void public_key_destroy(void *payload); +extern void public_key_free(struct public_key *key); /* * Public key cryptography signature data */ struct public_key_signature { + struct asymmetric_key_id *auth_ids[2]; u8 *s; /* Signature */ u32 s_size; /* Number of bytes in signature */ u8 *digest; @@ -55,17 +42,21 @@ struct public_key_signature { const char *hash_algo; }; +extern void public_key_signature_free(struct public_key_signature *sig); + extern struct asymmetric_key_subtype public_key_subtype; + struct key; +struct key_type; +union key_payload; + +extern int restrict_link_by_signature(struct key *trust_keyring, + const struct key_type *type, + const union key_payload *payload); + extern int verify_signature(const struct key *key, const struct public_key_signature *sig); -struct asymmetric_key_id; -extern struct key *x509_request_asymmetric_key(struct key *keyring, - const struct asymmetric_key_id *id, - const struct asymmetric_key_id *skid, - bool partial); - int public_key_verify_signature(const struct public_key *pkey, const struct public_key_signature *sig); diff --git a/include/keys/asymmetric-subtype.h b/include/keys/asymmetric-subtype.h index 4915d40d3c3c..2480469ce8fb 100644 --- a/include/keys/asymmetric-subtype.h +++ b/include/keys/asymmetric-subtype.h @@ -32,7 +32,7 @@ struct asymmetric_key_subtype { void (*describe)(const struct key *key, struct seq_file *m); /* Destroy a key of this subtype */ - void (*destroy)(void *payload); + void (*destroy)(void *payload_crypto, void *payload_auth); /* Verify the signature on a key of this subtype (optional) */ int (*verify_signature)(const struct key *key, diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h index 59c1df9cf922..b38240716d41 100644 --- a/include/keys/asymmetric-type.h +++ b/include/keys/asymmetric-type.h @@ -15,6 +15,7 @@ #define _KEYS_ASYMMETRIC_TYPE_H #include <linux/key-type.h> +#include <linux/verification.h> extern struct key_type key_type_asymmetric; @@ -23,9 +24,10 @@ extern struct key_type key_type_asymmetric; * follows: */ enum asymmetric_payload_bits { - asym_crypto, - asym_subtype, - asym_key_ids, + asym_crypto, /* The data representing the key */ + asym_subtype, /* Pointer to an asymmetric_key_subtype struct */ + asym_key_ids, /* Pointer to an asymmetric_key_ids struct */ + asym_auth /* The key's authorisation (signature, parent key ID) */ }; /* @@ -74,6 +76,11 @@ const struct asymmetric_key_ids *asymmetric_key_ids(const struct key *key) return key->payload.data[asym_key_ids]; } +extern struct key *find_asymmetric_key(struct key *keyring, + const struct asymmetric_key_id *id_0, + const struct asymmetric_key_id *id_1, + bool partial); + /* * The payload is at the discretion of the subtype. */ diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h index 39fd38cfa8c9..fbd4647767e9 100644 --- a/include/keys/system_keyring.h +++ b/include/keys/system_keyring.h @@ -12,51 +12,40 @@ #ifndef _KEYS_SYSTEM_KEYRING_H #define _KEYS_SYSTEM_KEYRING_H +#include <linux/key.h> + #ifdef CONFIG_SYSTEM_TRUSTED_KEYRING -#include <linux/key.h> -#include <crypto/public_key.h> +extern int restrict_link_by_builtin_trusted(struct key *keyring, + const struct key_type *type, + const union key_payload *payload); -extern struct key *system_trusted_keyring; -static inline struct key *get_system_trusted_keyring(void) -{ - return system_trusted_keyring; -} #else -static inline struct key *get_system_trusted_keyring(void) -{ - return NULL; -} +#define restrict_link_by_builtin_trusted restrict_link_reject #endif -#ifdef CONFIG_SYSTEM_DATA_VERIFICATION -extern int system_verify_data(const void *data, unsigned long len, - const void *raw_pkcs7, size_t pkcs7_len, - enum key_being_used_for usage); +#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING +extern int restrict_link_by_builtin_and_secondary_trusted( + struct key *keyring, + const struct key_type *type, + const union key_payload *payload); +#else +#define restrict_link_by_builtin_and_secondary_trusted restrict_link_by_builtin_trusted #endif -#ifdef CONFIG_IMA_MOK_KEYRING -extern struct key *ima_mok_keyring; +#ifdef CONFIG_IMA_BLACKLIST_KEYRING extern struct key *ima_blacklist_keyring; -static inline struct key *get_ima_mok_keyring(void) -{ - return ima_mok_keyring; -} static inline struct key *get_ima_blacklist_keyring(void) { return ima_blacklist_keyring; } #else -static inline struct key *get_ima_mok_keyring(void) -{ - return NULL; -} static inline struct key *get_ima_blacklist_keyring(void) { return NULL; } -#endif /* CONFIG_IMA_MOK_KEYRING */ +#endif /* CONFIG_IMA_BLACKLIST_KEYRING */ #endif /* _KEYS_SYSTEM_KEYRING_H */ diff --git a/include/linux/key-type.h b/include/linux/key-type.h index 7463355a198b..eaee981c5558 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -45,7 +45,6 @@ struct key_preparsed_payload { size_t datalen; /* Raw datalen */ size_t quotalen; /* Quota length for proposed payload */ time_t expiry; /* Expiry time of key */ - bool trusted; /* True if key is trusted */ }; typedef int (*request_key_actor_t)(struct key_construction *key, diff --git a/include/linux/key.h b/include/linux/key.h index 5f5b1129dc92..722914798f37 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -173,11 +173,9 @@ struct key { #define KEY_FLAG_NEGATIVE 5 /* set if key is negative */ #define KEY_FLAG_ROOT_CAN_CLEAR 6 /* set if key can be cleared by root without permission */ #define KEY_FLAG_INVALIDATED 7 /* set if key has been invalidated */ -#define KEY_FLAG_TRUSTED 8 /* set if key is trusted */ -#define KEY_FLAG_TRUSTED_ONLY 9 /* set if keyring only accepts links to trusted keys */ -#define KEY_FLAG_BUILTIN 10 /* set if key is builtin */ -#define KEY_FLAG_ROOT_CAN_INVAL 11 /* set if key can be invalidated by root without permission */ -#define KEY_FLAG_KEEP 12 /* set if key should not be removed */ +#define KEY_FLAG_BUILTIN 8 /* set if key is built in to the kernel */ +#define KEY_FLAG_ROOT_CAN_INVAL 9 /* set if key can be invalidated by root without permission */ +#define KEY_FLAG_KEEP 10 /* set if key should not be removed */ /* the key type and key description string * - the desc is used to match a key against search criteria @@ -205,6 +203,20 @@ struct key { }; int reject_error; }; + + /* 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 + * keyring is open to any addition. It is ignored for non-keyring + * keys. + * + * 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); }; extern struct key *key_alloc(struct key_type *type, @@ -212,14 +224,17 @@ extern struct key *key_alloc(struct key_type *type, kuid_t uid, kgid_t gid, const struct cred *cred, key_perm_t perm, - unsigned long flags); + unsigned long flags, + int (*restrict_link)(struct key *, + const struct key_type *, + const union key_payload *)); -#define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */ -#define KEY_ALLOC_QUOTA_OVERRUN 0x0001 /* add to quota, permit even if overrun */ -#define KEY_ALLOC_NOT_IN_QUOTA 0x0002 /* not in quota */ -#define KEY_ALLOC_TRUSTED 0x0004 /* Key should be flagged as trusted */ -#define KEY_ALLOC_BUILT_IN 0x0008 /* Key is built into kernel */ +#define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */ +#define KEY_ALLOC_QUOTA_OVERRUN 0x0001 /* add to quota, permit even if overrun */ +#define KEY_ALLOC_NOT_IN_QUOTA 0x0002 /* not in quota */ +#define KEY_ALLOC_BUILT_IN 0x0004 /* Key is built into kernel */ +#define KEY_ALLOC_BYPASS_RESTRICTION 0x0008 /* Override the check on restricted keyrings */ extern void key_revoke(struct key *key); extern void key_invalidate(struct key *key); @@ -288,8 +303,15 @@ 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 *dest); +extern int restrict_link_reject(struct key *keyring, + const struct key_type *type, + const union key_payload *payload); + extern int keyring_clear(struct key *keyring); extern key_ref_t keyring_search(key_ref_t keyring, diff --git a/include/linux/verification.h b/include/linux/verification.h new file mode 100644 index 000000000000..a10549a6c7cd --- /dev/null +++ b/include/linux/verification.h @@ -0,0 +1,49 @@ +/* Signature verification + * + * Copyright (C) 2014 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. + */ + +#ifndef _LINUX_VERIFICATION_H +#define _LINUX_VERIFICATION_H + +/* + * The use to which an asymmetric key is being put. + */ +enum key_being_used_for { + VERIFYING_MODULE_SIGNATURE, + VERIFYING_FIRMWARE_SIGNATURE, + VERIFYING_KEXEC_PE_SIGNATURE, + VERIFYING_KEY_SIGNATURE, + VERIFYING_KEY_SELF_SIGNATURE, + VERIFYING_UNSPECIFIED_SIGNATURE, + NR__KEY_BEING_USED_FOR +}; +extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR]; + +#ifdef CONFIG_SYSTEM_DATA_VERIFICATION + +struct key; + +extern int verify_pkcs7_signature(const void *data, size_t len, + const void *raw_pkcs7, size_t pkcs7_len, + struct key *trusted_keys, + enum key_being_used_for usage, + int (*view_content)(void *ctx, + const void *data, size_t len, + size_t asn1hdrlen), + void *ctx); + +#ifdef CONFIG_SIGNED_PE_FILE_VERIFICATION +extern int verify_pefile_signature(const void *pebuf, unsigned pelen, + struct key *trusted_keys, + enum key_being_used_for usage); +#endif + +#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */ +#endif /* _LINUX_VERIFY_PEFILE_H */ diff --git a/include/linux/verify_pefile.h b/include/linux/verify_pefile.h deleted file mode 100644 index da2049b5161c..000000000000 --- a/include/linux/verify_pefile.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Signed PE file verification - * - * Copyright (C) 2014 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. - */ - -#ifndef _LINUX_VERIFY_PEFILE_H -#define _LINUX_VERIFY_PEFILE_H - -#include <crypto/public_key.h> - -extern int verify_pefile_signature(const void *pebuf, unsigned pelen, - struct key *trusted_keyring, - enum key_being_used_for usage, - bool *_trusted); - -#endif /* _LINUX_VERIFY_PEFILE_H */ diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h index 840cb990abe2..86eddd6241f3 100644 --- a/include/uapi/linux/keyctl.h +++ b/include/uapi/linux/keyctl.h @@ -12,6 +12,8 @@ #ifndef _LINUX_KEYCTL_H #define _LINUX_KEYCTL_H +#include <linux/types.h> + /* special process keyring shortcut IDs */ #define KEY_SPEC_THREAD_KEYRING -1 /* - key ID for thread-specific keyring */ #define KEY_SPEC_PROCESS_KEYRING -2 /* - key ID for process-specific keyring */ @@ -57,5 +59,13 @@ #define KEYCTL_INSTANTIATE_IOV 20 /* instantiate a partially constructed key */ #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 */ + +/* keyctl structures */ +struct keyctl_dh_params { + __s32 private; + __s32 prime; + __s32 base; +}; #endif /* _LINUX_KEYCTL_H */ diff --git a/kernel/module_signing.c b/kernel/module_signing.c index 64b9dead4a07..937c844bee4a 100644 --- a/kernel/module_signing.c +++ b/kernel/module_signing.c @@ -12,7 +12,7 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/string.h> -#include <keys/system_keyring.h> +#include <linux/verification.h> #include <crypto/public_key.h> #include "module-internal.h" @@ -80,6 +80,7 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen) return -EBADMSG; } - return system_verify_data(mod, modlen, mod + modlen, sig_len, - VERIFYING_MODULE_SIGNATURE); + return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len, + NULL, VERIFYING_MODULE_SIGNATURE, + NULL, NULL); } diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c index c79b85eb4d4c..8737412c7b27 100644 --- a/net/dns_resolver/dns_key.c +++ b/net/dns_resolver/dns_key.c @@ -281,7 +281,7 @@ static int __init init_dns_resolver(void) GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_NOT_IN_QUOTA, NULL); + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto failed_put_cred; diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c index 3fb492eedeb9..1021b4c0bdd2 100644 --- a/net/rxrpc/ar-key.c +++ b/net/rxrpc/ar-key.c @@ -965,7 +965,7 @@ int rxrpc_get_server_data_key(struct rxrpc_connection *conn, key = key_alloc(&key_type_rxrpc, "x", GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, 0, - KEY_ALLOC_NOT_IN_QUOTA); + KEY_ALLOC_NOT_IN_QUOTA, NULL); if (IS_ERR(key)) { _leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key)); return -ENOMEM; @@ -1012,7 +1012,7 @@ struct key *rxrpc_get_null_key(const char *keyname) key = key_alloc(&key_type_rxrpc, keyname, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, - KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA); + KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA, NULL); if (IS_ERR(key)) return key; diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig index 979be65d22c4..da9565891738 100644 --- a/security/integrity/Kconfig +++ b/security/integrity/Kconfig @@ -35,7 +35,6 @@ config INTEGRITY_ASYMMETRIC_KEYS default n select ASYMMETRIC_KEY_TYPE select ASYMMETRIC_PUBLIC_KEY_SUBTYPE - select PUBLIC_KEY_ALGO_RSA select CRYPTO_RSA select X509_CERTIFICATE_PARSER help diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index 8ef15118cc78..4304372b323f 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -18,6 +18,8 @@ #include <linux/cred.h> #include <linux/key-type.h> #include <linux/digsig.h> +#include <crypto/public_key.h> +#include <keys/system_keyring.h> #include "integrity.h" @@ -40,6 +42,12 @@ static bool init_keyring __initdata = true; static bool init_keyring __initdata; #endif +#ifdef CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY +#define restrict_link_to_ima restrict_link_by_builtin_and_secondary_trusted +#else +#define restrict_link_to_ima restrict_link_by_builtin_trusted +#endif + int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, const char *digest, int digestlen) { @@ -83,10 +91,9 @@ int __init integrity_init_keyring(const unsigned int id) ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE | KEY_USR_SEARCH), - KEY_ALLOC_NOT_IN_QUOTA, NULL); - if (!IS_ERR(keyring[id])) - set_bit(KEY_FLAG_TRUSTED_ONLY, &keyring[id]->flags); - else { + KEY_ALLOC_NOT_IN_QUOTA, + restrict_link_to_ima, NULL); + if (IS_ERR(keyring[id])) { err = PTR_ERR(keyring[id]); pr_info("Can't allocate %s keyring (%d)\n", keyring_name[id], err); diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index e54a8a8dae94..5487827fa86c 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -155,23 +155,33 @@ config IMA_TRUSTED_KEYRING This option is deprecated in favor of INTEGRITY_TRUSTED_KEYRING -config IMA_MOK_KEYRING - bool "Create IMA machine owner keys (MOK) and blacklist keyrings" +config IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY + bool "Permit keys validly signed by a built-in or secondary CA cert (EXPERIMENTAL)" + depends on SYSTEM_TRUSTED_KEYRING + depends on SECONDARY_TRUSTED_KEYRING + depends on INTEGRITY_ASYMMETRIC_KEYS + select INTEGRITY_TRUSTED_KEYRING + default n + help + Keys may be added to the IMA or IMA blacklist keyrings, if the + key is validly signed by a CA cert in the system built-in or + secondary trusted keyrings. + + Intermediate keys between those the kernel has compiled in and the + IMA keys to be added may be added to the system secondary keyring, + provided they are validly signed by a key already resident in the + built-in or secondary trusted keyrings. + +config IMA_BLACKLIST_KEYRING + bool "Create IMA machine owner blacklist keyrings (EXPERIMENTAL)" depends on SYSTEM_TRUSTED_KEYRING depends on IMA_TRUSTED_KEYRING default n help - This option creates IMA MOK and blacklist keyrings. IMA MOK is an - intermediate keyring that sits between .system and .ima keyrings, - effectively forming a simple CA hierarchy. To successfully import a - key into .ima_mok it must be signed by a key which CA is in .system - keyring. On turn any key that needs to go in .ima keyring must be - signed by CA in either .system or .ima_mok keyrings. IMA MOK is empty - at kernel boot. - - IMA blacklist keyring contains all revoked IMA keys. It is consulted - before any other keyring. If the search is successful the requested - operation is rejected and error is returned to the caller. + This option creates an IMA blacklist keyring, which contains all + revoked IMA keys. It is consulted before any other keyring. If + the search is successful the requested operation is rejected and + an error is returned to the caller. config IMA_LOAD_X509 bool "Load X509 certificate onto the '.ima' trusted keyring" diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index a8539f9e060f..9aeaedad1e2b 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile @@ -8,4 +8,4 @@ obj-$(CONFIG_IMA) += ima.o ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ ima_policy.o ima_template.o ima_template_lib.o ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o -obj-$(CONFIG_IMA_MOK_KEYRING) += ima_mok.o +obj-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o diff --git a/security/integrity/ima/ima_mok.c b/security/integrity/ima/ima_mok.c index 676885e4320e..74a279957464 100644 --- a/security/integrity/ima/ima_mok.c +++ b/security/integrity/ima/ima_mok.c @@ -17,38 +17,29 @@ #include <linux/cred.h> #include <linux/err.h> #include <linux/init.h> -#include <keys/asymmetric-type.h> +#include <keys/system_keyring.h> -struct key *ima_mok_keyring; struct key *ima_blacklist_keyring; /* - * Allocate the IMA MOK and blacklist keyrings + * Allocate the IMA blacklist keyring */ __init int ima_mok_init(void) { - pr_notice("Allocating IMA MOK and blacklist keyrings.\n"); - - ima_mok_keyring = keyring_alloc(".ima_mok", - 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, NULL); + pr_notice("Allocating IMA blacklist keyring.\n"); 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, NULL); + KEY_ALLOC_NOT_IN_QUOTA, + restrict_link_by_builtin_trusted, NULL); - if (IS_ERR(ima_mok_keyring) || IS_ERR(ima_blacklist_keyring)) - panic("Can't allocate IMA MOK or blacklist keyrings."); - set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_mok_keyring->flags); + if (IS_ERR(ima_blacklist_keyring)) + panic("Can't allocate IMA blacklist keyring."); - set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_blacklist_keyring->flags); set_bit(KEY_FLAG_KEEP, &ima_blacklist_keyring->flags); return 0; } diff --git a/security/keys/Kconfig b/security/keys/Kconfig index fe4d74e126a7..f826e8739023 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -41,6 +41,10 @@ config BIG_KEYS bool "Large payload keys" depends on KEYS depends on TMPFS + select CRYPTO + select CRYPTO_AES + select CRYPTO_ECB + select CRYPTO_RNG help This option provides support for holding large keys within the kernel (for example Kerberos ticket caches). The data may be stored out to @@ -81,3 +85,14 @@ config ENCRYPTED_KEYS Userspace only ever sees/stores encrypted blobs. If you are unsure as to whether this is required, answer N. + +config KEY_DH_OPERATIONS + bool "Diffie-Hellman operations on retained keys" + depends on KEYS + select MPILIB + help + This option provides support for calculating Diffie-Hellman + public keys and shared secrets using values stored as keys + in the kernel. + + If you are unsure as to whether this is required, answer N. diff --git a/security/keys/Makefile b/security/keys/Makefile index dfb3a7bededf..1fd4a16e6daf 100644 --- a/security/keys/Makefile +++ b/security/keys/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_KEYS_COMPAT) += compat.o obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_SYSCTL) += sysctl.o obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o +obj-$(CONFIG_KEY_DH_OPERATIONS) += dh.o # # Key types diff --git a/security/keys/big_key.c b/security/keys/big_key.c index c721e398893a..9e443fccad4c 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -14,8 +14,10 @@ #include <linux/file.h> #include <linux/shmem_fs.h> #include <linux/err.h> +#include <linux/scatterlist.h> #include <keys/user-type.h> #include <keys/big_key-type.h> +#include <crypto/rng.h> /* * Layout of key payload words. @@ -28,6 +30,14 @@ enum { }; /* + * Crypto operation with big_key data + */ +enum big_key_op { + BIG_KEY_ENC, + BIG_KEY_DEC, +}; + +/* * If the data is under this limit, there's no point creating a shm file to * hold it as the permanently resident metadata for the shmem fs will be at * least as large as the data. @@ -35,6 +45,11 @@ enum { #define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry)) /* + * Key size for big_key data encryption + */ +#define ENC_KEY_SIZE 16 + +/* * big_key defined keys take an arbitrary string as the description and an * arbitrary blob of data as the payload */ @@ -50,12 +65,62 @@ struct key_type key_type_big_key = { }; /* + * Crypto names for big_key data encryption + */ +static const char big_key_rng_name[] = "stdrng"; +static const char big_key_alg_name[] = "ecb(aes)"; + +/* + * Crypto algorithms for big_key data encryption + */ +static struct crypto_rng *big_key_rng; +static struct crypto_blkcipher *big_key_blkcipher; + +/* + * Generate random key to encrypt big_key data + */ +static inline int big_key_gen_enckey(u8 *key) +{ + return crypto_rng_get_bytes(big_key_rng, key, ENC_KEY_SIZE); +} + +/* + * Encrypt/decrypt big_key data + */ +static int big_key_crypt(enum big_key_op op, u8 *data, size_t datalen, u8 *key) +{ + int ret = -EINVAL; + struct scatterlist sgio; + struct blkcipher_desc desc; + + if (crypto_blkcipher_setkey(big_key_blkcipher, key, ENC_KEY_SIZE)) { + ret = -EAGAIN; + goto error; + } + + desc.flags = 0; + desc.tfm = big_key_blkcipher; + + sg_init_one(&sgio, data, datalen); + + if (op == BIG_KEY_ENC) + ret = crypto_blkcipher_encrypt(&desc, &sgio, &sgio, datalen); + else + ret = crypto_blkcipher_decrypt(&desc, &sgio, &sgio, datalen); + +error: + return ret; +} + +/* * Preparse a big key */ int big_key_preparse(struct key_preparsed_payload *prep) { struct path *path = (struct path *)&prep->payload.data[big_key_path]; struct file *file; + u8 *enckey; + u8 *data = NULL; ssize_t written; size_t datalen = prep->datalen; int ret; @@ -73,16 +138,43 @@ int big_key_preparse(struct key_preparsed_payload *prep) /* Create a shmem file to store the data in. This will permit the data * to be swapped out if needed. * - * TODO: Encrypt the stored data with a temporary key. + * File content is stored encrypted with randomly generated key. */ - file = shmem_kernel_file_setup("", datalen, 0); + size_t enclen = ALIGN(datalen, crypto_blkcipher_blocksize(big_key_blkcipher)); + + /* prepare aligned data to encrypt */ + data = kmalloc(enclen, GFP_KERNEL); + if (!data) + return -ENOMEM; + + memcpy(data, prep->data, datalen); + memset(data + datalen, 0x00, enclen - datalen); + + /* generate random key */ + enckey = kmalloc(ENC_KEY_SIZE, GFP_KERNEL); + if (!enckey) { + ret = -ENOMEM; + goto error; + } + + ret = big_key_gen_enckey(enckey); + if (ret) + goto err_enckey; + + /* encrypt aligned data */ + ret = big_key_crypt(BIG_KEY_ENC, data, enclen, enckey); + if (ret) + goto err_enckey; + + /* save aligned data to file */ + file = shmem_kernel_file_setup("", enclen, 0); if (IS_ERR(file)) { ret = PTR_ERR(file); - goto error; + goto err_enckey; } - written = kernel_write(file, prep->data, prep->datalen, 0); - if (written != datalen) { + written = kernel_write(file, data, enclen, 0); + if (written != enclen) { ret = written; if (written >= 0) ret = -ENOMEM; @@ -92,12 +184,15 @@ int big_key_preparse(struct key_preparsed_payload *prep) /* Pin the mount and dentry to the key so that we can open it again * later */ + prep->payload.data[big_key_data] = enckey; *path = file->f_path; path_get(path); fput(file); + kfree(data); } else { /* Just store the data in a buffer */ void *data = kmalloc(datalen, GFP_KERNEL); + if (!data) return -ENOMEM; @@ -108,7 +203,10 @@ int big_key_preparse(struct key_preparsed_payload *prep) err_fput: fput(file); +err_enckey: + kfree(enckey); error: + kfree(data); return ret; } @@ -119,10 +217,10 @@ void big_key_free_preparse(struct key_preparsed_payload *prep) { if (prep->datalen > BIG_KEY_FILE_THRESHOLD) { struct path *path = (struct path *)&prep->payload.data[big_key_path]; + path_put(path); - } else { - kfree(prep->payload.data[big_key_data]); } + kfree(prep->payload.data[big_key_data]); } /* @@ -147,15 +245,15 @@ void big_key_destroy(struct key *key) { size_t datalen = (size_t)key->payload.data[big_key_len]; - if (datalen) { + if (datalen > BIG_KEY_FILE_THRESHOLD) { struct path *path = (struct path *)&key->payload.data[big_key_path]; + path_put(path); path->mnt = NULL; path->dentry = NULL; - } else { - kfree(key->payload.data[big_key_data]); - key->payload.data[big_key_data] = NULL; } + kfree(key->payload.data[big_key_data]); + key->payload.data[big_key_data] = NULL; } /* @@ -188,17 +286,41 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen) if (datalen > BIG_KEY_FILE_THRESHOLD) { struct path *path = (struct path *)&key->payload.data[big_key_path]; struct file *file; - loff_t pos; + u8 *data; + u8 *enckey = (u8 *)key->payload.data[big_key_data]; + size_t enclen = ALIGN(datalen, crypto_blkcipher_blocksize(big_key_blkcipher)); + + data = kmalloc(enclen, GFP_KERNEL); + if (!data) + return -ENOMEM; file = dentry_open(path, O_RDONLY, current_cred()); - if (IS_ERR(file)) - return PTR_ERR(file); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto error; + } - pos = 0; - ret = vfs_read(file, buffer, datalen, &pos); - fput(file); - if (ret >= 0 && ret != datalen) + /* read file to kernel and decrypt */ + ret = kernel_read(file, 0, data, enclen); + if (ret >= 0 && ret != enclen) { ret = -EIO; + goto err_fput; + } + + ret = big_key_crypt(BIG_KEY_DEC, data, enclen, enckey); + if (ret) + goto err_fput; + + ret = datalen; + + /* copy decrypted data to user */ + if (copy_to_user(buffer, data, datalen) != 0) + ret = -EFAULT; + +err_fput: + fput(file); +error: + kfree(data); } else { ret = datalen; if (copy_to_user(buffer, key->payload.data[big_key_data], @@ -209,8 +331,48 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen) return ret; } +/* + * Register key type + */ static int __init big_key_init(void) { return register_key_type(&key_type_big_key); } + +/* + * Initialize big_key crypto and RNG algorithms + */ +static int __init big_key_crypto_init(void) +{ + int ret = -EINVAL; + + /* init RNG */ + big_key_rng = crypto_alloc_rng(big_key_rng_name, 0, 0); + if (IS_ERR(big_key_rng)) { + big_key_rng = NULL; + return -EFAULT; + } + + /* seed RNG */ + ret = crypto_rng_reset(big_key_rng, NULL, crypto_rng_seedsize(big_key_rng)); + if (ret) + goto error; + + /* init block cipher */ + big_key_blkcipher = crypto_alloc_blkcipher(big_key_alg_name, 0, 0); + if (IS_ERR(big_key_blkcipher)) { + big_key_blkcipher = NULL; + ret = -EFAULT; + goto error; + } + + return 0; + +error: + crypto_free_rng(big_key_rng); + big_key_rng = NULL; + return ret; +} + device_initcall(big_key_init); +late_initcall(big_key_crypto_init); diff --git a/security/keys/compat.c b/security/keys/compat.c index 25430a3aa7f7..c8783b3b628c 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -132,6 +132,10 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option, case KEYCTL_GET_PERSISTENT: return keyctl_get_persistent(arg2, arg3); + case KEYCTL_DH_COMPUTE: + return keyctl_dh_compute(compat_ptr(arg2), compat_ptr(arg3), + arg4); + default: return -EOPNOTSUPP; } diff --git a/security/keys/dh.c b/security/keys/dh.c new file mode 100644 index 000000000000..880505a4b9f1 --- /dev/null +++ b/security/keys/dh.c @@ -0,0 +1,160 @@ +/* Crypto operations using stored keys + * + * Copyright (c) 2016, Intel Corporation + * + * 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/mpi.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <keys/user-type.h> +#include "internal.h" + +/* + * Public key or shared secret generation function [RFC2631 sec 2.1.1] + * + * ya = g^xa mod p; + * or + * ZZ = yb^xa mod p; + * + * where xa is the local private key, ya is the local public key, g is + * the generator, p is the prime, yb is the remote public key, and ZZ + * is the shared secret. + * + * Both are the same calculation, so g or yb are the "base" and ya or + * ZZ are the "result". + */ +static int do_dh(MPI result, MPI base, MPI xa, MPI p) +{ + return mpi_powm(result, base, xa, p); +} + +static ssize_t mpi_from_key(key_serial_t keyid, size_t maxlen, MPI *mpi) +{ + struct key *key; + key_ref_t key_ref; + long status; + ssize_t ret; + + key_ref = lookup_user_key(keyid, 0, KEY_NEED_READ); + if (IS_ERR(key_ref)) { + ret = -ENOKEY; + goto error; + } + + key = key_ref_to_ptr(key_ref); + + ret = -EOPNOTSUPP; + if (key->type == &key_type_user) { + down_read(&key->sem); + status = key_validate(key); + if (status == 0) { + const struct user_key_payload *payload; + + payload = user_key_payload(key); + + if (maxlen == 0) { + *mpi = NULL; + ret = payload->datalen; + } else if (payload->datalen <= maxlen) { + *mpi = mpi_read_raw_data(payload->data, + payload->datalen); + if (*mpi) + ret = payload->datalen; + } else { + ret = -EINVAL; + } + } + up_read(&key->sem); + } + + key_put(key); +error: + return ret; +} + +long keyctl_dh_compute(struct keyctl_dh_params __user *params, + char __user *buffer, size_t buflen) +{ + long ret; + MPI base, private, prime, result; + unsigned nbytes; + struct keyctl_dh_params pcopy; + uint8_t *kbuf; + ssize_t keylen; + size_t resultlen; + + if (!params || (!buffer && buflen)) { + ret = -EINVAL; + goto out; + } + if (copy_from_user(&pcopy, params, sizeof(pcopy)) != 0) { + ret = -EFAULT; + goto out; + } + + keylen = mpi_from_key(pcopy.prime, buflen, &prime); + if (keylen < 0 || !prime) { + /* buflen == 0 may be used to query the required buffer size, + * which is the prime key length. + */ + ret = keylen; + goto out; + } + + /* The result is never longer than the prime */ + resultlen = keylen; + + keylen = mpi_from_key(pcopy.base, SIZE_MAX, &base); + if (keylen < 0 || !base) { + ret = keylen; + goto error1; + } + + keylen = mpi_from_key(pcopy.private, SIZE_MAX, &private); + if (keylen < 0 || !private) { + ret = keylen; + goto error2; + } + + result = mpi_alloc(0); + if (!result) { + ret = -ENOMEM; + goto error3; + } + + kbuf = kmalloc(resultlen, GFP_KERNEL); + if (!kbuf) { + ret = -ENOMEM; + goto error4; + } + + ret = do_dh(result, base, private, prime); + if (ret) + goto error5; + + ret = mpi_read_buffer(result, kbuf, resultlen, &nbytes, NULL); + if (ret != 0) + goto error5; + + ret = nbytes; + if (copy_to_user(buffer, kbuf, nbytes) != 0) + ret = -EFAULT; + +error5: + kfree(kbuf); +error4: + mpi_free(result); +error3: + mpi_free(private); +error2: + mpi_free(base); +error1: + mpi_free(prime); +out: + return ret; +} diff --git a/security/keys/internal.h b/security/keys/internal.h index 5105c2c2da75..8ec7a528365d 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -15,6 +15,7 @@ #include <linux/sched.h> #include <linux/key-type.h> #include <linux/task_work.h> +#include <linux/keyctl.h> struct iovec; @@ -257,6 +258,17 @@ static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring) } #endif +#ifdef CONFIG_KEY_DH_OPERATIONS +extern long keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *, + size_t); +#else +static inline long keyctl_dh_compute(struct keyctl_dh_params __user *params, + char __user *buffer, size_t buflen) +{ + return -EOPNOTSUPP; +} +#endif + /* * Debugging key validation */ diff --git a/security/keys/key.c b/security/keys/key.c index b28755131687..bd5a272f28a6 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -201,6 +201,7 @@ 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. * * 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 @@ -223,7 +224,10 @@ 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_perm_t perm, unsigned long flags, + int (*restrict_link)(struct key *, + const struct key_type *, + const union key_payload *)) { struct key_user *user = NULL; struct key *key; @@ -291,11 +295,10 @@ struct key *key_alloc(struct key_type *type, const char *desc, key->uid = uid; key->gid = gid; key->perm = perm; + key->restrict_link = restrict_link; if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) key->flags |= 1 << KEY_FLAG_IN_QUOTA; - if (flags & KEY_ALLOC_TRUSTED) - key->flags |= 1 << KEY_FLAG_TRUSTED; if (flags & KEY_ALLOC_BUILT_IN) key->flags |= 1 << KEY_FLAG_BUILTIN; @@ -496,6 +499,12 @@ 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; @@ -551,8 +560,12 @@ int key_reject_and_link(struct key *key, awaken = 0; ret = -EBUSY; - if (keyring) + if (keyring) { + if (keyring->restrict_link) + return -EPERM; + link_ret = __key_link_begin(keyring, &key->index_key, &edit); + } mutex_lock(&key_construction_mutex); @@ -793,6 +806,9 @@ 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; /* look up the key type to see if it's one of the registered kernel * types */ @@ -811,6 +827,10 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, key_check(keyring); + key_ref = ERR_PTR(-EPERM); + if (!(flags & KEY_ALLOC_BYPASS_RESTRICTION)) + restrict_link = keyring->restrict_link; + key_ref = ERR_PTR(-ENOTDIR); if (keyring->type != &key_type_keyring) goto error_put_type; @@ -819,7 +839,6 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, prep.data = payload; prep.datalen = plen; prep.quotalen = index_key.type->def_datalen; - prep.trusted = flags & KEY_ALLOC_TRUSTED; prep.expiry = TIME_T_MAX; if (index_key.type->preparse) { ret = index_key.type->preparse(&prep); @@ -835,10 +854,13 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, } index_key.desc_len = strlen(index_key.description); - key_ref = ERR_PTR(-EPERM); - if (!prep.trusted && test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags)) - goto error_free_prep; - flags |= prep.trusted ? KEY_ALLOC_TRUSTED : 0; + 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) { @@ -879,7 +901,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, /* allocate a new key */ key = key_alloc(index_key.type, index_key.description, - cred->fsuid, cred->fsgid, cred, perm, flags); + cred->fsuid, cred->fsgid, cred, perm, flags, NULL); if (IS_ERR(key)) { key_ref = ERR_CAST(key); goto error_link_end; diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index ed73c6c1c326..3b135a0af344 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1686,6 +1686,11 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, case KEYCTL_GET_PERSISTENT: return keyctl_get_persistent((uid_t)arg2, (key_serial_t)arg3); + case KEYCTL_DH_COMPUTE: + return keyctl_dh_compute((struct keyctl_dh_params __user *) arg2, + (char __user *) arg3, + (size_t) arg4); + default: return -EOPNOTSUPP; } diff --git a/security/keys/keyring.c b/security/keys/keyring.c index f931ccfeefb0..c91e4e0cea08 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -491,13 +491,17 @@ 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, struct key *dest) + unsigned long flags, + int (*restrict_link)(struct key *, + const struct key_type *, + const union key_payload *), + struct key *dest) { struct key *keyring; int ret; keyring = key_alloc(&key_type_keyring, description, - uid, gid, cred, perm, flags); + uid, gid, cred, perm, flags, restrict_link); if (!IS_ERR(keyring)) { ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL); if (ret < 0) { @@ -510,6 +514,26 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, } EXPORT_SYMBOL(keyring_alloc); +/** + * restrict_link_reject - Give -EPERM to restrict link + * @keyring: The keyring being added to. + * @type: The type of key being added. + * @payload: The payload of the key intended to be added. + * + * 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(). + */ +int restrict_link_reject(struct key *keyring, + const struct key_type *type, + const union key_payload *payload) +{ + return -EPERM; +} + /* * By default, we keys found by getting an exact match on their descriptions. */ @@ -1191,6 +1215,16 @@ void __key_link_end(struct key *keyring, up_write(&keyring->sem); } +/* + * Check addition of keys to restricted keyrings. + */ +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); +} + /** * key_link - Link a key to a keyring * @keyring: The keyring to make the link in. @@ -1221,14 +1255,12 @@ int key_link(struct key *keyring, struct key *key) key_check(keyring); key_check(key); - if (test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags) && - !test_bit(KEY_FLAG_TRUSTED, &key->flags)) - return -EPERM; - ret = __key_link_begin(keyring, &key->index_key, &edit); if (ret == 0) { kdebug("begun {%d,%d}", keyring->serial, atomic_read(&keyring->usage)); - ret = __key_link_check_live_key(keyring, key); + ret = __key_link_check_restriction(keyring, key); + if (ret == 0) + ret = __key_link_check_live_key(keyring, key); if (ret == 0) __key_link(key, &edit); __key_link_end(keyring, &key->index_key, edit); diff --git a/security/keys/persistent.c b/security/keys/persistent.c index c9fae5ea89fe..2ef45b319dd9 100644 --- a/security/keys/persistent.c +++ b/security/keys/persistent.c @@ -26,7 +26,7 @@ static int key_create_persistent_register(struct user_namespace *ns) current_cred(), ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ), - KEY_ALLOC_NOT_IN_QUOTA, NULL); + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); if (IS_ERR(reg)) return PTR_ERR(reg); @@ -60,7 +60,7 @@ static key_ref_t key_create_persistent(struct user_namespace *ns, kuid_t uid, uid, INVALID_GID, current_cred(), ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ), - KEY_ALLOC_NOT_IN_QUOTA, + KEY_ALLOC_NOT_IN_QUOTA, NULL, ns->persistent_keyring_register); if (IS_ERR(persistent)) return ERR_CAST(persistent); diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index e6d50172872f..40a885239782 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -76,7 +76,8 @@ int install_user_keyrings(void) if (IS_ERR(uid_keyring)) { uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID, cred, user_keyring_perm, - KEY_ALLOC_IN_QUOTA, NULL); + KEY_ALLOC_IN_QUOTA, + NULL, NULL); if (IS_ERR(uid_keyring)) { ret = PTR_ERR(uid_keyring); goto error; @@ -92,7 +93,8 @@ int install_user_keyrings(void) session_keyring = keyring_alloc(buf, user->uid, INVALID_GID, cred, user_keyring_perm, - KEY_ALLOC_IN_QUOTA, NULL); + KEY_ALLOC_IN_QUOTA, + NULL, NULL); if (IS_ERR(session_keyring)) { ret = PTR_ERR(session_keyring); goto error_release; @@ -134,7 +136,8 @@ int install_thread_keyring_to_cred(struct cred *new) keyring = keyring_alloc("_tid", new->uid, new->gid, new, KEY_POS_ALL | KEY_USR_VIEW, - KEY_ALLOC_QUOTA_OVERRUN, NULL); + KEY_ALLOC_QUOTA_OVERRUN, + NULL, NULL); if (IS_ERR(keyring)) return PTR_ERR(keyring); @@ -180,7 +183,8 @@ int install_process_keyring_to_cred(struct cred *new) keyring = keyring_alloc("_pid", new->uid, new->gid, new, KEY_POS_ALL | KEY_USR_VIEW, - KEY_ALLOC_QUOTA_OVERRUN, NULL); + KEY_ALLOC_QUOTA_OVERRUN, + NULL, NULL); if (IS_ERR(keyring)) return PTR_ERR(keyring); @@ -231,7 +235,7 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring) keyring = keyring_alloc("_ses", cred->uid, cred->gid, cred, KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ, - flags, NULL); + flags, NULL, NULL); if (IS_ERR(keyring)) return PTR_ERR(keyring); } else { @@ -785,7 +789,7 @@ long join_session_keyring(const char *name) keyring = keyring_alloc( name, old->uid, old->gid, old, KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_LINK, - KEY_ALLOC_IN_QUOTA, NULL); + KEY_ALLOC_IN_QUOTA, NULL, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error2; diff --git a/security/keys/request_key.c b/security/keys/request_key.c index c7a117c9a8f3..a29e3554751e 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -116,7 +116,7 @@ static int call_sbin_request_key(struct key_construction *cons, cred = get_current_cred(); keyring = keyring_alloc(desc, cred->fsuid, cred->fsgid, cred, KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_QUOTA_OVERRUN, NULL); + KEY_ALLOC_QUOTA_OVERRUN, NULL, NULL); put_cred(cred); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); @@ -355,7 +355,7 @@ static int construct_alloc_key(struct keyring_search_context *ctx, key = key_alloc(ctx->index_key.type, ctx->index_key.description, ctx->cred->fsuid, ctx->cred->fsgid, ctx->cred, - perm, flags); + perm, flags, NULL); if (IS_ERR(key)) goto alloc_failed; diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 4f0f112fe276..9db8b4a82787 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -202,7 +202,7 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info, authkey = key_alloc(&key_type_request_key_auth, desc, cred->fsuid, cred->fsgid, cred, KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH | - KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA); + KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA, NULL); if (IS_ERR(authkey)) { ret = PTR_ERR(authkey); goto error_alloc; diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index 8705d79b2c6f..66b1840b4110 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -96,45 +96,25 @@ EXPORT_SYMBOL_GPL(user_free_preparse); */ int user_update(struct key *key, struct key_preparsed_payload *prep) { - struct user_key_payload *upayload, *zap; - size_t datalen = prep->datalen; + struct user_key_payload *zap = NULL; int ret; - ret = -EINVAL; - if (datalen <= 0 || datalen > 32767 || !prep->data) - goto error; - - /* construct a replacement payload */ - ret = -ENOMEM; - upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL); - if (!upayload) - goto error; - - upayload->datalen = datalen; - memcpy(upayload->data, prep->data, datalen); - /* check the quota and attach the new data */ - zap = upayload; - - ret = key_payload_reserve(key, datalen); - - if (ret == 0) { - /* attach the new data, displacing the old */ - if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags)) - zap = key->payload.data[0]; - else - zap = NULL; - rcu_assign_keypointer(key, upayload); - key->expiry = 0; - } + ret = key_payload_reserve(key, prep->datalen); + if (ret < 0) + return ret; + + /* attach the new data, displacing the old */ + key->expiry = prep->expiry; + if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags)) + zap = rcu_dereference_key(key); + rcu_assign_keypointer(key, prep->payload.data[0]); + prep->payload.data[0] = NULL; if (zap) kfree_rcu(zap, rcu); - -error: return ret; } - EXPORT_SYMBOL_GPL(user_update); /* |