From 469ff8f7d46d75b36de68a0411a2ce80109ad00b Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Mon, 25 Apr 2016 11:30:39 -0700 Subject: KEYS: Use a typedef for restrict_link function pointers This pointer type needs to be returned from a lookup function, and without a typedef the syntax gets cumbersome. Signed-off-by: Mat Martineau --- Documentation/security/keys.txt | 5 +---- include/linux/key.h | 16 +++++++--------- security/keys/key.c | 8 ++------ security/keys/keyring.c | 4 +--- 4 files changed, 11 insertions(+), 22 deletions(-) diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index 0e03baf271bd..4502237b12a7 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -1032,10 +1032,7 @@ payload contents" for more information. struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, const struct cred *cred, key_perm_t perm, - int (*restrict_link)(struct key *, - const struct key_type *, - unsigned long, - const union key_payload *), + key_restrict_link_func_t restrict_link, unsigned long flags, struct key *dest); diff --git a/include/linux/key.h b/include/linux/key.h index 9d9fac583dd3..3bb327043869 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -127,6 +127,10 @@ static inline bool is_key_possessed(const key_ref_t key_ref) return (unsigned long) key_ref & 1UL; } +typedef int (*key_restrict_link_func_t)(struct key *keyring, + const struct key_type *type, + const union key_payload *payload); + /*****************************************************************************/ /* * authentication token / access credential / keyring @@ -215,9 +219,7 @@ struct key { * overrides this, allowing the kernel to add extra keys without * restriction. */ - int (*restrict_link)(struct key *keyring, - const struct key_type *type, - const union key_payload *payload); + key_restrict_link_func_t restrict_link; }; extern struct key *key_alloc(struct key_type *type, @@ -226,9 +228,7 @@ extern struct key *key_alloc(struct key_type *type, const struct cred *cred, key_perm_t perm, unsigned long flags, - int (*restrict_link)(struct key *, - const struct key_type *, - const union key_payload *)); + key_restrict_link_func_t restrict_link); #define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */ @@ -304,9 +304,7 @@ extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid const struct cred *cred, key_perm_t perm, unsigned long flags, - int (*restrict_link)(struct key *, - const struct key_type *, - const union key_payload *), + key_restrict_link_func_t restrict_link, struct key *dest); extern int restrict_link_reject(struct key *keyring, diff --git a/security/keys/key.c b/security/keys/key.c index b4958b36fa27..08dfa13f6a85 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -225,9 +225,7 @@ serial_exists: struct key *key_alloc(struct key_type *type, const char *desc, kuid_t uid, kgid_t gid, const struct cred *cred, key_perm_t perm, unsigned long flags, - int (*restrict_link)(struct key *, - const struct key_type *, - const union key_payload *)) + key_restrict_link_func_t restrict_link) { struct key_user *user = NULL; struct key *key; @@ -806,9 +804,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, struct key *keyring, *key = NULL; key_ref_t key_ref; int ret; - int (*restrict_link)(struct key *, - const struct key_type *, - const union key_payload *) = NULL; + key_restrict_link_func_t restrict_link = NULL; /* look up the key type to see if it's one of the registered kernel * types */ diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 3d95f7d02ba1..1b29ac759bf7 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -492,9 +492,7 @@ static long keyring_read(const struct key *keyring, struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, const struct cred *cred, key_perm_t perm, unsigned long flags, - int (*restrict_link)(struct key *, - const struct key_type *, - const union key_payload *), + key_restrict_link_func_t restrict_link, struct key *dest) { struct key *keyring; -- cgit v1.2.3-59-g8ed1b From aaf66c883813f0078e3dafe7d20d1461321ac14f Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 30 Aug 2016 11:33:13 -0700 Subject: KEYS: Split role of the keyring pointer for keyring restrict functions The first argument to the restrict_link_func_t functions was a keyring pointer. These functions are called by the key subsystem with this argument set to the destination keyring, but restrict_link_by_signature expects a pointer to the relevant trusted keyring. Restrict functions may need something other than a single struct key pointer to allow or reject key linkage, so the data used to make that decision (such as the trust keyring) is moved to a new, fourth argument. The first argument is now always the destination keyring. Signed-off-by: Mat Martineau --- Documentation/security/keys.txt | 8 ++++---- certs/system_keyring.c | 18 +++++++++++------- crypto/asymmetric_keys/restrict.c | 8 +++++--- include/crypto/public_key.h | 5 +++-- include/keys/system_keyring.h | 6 ++++-- include/linux/key.h | 8 +++++--- security/keys/key.c | 5 +++-- security/keys/keyring.c | 6 ++++-- 8 files changed, 39 insertions(+), 25 deletions(-) diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index 4502237b12a7..bb575ab80207 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -1054,10 +1054,10 @@ payload contents" for more information. can be verified by a key the kernel already has. When called, the restriction function will be passed the keyring being - added to, the key flags value and the type and payload of the key being - added. Note that when a new key is being created, this is called between - payload preparsing and actual key creation. The function should return 0 - to allow the link or an error to reject it. + added to, the key type, the payload of the key being added, and data to be + used in the restriction check. Note that when a new key is being created, + this is called between payload preparsing and actual key creation. The + function should return 0 to allow the link or an error to reject it. A convenience function, restrict_link_reject, exists to always return -EPERM to in this case. diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 50979d6dcecd..e39cce68dcfa 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -32,11 +32,13 @@ extern __initconst const unsigned long system_certificate_list_size; * Restrict the addition of keys into a keyring based on the key-to-be-added * being vouched for by a key in the built in system keyring. */ -int restrict_link_by_builtin_trusted(struct key *keyring, +int restrict_link_by_builtin_trusted(struct key *dest_keyring, const struct key_type *type, - const union key_payload *payload) + const union key_payload *payload, + struct key *restriction_key) { - return restrict_link_by_signature(builtin_trusted_keys, type, payload); + return restrict_link_by_signature(dest_keyring, type, payload, + builtin_trusted_keys); } #ifdef CONFIG_SECONDARY_TRUSTED_KEYRING @@ -49,20 +51,22 @@ int restrict_link_by_builtin_trusted(struct key *keyring, * keyrings. */ int restrict_link_by_builtin_and_secondary_trusted( - struct key *keyring, + struct key *dest_keyring, const struct key_type *type, - const union key_payload *payload) + const union key_payload *payload, + struct key *restrict_key) { /* If we have a secondary trusted keyring, then that contains a link * through to the builtin keyring and the search will follow that link. */ if (type == &key_type_keyring && - keyring == secondary_trusted_keys && + dest_keyring == secondary_trusted_keys && payload == &builtin_trusted_keys->payload) /* Allow the builtin keyring to be added to the secondary */ return 0; - return restrict_link_by_signature(secondary_trusted_keys, type, payload); + return restrict_link_by_signature(dest_keyring, type, payload, + secondary_trusted_keys); } #endif diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c index 19d1afb9890f..a3afbf783255 100644 --- a/crypto/asymmetric_keys/restrict.c +++ b/crypto/asymmetric_keys/restrict.c @@ -56,9 +56,10 @@ __setup("ca_keys=", ca_keys_setup); /** * restrict_link_by_signature - Restrict additions to a ring of public keys - * @trust_keyring: A ring of keys that can be used to vouch for the new cert. + * @dest_keyring: Keyring being linked to. * @type: The type of key being added. * @payload: The payload of the new key. + * @trust_keyring: A ring of keys that can be used to vouch for the new cert. * * Check the new certificate against the ones in the trust keyring. If one of * those is the signing key and validates the new certificate, then mark the @@ -69,9 +70,10 @@ __setup("ca_keys=", ca_keys_setup); * signature check fails or the key is blacklisted and some other error if * there is a matching certificate but the signature check cannot be performed. */ -int restrict_link_by_signature(struct key *trust_keyring, +int restrict_link_by_signature(struct key *dest_keyring, const struct key_type *type, - const union key_payload *payload) + const union key_payload *payload, + struct key *trust_keyring) { const struct public_key_signature *sig; struct key *key; diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 882ca0e1e7a5..ec0262fa08f8 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -50,9 +50,10 @@ struct key; struct key_type; union key_payload; -extern int restrict_link_by_signature(struct key *trust_keyring, +extern int restrict_link_by_signature(struct key *dest_keyring, const struct key_type *type, - const union key_payload *payload); + const union key_payload *payload, + struct key *trust_keyring); extern int verify_signature(const struct key *key, const struct public_key_signature *sig); diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h index 0d8762622ab9..359c2f936004 100644 --- a/include/keys/system_keyring.h +++ b/include/keys/system_keyring.h @@ -18,7 +18,8 @@ extern int restrict_link_by_builtin_trusted(struct key *keyring, const struct key_type *type, - const union key_payload *payload); + const union key_payload *payload, + struct key *restriction_key); #else #define restrict_link_by_builtin_trusted restrict_link_reject @@ -28,7 +29,8 @@ extern int restrict_link_by_builtin_trusted(struct key *keyring, extern int restrict_link_by_builtin_and_secondary_trusted( struct key *keyring, const struct key_type *type, - const union key_payload *payload); + const union key_payload *payload, + struct key *restriction_key); #else #define restrict_link_by_builtin_and_secondary_trusted restrict_link_by_builtin_trusted #endif diff --git a/include/linux/key.h b/include/linux/key.h index 3bb327043869..c59d1008c4fc 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -127,9 +127,10 @@ static inline bool is_key_possessed(const key_ref_t key_ref) return (unsigned long) key_ref & 1UL; } -typedef int (*key_restrict_link_func_t)(struct key *keyring, +typedef int (*key_restrict_link_func_t)(struct key *dest_keyring, const struct key_type *type, - const union key_payload *payload); + const union key_payload *payload, + struct key *restriction_key); /*****************************************************************************/ /* @@ -309,7 +310,8 @@ extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid extern int restrict_link_reject(struct key *keyring, const struct key_type *type, - const union key_payload *payload); + const union key_payload *payload, + struct key *restriction_key); extern int keyring_clear(struct key *keyring); diff --git a/security/keys/key.c b/security/keys/key.c index 08dfa13f6a85..27fc1bb40034 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -499,7 +499,7 @@ int key_instantiate_and_link(struct key *key, if (keyring) { if (keyring->restrict_link) { ret = keyring->restrict_link(keyring, key->type, - &prep.payload); + &prep.payload, NULL); if (ret < 0) goto error; } @@ -851,7 +851,8 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, index_key.desc_len = strlen(index_key.description); if (restrict_link) { - ret = restrict_link(keyring, index_key.type, &prep.payload); + ret = restrict_link(keyring, index_key.type, &prep.payload, + NULL); if (ret < 0) { key_ref = ERR_PTR(ret); goto error_free_prep; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 1b29ac759bf7..2ccc66ec35d7 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -517,6 +517,7 @@ EXPORT_SYMBOL(keyring_alloc); * @keyring: The keyring being added to. * @type: The type of key being added. * @payload: The payload of the key intended to be added. + * @data: Additional data for evaluating restriction. * * Reject the addition of any links to a keyring. It can be overridden by * passing KEY_ALLOC_BYPASS_RESTRICTION to key_instantiate_and_link() when @@ -527,7 +528,8 @@ EXPORT_SYMBOL(keyring_alloc); */ int restrict_link_reject(struct key *keyring, const struct key_type *type, - const union key_payload *payload) + const union key_payload *payload, + struct key *restriction_key) { return -EPERM; } @@ -1220,7 +1222,7 @@ static int __key_link_check_restriction(struct key *keyring, struct key *key) { if (!keyring->restrict_link) return 0; - return keyring->restrict_link(keyring, key->type, &key->payload); + return keyring->restrict_link(keyring, key->type, &key->payload, NULL); } /** -- cgit v1.2.3-59-g8ed1b From e9cc0f689a7c0c9be6fed6861b3a3f49ad0e7a52 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Mon, 27 Jun 2016 16:10:59 -0700 Subject: KEYS: Add a key restriction struct Key link restrictions require restriction-specific data as well as a restriction-specific function pointer. As a first step toward replacing the restrict_link pointer in struct key, define a more general key_restriction structure that captures the required function, key, and key type pointers. Key type modules should not be pinned on account of this key type pointer because the pointer will be cleared by the garbage collector if the key type is unregistered. Signed-off-by: Mat Martineau --- include/linux/key.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/linux/key.h b/include/linux/key.h index c59d1008c4fc..a06649f3223d 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -132,6 +132,12 @@ typedef int (*key_restrict_link_func_t)(struct key *dest_keyring, const union key_payload *payload, struct key *restriction_key); +struct key_restriction { + key_restrict_link_func_t check; + struct key *key; + struct key_type *keytype; +}; + /*****************************************************************************/ /* * authentication token / access credential / keyring -- cgit v1.2.3-59-g8ed1b From 2b6aa412ff23a02ac777ad307249c60a839cfd25 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Wed, 31 Aug 2016 16:05:43 -0700 Subject: KEYS: Use structure to capture key restriction function and data Replace struct key's restrict_link function pointer with a pointer to the new struct key_restriction. The structure contains pointers to the restriction function as well as relevant data for evaluating the restriction. The garbage collector checks restrict_link->keytype when key types are unregistered. Restrictions involving a removed key type are converted to use restrict_link_reject so that restrictions cannot be removed by unregistering key types. Signed-off-by: Mat Martineau --- Documentation/security/keys.txt | 21 +++++++------ certs/system_keyring.c | 21 ++++++++++++- include/linux/key.h | 8 ++--- security/integrity/digsig.c | 9 +++++- security/integrity/ima/ima_mok.c | 11 ++++++- security/keys/gc.c | 11 +++++++ security/keys/internal.h | 2 ++ security/keys/key.c | 23 ++++++++------ security/keys/keyring.c | 68 +++++++++++++++++++++++++++++++++++++--- 9 files changed, 144 insertions(+), 30 deletions(-) diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index bb575ab80207..e35de987fc48 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -1032,7 +1032,7 @@ payload contents" for more information. struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, const struct cred *cred, key_perm_t perm, - key_restrict_link_func_t restrict_link, + struct key_restriction *restrict_link, unsigned long flags, struct key *dest); @@ -1044,14 +1044,17 @@ payload contents" for more information. KEY_ALLOC_NOT_IN_QUOTA in flags if the keyring shouldn't be accounted towards the user's quota). Error ENOMEM can also be returned. - If restrict_link not NULL, it should point to a function that will be - called each time an attempt is made to link a key into the new keyring. - This function is called to check whether a key may be added into the keying - or not. Callers of key_create_or_update() within the kernel can pass - KEY_ALLOC_BYPASS_RESTRICTION to suppress the check. An example of using - this is to manage rings of cryptographic keys that are set up when the - kernel boots where userspace is also permitted to add keys - provided they - can be verified by a key the kernel already has. + If restrict_link is not NULL, it should point to a structure that contains + the function that will be called each time an attempt is made to link a + key into the new keyring. The structure may also contain a key pointer + and an associated key type. The function is called to check whether a key + may be added into the keyring or not. The key type is used by the garbage + collector to clean up function or data pointers in this structure if the + given key type is unregistered. Callers of key_create_or_update() within + the kernel can pass KEY_ALLOC_BYPASS_RESTRICTION to suppress the check. + An example of using this is to manage rings of cryptographic keys that are + set up when the kernel boots where userspace is also permitted to add keys + - provided they can be verified by a key the kernel already has. When called, the restriction function will be passed the keyring being added to, the key type, the payload of the key being added, and data to be diff --git a/certs/system_keyring.c b/certs/system_keyring.c index e39cce68dcfa..6251d1b27f0c 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -68,6 +69,24 @@ int restrict_link_by_builtin_and_secondary_trusted( return restrict_link_by_signature(dest_keyring, type, payload, secondary_trusted_keys); } + +/** + * Allocate a struct key_restriction for the "builtin and secondary trust" + * keyring. Only for use in system_trusted_keyring_init(). + */ +static __init struct key_restriction *get_builtin_and_secondary_restriction(void) +{ + struct key_restriction *restriction; + + restriction = kzalloc(sizeof(struct key_restriction), GFP_KERNEL); + + if (!restriction) + panic("Can't allocate secondary trusted keyring restriction\n"); + + restriction->check = restrict_link_by_builtin_and_secondary_trusted; + + return restriction; +} #endif /* @@ -95,7 +114,7 @@ static __init int system_trusted_keyring_init(void) KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH | KEY_USR_WRITE), KEY_ALLOC_NOT_IN_QUOTA, - restrict_link_by_builtin_and_secondary_trusted, + get_builtin_and_secondary_restriction(), NULL); if (IS_ERR(secondary_trusted_keys)) panic("Can't allocate secondary trusted keyring\n"); diff --git a/include/linux/key.h b/include/linux/key.h index a06649f3223d..d2916363689c 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -217,7 +217,7 @@ struct key { }; /* This is set on a keyring to restrict the addition of a link to a key - * to it. If this method isn't provided then it is assumed that the + * to it. If this structure isn't provided then it is assumed that the * keyring is open to any addition. It is ignored for non-keyring * keys. * @@ -226,7 +226,7 @@ struct key { * overrides this, allowing the kernel to add extra keys without * restriction. */ - key_restrict_link_func_t restrict_link; + struct key_restriction *restrict_link; }; extern struct key *key_alloc(struct key_type *type, @@ -235,7 +235,7 @@ extern struct key *key_alloc(struct key_type *type, const struct cred *cred, key_perm_t perm, unsigned long flags, - key_restrict_link_func_t restrict_link); + struct key_restriction *restrict_link); #define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */ @@ -311,7 +311,7 @@ extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid const struct cred *cred, key_perm_t perm, unsigned long flags, - key_restrict_link_func_t restrict_link, + struct key_restriction *restrict_link, struct key *dest); extern int restrict_link_reject(struct key *keyring, diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index 106e855e2d9d..06554c448dce 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -81,18 +81,25 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, int __init integrity_init_keyring(const unsigned int id) { const struct cred *cred = current_cred(); + struct key_restriction *restriction; int err = 0; if (!init_keyring) return 0; + restriction = kzalloc(sizeof(struct key_restriction), GFP_KERNEL); + if (!restriction) + return -ENOMEM; + + restriction->check = restrict_link_to_ima; + keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0), KGIDT_INIT(0), cred, ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE | KEY_USR_SEARCH), KEY_ALLOC_NOT_IN_QUOTA, - restrict_link_to_ima, NULL); + restriction, NULL); if (IS_ERR(keyring[id])) { err = PTR_ERR(keyring[id]); pr_info("Can't allocate %s keyring (%d)\n", diff --git a/security/integrity/ima/ima_mok.c b/security/integrity/ima/ima_mok.c index 74a279957464..073ddc9bce5b 100644 --- a/security/integrity/ima/ima_mok.c +++ b/security/integrity/ima/ima_mok.c @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -27,15 +28,23 @@ struct key *ima_blacklist_keyring; */ __init int ima_mok_init(void) { + struct key_restriction *restriction; + pr_notice("Allocating IMA blacklist keyring.\n"); + restriction = kzalloc(sizeof(struct key_restriction), GFP_KERNEL); + if (!restriction) + panic("Can't allocate IMA blacklist restriction."); + + restriction->check = restrict_link_by_builtin_trusted; + ima_blacklist_keyring = keyring_alloc(".ima_blacklist", KUIDT_INIT(0), KGIDT_INIT(0), current_cred(), (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE | KEY_USR_SEARCH, KEY_ALLOC_NOT_IN_QUOTA, - restrict_link_by_builtin_trusted, NULL); + restriction, NULL); if (IS_ERR(ima_blacklist_keyring)) panic("Can't allocate IMA blacklist keyring."); diff --git a/security/keys/gc.c b/security/keys/gc.c index 44789256c88c..15b9ddf510e4 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -229,6 +229,9 @@ continue_scanning: set_bit(KEY_FLAG_DEAD, &key->flags); key->perm = 0; goto skip_dead_key; + } else if (key->type == &key_type_keyring && + key->restrict_link) { + goto found_restricted_keyring; } } @@ -334,6 +337,14 @@ found_unreferenced_key: gc_state |= KEY_GC_REAP_AGAIN; goto maybe_resched; + /* We found a restricted keyring and need to update the restriction if + * it is associated with the dead key type. + */ +found_restricted_keyring: + spin_unlock(&key_serial_lock); + keyring_restriction_gc(key, key_gc_dead_keytype); + goto maybe_resched; + /* We found a keyring and we need to check the payload for links to * dead or expired keys. We don't flag another reap immediately as we * have to wait for the old payload to be destroyed by RCU before we diff --git a/security/keys/internal.h b/security/keys/internal.h index 6bee06ae026d..24762ae9a198 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -168,6 +168,8 @@ extern void key_change_session_keyring(struct callback_head *twork); extern struct work_struct key_gc_work; extern unsigned key_gc_delay; extern void keyring_gc(struct key *keyring, time_t limit); +extern void keyring_restriction_gc(struct key *keyring, + struct key_type *dead_type); extern void key_schedule_gc(time_t gc_at); extern void key_schedule_gc_links(void); extern void key_gc_keytype(struct key_type *ktype); diff --git a/security/keys/key.c b/security/keys/key.c index 27fc1bb40034..2ea5967121de 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -201,12 +201,15 @@ serial_exists: * @cred: The credentials specifying UID namespace. * @perm: The permissions mask of the new key. * @flags: Flags specifying quota properties. - * @restrict_link: Optional link restriction method for new keyrings. + * @restrict_link: Optional link restriction for new keyrings. * * Allocate a key of the specified type with the attributes given. The key is * returned in an uninstantiated state and the caller needs to instantiate the * key before returning. * + * The restrict_link structure (if not NULL) will be freed when the + * keyring is destroyed, so it must be dynamically allocated. + * * The user's key count quota is updated to reflect the creation of the key and * the user's key data quota has the default for the key type reserved. The * instantiation function should amend this as necessary. If insufficient @@ -225,7 +228,7 @@ serial_exists: struct key *key_alloc(struct key_type *type, const char *desc, kuid_t uid, kgid_t gid, const struct cred *cred, key_perm_t perm, unsigned long flags, - key_restrict_link_func_t restrict_link) + struct key_restriction *restrict_link) { struct key_user *user = NULL; struct key *key; @@ -497,9 +500,11 @@ int key_instantiate_and_link(struct key *key, } if (keyring) { - if (keyring->restrict_link) { - ret = keyring->restrict_link(keyring, key->type, - &prep.payload, NULL); + if (keyring->restrict_link && keyring->restrict_link->check) { + struct key_restriction *keyres = keyring->restrict_link; + + ret = keyres->check(keyring, key->type, &prep.payload, + keyres->key); if (ret < 0) goto error; } @@ -804,7 +809,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, struct key *keyring, *key = NULL; key_ref_t key_ref; int ret; - key_restrict_link_func_t restrict_link = NULL; + struct key_restriction *restrict_link = NULL; /* look up the key type to see if it's one of the registered kernel * types */ @@ -850,9 +855,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, } index_key.desc_len = strlen(index_key.description); - if (restrict_link) { - ret = restrict_link(keyring, index_key.type, &prep.payload, - NULL); + if (restrict_link && restrict_link->check) { + ret = restrict_link->check(keyring, index_key.type, + &prep.payload, restrict_link->key); if (ret < 0) { key_ref = ERR_PTR(ret); goto error_free_prep; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 2ccc66ec35d7..838334fec6ce 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -394,6 +394,13 @@ static void keyring_destroy(struct key *keyring) write_unlock(&keyring_name_lock); } + if (keyring->restrict_link) { + struct key_restriction *keyres = keyring->restrict_link; + + key_put(keyres->key); + kfree(keyres); + } + assoc_array_destroy(&keyring->keys, &keyring_assoc_array_ops); } @@ -492,7 +499,7 @@ static long keyring_read(const struct key *keyring, struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, const struct cred *cred, key_perm_t perm, unsigned long flags, - key_restrict_link_func_t restrict_link, + struct key_restriction *restrict_link, struct key *dest) { struct key *keyring; @@ -523,8 +530,8 @@ EXPORT_SYMBOL(keyring_alloc); * passing KEY_ALLOC_BYPASS_RESTRICTION to key_instantiate_and_link() when * adding a key to a keyring. * - * This is meant to be passed as the restrict_link parameter to - * keyring_alloc(). + * This is meant to be stored in a key_restriction structure which is passed + * in the restrict_link parameter to keyring_alloc(). */ int restrict_link_reject(struct key *keyring, const struct key_type *type, @@ -1220,9 +1227,10 @@ void __key_link_end(struct key *keyring, */ static int __key_link_check_restriction(struct key *keyring, struct key *key) { - if (!keyring->restrict_link) + if (!keyring->restrict_link || !keyring->restrict_link->check) return 0; - return keyring->restrict_link(keyring, key->type, &key->payload, NULL); + return keyring->restrict_link->check(keyring, key->type, &key->payload, + keyring->restrict_link->key); } /** @@ -1426,3 +1434,53 @@ do_gc: up_write(&keyring->sem); kleave(" [gc]"); } + +/* + * Garbage collect restriction pointers from a keyring. + * + * Keyring restrictions are associated with a key type, and must be cleaned + * up if the key type is unregistered. The restriction is altered to always + * reject additional keys so a keyring cannot be opened up by unregistering + * a key type. + * + * Not called with any keyring locks held. The keyring's key struct will not + * be deallocated under us as only our caller may deallocate it. + * + * The caller is required to hold key_types_sem and dead_type->sem. This is + * fulfilled by key_gc_keytype() holding the locks on behalf of + * key_garbage_collector(), which it invokes on a workqueue. + */ +void keyring_restriction_gc(struct key *keyring, struct key_type *dead_type) +{ + struct key_restriction *keyres; + + kenter("%x{%s}", keyring->serial, keyring->description ?: ""); + + /* + * keyring->restrict_link is only assigned at key allocation time + * or with the key type locked, so the only values that could be + * concurrently assigned to keyring->restrict_link are for key + * types other than dead_type. Given this, it's ok to check + * the key type before acquiring keyring->sem. + */ + if (!dead_type || !keyring->restrict_link || + keyring->restrict_link->keytype != dead_type) { + kleave(" [no restriction gc]"); + return; + } + + /* Lock the keyring to ensure that a link is not in progress */ + down_write(&keyring->sem); + + keyres = keyring->restrict_link; + + keyres->check = restrict_link_reject; + + key_put(keyres->key); + keyres->key = NULL; + keyres->keytype = NULL; + + up_write(&keyring->sem); + + kleave(" [restriction gc]"); +} -- cgit v1.2.3-59-g8ed1b From efba797b977c99bc6e0c301299272c80fb8b287f Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Fri, 6 May 2016 15:38:17 -0700 Subject: KEYS: Add an optional lookup_restriction hook to key_type The restrict_link functions used to validate keys as they are linked to a keyring can be associated with specific key types. Each key type may be loaded (or not) at runtime, so lookup of restrict_link functions needs to be part of the key type implementation to ensure that the requested keys can be examined. Signed-off-by: Mat Martineau --- Documentation/security/keys.txt | 9 +++++++++ include/linux/key-type.h | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index e35de987fc48..5fe04a7cc03d 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -1445,6 +1445,15 @@ The structure has a number of fields, some of which are mandatory: The authorisation key. + (*) struct key_restriction *(*lookup_restriction)(const char *params); + + This optional method is used to enable userspace configuration of keyring + restrictions. The restriction parameter string (not including the key type + name) is passed in, and this method returns a pointer to a key_restriction + structure containing the relevant functions and data to evaluate each + attempted key link operation. If there is no match, -EINVAL is returned. + + ============================ REQUEST-KEY CALLBACK SERVICE ============================ diff --git a/include/linux/key-type.h b/include/linux/key-type.h index eaee981c5558..8496cf64575c 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -147,6 +147,14 @@ struct key_type { */ request_key_actor_t request_key; + /* Look up a keyring access restriction (optional) + * + * - NULL is a valid return value (meaning the requested restriction + * is known but will never block addition of a key) + * - should return -EINVAL if the restriction is unknown + */ + struct key_restriction *(*lookup_restriction)(const char *params); + /* internal fields */ struct list_head link; /* link in types list */ struct lock_class_key lock_class; /* key->sem lock class */ -- cgit v1.2.3-59-g8ed1b From 4a420896f12d2d043602f134ae18ad6be5b9d9dd Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 4 Oct 2016 16:27:32 -0700 Subject: KEYS: Consistent ordering for __key_link_begin and restrict check The keyring restrict callback was sometimes called before __key_link_begin and sometimes after, which meant that the keyring semaphores were not always held during the restrict callback. If the semaphores are consistently acquired before checking link restrictions, keyring contents cannot be changed after the restrict check is complete but before the evaluated key is linked to the keyring. Signed-off-by: Mat Martineau --- security/keys/key.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/security/keys/key.c b/security/keys/key.c index 2ea5967121de..455c04d80bbb 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -500,21 +500,23 @@ int key_instantiate_and_link(struct key *key, } if (keyring) { + ret = __key_link_begin(keyring, &key->index_key, &edit); + if (ret < 0) + goto error; + if (keyring->restrict_link && keyring->restrict_link->check) { struct key_restriction *keyres = keyring->restrict_link; ret = keyres->check(keyring, key->type, &prep.payload, keyres->key); if (ret < 0) - goto error; + goto error_link_end; } - ret = __key_link_begin(keyring, &key->index_key, &edit); - if (ret < 0) - goto error; } ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &edit); +error_link_end: if (keyring) __key_link_end(keyring, &key->index_key, edit); @@ -855,21 +857,21 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, } index_key.desc_len = strlen(index_key.description); + ret = __key_link_begin(keyring, &index_key, &edit); + if (ret < 0) { + key_ref = ERR_PTR(ret); + goto error_free_prep; + } + if (restrict_link && restrict_link->check) { ret = restrict_link->check(keyring, index_key.type, &prep.payload, restrict_link->key); if (ret < 0) { key_ref = ERR_PTR(ret); - goto error_free_prep; + goto error_link_end; } } - ret = __key_link_begin(keyring, &index_key, &edit); - if (ret < 0) { - key_ref = ERR_PTR(ret); - goto error_free_prep; - } - /* if we're going to allocate a new key, we're going to have * to modify the keyring */ ret = key_permission(keyring_ref, KEY_NEED_WRITE); -- cgit v1.2.3-59-g8ed1b From 6563c91fd645556c7801748f15bc727c77fcd311 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Wed, 1 Mar 2017 16:44:09 -0800 Subject: KEYS: Add KEYCTL_RESTRICT_KEYRING Keyrings recently gained restrict_link capabilities that allow individual keys to be validated prior to linking. This functionality was only available using internal kernel APIs. With the KEYCTL_RESTRICT_KEYRING command existing keyrings can be configured to check the content of keys before they are linked, and then allow or disallow linkage of that key to the keyring. To restrict a keyring, call: keyctl(KEYCTL_RESTRICT_KEYRING, key_serial_t keyring, const char *type, const char *restriction) where 'type' is the name of a registered key type and 'restriction' is a string describing how key linkage is to be restricted. The restriction option syntax is specific to each key type. Signed-off-by: Mat Martineau --- Documentation/security/keys.txt | 25 ++++++++++ include/linux/key.h | 6 ++- include/uapi/linux/keyctl.h | 1 + security/keys/compat.c | 4 ++ security/keys/internal.h | 3 ++ security/keys/keyctl.c | 58 ++++++++++++++++++++++ security/keys/keyring.c | 105 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 201 insertions(+), 1 deletion(-) diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index 5fe04a7cc03d..5f554aab8751 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -857,6 +857,31 @@ The keyctl syscall functions are: supported, error ENOKEY if the key could not be found, or error EACCES if the key is not readable by the caller. + (*) Restrict keyring linkage + + long keyctl(KEYCTL_RESTRICT_KEYRING, key_serial_t keyring, + const char *type, const char *restriction); + + An existing keyring can restrict linkage of additional keys by evaluating + the contents of the key according to a restriction scheme. + + "keyring" is the key ID for an existing keyring to apply a restriction + to. It may be empty or may already have keys linked. Existing linked keys + will remain in the keyring even if the new restriction would reject them. + + "type" is a registered key type. + + "restriction" is a string describing how key linkage is to be restricted. + The format varies depending on the key type, and the string is passed to + the lookup_restriction() function for the requested type. It may specify + a method and relevant data for the restriction such as signature + verification or constraints on key payload. If the requested key type is + later unregistered, no keys may be added to the keyring after the key type + is removed. + + To apply a keyring restriction the process must have Set Attribute + permission and the keyring must not be previously restricted. + =============== KERNEL SERVICES =============== diff --git a/include/linux/key.h b/include/linux/key.h index d2916363689c..0c9b93b0d1f7 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -219,7 +219,8 @@ struct key { /* This is set on a keyring to restrict the addition of a link to a key * to it. If this structure isn't provided then it is assumed that the * keyring is open to any addition. It is ignored for non-keyring - * keys. + * keys. Only set this value using keyring_restrict(), keyring_alloc(), + * or key_alloc(). * * This is intended for use with rings of trusted keys whereby addition * to the keyring needs to be controlled. KEY_ALLOC_BYPASS_RESTRICTION @@ -328,6 +329,9 @@ extern key_ref_t keyring_search(key_ref_t keyring, extern int keyring_add_key(struct key *keyring, struct key *key); +extern int keyring_restrict(key_ref_t keyring, const char *type, + const char *restriction); + extern struct key *key_lookup(key_serial_t id); static inline key_serial_t key_serial(const struct key *key) diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h index 86eddd6241f3..ff79c44e49a3 100644 --- a/include/uapi/linux/keyctl.h +++ b/include/uapi/linux/keyctl.h @@ -60,6 +60,7 @@ #define KEYCTL_INVALIDATE 21 /* invalidate a key */ #define KEYCTL_GET_PERSISTENT 22 /* get a user's persistent keyring */ #define KEYCTL_DH_COMPUTE 23 /* Compute Diffie-Hellman values */ +#define KEYCTL_RESTRICT_KEYRING 29 /* Restrict keys allowed to link to a keyring */ /* keyctl structures */ struct keyctl_dh_params { diff --git a/security/keys/compat.c b/security/keys/compat.c index 36c80bf5b89c..bb98f2b8dd7d 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -136,6 +136,10 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option, return keyctl_dh_compute(compat_ptr(arg2), compat_ptr(arg3), arg4, compat_ptr(arg5)); + case KEYCTL_RESTRICT_KEYRING: + return keyctl_restrict_keyring(arg2, compat_ptr(arg3), + compat_ptr(arg4)); + default: return -EOPNOTSUPP; } diff --git a/security/keys/internal.h b/security/keys/internal.h index 24762ae9a198..6ce016314897 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -252,6 +252,9 @@ struct iov_iter; extern long keyctl_instantiate_key_common(key_serial_t, struct iov_iter *, key_serial_t); +extern long keyctl_restrict_keyring(key_serial_t id, + const char __user *_type, + const char __user *_restriction); #ifdef CONFIG_PERSISTENT_KEYRINGS extern long keyctl_get_persistent(uid_t, key_serial_t); extern unsigned persistent_keyring_expiry; diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 52c34532c785..6ee2826a2d06 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1582,6 +1582,59 @@ error_keyring: return ret; } +/* + * Apply a restriction to a given keyring. + * + * The caller must have Setattr permission to change keyring restrictions. + * + * The requested type name may be a NULL pointer to reject all attempts + * to link to the keyring. If _type is non-NULL, _restriction can be + * NULL or a pointer to a string describing the restriction. If _type is + * NULL, _restriction must also be NULL. + * + * Returns 0 if successful. + */ +long keyctl_restrict_keyring(key_serial_t id, const char __user *_type, + const char __user *_restriction) +{ + key_ref_t key_ref; + bool link_reject = !_type; + char type[32]; + char *restriction = NULL; + long ret; + + key_ref = lookup_user_key(id, 0, KEY_NEED_SETATTR); + if (IS_ERR(key_ref)) + return PTR_ERR(key_ref); + + if (_type) { + ret = key_get_type_from_user(type, _type, sizeof(type)); + if (ret < 0) + goto error; + } + + if (_restriction) { + if (!_type) { + ret = -EINVAL; + goto error; + } + + restriction = strndup_user(_restriction, PAGE_SIZE); + if (IS_ERR(restriction)) { + ret = PTR_ERR(restriction); + goto error; + } + } + + ret = keyring_restrict(key_ref, link_reject ? NULL : type, restriction); + kfree(restriction); + +error: + key_ref_put(key_ref); + + return ret; +} + /* * The key control system call */ @@ -1693,6 +1746,11 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, (char __user *) arg3, (size_t) arg4, (void __user *) arg5); + case KEYCTL_RESTRICT_KEYRING: + return keyctl_restrict_keyring((key_serial_t) arg2, + (const char __user *) arg3, + (const char __user *) arg4); + default: return -EOPNOTSUPP; } diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 838334fec6ce..4d1678e4586f 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -947,6 +947,111 @@ key_ref_t keyring_search(key_ref_t keyring, } EXPORT_SYMBOL(keyring_search); +static struct key_restriction *keyring_restriction_alloc( + key_restrict_link_func_t check) +{ + struct key_restriction *keyres = + kzalloc(sizeof(struct key_restriction), GFP_KERNEL); + + if (!keyres) + return ERR_PTR(-ENOMEM); + + keyres->check = check; + + return keyres; +} + +/* + * Semaphore to serialise restriction setup to prevent reference count + * cycles through restriction key pointers. + */ +static DECLARE_RWSEM(keyring_serialise_restrict_sem); + +/* + * Check for restriction cycles that would prevent keyring garbage collection. + * keyring_serialise_restrict_sem must be held. + */ +static bool keyring_detect_restriction_cycle(const struct key *dest_keyring, + struct key_restriction *keyres) +{ + while (keyres && keyres->key && + keyres->key->type == &key_type_keyring) { + if (keyres->key == dest_keyring) + return true; + + keyres = keyres->key->restrict_link; + } + + return false; +} + +/** + * keyring_restrict - Look up and apply a restriction to a keyring + * + * @keyring: The keyring to be restricted + * @restriction: The restriction options to apply to the keyring + */ +int keyring_restrict(key_ref_t keyring_ref, const char *type, + const char *restriction) +{ + struct key *keyring; + struct key_type *restrict_type = NULL; + struct key_restriction *restrict_link; + int ret = 0; + + keyring = key_ref_to_ptr(keyring_ref); + key_check(keyring); + + if (keyring->type != &key_type_keyring) + return -ENOTDIR; + + if (!type) { + restrict_link = keyring_restriction_alloc(restrict_link_reject); + } else { + restrict_type = key_type_lookup(type); + + if (IS_ERR(restrict_type)) + return PTR_ERR(restrict_type); + + if (!restrict_type->lookup_restriction) { + ret = -ENOENT; + goto error; + } + + restrict_link = restrict_type->lookup_restriction(restriction); + } + + if (IS_ERR(restrict_link)) { + ret = PTR_ERR(restrict_link); + goto error; + } + + down_write(&keyring->sem); + down_write(&keyring_serialise_restrict_sem); + + if (keyring->restrict_link) + ret = -EEXIST; + else if (keyring_detect_restriction_cycle(keyring, restrict_link)) + ret = -EDEADLK; + else + keyring->restrict_link = restrict_link; + + up_write(&keyring_serialise_restrict_sem); + up_write(&keyring->sem); + + if (ret < 0) { + key_put(restrict_link->key); + kfree(restrict_link); + } + +error: + if (restrict_type) + key_type_put(restrict_type); + + return ret; +} +EXPORT_SYMBOL(keyring_restrict); + /* * Search the given keyring for a key that might be updated. * -- cgit v1.2.3-59-g8ed1b From 97d3aa0f313435a24440e7157c9c9115c58ca463 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Fri, 6 May 2016 14:25:39 -0700 Subject: KEYS: Add a lookup_restriction function for the asymmetric key type Look up asymmetric keyring restriction information using the key-type lookup_restrict hook. Signed-off-by: Mat Martineau --- Documentation/crypto/asymmetric-keys.txt | 35 +++++++++++++++++++++ crypto/asymmetric_keys/asymmetric_type.c | 52 +++++++++++++++++++++++++++----- 2 files changed, 79 insertions(+), 8 deletions(-) diff --git a/Documentation/crypto/asymmetric-keys.txt b/Documentation/crypto/asymmetric-keys.txt index 2b7816dea370..4373e7d86c6a 100644 --- a/Documentation/crypto/asymmetric-keys.txt +++ b/Documentation/crypto/asymmetric-keys.txt @@ -311,3 +311,38 @@ Functions are provided to register and unregister parsers: Parsers may not have the same name. The names are otherwise only used for displaying in debugging messages. + + +========================= +KEYRING LINK RESTRICTIONS +========================= + +Keyrings created from userspace using add_key can be configured to check the +signature of the key being linked. + +Several restriction methods are available: + + (1) Restrict using the kernel builtin trusted keyring + + - Option string used with KEYCTL_RESTRICT_KEYRING: + - "builtin_trusted" + + The kernel builtin trusted keyring will be searched for the signing + key. The ca_keys kernel parameter also affects which keys are used for + signature verification. + + (2) Restrict using the kernel builtin and secondary trusted keyrings + + - Option string used with KEYCTL_RESTRICT_KEYRING: + - "builtin_and_secondary_trusted" + + The kernel builtin and secondary trusted keyrings will be searched for the + signing key. The ca_keys kernel parameter also affects which keys are used + for signature verification. + +In all of these cases, if the signing key is found the signature of the key to +be linked will be verified using the signing key. The requested key is added +to the keyring only if the signature is successfully verified. -ENOKEY is +returned if the parent certificate could not be found, or -EKEYREJECTED is +returned if the signature check fails or the key is blacklisted. Other errors +may be returned if the signature check could not be performed. diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 6600181d5d01..2e3380d09631 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "asymmetric_keys.h" MODULE_LICENSE("GPL"); @@ -451,15 +452,50 @@ static void asymmetric_key_destroy(struct key *key) asymmetric_key_free_kids(kids); } +static struct key_restriction *asymmetric_restriction_alloc( + key_restrict_link_func_t check, + struct key *key) +{ + struct key_restriction *keyres = + kzalloc(sizeof(struct key_restriction), GFP_KERNEL); + + if (!keyres) + return ERR_PTR(-ENOMEM); + + keyres->check = check; + keyres->key = key; + keyres->keytype = &key_type_asymmetric; + + return keyres; +} + +/* + * look up keyring restrict functions for asymmetric keys + */ +static struct key_restriction *asymmetric_lookup_restriction( + const char *restriction) +{ + if (strcmp("builtin_trusted", restriction) == 0) + return asymmetric_restriction_alloc( + restrict_link_by_builtin_trusted, NULL); + + if (strcmp("builtin_and_secondary_trusted", restriction) == 0) + return asymmetric_restriction_alloc( + restrict_link_by_builtin_and_secondary_trusted, NULL); + + return ERR_PTR(-EINVAL); +} + struct key_type key_type_asymmetric = { - .name = "asymmetric", - .preparse = asymmetric_key_preparse, - .free_preparse = asymmetric_key_free_preparse, - .instantiate = generic_key_instantiate, - .match_preparse = asymmetric_key_match_preparse, - .match_free = asymmetric_key_match_free, - .destroy = asymmetric_key_destroy, - .describe = asymmetric_key_describe, + .name = "asymmetric", + .preparse = asymmetric_key_preparse, + .free_preparse = asymmetric_key_free_preparse, + .instantiate = generic_key_instantiate, + .match_preparse = asymmetric_key_match_preparse, + .match_free = asymmetric_key_match_free, + .destroy = asymmetric_key_destroy, + .describe = asymmetric_key_describe, + .lookup_restriction = asymmetric_lookup_restriction, }; EXPORT_SYMBOL_GPL(key_type_asymmetric); -- cgit v1.2.3-59-g8ed1b From 7e3c4d22083f6e7316c5229b6197ca2d5335aa35 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Mon, 27 Jun 2016 16:45:16 -0700 Subject: KEYS: Restrict asymmetric key linkage using a specific keychain Adds restrict_link_by_signature_keyring(), which uses the restrict_key member of the provided destination_keyring data structure as the key or keyring to search for signing keys. Signed-off-by: Mat Martineau --- Documentation/crypto/asymmetric-keys.txt | 11 +++++ crypto/asymmetric_keys/asymmetric_type.c | 35 +++++++++++++++- crypto/asymmetric_keys/restrict.c | 71 ++++++++++++++++++++++++++++++++ include/crypto/public_key.h | 5 +++ 4 files changed, 121 insertions(+), 1 deletion(-) diff --git a/Documentation/crypto/asymmetric-keys.txt b/Documentation/crypto/asymmetric-keys.txt index 4373e7d86c6a..9814722f4b6b 100644 --- a/Documentation/crypto/asymmetric-keys.txt +++ b/Documentation/crypto/asymmetric-keys.txt @@ -340,6 +340,17 @@ Several restriction methods are available: signing key. The ca_keys kernel parameter also affects which keys are used for signature verification. + (3) Restrict using a separate key or keyring + + - Option string used with KEYCTL_RESTRICT_KEYRING: + - "key_or_keyring:" + + Whenever a key link is requested, the link will only succeed if the key + being linked is signed by one of the designated keys. This key may be + specified directly by providing a serial number for one asymmetric key, or + a group of keys may be searched for the signing key by providing the + serial number for a keyring. + In all of these cases, if the signing key is found the signature of the key to be linked will be verified using the signing key. The requested key is added to the keyring only if the signature is successfully verified. -ENOKEY is diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 2e3380d09631..72700ed81594 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -475,6 +475,11 @@ static struct key_restriction *asymmetric_restriction_alloc( static struct key_restriction *asymmetric_lookup_restriction( const char *restriction) { + char *restrict_method; + char *parse_buf; + char *next; + struct key_restriction *ret = ERR_PTR(-EINVAL); + if (strcmp("builtin_trusted", restriction) == 0) return asymmetric_restriction_alloc( restrict_link_by_builtin_trusted, NULL); @@ -483,7 +488,35 @@ static struct key_restriction *asymmetric_lookup_restriction( return asymmetric_restriction_alloc( restrict_link_by_builtin_and_secondary_trusted, NULL); - return ERR_PTR(-EINVAL); + parse_buf = kstrndup(restriction, PAGE_SIZE, GFP_KERNEL); + if (!parse_buf) + return ERR_PTR(-ENOMEM); + + next = parse_buf; + restrict_method = strsep(&next, ":"); + + if ((strcmp(restrict_method, "key_or_keyring") == 0) && next) { + key_serial_t serial; + struct key *key; + + if (kstrtos32(next, 0, &serial) < 0) + goto out; + + key = key_lookup(serial); + if (IS_ERR(key)) { + ret = ERR_CAST(key); + goto out; + } + + ret = asymmetric_restriction_alloc( + restrict_link_by_key_or_keyring, key); + if (IS_ERR(ret)) + key_put(key); + } + +out: + kfree(parse_buf); + return ret; } struct key_type key_type_asymmetric = { diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c index a3afbf783255..183cb642580e 100644 --- a/crypto/asymmetric_keys/restrict.c +++ b/crypto/asymmetric_keys/restrict.c @@ -108,3 +108,74 @@ int restrict_link_by_signature(struct key *dest_keyring, key_put(key); return ret; } + +/** + * restrict_link_by_key_or_keyring - Restrict additions to a ring of public + * keys using the restrict_key information stored in the ring. + * @dest_keyring: Keyring being linked to. + * @type: The type of key being added. + * @payload: The payload of the new key. + * @trusted: A key or ring of keys that can be used to vouch for the new cert. + * + * Check the new certificate only against the key or keys passed in the data + * parameter. If one of those is the signing key and validates the new + * certificate, then mark the new certificate as being ok to link. + * + * Returns 0 if the new certificate was accepted, -ENOKEY if we + * couldn't find a matching parent certificate in the trusted list, + * -EKEYREJECTED if the signature check fails, and some other error if + * there is a matching certificate but the signature check cannot be + * performed. + */ +int restrict_link_by_key_or_keyring(struct key *dest_keyring, + const struct key_type *type, + const union key_payload *payload, + struct key *trusted) +{ + const struct public_key_signature *sig; + struct key *key; + int ret; + + pr_devel("==>%s()\n", __func__); + + if (!dest_keyring) + return -ENOKEY; + else if (dest_keyring->type != &key_type_keyring) + return -EOPNOTSUPP; + + if (!trusted) + return -ENOKEY; + + if (type != &key_type_asymmetric) + return -EOPNOTSUPP; + + sig = payload->data[asym_auth]; + if (!sig->auth_ids[0] && !sig->auth_ids[1]) + return -ENOKEY; + + if (trusted->type == &key_type_keyring) { + /* See if we have a key that signed this one. */ + key = find_asymmetric_key(trusted, sig->auth_ids[0], + sig->auth_ids[1], false); + if (IS_ERR(key)) + return -ENOKEY; + } else if (trusted->type == &key_type_asymmetric) { + const struct asymmetric_key_ids *kids; + + kids = asymmetric_key_ids(trusted); + + if (!asymmetric_key_id_same(kids->id[1], sig->auth_ids[0])) + return -ENOKEY; + + key = __key_get(trusted); + } else { + return -EOPNOTSUPP; + } + + ret = key_validate(key); + if (ret == 0) + ret = verify_signature(key, sig); + + key_put(key); + return ret; +} diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index ec0262fa08f8..bb6a884352a5 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -55,6 +55,11 @@ extern int restrict_link_by_signature(struct key *dest_keyring, const union key_payload *payload, struct key *trust_keyring); +extern int restrict_link_by_key_or_keyring(struct key *dest_keyring, + const struct key_type *type, + const union key_payload *payload, + struct key *trusted); + extern int verify_signature(const struct key *key, const struct public_key_signature *sig); -- cgit v1.2.3-59-g8ed1b From 8e323a02e866014091180443ccb186fee1e3d30d Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 4 Oct 2016 16:42:45 -0700 Subject: KEYS: Keyring asymmetric key restrict method with chaining Add a restrict_link_by_key_or_keyring_chain link restriction that searches for signing keys in the destination keyring in addition to the signing key or keyring designated when the destination keyring was created. Userspace enables this behavior by including the "chain" option in the keyring restriction: keyctl(KEYCTL_RESTRICT_KEYRING, keyring, "asymmetric", "key_or_keyring::chain"); Signed-off-by: Mat Martineau --- Documentation/crypto/asymmetric-keys.txt | 7 +- crypto/asymmetric_keys/asymmetric_type.c | 31 ++++-- crypto/asymmetric_keys/restrict.c | 158 +++++++++++++++++++++++-------- include/crypto/public_key.h | 5 + 4 files changed, 155 insertions(+), 46 deletions(-) diff --git a/Documentation/crypto/asymmetric-keys.txt b/Documentation/crypto/asymmetric-keys.txt index 9814722f4b6b..5ad6480e3fb9 100644 --- a/Documentation/crypto/asymmetric-keys.txt +++ b/Documentation/crypto/asymmetric-keys.txt @@ -343,7 +343,7 @@ Several restriction methods are available: (3) Restrict using a separate key or keyring - Option string used with KEYCTL_RESTRICT_KEYRING: - - "key_or_keyring:" + - "key_or_keyring:[:chain]" Whenever a key link is requested, the link will only succeed if the key being linked is signed by one of the designated keys. This key may be @@ -351,6 +351,11 @@ Several restriction methods are available: a group of keys may be searched for the signing key by providing the serial number for a keyring. + When the "chain" option is provided at the end of the string, the keys + within the destination keyring will also be searched for signing keys. + This allows for verification of certificate chains by adding each + cert in order (starting closest to the root) to one keyring. + In all of these cases, if the signing key is found the signature of the key to be linked will be verified using the signing key. The requested key is added to the keyring only if the signature is successfully verified. -ENOKEY is diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 72700ed81594..e4b0ed386bc8 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -496,20 +496,37 @@ static struct key_restriction *asymmetric_lookup_restriction( restrict_method = strsep(&next, ":"); if ((strcmp(restrict_method, "key_or_keyring") == 0) && next) { + char *key_text; key_serial_t serial; struct key *key; + key_restrict_link_func_t link_fn = + restrict_link_by_key_or_keyring; + bool allow_null_key = false; - if (kstrtos32(next, 0, &serial) < 0) - goto out; + key_text = strsep(&next, ":"); + + if (next) { + if (strcmp(next, "chain") != 0) + goto out; + + link_fn = restrict_link_by_key_or_keyring_chain; + allow_null_key = true; + } - key = key_lookup(serial); - if (IS_ERR(key)) { - ret = ERR_CAST(key); + if (kstrtos32(key_text, 0, &serial) < 0) goto out; + + if ((serial == 0) && allow_null_key) { + key = NULL; + } else { + key = key_lookup(serial); + if (IS_ERR(key)) { + ret = ERR_CAST(key); + goto out; + } } - ret = asymmetric_restriction_alloc( - restrict_link_by_key_or_keyring, key); + ret = asymmetric_restriction_alloc(link_fn, key); if (IS_ERR(ret)) key_put(key); } diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c index 183cb642580e..86fb68508952 100644 --- a/crypto/asymmetric_keys/restrict.c +++ b/crypto/asymmetric_keys/restrict.c @@ -109,31 +109,20 @@ int restrict_link_by_signature(struct key *dest_keyring, return ret; } -/** - * restrict_link_by_key_or_keyring - Restrict additions to a ring of public - * keys using the restrict_key information stored in the ring. - * @dest_keyring: Keyring being linked to. - * @type: The type of key being added. - * @payload: The payload of the new key. - * @trusted: A key or ring of keys that can be used to vouch for the new cert. - * - * Check the new certificate only against the key or keys passed in the data - * parameter. If one of those is the signing key and validates the new - * certificate, then mark the new certificate as being ok to link. - * - * Returns 0 if the new certificate was accepted, -ENOKEY if we - * couldn't find a matching parent certificate in the trusted list, - * -EKEYREJECTED if the signature check fails, and some other error if - * there is a matching certificate but the signature check cannot be - * performed. - */ -int restrict_link_by_key_or_keyring(struct key *dest_keyring, - const struct key_type *type, - const union key_payload *payload, - struct key *trusted) +static bool match_either_id(const struct asymmetric_key_ids *pair, + const struct asymmetric_key_id *single) +{ + return (asymmetric_key_id_same(pair->id[0], single) || + asymmetric_key_id_same(pair->id[1], single)); +} + +static int key_or_keyring_common(struct key *dest_keyring, + const struct key_type *type, + const union key_payload *payload, + struct key *trusted, bool check_dest) { const struct public_key_signature *sig; - struct key *key; + struct key *key = NULL; int ret; pr_devel("==>%s()\n", __func__); @@ -143,7 +132,7 @@ int restrict_link_by_key_or_keyring(struct key *dest_keyring, else if (dest_keyring->type != &key_type_keyring) return -EOPNOTSUPP; - if (!trusted) + if (!trusted && !check_dest) return -ENOKEY; if (type != &key_type_asymmetric) @@ -153,25 +142,64 @@ int restrict_link_by_key_or_keyring(struct key *dest_keyring, if (!sig->auth_ids[0] && !sig->auth_ids[1]) return -ENOKEY; - if (trusted->type == &key_type_keyring) { - /* See if we have a key that signed this one. */ - key = find_asymmetric_key(trusted, sig->auth_ids[0], - sig->auth_ids[1], false); - if (IS_ERR(key)) - return -ENOKEY; - } else if (trusted->type == &key_type_asymmetric) { - const struct asymmetric_key_ids *kids; + if (trusted) { + if (trusted->type == &key_type_keyring) { + /* See if we have a key that signed this one. */ + key = find_asymmetric_key(trusted, sig->auth_ids[0], + sig->auth_ids[1], false); + if (IS_ERR(key)) + key = NULL; + } else if (trusted->type == &key_type_asymmetric) { + const struct asymmetric_key_ids *signer_ids; - kids = asymmetric_key_ids(trusted); + signer_ids = asymmetric_key_ids(trusted); - if (!asymmetric_key_id_same(kids->id[1], sig->auth_ids[0])) - return -ENOKEY; + /* + * The auth_ids come from the candidate key (the + * one that is being considered for addition to + * dest_keyring) and identify the key that was + * used to sign. + * + * The signer_ids are identifiers for the + * signing key specified for dest_keyring. + * + * The first auth_id is the preferred id, and + * the second is the fallback. If only one + * auth_id is present, it may match against + * either signer_id. If two auth_ids are + * present, the first auth_id must match one + * signer_id and the second auth_id must match + * the second signer_id. + */ + if (!sig->auth_ids[0] || !sig->auth_ids[1]) { + const struct asymmetric_key_id *auth_id; - key = __key_get(trusted); - } else { - return -EOPNOTSUPP; + auth_id = sig->auth_ids[0] ?: sig->auth_ids[1]; + if (match_either_id(signer_ids, auth_id)) + key = __key_get(trusted); + + } else if (asymmetric_key_id_same(signer_ids->id[1], + sig->auth_ids[1]) && + match_either_id(signer_ids, + sig->auth_ids[0])) { + key = __key_get(trusted); + } + } else { + return -EOPNOTSUPP; + } } + if (check_dest && !key) { + /* See if the destination has a key that signed this one. */ + key = find_asymmetric_key(dest_keyring, sig->auth_ids[0], + sig->auth_ids[1], false); + if (IS_ERR(key)) + key = NULL; + } + + if (!key) + return -ENOKEY; + ret = key_validate(key); if (ret == 0) ret = verify_signature(key, sig); @@ -179,3 +207,57 @@ int restrict_link_by_key_or_keyring(struct key *dest_keyring, key_put(key); return ret; } + +/** + * restrict_link_by_key_or_keyring - Restrict additions to a ring of public + * keys using the restrict_key information stored in the ring. + * @dest_keyring: Keyring being linked to. + * @type: The type of key being added. + * @payload: The payload of the new key. + * @trusted: A key or ring of keys that can be used to vouch for the new cert. + * + * Check the new certificate only against the key or keys passed in the data + * parameter. If one of those is the signing key and validates the new + * certificate, then mark the new certificate as being ok to link. + * + * Returns 0 if the new certificate was accepted, -ENOKEY if we + * couldn't find a matching parent certificate in the trusted list, + * -EKEYREJECTED if the signature check fails, and some other error if + * there is a matching certificate but the signature check cannot be + * performed. + */ +int restrict_link_by_key_or_keyring(struct key *dest_keyring, + const struct key_type *type, + const union key_payload *payload, + struct key *trusted) +{ + return key_or_keyring_common(dest_keyring, type, payload, trusted, + false); +} + +/** + * restrict_link_by_key_or_keyring_chain - Restrict additions to a ring of + * public keys using the restrict_key information stored in the ring. + * @dest_keyring: Keyring being linked to. + * @type: The type of key being added. + * @payload: The payload of the new key. + * @trusted: A key or ring of keys that can be used to vouch for the new cert. + * + * Check the new certificate only against the key or keys passed in the data + * parameter. If one of those is the signing key and validates the new + * certificate, then mark the new certificate as being ok to link. + * + * Returns 0 if the new certificate was accepted, -ENOKEY if we + * couldn't find a matching parent certificate in the trusted list, + * -EKEYREJECTED if the signature check fails, and some other error if + * there is a matching certificate but the signature check cannot be + * performed. + */ +int restrict_link_by_key_or_keyring_chain(struct key *dest_keyring, + const struct key_type *type, + const union key_payload *payload, + struct key *trusted) +{ + return key_or_keyring_common(dest_keyring, type, payload, trusted, + true); +} diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index bb6a884352a5..e0b681a717ba 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -60,6 +60,11 @@ extern int restrict_link_by_key_or_keyring(struct key *dest_keyring, const union key_payload *payload, struct key *trusted); +extern int restrict_link_by_key_or_keyring_chain(struct key *trust_keyring, + const struct key_type *type, + const union key_payload *payload, + struct key *trusted); + extern int verify_signature(const struct key *key, const struct public_key_signature *sig); -- cgit v1.2.3-59-g8ed1b