From a4e3b8d79a5c6d40f4a9703abf7fe3abcc6c3b8d Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Thu, 22 May 2014 14:02:23 -0400 Subject: KEYS: special dot prefixed keyring name bug fix Dot prefixed keyring names are supposed to be reserved for the kernel, but add_key() calls key_get_type_from_user(), which incorrectly verifies the 'type' field, not the 'description' field. This patch verifies the 'description' field isn't dot prefixed, when creating a new keyring, and removes the dot prefix test in key_get_type_from_user(). Changelog v6: - whitespace and other cleanup Changelog v5: - Only prevent userspace from creating a dot prefixed keyring, not regular keys - Dmitry Reported-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar Acked-by: David Howells --- security/keys/keyctl.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'security/keys') diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index cd5bd0c..8a8c233 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -37,8 +37,6 @@ static int key_get_type_from_user(char *type, return ret; if (ret == 0 || ret >= len) return -EINVAL; - if (type[0] == '.') - return -EPERM; type[len - 1] = '\0'; return 0; } @@ -86,6 +84,10 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type, if (!*description) { kfree(description); description = NULL; + } else if ((description[0] == '.') && + (strncmp(type, "keyring", 7) == 0)) { + ret = -EPERM; + goto error2; } } -- cgit v1.1 From 0c7774abb41bd00d5836d9ba098825a40fa94133 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 17 Jul 2014 20:45:08 +0100 Subject: KEYS: Allow special keys (eg. DNS results) to be invalidated by CAP_SYS_ADMIN Special kernel keys, such as those used to hold DNS results for AFS, CIFS and NFS and those used to hold idmapper results for NFS, used to be 'invalidateable' with key_revoke(). However, since the default permissions for keys were reduced: Commit: 96b5c8fea6c0861621051290d705ec2e971963f1 KEYS: Reduce initial permissions on keys it has become impossible to do this. Add a key flag (KEY_FLAG_ROOT_CAN_INVAL) that will permit a key to be invalidated by root. This should not be used for system keyrings as the garbage collector will try and remove any invalidate key. For system keyrings, KEY_FLAG_ROOT_CAN_CLEAR can be used instead. After this, from userspace, keyctl_invalidate() and "keyctl invalidate" can be used by any possessor of CAP_SYS_ADMIN (typically root) to invalidate DNS and idmapper keys. Invalidated keys are immediately garbage collected and will be immediately rerequested if needed again. Signed-off-by: David Howells Tested-by: Steve Dickson --- security/keys/keyctl.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'security/keys') diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index cd5bd0c..609f8d3 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -404,12 +404,25 @@ long keyctl_invalidate_key(key_serial_t id) key_ref = lookup_user_key(id, 0, KEY_NEED_SEARCH); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); + + /* Root is permitted to invalidate certain special keys */ + if (capable(CAP_SYS_ADMIN)) { + key_ref = lookup_user_key(id, 0, 0); + if (IS_ERR(key_ref)) + goto error; + if (test_bit(KEY_FLAG_ROOT_CAN_INVAL, + &key_ref_to_ptr(key_ref)->flags)) + goto invalidate; + goto error_put; + } + goto error; } +invalidate: key_invalidate(key_ref_to_ptr(key_ref)); ret = 0; - +error_put: key_ref_put(key_ref); error: kleave(" = %ld", ret); -- cgit v1.1 From 6a09d17bb66a533c165be81e8a4c3557f68e1a3b Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 18 Jul 2014 18:56:34 +0100 Subject: KEYS: Provide a generic instantiation function Provide a generic instantiation function for key types that use the preparse hook. This makes it easier to prereserve key quota before keyrings get locked to retain the new key. Signed-off-by: David Howells Acked-by: Steve Dickson Acked-by: Jeff Layton Reviewed-by: Sage Weil --- security/keys/key.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'security/keys') diff --git a/security/keys/key.c b/security/keys/key.c index 2048a11..7c9acbf 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -1024,6 +1024,36 @@ void key_invalidate(struct key *key) EXPORT_SYMBOL(key_invalidate); /** + * generic_key_instantiate - Simple instantiation of a key from preparsed data + * @key: The key to be instantiated + * @prep: The preparsed data to load. + * + * Instantiate a key from preparsed data. We assume we can just copy the data + * in directly and clear the old pointers. + * + * This can be pointed to directly by the key type instantiate op pointer. + */ +int generic_key_instantiate(struct key *key, struct key_preparsed_payload *prep) +{ + int ret; + + pr_devel("==>%s()\n", __func__); + + ret = key_payload_reserve(key, prep->quotalen); + if (ret == 0) { + key->type_data.p[0] = prep->type_data[0]; + key->type_data.p[1] = prep->type_data[1]; + rcu_assign_keypointer(key, prep->payload); + prep->type_data[0] = NULL; + prep->type_data[1] = NULL; + prep->payload = NULL; + } + pr_devel("<==%s() = %d\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL(generic_key_instantiate); + +/** * register_key_type - Register a type of key. * @ktype: The new key type. * -- cgit v1.1 From fc7c70e0b6b637bbf6cf8b9cee547d5ae83899c9 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 18 Jul 2014 18:56:34 +0100 Subject: KEYS: struct key_preparsed_payload should have two payload pointers struct key_preparsed_payload should have two payload pointers to correspond with those in struct key. Signed-off-by: David Howells Acked-by: Steve Dickson Acked-by: Jeff Layton Reviewed-by: Sage Weil --- security/keys/encrypted-keys/encrypted.c | 2 +- security/keys/key.c | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'security/keys') diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 5fe443d..d252c57 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -811,7 +811,7 @@ static int encrypted_instantiate(struct key *key, goto out; } - rcu_assign_keypointer(key, epayload); + prep->payload[0] = epayload; out: kfree(datablob); return ret; diff --git a/security/keys/key.c b/security/keys/key.c index 7c9acbf..03620a3 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -1043,10 +1043,12 @@ int generic_key_instantiate(struct key *key, struct key_preparsed_payload *prep) if (ret == 0) { key->type_data.p[0] = prep->type_data[0]; key->type_data.p[1] = prep->type_data[1]; - rcu_assign_keypointer(key, prep->payload); + rcu_assign_keypointer(key, prep->payload[0]); + key->payload.data2[1] = prep->payload[1]; prep->type_data[0] = NULL; prep->type_data[1] = NULL; - prep->payload = NULL; + prep->payload[0] = NULL; + prep->payload[1] = NULL; } pr_devel("<==%s() = %d\n", __func__, ret); return ret; -- cgit v1.1 From 7dfa0ca6a95de65b7a7760630cdbd7d30f204bfa Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 18 Jul 2014 18:56:34 +0100 Subject: KEYS: Allow expiry time to be set when preparsing a key Allow a key type's preparsing routine to set the expiry time for a key. Signed-off-by: David Howells Acked-by: Steve Dickson Acked-by: Jeff Layton Reviewed-by: Sage Weil --- security/keys/key.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'security/keys') diff --git a/security/keys/key.c b/security/keys/key.c index 03620a3..755fb02 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -437,6 +437,11 @@ static int __key_instantiate_and_link(struct key *key, /* disable the authorisation key */ if (authkey) key_revoke(authkey); + + if (prep->expiry != TIME_T_MAX) { + key->expiry = prep->expiry; + key_schedule_gc(prep->expiry + key_gc_delay); + } } } @@ -479,6 +484,7 @@ int key_instantiate_and_link(struct key *key, prep.data = data; prep.datalen = datalen; prep.quotalen = key->type->def_datalen; + prep.expiry = TIME_T_MAX; if (key->type->preparse) { ret = key->type->preparse(&prep); if (ret < 0) @@ -811,6 +817,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, 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); if (ret < 0) { @@ -941,6 +948,7 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen) prep.data = payload; prep.datalen = plen; prep.quotalen = key->type->def_datalen; + prep.expiry = TIME_T_MAX; if (key->type->preparse) { ret = key->type->preparse(&prep); if (ret < 0) -- cgit v1.1 From 4d8c0250b841159b128785f7a7efbaff40cc8501 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 18 Jul 2014 18:56:34 +0100 Subject: KEYS: Call ->free_preparse() even after ->preparse() returns an error Call the ->free_preparse() key type op even after ->preparse() returns an error as it does cleaning up type stuff. Signed-off-by: David Howells Acked-by: Steve Dickson Acked-by: Jeff Layton Reviewed-by: Sage Weil --- security/keys/key.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'security/keys') diff --git a/security/keys/key.c b/security/keys/key.c index 755fb02..b90a68c 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -494,7 +494,7 @@ int key_instantiate_and_link(struct key *key, if (keyring) { ret = __key_link_begin(keyring, &key->index_key, &edit); if (ret < 0) - goto error_free_preparse; + goto error; } ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &edit); @@ -502,10 +502,9 @@ int key_instantiate_and_link(struct key *key, if (keyring) __key_link_end(keyring, &key->index_key, edit); -error_free_preparse: +error: if (key->type->preparse) key->type->free_preparse(&prep); -error: return ret; } @@ -822,7 +821,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, ret = index_key.type->preparse(&prep); if (ret < 0) { key_ref = ERR_PTR(ret); - goto error_put_type; + goto error_free_prep; } if (!index_key.description) index_key.description = prep.description; @@ -964,9 +963,9 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen) up_write(&key->sem); +error: if (key->type->preparse) key->type->free_preparse(&prep); -error: return ret; } EXPORT_SYMBOL(key_update); -- cgit v1.1 From f9167789df53f22af771fb6690a3d36aa21d74c5 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 18 Jul 2014 18:56:35 +0100 Subject: KEYS: user: Use key preparsing Make use of key preparsing in user-defined and logon keys so that quota size determination can take place prior to keyring locking when a key is being added. Also the idmapper key types need to change to match as they use the user-defined key type routines. Signed-off-by: David Howells Acked-by: Steve Dickson Acked-by: Jeff Layton --- security/keys/user_defined.c | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) (limited to 'security/keys') diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index faa2cae..eee3400 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -27,7 +27,9 @@ static int logon_vet_description(const char *desc); struct key_type key_type_user = { .name = "user", .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, - .instantiate = user_instantiate, + .preparse = user_preparse, + .free_preparse = user_free_preparse, + .instantiate = generic_key_instantiate, .update = user_update, .match = user_match, .revoke = user_revoke, @@ -47,7 +49,9 @@ EXPORT_SYMBOL_GPL(key_type_user); struct key_type key_type_logon = { .name = "logon", .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, - .instantiate = user_instantiate, + .preparse = user_preparse, + .free_preparse = user_free_preparse, + .instantiate = generic_key_instantiate, .update = user_update, .match = user_match, .revoke = user_revoke, @@ -58,38 +62,37 @@ struct key_type key_type_logon = { EXPORT_SYMBOL_GPL(key_type_logon); /* - * instantiate a user defined key + * Preparse a user defined key payload */ -int user_instantiate(struct key *key, struct key_preparsed_payload *prep) +int user_preparse(struct key_preparsed_payload *prep) { struct user_key_payload *upayload; size_t datalen = prep->datalen; - int ret; - ret = -EINVAL; if (datalen <= 0 || datalen > 32767 || !prep->data) - goto error; - - ret = key_payload_reserve(key, datalen); - if (ret < 0) - goto error; + return -EINVAL; - ret = -ENOMEM; upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL); if (!upayload) - goto error; + return -ENOMEM; /* attach the data */ + prep->quotalen = datalen; + prep->payload[0] = upayload; upayload->datalen = datalen; memcpy(upayload->data, prep->data, datalen); - rcu_assign_keypointer(key, upayload); - ret = 0; - -error: - return ret; + return 0; } +EXPORT_SYMBOL_GPL(user_preparse); -EXPORT_SYMBOL_GPL(user_instantiate); +/* + * Free a preparse of a user defined key payload + */ +void user_free_preparse(struct key_preparsed_payload *prep) +{ + kfree(prep->payload[0]); +} +EXPORT_SYMBOL_GPL(user_free_preparse); /* * update a user defined key -- cgit v1.1 From 002edaf76f09af658241029817f5ef66f6bef5e4 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 18 Jul 2014 18:56:36 +0100 Subject: KEYS: big_key: Use key preparsing Make use of key preparsing in the big key type so that quota size determination can take place prior to keyring locking when a key is being added. Signed-off-by: David Howells Acked-by: Steve Dickson --- security/keys/big_key.c | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) (limited to 'security/keys') diff --git a/security/keys/big_key.c b/security/keys/big_key.c index 8137b27..c2f91a0 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -34,7 +34,9 @@ MODULE_LICENSE("GPL"); struct key_type key_type_big_key = { .name = "big_key", .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, - .instantiate = big_key_instantiate, + .preparse = big_key_preparse, + .free_preparse = big_key_free_preparse, + .instantiate = generic_key_instantiate, .match = user_match, .revoke = big_key_revoke, .destroy = big_key_destroy, @@ -43,11 +45,11 @@ struct key_type key_type_big_key = { }; /* - * Instantiate a big key + * Preparse a big key */ -int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) +int big_key_preparse(struct key_preparsed_payload *prep) { - struct path *path = (struct path *)&key->payload.data2; + struct path *path = (struct path *)&prep->payload; struct file *file; ssize_t written; size_t datalen = prep->datalen; @@ -58,11 +60,9 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) goto error; /* Set an arbitrary quota */ - ret = key_payload_reserve(key, 16); - if (ret < 0) - goto error; + prep->quotalen = 16; - key->type_data.x[1] = datalen; + prep->type_data[1] = (void *)(unsigned long)datalen; if (datalen > BIG_KEY_FILE_THRESHOLD) { /* Create a shmem file to store the data in. This will permit the data @@ -73,7 +73,7 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) file = shmem_kernel_file_setup("", datalen, 0); if (IS_ERR(file)) { ret = PTR_ERR(file); - goto err_quota; + goto error; } written = kernel_write(file, prep->data, prep->datalen, 0); @@ -93,24 +93,33 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) } else { /* Just store the data in a buffer */ void *data = kmalloc(datalen, GFP_KERNEL); - if (!data) { - ret = -ENOMEM; - goto err_quota; - } + if (!data) + return -ENOMEM; - key->payload.data = memcpy(data, prep->data, prep->datalen); + prep->payload[0] = memcpy(data, prep->data, prep->datalen); } return 0; err_fput: fput(file); -err_quota: - key_payload_reserve(key, 0); error: return ret; } /* + * Clear preparsement. + */ +void big_key_free_preparse(struct key_preparsed_payload *prep) +{ + if (prep->datalen > BIG_KEY_FILE_THRESHOLD) { + struct path *path = (struct path *)&prep->payload; + path_put(path); + } else { + kfree(prep->payload[0]); + } +} + +/* * dispose of the links from a revoked keyring * - called with the key sem write-locked */ -- cgit v1.1 From 5d19e20b534ff4c17dfba792f1f9e33e1378e3f9 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 18 Jul 2014 18:56:36 +0100 Subject: KEYS: keyring: Provide key preparsing Provide key preparsing in the keyring so that we can make preparsing mandatory. For keyrings, however, only an empty payload is permitted. Signed-off-by: David Howells Acked-by: Steve Dickson Acked-by: Jeff Layton --- security/keys/keyring.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) (limited to 'security/keys') diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 9cf2575..8314a7d 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -73,6 +73,8 @@ static inline unsigned keyring_hash(const char *desc) * can be treated as ordinary keys in addition to having their own special * operations. */ +static int keyring_preparse(struct key_preparsed_payload *prep); +static void keyring_free_preparse(struct key_preparsed_payload *prep); static int keyring_instantiate(struct key *keyring, struct key_preparsed_payload *prep); static void keyring_revoke(struct key *keyring); @@ -84,6 +86,8 @@ static long keyring_read(const struct key *keyring, struct key_type key_type_keyring = { .name = "keyring", .def_datalen = 0, + .preparse = keyring_preparse, + .free_preparse = keyring_free_preparse, .instantiate = keyring_instantiate, .match = user_match, .revoke = keyring_revoke, @@ -123,6 +127,21 @@ static void keyring_publish_name(struct key *keyring) } /* + * Preparse a keyring payload + */ +static int keyring_preparse(struct key_preparsed_payload *prep) +{ + return prep->datalen != 0 ? -EINVAL : 0; +} + +/* + * Free a preparse of a user defined key payload + */ +static void keyring_free_preparse(struct key_preparsed_payload *prep) +{ +} + +/* * Initialise a keyring. * * Returns 0 on success, -EINVAL if given any data. @@ -130,17 +149,10 @@ static void keyring_publish_name(struct key *keyring) static int keyring_instantiate(struct key *keyring, struct key_preparsed_payload *prep) { - int ret; - - ret = -EINVAL; - if (prep->datalen == 0) { - assoc_array_init(&keyring->keys); - /* make the keyring available by name if it has one */ - keyring_publish_name(keyring); - ret = 0; - } - - return ret; + assoc_array_init(&keyring->keys); + /* make the keyring available by name if it has one */ + keyring_publish_name(keyring); + return 0; } /* -- cgit v1.1 From f1dcde91a3503f68ef209667a8798ead2b50b02a Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 18 Jul 2014 18:56:36 +0100 Subject: KEYS: request_key_auth: Provide key preparsing Provide key preparsing for the request_key_auth key type so that we can make preparsing mandatory. This does nothing as this type can only be set up internally to the kernel. Signed-off-by: David Howells Acked-by: Steve Dickson Acked-by: Jeff Layton --- security/keys/request_key_auth.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'security/keys') diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 7495a93..842e6f4 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -20,6 +20,8 @@ #include "internal.h" #include +static int request_key_auth_preparse(struct key_preparsed_payload *); +static void request_key_auth_free_preparse(struct key_preparsed_payload *); static int request_key_auth_instantiate(struct key *, struct key_preparsed_payload *); static void request_key_auth_describe(const struct key *, struct seq_file *); @@ -33,6 +35,8 @@ static long request_key_auth_read(const struct key *, char __user *, size_t); struct key_type key_type_request_key_auth = { .name = ".request_key_auth", .def_datalen = sizeof(struct request_key_auth), + .preparse = request_key_auth_preparse, + .free_preparse = request_key_auth_free_preparse, .instantiate = request_key_auth_instantiate, .describe = request_key_auth_describe, .revoke = request_key_auth_revoke, @@ -40,6 +44,15 @@ struct key_type key_type_request_key_auth = { .read = request_key_auth_read, }; +int request_key_auth_preparse(struct key_preparsed_payload *prep) +{ + return 0; +} + +void request_key_auth_free_preparse(struct key_preparsed_payload *prep) +{ +} + /* * Instantiate a request-key authorisation key. */ -- cgit v1.1 From b64cc5fb85f38ae7ca3c67a8fea9ad8c0d068bfa Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Sat, 26 Jul 2014 23:21:02 -0400 Subject: KEYS: revert encrypted key change Commit fc7c70e "KEYS: struct key_preparsed_payload should have two payload pointers" erroneously modified encrypted-keys. This patch reverts the change to that file. Signed-off-by: Mimi Zohar Signed-off-by: David Howells --- security/keys/encrypted-keys/encrypted.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security/keys') diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index d252c57..5fe443d 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -811,7 +811,7 @@ static int encrypted_instantiate(struct key *key, goto out; } - prep->payload[0] = epayload; + rcu_assign_keypointer(key, epayload); out: kfree(datablob); return ret; -- cgit v1.1