diff options
| author | David Howells <dhowells@redhat.com> | 2020-07-16 14:23:04 +0100 |
|---|---|---|
| committer | David Howells <dhowells@redhat.com> | 2021-02-05 13:19:59 +0000 |
| commit | eed986d8643280beed60cf7e7b9599f768706a53 (patch) | |
| tree | 9afa6e04f2da2af35d4386cb8452049d6753b866 | |
| parent | bc1a24985a3f075919096dadb5ea283d62d2b03c (diff) | |
| download | linux-fs-eed986d8643280beed60cf7e7b9599f768706a53.tar.gz | |
keys: Split the search perms between KEY_NEED_USE and KEY_NEED_SEARCH
Allow the permission needed for a keyring search to be specified and split
the permissions between KEY_NEED_USE (the kernel wants to do something with
the key) and KEY_NEED_SEARCH (userspace wants to do something with the
key).
This primarily affects how request_key() works, differentiating implicit
calls (e.g. from filesystems) from userspace calling the request_key()
system call.
This will allow the kernel to find keys in a hidden container keyring, but
not the denizens of the container.
Signed-off-by: David Howells <dhowells@redhat.com>
| -rw-r--r-- | certs/blacklist.c | 2 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/asymmetric_type.c | 2 | ||||
| -rw-r--r-- | include/linux/key.h | 2 | ||||
| -rw-r--r-- | net/rxrpc/security.c | 2 | ||||
| -rw-r--r-- | security/keys/internal.h | 4 | ||||
| -rw-r--r-- | security/keys/keyctl.c | 6 | ||||
| -rw-r--r-- | security/keys/keyring.c | 13 | ||||
| -rw-r--r-- | security/keys/permission.c | 31 | ||||
| -rw-r--r-- | security/keys/proc.c | 1 | ||||
| -rw-r--r-- | security/keys/process_keys.c | 8 | ||||
| -rw-r--r-- | security/keys/request_key.c | 5 | ||||
| -rw-r--r-- | security/keys/request_key_auth.c | 1 |
12 files changed, 65 insertions, 12 deletions
diff --git a/certs/blacklist.c b/certs/blacklist.c index cf97d5deef842..16587658e314f 100644 --- a/certs/blacklist.c +++ b/certs/blacklist.c @@ -124,7 +124,7 @@ int is_hash_blacklisted(const u8 *hash, size_t hash_len, const char *type) *p = 0; kref = keyring_search(make_key_ref(blacklist_keyring, true), - &key_type_blacklist, buffer, false); + &key_type_blacklist, buffer, KEY_NEED_USE, false); if (!IS_ERR(kref)) { key_ref_put(kref); ret = -EKEYREJECTED; diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index ad8af3d70ac04..280c5e36ec8b8 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -83,7 +83,7 @@ struct key *find_asymmetric_key(struct key *keyring, pr_debug("Look up: \"%s\"\n", req); ref = keyring_search(make_key_ref(keyring, 1), - &key_type_asymmetric, req, true); + &key_type_asymmetric, req, KEY_NEED_USE, true); if (IS_ERR(ref)) pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref)); kfree(req); diff --git a/include/linux/key.h b/include/linux/key.h index 825177cec27d4..451ac85f44738 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -297,6 +297,7 @@ extern struct key *key_alloc(struct key_type *type, #define KEY_ALLOC_BYPASS_RESTRICTION 0x0008 /* Override the check on restricted keyrings */ #define KEY_ALLOC_UID_KEYRING 0x0010 /* allocating a user or user session keyring */ #define KEY_ALLOC_SET_KEEP 0x0020 /* Set the KEEP flag on the key/keyring */ +#define KEY_ALLOC_USERSPACE_REQUEST 0x0040 /* Userspace requested the key */ extern void key_revoke(struct key *key); extern void key_invalidate(struct key *key); @@ -433,6 +434,7 @@ extern int keyring_clear(struct key *keyring); extern key_ref_t keyring_search(key_ref_t keyring, struct key_type *type, const char *description, + enum key_need_perm need_perm, bool recurse); extern int keyring_add_key(struct key *keyring, diff --git a/net/rxrpc/security.c b/net/rxrpc/security.c index 50cb5f1ee0c02..9efb264ec993b 100644 --- a/net/rxrpc/security.c +++ b/net/rxrpc/security.c @@ -169,7 +169,7 @@ struct key *rxrpc_look_up_server_security(struct rxrpc_connection *conn, /* look through the service's keyring */ kref = keyring_search(make_key_ref(rx->securities, 1UL), - &key_type_rxrpc_s, kdesc, true); + &key_type_rxrpc_s, kdesc, KEY_NEED_USE, true); if (IS_ERR(kref)) { key = ERR_CAST(kref); goto out; diff --git a/security/keys/internal.h b/security/keys/internal.h index 3e4da92031f9d..7108106824c87 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -131,6 +131,7 @@ struct keyring_search_context { struct keyring_index_key index_key; const struct cred *cred; struct key_match_data match_data; + enum key_need_perm need_perm; /* Permission required for search */ unsigned flags; #define KEYRING_SEARCH_NO_STATE_CHECK 0x0001 /* Skip state checks */ #define KEYRING_SEARCH_DO_STATE_CHECK 0x0002 /* Override NO_STATE_CHECK */ @@ -196,6 +197,9 @@ extern void key_gc_keytype(struct key_type *ktype); extern int key_task_permission(const key_ref_t key_ref, const struct cred *cred, enum key_need_perm need_perm); +extern int key_search_permission(const key_ref_t key_ref, + struct keyring_search_context *ctx, + enum key_need_perm need_perm); extern unsigned int key_acl_to_perm(const struct key_acl *acl); extern long key_set_acl(struct key *key, struct key_acl *acl); extern void key_put_acl(struct key_acl *acl); diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index a2960f74ada39..a8b28e099e5da 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -225,7 +225,8 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type, key = request_key_and_link(ktype, description, NULL, callout_info, callout_len, NULL, NULL, key_ref_to_ptr(dest_ref), - KEY_ALLOC_IN_QUOTA); + KEY_ALLOC_IN_QUOTA | + KEY_ALLOC_USERSPACE_REQUEST); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error5; @@ -685,7 +686,8 @@ long keyctl_keyring_search(key_serial_t ringid, } /* do the search */ - key_ref = keyring_search(keyring_ref, ktype, description, true); + key_ref = keyring_search(keyring_ref, ktype, description, + KEY_NEED_SEARCH, true); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 84f99f525772b..8a56ddbac28f6 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -621,8 +621,8 @@ static int keyring_search_iterator(const void *object, void *iterator_data) /* key must have search permissions */ if (!(ctx->flags & KEYRING_SEARCH_NO_CHECK_PERM) && - key_task_permission(make_key_ref(key, ctx->possessed), - ctx->cred, KEY_NEED_SEARCH) < 0) { + key_search_permission(make_key_ref(key, ctx->possessed), + ctx, ctx->need_perm) < 0) { ctx->result = ERR_PTR(-EACCES); kleave(" = %d [!perm]", ctx->skipped_ret); goto skipped; @@ -798,8 +798,8 @@ ascend_to_node: /* Search a nested keyring */ if (!(ctx->flags & KEYRING_SEARCH_NO_CHECK_PERM) && - key_task_permission(make_key_ref(key, ctx->possessed), - ctx->cred, KEY_NEED_SEARCH) < 0) + key_search_permission(make_key_ref(key, ctx->possessed), + ctx, KEY_NEED_SEARCH) < 0) continue; /* stack the current position */ @@ -921,7 +921,7 @@ key_ref_t keyring_search_rcu(key_ref_t keyring_ref, return ERR_PTR(-ENOTDIR); if (!(ctx->flags & KEYRING_SEARCH_NO_CHECK_PERM)) { - err = key_task_permission(keyring_ref, ctx->cred, KEY_NEED_SEARCH); + err = key_search_permission(keyring_ref, ctx, ctx->need_perm); if (err < 0) return ERR_PTR(err); } @@ -937,6 +937,7 @@ key_ref_t keyring_search_rcu(key_ref_t keyring_ref, * @keyring: The root of the keyring tree to be searched. * @type: The type of keyring we want to find. * @description: The name of the keyring we want to find. + * @need_perm: The permission required of the target key. * @recurse: True to search the children of @keyring also * * As keyring_search_rcu() above, but using the current task's credentials and @@ -945,6 +946,7 @@ key_ref_t keyring_search_rcu(key_ref_t keyring_ref, key_ref_t keyring_search(key_ref_t keyring, struct key_type *type, const char *description, + enum key_need_perm need_perm, bool recurse) { struct keyring_search_context ctx = { @@ -955,6 +957,7 @@ key_ref_t keyring_search(key_ref_t keyring, .match_data.cmp = key_default_cmp, .match_data.raw_data = description, .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, + .need_perm = need_perm, .flags = KEYRING_SEARCH_DO_STATE_CHECK, }; key_ref_t key; diff --git a/security/keys/permission.c b/security/keys/permission.c index 0bb7f6b695f46..3ae4d9aedc3a8 100644 --- a/security/keys/permission.c +++ b/security/keys/permission.c @@ -254,6 +254,37 @@ int key_task_permission(const key_ref_t key_ref, const struct cred *cred, } /** + * key_search_permission - Check a key can be searched for + * @key_ref: The key to check. + * @cred: The credentials to use. + * @need_perm: The permission required. + * + * Check to see whether permission is granted to use a key in the desired way, + * but permit the security modules to override. + * + * The caller must hold the RCU readlock. + * + * Returns 0 if successful, -EACCES if access is denied based on the + * permissions bits or the LSM check. + */ +int key_search_permission(const key_ref_t key_ref, + struct keyring_search_context *ctx, + enum key_need_perm need_perm) +{ + unsigned int allow, notes = 0; + int ret; + + allow = key_resolve_acl(key_ref, ctx->cred); + + ret = check_key_permission(key_ref, ctx->cred, allow, need_perm, ¬es); + if (ret < 0) + return ret; + + /* Let the LSMs be the final arbiter */ + return security_key_permission(key_ref, ctx->cred, need_perm, notes); +} + +/** * key_validate - Validate a key. * @key: The key to be validated. * diff --git a/security/keys/proc.c b/security/keys/proc.c index c68ec5f986593..a6b349ee1759b 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -174,6 +174,7 @@ static int proc_keys_show(struct seq_file *m, void *v) .match_data.cmp = lookup_user_key_possessed, .match_data.raw_data = key, .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, + .need_perm = KEY_NEED_SEARCH, .flags = (KEYRING_SEARCH_NO_STATE_CHECK | KEYRING_SEARCH_RECURSE), }; diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 2c04e90d2da06..fe1285a8452a0 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -135,7 +135,8 @@ int look_up_user_keyrings(struct key **_user_keyring, */ snprintf(buf, sizeof(buf), "_uid.%u", uid); uid_keyring_r = keyring_search(make_key_ref(reg_keyring, true), - &key_type_keyring, buf, false); + &key_type_keyring, buf, KEY_NEED_SEARCH, + false); kdebug("_uid %p", uid_keyring_r); if (uid_keyring_r == ERR_PTR(-EAGAIN)) { uid_keyring = keyring_alloc(buf, cred->user->uid, INVALID_GID, @@ -157,7 +158,8 @@ int look_up_user_keyrings(struct key **_user_keyring, /* Get a default session keyring (which might also exist already) */ snprintf(buf, sizeof(buf), "_uid_ses.%u", uid); session_keyring_r = keyring_search(make_key_ref(reg_keyring, true), - &key_type_keyring, buf, false); + &key_type_keyring, buf, KEY_NEED_SEARCH, + false); kdebug("_uid_ses %p", session_keyring_r); if (session_keyring_r == ERR_PTR(-EAGAIN)) { session_keyring = keyring_alloc(buf, cred->user->uid, INVALID_GID, @@ -230,6 +232,7 @@ struct key *get_user_session_keyring_rcu(const struct cred *cred) .match_data.cmp = key_default_cmp, .match_data.raw_data = buf, .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, + .need_perm = KEY_NEED_SEARCH, .flags = KEYRING_SEARCH_DO_STATE_CHECK, }; @@ -648,6 +651,7 @@ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags, struct keyring_search_context ctx = { .match_data.cmp = lookup_user_key_possessed, .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, + .need_perm = KEY_NEED_SEARCH, .flags = (KEYRING_SEARCH_NO_STATE_CHECK | KEYRING_SEARCH_RECURSE), }; diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 4629c28654690..1c5f846748040 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -567,6 +567,7 @@ struct key *request_key_and_link(struct key_type *type, .match_data.cmp = key_default_cmp, .match_data.raw_data = description, .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, + .need_perm = KEY_NEED_USE, .flags = (KEYRING_SEARCH_DO_STATE_CHECK | KEYRING_SEARCH_SKIP_EXPIRED | KEYRING_SEARCH_RECURSE), @@ -579,6 +580,9 @@ struct key *request_key_and_link(struct key_type *type, ctx.index_key.type->name, ctx.index_key.description, callout_info, callout_len, aux, dest_keyring, flags); + if (flags & KEY_ALLOC_USERSPACE_REQUEST) + ctx.need_perm = KEY_NEED_SEARCH; + if (type->match_preparse) { ret = type->match_preparse(&ctx.match_data); if (ret < 0) { @@ -774,6 +778,7 @@ struct key *request_key_rcu(struct key_type *type, .match_data.cmp = key_default_cmp, .match_data.raw_data = description, .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, + .need_perm = KEY_NEED_USE, .flags = (KEYRING_SEARCH_DO_STATE_CHECK | KEYRING_SEARCH_SKIP_EXPIRED), }; diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index ee8c5fe6ed61e..f8f77af152dec 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -264,6 +264,7 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id) .match_data.cmp = key_default_cmp, .match_data.raw_data = description, .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, + .need_perm = KEY_NEED_USE, .flags = (KEYRING_SEARCH_DO_STATE_CHECK | KEYRING_SEARCH_RECURSE), }; |
