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 <[email protected]>
---

 certs/blacklist.c                        |    2 +-
 crypto/asymmetric_keys/asymmetric_type.c |    2 +-
 include/linux/key.h                      |    2 ++
 net/rxrpc/security.c                     |    2 +-
 security/keys/internal.h                 |    4 ++++
 security/keys/keyctl.c                   |    6 ++++--
 security/keys/keyring.c                  |   13 ++++++++-----
 security/keys/permission.c               |   31 ++++++++++++++++++++++++++++++
 security/keys/proc.c                     |    1 +
 security/keys/process_keys.c             |    8 ++++++--
 security/keys/request_key.c              |    5 +++++
 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 aff83e3a9f49..29c3cb6254d9 100644
--- a/certs/blacklist.c
+++ b/certs/blacklist.c
@@ -123,7 +123,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 6e5fc8e31f01..4559ac2f0bb7 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 94a6d51464b5..0db5539366e7 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -296,6 +296,7 @@ extern struct key *key_alloc(struct key_type *type,
 #define KEY_ALLOC_BUILT_IN             0x0004  /* Key is built into kernel */
 #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_USERSPACE_REQUEST    0x0020  /* Userspace requested the key 
*/
 
 extern void key_revoke(struct key *key);
 extern void key_invalidate(struct key *key);
@@ -432,6 +433,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 9b1fb9ed0717..23077cfe3d44 100644
--- a/net/rxrpc/security.c
+++ b/net/rxrpc/security.c
@@ -141,7 +141,7 @@ bool rxrpc_look_up_server_security(struct rxrpc_local 
*local, struct rxrpc_sock
 
        /* 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)) {
                trace_rxrpc_abort(0, "SVK",
                                  sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq,
diff --git a/security/keys/internal.h b/security/keys/internal.h
index af2c9531c435..d0d1bce95674 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 fae2df676e30..54a2bfff9af2 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 f14aabf27a51..1779c95b428c 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 @@ static bool search_nested_keyrings(struct key *keyring,
 
                /* 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 0bb7f6b695f4..3ae4d9aedc3a 100644
--- a/security/keys/permission.c
+++ b/security/keys/permission.c
@@ -253,6 +253,37 @@ int key_task_permission(const key_ref_t key_ref, const 
struct cred *cred,
        return security_key_permission(key_ref, cred, need_perm, notes);
 }
 
+/**
+ * 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, 
&notes);
+       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 c68ec5f98659..a6b349ee1759 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 11227101bea0..3721f96dd6fb 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 2b84efb420cb..479ae0573d1e 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 ee8c5fe6ed61..f8f77af152de 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),
        };


Reply via email to