Allow the ACL attached to a key to grant permissions to the denizens of a
container object when request_key() is called.  This allows separate
permissions to those granted in the possessor set.

        int cfd = container_create("foo", 0);

        int ret = keyctl_grant_permission(key,
                                          KEY_ACE_SUBJ_CONTAINER,
                                          cfd,
                                          KEY_ACE_SEARCH);

To allow request_key() to find a key, KEY_ACE_SEARCH must be included in
the ACE.  This will allow filesystems and network protocols (eg. AFS and
AF_RXRPC) to use the key.  For the request_key() system call to be able to
find a key for a process inside the container, KEY_ACE_LINK must be granted
also.

Keys on the container keyring (and the container keyring itself) can be
accessed directly by ID from inside the container if other KEY_ACE_*
permits are granted.

Signed-off-by: David Howells <dhowe...@redhat.com>
---

 include/linux/container.h    |    6 ++-
 include/linux/key.h          |    3 +
 include/uapi/linux/keyctl.h  |    1 
 kernel/container.c           |   41 ++++++++++++++++++-
 samples/vfs/test-container.c |   60 ++++++++++++++++++++++++++++
 security/keys/permission.c   |   90 ++++++++++++++++++++++++++++++++++++++----
 security/keys/process_keys.c |    2 -
 7 files changed, 188 insertions(+), 15 deletions(-)

