From 5ac7eace2d00eab5ae0e9fdee63e38aee6001f7c Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:14:24 +0100 Subject: KEYS: Add a facility to restrict new links into a keyring Add a facility whereby proposed new links to be added to a keyring can be vetted, permitting them to be rejected if necessary. This can be used to block public keys from which the signature cannot be verified or for which the signature verification fails. It could also be used to provide blacklisting. This affects operations like add_key(), KEYCTL_LINK and KEYCTL_INSTANTIATE. To this end: (1) A function pointer is added to the key struct that, if set, points to the vetting function. This is called as: int (*restrict_link)(struct key *keyring, const struct key_type *key_type, unsigned long key_flags, const union key_payload *key_payload), where 'keyring' will be the keyring being added to, key_type and key_payload will describe the key being added and key_flags[*] can be AND'ed with KEY_FLAG_TRUSTED. [*] This parameter will be removed in a later patch when KEY_FLAG_TRUSTED is removed. The function should return 0 to allow the link to take place or an error (typically -ENOKEY, -ENOPKG or -EKEYREJECTED) to reject the link. The pointer should not be set directly, but rather should be set through keyring_alloc(). Note that if called during add_key(), preparse is called before this method, but a key isn't actually allocated until after this function is called. (2) KEY_ALLOC_BYPASS_RESTRICTION is added. This can be passed to key_create_or_update() or key_instantiate_and_link() to bypass the restriction check. (3) KEY_FLAG_TRUSTED_ONLY is removed. The entire contents of a keyring with this restriction emplaced can be considered 'trustworthy' by virtue of being in the keyring when that keyring is consulted. (4) key_alloc() and keyring_alloc() take an extra argument that will be used to set restrict_link in the new key. This ensures that the pointer is set before the key is published, thus preventing a window of unrestrictedness. Normally this argument will be NULL. (5) As a temporary affair, keyring_restrict_trusted_only() is added. It should be passed to keyring_alloc() as the extra argument instead of setting KEY_FLAG_TRUSTED_ONLY on a keyring. This will be replaced in a later patch with functions that look in the appropriate places for authoritative keys. Signed-off-by: David Howells Reviewed-by: Mimi Zohar --- security/keys/key.c | 43 +++++++++++++++++++---- security/keys/keyring.c | 73 ++++++++++++++++++++++++++++++++++++---- security/keys/persistent.c | 4 +-- security/keys/process_keys.c | 16 +++++---- security/keys/request_key.c | 4 +-- security/keys/request_key_auth.c | 2 +- 6 files changed, 117 insertions(+), 25 deletions(-) (limited to 'security/keys') diff --git a/security/keys/key.c b/security/keys/key.c index b28755131687..deb881754e03 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,11 @@ 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 *, + unsigned long, + const union key_payload *)) { struct key_user *user = NULL; struct key *key; @@ -291,6 +296,7 @@ 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; @@ -496,6 +502,12 @@ int key_instantiate_and_link(struct key *key, } if (keyring) { + if (keyring->restrict_link) { + ret = keyring->restrict_link(keyring, key->type, + key->flags, &prep.payload); + if (ret < 0) + goto error; + } ret = __key_link_begin(keyring, &key->index_key, &edit); if (ret < 0) goto error; @@ -551,8 +563,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 +809,10 @@ 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 *, + unsigned long, + const union key_payload *) = NULL; /* look up the key type to see if it's one of the registered kernel * types */ @@ -811,6 +831,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; @@ -835,10 +859,15 @@ 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) { + unsigned long kflags = prep.trusted ? KEY_FLAG_TRUSTED : 0; + ret = restrict_link(keyring, + index_key.type, kflags, &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 +908,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/keyring.c b/security/keys/keyring.c index f931ccfeefb0..d2d1f3378008 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -491,13 +491,18 @@ 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 *, + unsigned long, + 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 +515,51 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, } EXPORT_SYMBOL(keyring_alloc); +/** + * keyring_restrict_trusted_only - Restrict additions to a keyring to trusted keys only + * @keyring: The keyring being added to. + * @type: The type of key being added. + * @flags: The key flags. + * @payload: The payload of the key intended to be added. + * + * Reject the addition of any links to a keyring that point to keys that aren't + * marked as being trusted. 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 keyring_restrict_trusted_only(struct key *keyring, + const struct key_type *type, + unsigned long flags, + const union key_payload *payload) +{ + return flags & KEY_FLAG_TRUSTED ? 0 : -EPERM; +} + +/** + * restrict_link_reject - Give -EPERM to restrict link + * @keyring: The keyring being added to. + * @type: The type of key being added. + * @flags: The key flags. + * @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, + unsigned long flags, + const union key_payload *payload) +{ + return -EPERM; +} + /* * By default, we keys found by getting an exact match on their descriptions. */ @@ -1191,6 +1241,17 @@ 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->flags, &key->payload); +} + /** * key_link - Link a key to a keyring * @keyring: The keyring to make the link in. @@ -1221,14 +1282,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; -- cgit v1.3-8-gc7d7 From 77f68bac9481ad440f4f34dda3d28c2dce6eb87b Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 Apr 2016 16:14:26 +0100 Subject: KEYS: Remove KEY_FLAG_TRUSTED and KEY_ALLOC_TRUSTED Remove KEY_FLAG_TRUSTED and KEY_ALLOC_TRUSTED as they're no longer meaningful. Also we can drop the trusted flag from the preparse structure. Given this, we no longer need to pass the key flags through to restrict_link(). Further, we can now get rid of keyring_restrict_trusted_only() also. Signed-off-by: David Howells --- certs/system_keyring.c | 2 -- include/keys/system_keyring.h | 1 - include/linux/key-type.h | 1 - include/linux/key.h | 21 +++++---------------- security/integrity/digsig.c | 3 +-- security/keys/key.c | 11 ++--------- security/keys/keyring.c | 29 +---------------------------- 7 files changed, 9 insertions(+), 59 deletions(-) (limited to 'security/keys') diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 4e2fa8ab01d6..e460d00a7781 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -31,7 +31,6 @@ extern __initconst const unsigned long system_certificate_list_size; */ int restrict_link_by_builtin_trusted(struct key *keyring, const struct key_type *type, - unsigned long flags, const union key_payload *payload) { return restrict_link_by_signature(system_trusted_keyring, @@ -97,7 +96,6 @@ 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_BYPASS_RESTRICTION); if (IS_ERR(key)) { diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h index 93715913a0b1..c72330ae76df 100644 --- a/include/keys/system_keyring.h +++ b/include/keys/system_keyring.h @@ -18,7 +18,6 @@ extern int restrict_link_by_builtin_trusted(struct key *keyring, const struct key_type *type, - unsigned long flags, const union key_payload *payload); #else 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 83b603639d2e..722914798f37 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -173,10 +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_BUILTIN 9 /* set if key is built in to the kernel */ -#define KEY_FLAG_ROOT_CAN_INVAL 10 /* set if key can be invalidated by root without permission */ -#define KEY_FLAG_KEEP 11 /* 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 @@ -217,7 +216,6 @@ struct key { */ int (*restrict_link)(struct key *keyring, const struct key_type *type, - unsigned long flags, const union key_payload *payload); }; @@ -229,16 +227,14 @@ extern struct key *key_alloc(struct key_type *type, unsigned long flags, int (*restrict_link)(struct key *, const struct key_type *, - unsigned long, 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_BYPASS_RESTRICTION 0x0010 /* Override the check on restricted keyrings */ +#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); @@ -309,18 +305,11 @@ extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid unsigned long flags, int (*restrict_link)(struct key *, const struct key_type *, - unsigned long, const union key_payload *), struct key *dest); -extern int keyring_restrict_trusted_only(struct key *keyring, - const struct key_type *type, - unsigned long, - const union key_payload *payload); - extern int restrict_link_reject(struct key *keyring, const struct key_type *type, - unsigned long flags, const union key_payload *payload); extern int keyring_clear(struct key *keyring); diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index d647178c6bbd..98ee4c752cf5 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -51,12 +51,11 @@ static bool init_keyring __initdata; */ static int restrict_link_by_ima_mok(struct key *keyring, const struct key_type *type, - unsigned long flags, const union key_payload *payload) { int ret; - ret = restrict_link_by_builtin_trusted(keyring, type, flags, payload); + ret = restrict_link_by_builtin_trusted(keyring, type, payload); if (ret != -ENOKEY) return ret; diff --git a/security/keys/key.c b/security/keys/key.c index deb881754e03..bd5a272f28a6 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -227,7 +227,6 @@ struct key *key_alloc(struct key_type *type, const char *desc, key_perm_t perm, unsigned long flags, int (*restrict_link)(struct key *, const struct key_type *, - unsigned long, const union key_payload *)) { struct key_user *user = NULL; @@ -300,8 +299,6 @@ struct key *key_alloc(struct key_type *type, const char *desc, 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; @@ -504,7 +501,7 @@ int key_instantiate_and_link(struct key *key, if (keyring) { if (keyring->restrict_link) { ret = keyring->restrict_link(keyring, key->type, - key->flags, &prep.payload); + &prep.payload); if (ret < 0) goto error; } @@ -811,7 +808,6 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, int ret; int (*restrict_link)(struct key *, const struct key_type *, - unsigned long, const union key_payload *) = NULL; /* look up the key type to see if it's one of the registered kernel @@ -843,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); @@ -860,9 +855,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, index_key.desc_len = strlen(index_key.description); if (restrict_link) { - unsigned long kflags = prep.trusted ? KEY_FLAG_TRUSTED : 0; - ret = restrict_link(keyring, - index_key.type, kflags, &prep.payload); + ret = restrict_link(keyring, index_key.type, &prep.payload); if (ret < 0) { key_ref = ERR_PTR(ret); goto error_free_prep; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index d2d1f3378008..c91e4e0cea08 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -494,7 +494,6 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, unsigned long flags, int (*restrict_link)(struct key *, const struct key_type *, - unsigned long, const union key_payload *), struct key *dest) { @@ -515,34 +514,10 @@ struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, } EXPORT_SYMBOL(keyring_alloc); -/** - * keyring_restrict_trusted_only - Restrict additions to a keyring to trusted keys only - * @keyring: The keyring being added to. - * @type: The type of key being added. - * @flags: The key flags. - * @payload: The payload of the key intended to be added. - * - * Reject the addition of any links to a keyring that point to keys that aren't - * marked as being trusted. 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 keyring_restrict_trusted_only(struct key *keyring, - const struct key_type *type, - unsigned long flags, - const union key_payload *payload) -{ - return flags & KEY_FLAG_TRUSTED ? 0 : -EPERM; -} - /** * restrict_link_reject - Give -EPERM to restrict link * @keyring: The keyring being added to. * @type: The type of key being added. - * @flags: The key flags. * @payload: The payload of the key intended to be added. * * Reject the addition of any links to a keyring. It can be overridden by @@ -554,7 +529,6 @@ int keyring_restrict_trusted_only(struct key *keyring, */ int restrict_link_reject(struct key *keyring, const struct key_type *type, - unsigned long flags, const union key_payload *payload) { return -EPERM; @@ -1248,8 +1222,7 @@ static int __key_link_check_restriction(struct key *keyring, struct key *key) { if (!keyring->restrict_link) return 0; - return keyring->restrict_link(keyring, - key->type, key->flags, &key->payload); + return keyring->restrict_link(keyring, key->type, &key->payload); } /** -- cgit v1.3-8-gc7d7 From 898de7d0f298e53568891f0ec3547b14fe8bb5d5 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 12 Apr 2016 19:54:58 +0100 Subject: KEYS: user_update should use copy of payload made during preparsing The payload preparsing routine for user keys makes a copy of the payload provided by the caller and stashes it in the key_preparsed_payload struct for ->instantiate() or ->update() to use. However, ->update() takes another copy of this to attach to the keyring. ->update() should be using this directly and clearing the pointer in the preparse data. Signed-off-by: David Howells --- security/keys/user_defined.c | 42 +++++++++++------------------------------- 1 file changed, 11 insertions(+), 31 deletions(-) (limited to 'security/keys') 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); /* -- cgit v1.3-8-gc7d7 From 13100a72f40f5748a04017e0ab3df4cf27c809ef Mon Sep 17 00:00:00 2001 From: Kirill Marinushkin Date: Tue, 12 Apr 2016 19:54:58 +0100 Subject: Security: Keys: Big keys stored encrypted Solved TODO task: big keys saved to shmem file are now stored encrypted. The encryption key is randomly generated and saved to payload[big_key_data]. Signed-off-by: Kirill Marinushkin Signed-off-by: David Howells --- security/keys/Kconfig | 4 + security/keys/big_key.c | 198 +++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 184 insertions(+), 18 deletions(-) (limited to 'security/keys') diff --git a/security/keys/Kconfig b/security/keys/Kconfig index fe4d74e126a7..45828095080d 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 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 #include #include +#include #include #include +#include /* * Layout of key payload words. @@ -27,6 +29,14 @@ enum { big_key_len, }; +/* + * 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 @@ -34,6 +44,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 @@ -49,6 +64,54 @@ struct key_type key_type_big_key = { .read = big_key_read, }; +/* + * 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 */ @@ -56,6 +119,8 @@ 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); -- cgit v1.3-8-gc7d7 From ddbb41148724367394d0880c516bfaeed127b52e Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 12 Apr 2016 19:54:58 +0100 Subject: KEYS: Add KEYCTL_DH_COMPUTE command This adds userspace access to Diffie-Hellman computations through a new keyctl() syscall command to calculate shared secrets or public keys using input parameters stored in the keyring. Input key ids are provided in a struct due to the current 5-arg limit for the keyctl syscall. Only user keys are supported in order to avoid exposing the content of logon or encrypted keys. The output is written to the provided buffer, based on the assumption that the values are only needed in userspace. Future support for other types of key derivation would involve a new command, like KEYCTL_ECDH_COMPUTE. Once Diffie-Hellman support is included in the crypto API, this code can be converted to use the crypto API to take advantage of possible hardware acceleration and reduce redundant code. Signed-off-by: Mat Martineau Signed-off-by: David Howells --- Documentation/security/keys.txt | 30 ++++++++ include/uapi/linux/keyctl.h | 10 +++ security/keys/Kconfig | 11 +++ security/keys/Makefile | 1 + security/keys/compat.c | 4 + security/keys/dh.c | 160 ++++++++++++++++++++++++++++++++++++++++ security/keys/internal.h | 12 +++ security/keys/keyctl.c | 5 ++ 8 files changed, 233 insertions(+) create mode 100644 security/keys/dh.c (limited to 'security/keys') diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index 8c183873b2b7..a2f70cf6763a 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 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 + /* 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/security/keys/Kconfig b/security/keys/Kconfig index 45828095080d..f826e8739023 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -85,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/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 +#include +#include +#include +#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 #include #include +#include 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/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; } -- cgit v1.3-8-gc7d7 From 4693fc734d675c5518ea9bd4c9623db45bc37402 Mon Sep 17 00:00:00 2001 From: Stephan Mueller Date: Thu, 26 May 2016 23:38:12 +0200 Subject: KEYS: Add placeholder for KDF usage with DH The values computed during Diffie-Hellman key exchange are often used in combination with key derivation functions to create cryptographic keys. Add a placeholder for a later implementation to configure a key derivation function that will transform the Diffie-Hellman result returned by the KEYCTL_DH_COMPUTE command. [This patch was stripped down from a patch produced by Mat Martineau that had a bug in the compat code - so for the moment Stephan's patch simply requires that the placeholder argument must be NULL] Original-signed-off-by: Mat Martineau Signed-off-by: Stephan Mueller Signed-off-by: David Howells Signed-off-by: James Morris --- Documentation/security/keys.txt | 5 ++++- security/keys/compat.c | 2 +- security/keys/dh.c | 8 +++++++- security/keys/internal.h | 5 +++-- security/keys/keyctl.c | 4 ++-- 5 files changed, 17 insertions(+), 7 deletions(-) (limited to 'security/keys') diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index 20d05719bceb..3849814bfe6d 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -826,7 +826,8 @@ The keyctl syscall functions are: (*) Compute a Diffie-Hellman shared secret or public key long keyctl(KEYCTL_DH_COMPUTE, struct keyctl_dh_params *params, - char *buffer, size_t buflen); + char *buffer, size_t buflen, + void *reserved); The params struct contains serial numbers for three keys: @@ -843,6 +844,8 @@ The keyctl syscall functions are: public key. If the base is the remote public key, the result is the shared secret. + The reserved argument must be set to NULL. + 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 diff --git a/security/keys/compat.c b/security/keys/compat.c index c8783b3b628c..36c80bf5b89c 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -134,7 +134,7 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option, case KEYCTL_DH_COMPUTE: return keyctl_dh_compute(compat_ptr(arg2), compat_ptr(arg3), - arg4); + arg4, compat_ptr(arg5)); default: return -EOPNOTSUPP; diff --git a/security/keys/dh.c b/security/keys/dh.c index 880505a4b9f1..531ed2ec132f 100644 --- a/security/keys/dh.c +++ b/security/keys/dh.c @@ -78,7 +78,8 @@ error: } long keyctl_dh_compute(struct keyctl_dh_params __user *params, - char __user *buffer, size_t buflen) + char __user *buffer, size_t buflen, + void __user *reserved) { long ret; MPI base, private, prime, result; @@ -97,6 +98,11 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params, goto out; } + if (reserved) { + ret = -EINVAL; + 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, diff --git a/security/keys/internal.h b/security/keys/internal.h index 8ec7a528365d..a705a7d92ad7 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -260,10 +260,11 @@ static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring) #ifdef CONFIG_KEY_DH_OPERATIONS extern long keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *, - size_t); + size_t, void __user *); #else static inline long keyctl_dh_compute(struct keyctl_dh_params __user *params, - char __user *buffer, size_t buflen) + char __user *buffer, size_t buflen, + void __user *reserved) { return -EOPNOTSUPP; } diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 3b135a0af344..d580ad06b792 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1688,8 +1688,8 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, case KEYCTL_DH_COMPUTE: return keyctl_dh_compute((struct keyctl_dh_params __user *) arg2, - (char __user *) arg3, - (size_t) arg4); + (char __user *) arg3, (size_t) arg4, + (void __user *) arg5); default: return -EOPNOTSUPP; -- cgit v1.3-8-gc7d7 From 38327424b40bcebe2de92d07312c89360ac9229a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 16 Jun 2016 15:48:57 +0100 Subject: KEYS: potential uninitialized variable If __key_link_begin() failed then "edit" would be uninitialized. I've added a check to fix that. This allows a random user to crash the kernel, though it's quite difficult to achieve. There are three ways it can be done as the user would have to cause an error to occur in __key_link(): (1) Cause the kernel to run out of memory. In practice, this is difficult to achieve without ENOMEM cropping up elsewhere and aborting the attempt. (2) Revoke the destination keyring between the keyring ID being looked up and it being tested for revocation. In practice, this is difficult to time correctly because the KEYCTL_REJECT function can only be used from the request-key upcall process. Further, users can only make use of what's in /sbin/request-key.conf, though this does including a rejection debugging test - which means that the destination keyring has to be the caller's session keyring in practice. (3) Have just enough key quota available to create a key, a new session keyring for the upcall and a link in the session keyring, but not then sufficient quota to create a link in the nominated destination keyring so that it fails with EDQUOT. The bug can be triggered using option (3) above using something like the following: echo 80 >/proc/sys/kernel/keys/root_maxbytes keyctl request2 user debug:fred negate @t The above sets the quota to something much lower (80) to make the bug easier to trigger, but this is dependent on the system. Note also that the name of the keyring created contains a random number that may be between 1 and 10 characters in size, so may throw the test off by changing the amount of quota used. Assuming the failure occurs, something like the following will be seen: kfree_debugcheck: out of range ptr 6b6b6b6b6b6b6b68h ------------[ cut here ]------------ kernel BUG at ../mm/slab.c:2821! ... RIP: 0010:[] kfree_debugcheck+0x20/0x25 RSP: 0018:ffff8804014a7de8 EFLAGS: 00010092 RAX: 0000000000000034 RBX: 6b6b6b6b6b6b6b68 RCX: 0000000000000000 RDX: 0000000000040001 RSI: 00000000000000f6 RDI: 0000000000000300 RBP: ffff8804014a7df0 R08: 0000000000000001 R09: 0000000000000000 R10: ffff8804014a7e68 R11: 0000000000000054 R12: 0000000000000202 R13: ffffffff81318a66 R14: 0000000000000000 R15: 0000000000000001 ... Call Trace: kfree+0xde/0x1bc assoc_array_cancel_edit+0x1f/0x36 __key_link_end+0x55/0x63 key_reject_and_link+0x124/0x155 keyctl_reject_key+0xb6/0xe0 keyctl_negate_key+0x10/0x12 SyS_keyctl+0x9f/0xe7 do_syscall_64+0x63/0x13a entry_SYSCALL64_slow_path+0x25/0x25 Fixes: f70e2e06196a ('KEYS: Do preallocation for __key_link()') Signed-off-by: Dan Carpenter Signed-off-by: David Howells cc: stable@vger.kernel.org Signed-off-by: Linus Torvalds --- security/keys/key.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security/keys') diff --git a/security/keys/key.c b/security/keys/key.c index bd5a272f28a6..346fbf201c22 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -597,7 +597,7 @@ int key_reject_and_link(struct key *key, mutex_unlock(&key_construction_mutex); - if (keyring) + if (keyring && link_ret == 0) __key_link_end(keyring, &key->index_key, edit); /* wake up anyone waiting for a key to be constructed */ -- cgit v1.3-8-gc7d7