diff --git a/include/linux/container.h b/include/linux/container.h
index 7424f7fb5560..cd82074c26a3 100644
--- a/include/linux/container.h
+++ b/include/linux/container.h
@@ -33,7 +33,11 @@ struct container {
        refcount_t              usage;
        int                     exit_code;      /* The exit code of 'init' */
        const struct cred       *cred;          /* Creds for this container, 
including userns */
+#ifdef CONFIG_KEYS
        struct key              *keyring;       /* Externally managed container 
keyring */
+       struct key_tag          *tag;           /* Container ID for key ACL */
+       struct list_head        req_key_traps;  /* Traps for request-key 
upcalls */
+#endif
        struct nsproxy          *ns;            /* This container's namespaces 
*/
        struct path             root;           /* The root of the container's 
fs namespace */
        struct task_struct      *init;          /* The 'init' task for this 
container */
@@ -43,7 +47,6 @@ struct container {
        struct list_head        members;        /* Member processes, guarded 
with ->lock */
        struct list_head        child_link;     /* Link in parent->children */
        struct list_head        children;       /* Child containers */
-       struct list_head        req_key_traps;  /* Traps for request-key 
upcalls */
        wait_queue_head_t       waitq;          /* Someone waiting for init to 
exit waits here */
        unsigned long           flags;
 #define CONTAINER_FLAG_INIT_STARTED    0       /* Init is started - certain 
ops now prohibited */
@@ -63,6 +66,7 @@ extern int copy_container(unsigned long flags, struct 
task_struct *tsk,
 extern void exit_container(struct task_struct *tsk);
 extern void put_container(struct container *c);
 extern long key_del_intercept(struct container *c, const char *type);
+extern struct container *fd_to_container(int fd);
 
 static inline struct container *get_container(struct container *c)
 {
diff --git a/include/linux/key.h b/include/linux/key.h
index a38b89bd414c..01bccaa40047 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -90,6 +90,9 @@ struct key_ace {
                kuid_t          uid;
                kgid_t          gid;
                unsigned int    subject_id;
+#ifdef CONFIG_CONTAINERS
+               struct key_tag __rcu *container_tag;
+#endif
        };
 };
 
diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h
index 045dcbb6bb8d..7136d14dd4d7 100644
--- a/include/uapi/linux/keyctl.h
+++ b/include/uapi/linux/keyctl.h
@@ -20,6 +20,7 @@
  */
 enum key_ace_subject_type {
        KEY_ACE_SUBJ_STANDARD   = 0,    /* subject is one of 
key_ace_standard_subject */
+       KEY_ACE_SUBJ_CONTAINER  = 1,    /* subject is a container fd */
        nr__key_ace_subject_type
 };
 
diff --git a/kernel/container.c b/kernel/container.c
index f2706a45f364..81be4ed915c2 100644
--- a/kernel/container.c
+++ b/kernel/container.c
@@ -35,7 +35,9 @@ struct container init_container = {
        .members.next   = &init_task.container_link,
        .members.prev   = &init_task.container_link,
        .children       = LIST_HEAD_INIT(init_container.children),
+#ifdef CONFIG_KEYS
        .req_key_traps  = LIST_HEAD_INIT(init_container.req_key_traps),
+#endif
        .flags          = (1 << CONTAINER_FLAG_INIT_STARTED),
        .lock           = __SPIN_LOCK_UNLOCKED(init_container.lock),
        .seq            = SEQCNT_ZERO(init_fs.seq),
@@ -54,8 +56,6 @@ void put_container(struct container *c)
 
        while (c && refcount_dec_and_test(&c->usage)) {
                BUG_ON(!list_empty(&c->members));
-               if (!list_empty(&c->req_key_traps))
-                       key_del_intercept(c, NULL);
                if (c->pid_ns)
                        put_pid_ns(c->pid_ns);
                if (c->ns)
@@ -71,7 +71,15 @@ void put_container(struct container *c)
 
                if (c->cred)
                        put_cred(c->cred);
+#ifdef CONFIG_KEYS
+               if (!list_empty(&c->req_key_traps))
+                       key_del_intercept(c, NULL);
+               if (c->tag) {
+                       c->tag->removed = true;
+                       key_put_tag(c->tag);
+               }
                key_put(c->keyring);
+#endif
                security_container_free(c);
                kfree(c);
                c = parent;
@@ -209,6 +217,24 @@ const struct file_operations container_fops = {
        .release        = container_release,
 };
 
+/**
+ * fd_to_container - Get the container attached to an fd.
+ */
+struct container *fd_to_container(int fd)
+{
+       struct container *c = ERR_PTR(-EINVAL);
+       struct fd f = fdget(fd);
+
+       if (!f.file)
+               return ERR_PTR(-EBADF);
+
+       if (is_container_file(f.file))
+               c = get_container(f.file->private_data);
+
+       fdput(f);
+       return c;
+}
+
 /*
  * Handle fork/clone.
  *
@@ -290,7 +316,9 @@ static struct container *alloc_container(const char __user 
*name)
 
        INIT_LIST_HEAD(&c->members);
        INIT_LIST_HEAD(&c->children);
+#ifdef CONFIG_KEYS
        INIT_LIST_HEAD(&c->req_key_traps);
+#endif
        init_waitqueue_head(&c->waitq);
        spin_lock_init(&c->lock);
        refcount_set(&c->usage, 1);
@@ -305,8 +333,15 @@ static struct container *alloc_container(const char __user 
*name)
        ret = -EINVAL;
        if (strchr(c->name, '/'))
                goto err;
-
        c->name[len] = 0;
+
+#ifdef CONFIG_KEYS
+       ret = -ENOMEM;
+       c->tag = kzalloc(sizeof(*c->tag), GFP_KERNEL);
+       if (!c->tag)
+               goto err;
+       refcount_set(&c->tag->usage, 1);
+#endif
        return c;
 
 err:
diff --git a/samples/vfs/test-container.c b/samples/vfs/test-container.c
index e24048fdbe33..7b2081693fce 100644
--- a/samples/vfs/test-container.c
+++ b/samples/vfs/test-container.c
@@ -22,6 +22,30 @@
 
 #define KEYCTL_CONTAINER_INTERCEPT     31      /* Intercept upcalls inside a 
container */
 #define KEYCTL_SET_CONTAINER_KEYRING   35      /* Attach a keyring to a 
container */
+#define KEYCTL_GRANT_PERMISSION                36      /* Grant a permit to a 
key */
+
+enum key_ace_subject_type {
+       KEY_ACE_SUBJ_STANDARD   = 0,    /* subject is one of 
key_ace_standard_subject */
+       KEY_ACE_SUBJ_CONTAINER  = 1,    /* subject is a container fd */
+};
+
+enum key_ace_standard_subject {
+       KEY_ACE_EVERYONE        = 0,    /* Everyone, including owner and group 
*/
+       KEY_ACE_GROUP           = 1,    /* The key's group */
+       KEY_ACE_OWNER           = 2,    /* The owner of the key */
+       KEY_ACE_POSSESSOR       = 3,    /* Any process that possesses of the 
key */
+};
+
+#define KEY_ACE_VIEW           0x00000001 /* Can describe the key */
+#define KEY_ACE_READ           0x00000002 /* Can read the key content */
+#define KEY_ACE_WRITE          0x00000004 /* Can update/modify the key content 
*/
+#define KEY_ACE_SEARCH         0x00000008 /* Can find the key by search */
+#define KEY_ACE_LINK           0x00000010 /* Can make a link to the key */
+#define KEY_ACE_SET_SECURITY   0x00000020 /* Can set owner, group, ACL */
+#define KEY_ACE_INVAL          0x00000040 /* Can invalidate the key */
+#define KEY_ACE_REVOKE         0x00000080 /* Can revoke the key */
+#define KEY_ACE_JOIN           0x00000100 /* Can join keyring */
+#define KEY_ACE_CLEAR          0x00000200 /* Can clear keyring */
 
 /* Hope -1 isn't a syscall */
 #ifndef __NR_fsopen
@@ -190,7 +214,7 @@ void container_init(void)
  */
 int main(int argc, char *argv[])
 {
-       key_serial_t keyring;
+       key_serial_t keyring, key;
        pid_t pid;
        int fsfd, mfd, cfd, ws;
 
@@ -271,11 +295,45 @@ int main(int argc, char *argv[])
                exit(1);
        }
 
+       /* We need to grant the container permission to search for keys in the
+        * container keyring.
+        */
+       if (keyctl(KEYCTL_GRANT_PERMISSION, keyring, KEY_ACE_SUBJ_CONTAINER, 
cfd,
+                  KEY_ACE_SEARCH) < 0) {
+               perror("keyctl_grant/s");
+               exit(1);
+       }
+
+       if (keyctl(KEYCTL_GRANT_PERMISSION, keyring,
+                  KEY_ACE_SUBJ_STANDARD, KEY_ACE_OWNER, 0) < 0) {
+               perror("keyctl_grant/s");
+               exit(1);
+       }
+
        if (keyctl(KEYCTL_SET_CONTAINER_KEYRING, cfd, keyring) < 0) {
                perror("keyctl_set_container_keyring");
                exit(1);
        }
 
+       /* Create a key that can be accessed from within the container */
+       printf("Sample key...\n");
+       key = add_key("user", "foobar", "wibble", 6, keyring);
+       if (key == -1) {
+               perror("add_key/s");
+               exit(1);
+       }
+
+       if (keyctl(KEYCTL_GRANT_PERMISSION, key, KEY_ACE_SUBJ_CONTAINER, cfd,
+                  KEY_ACE_VIEW | KEY_ACE_SEARCH | KEY_ACE_READ | KEY_ACE_LINK) 
< 0) {
+               perror("keyctl_grant/s");
+               exit(1);
+       }
+
+       if (keyctl_link(key, keyring) < 0) {
+               perror("keyctl_link");
+               exit(1);
+       }
+
        /* Create a keyring to catch upcalls. */
        printf("Intercepting...\n");
        keyring = add_key("keyring", "upcall", NULL, 0, 
KEY_SPEC_SESSION_KEYRING);
diff --git a/security/keys/permission.c b/security/keys/permission.c
index cb1359f6c668..f16d1665885f 100644
--- a/security/keys/permission.c
+++ b/security/keys/permission.c
@@ -13,6 +13,7 @@
 #include <linux/security.h>
 #include <linux/user_namespace.h>
 #include <linux/uaccess.h>
+#include <linux/container.h>
 #include "internal.h"
 
 struct key_acl default_key_acl = {
@@ -130,6 +131,15 @@ int key_task_permission(const key_ref_t key_ref, const 
struct cred *cred,
                                break;
                        }
                        break;
+#ifdef CONFIG_CONTAINERS
+               case KEY_ACE_SUBJ_CONTAINER: {
+                       const struct key_tag *tag = 
rcu_dereference(ace->container_tag);
+
+                       if (!tag->removed && current->container->tag == tag)
+                               allow |= ace->perm;
+                       break;
+               }
+#endif
                }
        }
 
@@ -185,8 +195,7 @@ EXPORT_SYMBOL(key_validate);
  */
 unsigned int key_acl_to_perm(const struct key_acl *acl)
 {
-       unsigned int perm = 0, tperm;
-       int i;
+       unsigned int perm = 0, tperm, i;
 
        BUILD_BUG_ON(KEY_OTH_VIEW       != KEY_ACE_VIEW         ||
                     KEY_OTH_READ       != KEY_ACE_READ         ||
@@ -237,13 +246,37 @@ unsigned int key_acl_to_perm(const struct key_acl *acl)
        return perm;
 }
 
+/*
+ * Clean up an ACL.
+ */
+static void key_free_acl(struct rcu_head *rcu)
+{
+       struct key_acl *acl = container_of(rcu, struct key_acl, rcu);
+#ifdef CONFIG_CONTAINERS
+       struct key_tag *tag;
+       unsigned int i;
+
+       for (i = 0; i < acl->nr_ace; i++) {
+               const struct key_ace *ace = &acl->aces[i];
+               switch (ace->type) {
+               case KEY_ACE_SUBJ_CONTAINER:
+                       tag = rcu_access_pointer(ace->container_tag);
+                       key_put_tag(ace->container_tag);
+                       break;
+               }
+       }
+#endif
+
+       kfree(acl);
+}
+
 /*
  * Destroy a key's ACL.
  */
 void key_put_acl(struct key_acl *acl)
 {
        if (acl && refcount_dec_and_test(&acl->usage))
-               kfree_rcu(acl, rcu);
+               call_rcu(&acl->rcu, key_free_acl);
 }
 
 /*
@@ -297,6 +330,10 @@ static struct key_acl *key_alloc_acl(const struct key_acl 
*old_acl, int nr, int
                if (i == skip)
                        continue;
                acl->aces[j] = old_acl->aces[i];
+#ifdef CONFIG_CONTAINERS
+               if (acl->aces[j].type == KEY_ACE_SUBJ_CONTAINER)
+                       refcount_inc(&acl->aces[j].container_tag->usage);
+#endif
                j++;
        }
        return acl;
@@ -312,21 +349,39 @@ static long key_change_acl(struct key *key, struct 
key_ace *new_ace)
 
        old = rcu_dereference_protected(key->acl, lockdep_is_held(&key->sem));
 
-       for (i = 0; i < old->nr_ace; i++)
-               if (old->aces[i].type == new_ace->type &&
-                   old->aces[i].subject_id == new_ace->subject_id)
-                       goto found_match;
+       for (i = 0; i < old->nr_ace; i++) {
+               if (old->aces[i].type != new_ace->type)
+                       continue;
+               switch (old->aces[i].type) {
+               case KEY_ACE_SUBJ_STANDARD:
+                       if (old->aces[i].subject_id == new_ace->subject_id)
+                               goto replace_ace;
+                       break;
+#ifdef CONFIG_CONTAINERS
+               case KEY_ACE_SUBJ_CONTAINER:
+                       if (old->aces[i].container_tag == 
new_ace->container_tag)
+                               goto replace_ace;
+                       break;
+#endif
+               default:
+                       break;
+               }
+       }
 
        if (new_ace->perm == 0)
-               return 0; /* No permissions to remove.  Add deny record? */
+               return 0; /* No permissions to cancel.  Add deny record? */
 
        acl = key_alloc_acl(old, 1, -1);
        if (IS_ERR(acl))
                return PTR_ERR(acl);
        acl->aces[i] = *new_ace;
+#ifdef CONFIG_CONTAINERS
+       if (acl->aces[i].type == KEY_ACE_SUBJ_CONTAINER)
+               refcount_inc(&acl->aces[i].container_tag->usage);
+#endif
        goto change;
 
-found_match:
+replace_ace:
        if (new_ace->perm == 0)
                goto delete_ace;
        if (new_ace->perm == old->aces[i].perm)
@@ -360,6 +415,7 @@ long keyctl_grant_permission(key_serial_t keyid,
        key_ref_t key_ref;
        long ret;
 
+       memset(&new_ace, 0, sizeof(new_ace));
        new_ace.type = type;
        new_ace.perm = perm;
 
@@ -370,6 +426,18 @@ long keyctl_grant_permission(key_serial_t keyid,
                new_ace.subject_id = subject;
                break;
 
+#ifdef CONFIG_CONTAINERS
+       case KEY_ACE_SUBJ_CONTAINER: {
+               struct container *c = fd_to_container(subject);
+               if (IS_ERR(c))
+                       return -EINVAL;
+               refcount_inc(&c->tag->usage);
+               new_ace.container_tag = c->tag;
+               put_container(c);
+               break;
+       }
+#endif
+
        default:
                return -ENOENT;
        }
@@ -391,5 +459,9 @@ long keyctl_grant_permission(key_serial_t keyid,
        up_write(&key->sem);
        key_put(key);
 error:
+#ifdef CONFIG_CONTAINERS
+       if (new_ace.type == KEY_ACE_SUBJ_CONTAINER && new_ace.container_tag)
+               key_put_tag(new_ace.container_tag);
+#endif
        return ret;
 }
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index 0a231ede4d2b..f296a1cc979a 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -466,7 +466,7 @@ key_ref_t search_my_process_keyrings(struct 
keyring_search_context *ctx)
 #ifdef CONFIG_CONTAINERS
        if (current->container->keyring) {
                key_ref = keyring_search_aux(
-                       make_key_ref(current->container->keyring, 1), ctx);
+                       make_key_ref(current->container->keyring, false), ctx);
                if (!IS_ERR(key_ref))
                        goto found;
 

Reply via email to