type provides an operation to perform a match between the description on a
      key and a criterion string.
 
-  *  Each key has an owner user ID, a group ID and a permissions mask. These
-     are used to control what a process may do to a key from userspace, and
-     whether a kernel service will be able to find the key.
+  *  Each key has an owner user ID, a group ID and an ACL.  These are used to
+     control what a process may do to a key from userspace, and whether a
+     kernel service will be able to find the key.
 
   *  Each key can be set to expire at a specific time by the key type's
      instantiation function. Keys can also be immortal.
 Key Access Permissions
 ======================
 
-Keys have an owner user ID, a group access ID, and a permissions mask. The mask
-has up to eight bits each for possessor, user, group and other access. Only
-six of each set of eight bits are defined. These permissions granted are:
+Keys have an owner user ID, a group ID and an ACL.  The ACL is made up of a
+sequence of ACEs that each contain three elements:
 
-  *  View
+  * The type of subject.
+  * The subject.
 
-     This permits a key or keyring's attributes to be viewed - including key
-     type and description.
+    These two together indicate the subject to whom the permits are granted.
+    The type can be one of:
 
-  *  Read
+     * ``KEY_ACE_SUBJ_STANDARD``
 
-     This permits a key's payload to be viewed or a keyring's list of linked
-     keys.
+       The subject is a standard 'macro' type.  The subject can be one of:
+
+        * ``KEY_ACE_EVERYONE``
+
+         The permits are granted to everyone.  It replaces the old 'other'
+         type on the assumption that you wouldn't grant a permission to other
+         that you you wouldn't grant to everyone else.
+
+       * ``KEY_ACE_OWNER``
+
+         The permits are granted to the owner of the key (key->uid).
+
+       * ``KEY_ACE_GROUP``
+
+         The permits are granted to the key's group (key->gid).
+
+       * ``KEY_ACE_POSSESSOR``
+
+         The permits are granted to anyone who possesses the key.
+
+  * The set of permits granted to the subject.  These include:
+
+     * ``KEY_ACE_VIEW``
+
+       This permits a key or keyring's attributes to be viewed - including the
+       key type and description.
+
+     * ``KEY_ACE_READ``
+
+       This permits a key's payload to be viewed or a keyring's list of linked
+       keys.
 
-  *  Write
+     * ``KEY_ACE_WRITE``
 
-     This permits a key's payload to be instantiated or updated, or it allows a
-     link to be added to or removed from a keyring.
+       This permits a key's payload to be instantiated or updated, or it allows
+       a link to be added to or removed from a keyring.
 
-  *  Search
+     * ``KEY_ACE_SEARCH``
 
-     This permits keyrings to be searched and keys to be found. Searches can
-     only recurse into nested keyrings that have search permission set.
+       This permits keyrings to be searched and keys to be found. Searches can
+       only recurse into nested keyrings that have search permission set.
 
-  *  Link
+     * ``KEY_ACE_LINK``
 
-     This permits a key or keyring to be linked to. To create a link from a
-     keyring to a key, a process must have Write permission on the keyring and
-     Link permission on the key.
+       This permits a key or keyring to be linked to. To create a link from a
+       keyring to a key, a process must have Write permission on the keyring
+       and Link permission on the key.
 
-  *  Set Attribute
+     * ``KEY_ACE_SET_SECURITY``
 
-     This permits a key's UID, GID and permissions mask to be changed.
+       This permits a key's UID, GID and permissions mask to be changed.
+
+     * ``KEY_ACE_INVAL``
+
+       This permits a key to be invalidated with KEYCTL_INVALIDATE.
+
+     * ``KEY_ACE_REVOKE``
+
+       This permits a key to be revoked with KEYCTL_REVOKE.
+
+     * ``KEY_ACE_JOIN``
+
+       This permits a keyring to be joined as a session by
+       KEYCTL_JOIN_SESSION_KEYRING or KEYCTL_SESSION_TO_PARENT.
+
+     * ``KEY_ACE_CLEAR``
+
+       This permits a keyring to be cleared.
 
 For changing the ownership, group ID or permissions mask, being the owner of
 the key or having the sysadmin capability is sufficient.
 
+The legacy KEYCTL_SETPERM and KEYCTL_DESCRIBE functions can only see/generate
+View, Read, Write, Search, Link and SetAttr permits, and do this for each of
+possessor, user, group and other permission sets as a 32-bit flag mask.  These
+will be approximated/inferred:
+
+       SETPERM Permit  Implied ACE Permit
+       =============== =======================
+       Search          Inval, Join
+       Write           Revoke, Clear
+       Setattr         Set Security, Revoke
+
+       ACE Permit      Described as
+       =============== =======================
+       Inval           Search
+       Join            Search
+       Revoke          Write (unless Setattr)
+       Clear           write
+       Set Security    Setattr
+
+'Other' will be approximated as/inferred from the 'Everyone' subject.
+
 
 SELinux Support
 ===============
 
        struct key *request_key(const struct key_type *type,
                                const char *description,
-                               const char *callout_info);
+                               const char *callout_info,
+                               struct key_acl *acl);
 
     This is used to request a key or keyring with a description that matches
     the description specified according to the key type's match_preparse()
     If successful, the key will have been attached to the default keyring for
     implicitly obtained request-key keys, as set by KEYCTL_SET_REQKEY_KEYRING.
 
+    If a key is created, it will be given the specified ACL.
+
     See also Documentation/security/keys/request-key.rst.
 
 
        struct key *request_key_tag(const struct key_type *type,
                                    const char *description,
                                    struct key_tag *domain_tag,
-                                   const char *callout_info);
+                                   const char *callout_info,
+                                   struct key_acl *acl);
 
     This is identical to request_key(), except that a domain tag may be
     specifies that causes search algorithm to only match keys matching that
                                             struct key_tag *domain_tag,
                                             const void *callout_info,
                                             size_t callout_len,
-                                            void *aux);
+                                            void *aux,
+                                            struct key_acl *acl);
 
     This is identical to request_key_tag(), except that the auxiliary data is
     passed to the key_type->request_key() op if it exists, and the
 
        struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
                                  const struct cred *cred,
-                                 key_perm_t perm,
+                                 struct key_acl *acl,
                                  struct key_restriction *restrict_link,
                                  unsigned long flags,
                                  struct key *dest);
 
 
        struct key *request_key(const struct key_type *type,
                                const char *description,
-                               const char *callout_info);
+                               const char *callout_info,
+                               struct key_acl *acl);
 
 or::
 
        struct key *request_key_tag(const struct key_type *type,
                                    const char *description,
                                    const struct key_tag *domain_tag,
-                                   const char *callout_info);
+                                   const char *callout_info,
+                                   struct key_acl *acl);
 
 or::
 
                                             const struct key_tag *domain_tag,
                                             const char *callout_info,
                                             size_t callout_len,
-                                            void *aux);
+                                            void *aux,
+                                            struct key_acl *acl);
 
 or::
 
 
                                   hash,
                                   NULL,
                                   0,
-                                  ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
-                                   KEY_USR_VIEW),
+                                  &internal_key_acl,
                                   KEY_ALLOC_NOT_IN_QUOTA |
                                   KEY_ALLOC_BUILT_IN);
        if (IS_ERR(key)) {
                keyring_alloc(".blacklist",
                              KUIDT_INIT(0), KGIDT_INIT(0),
                              current_cred(),
-                             (KEY_POS_ALL & ~KEY_POS_SETATTR) |
-                             KEY_USR_VIEW | KEY_USR_READ |
-                             KEY_USR_SEARCH,
+                             &internal_keyring_acl,
                              KEY_ALLOC_NOT_IN_QUOTA |
                              KEY_FLAG_KEEP,
                              NULL, NULL);
 
        builtin_trusted_keys =
                keyring_alloc(".builtin_trusted_keys",
                              KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
-                             ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
-                             KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH),
-                             KEY_ALLOC_NOT_IN_QUOTA,
+                             &internal_key_acl, KEY_ALLOC_NOT_IN_QUOTA,
                              NULL, NULL);
        if (IS_ERR(builtin_trusted_keys))
                panic("Can't allocate builtin trusted keyring\n");
        secondary_trusted_keys =
                keyring_alloc(".secondary_trusted_keys",
                              KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
-                             ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
-                              KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH |
-                              KEY_USR_WRITE),
-                             KEY_ALLOC_NOT_IN_QUOTA,
+                             &internal_writable_keyring_acl, KEY_ALLOC_NOT_IN_QUOTA,
                              get_builtin_and_secondary_restriction(),
                              NULL);
        if (IS_ERR(secondary_trusted_keys))
                                           NULL,
                                           p,
                                           plen,
-                                          ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
-                                          KEY_USR_VIEW | KEY_USR_READ),
+                                          &internal_key_acl,
                                           KEY_ALLOC_NOT_IN_QUOTA |
                                           KEY_ALLOC_BUILT_IN |
                                           KEY_ALLOC_BYPASS_RESTRICTION);
 
                return -ENOMEM;
 
        key = request_key(key_string[0] == 'l' ? &key_type_logon : &key_type_user,
-                         key_desc + 1, NULL);
+                         key_desc + 1, NULL, NULL);
        if (IS_ERR(key)) {
                kzfree(new_key_string);
                return PTR_ERR(key);
 
        struct device *dev = &nvdimm->dev;
 
        sprintf(desc, "%s%s", NVDIMM_PREFIX, nvdimm->dimm_id);
-       key = request_key(&key_type_encrypted, desc, "");
+       key = request_key(&key_type_encrypted, desc, "", NULL);
        if (IS_ERR(key)) {
                if (PTR_ERR(key) == -ENOKEY)
                        dev_dbg(dev, "request_key() found no key\n");
 
 
        _debug("key %s", cell->anonymous_key->description);
        key = request_key(&key_type_rxrpc, cell->anonymous_key->description,
-                         NULL);
+                         NULL, NULL);
        if (IS_ERR(key)) {
                if (PTR_ERR(key) != -ENOKEY) {
                        _leave(" = %ld", PTR_ERR(key));
 
 #include "cifsproto.h"
 static const struct cred *spnego_cred;
 
+static struct key_acl cifs_spnego_key_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .nr_ace = 2,
+       .possessor_viewable = true,
+       .aces = {
+               KEY_POSSESSOR_ACE(KEY_ACE_VIEW | KEY_ACE_SEARCH | KEY_ACE_READ),
+               KEY_OWNER_ACE(KEY_ACE_VIEW),
+       }
+};
+
+static struct key_acl cifs_spnego_keyring_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .nr_ace = 2,
+       .aces = {
+               KEY_POSSESSOR_ACE(KEY_ACE_SEARCH | KEY_ACE_WRITE),
+               KEY_OWNER_ACE(KEY_ACE_VIEW | KEY_ACE_READ | KEY_ACE_CLEAR),
+       }
+};
+
 /* create a new cifs key */
 static int
 cifs_spnego_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
 
        cifs_dbg(FYI, "key description = %s\n", description);
        saved_cred = override_creds(spnego_cred);
-       spnego_key = request_key(&cifs_spnego_key_type, description, "");
+       spnego_key = request_key(&cifs_spnego_key_type, description, "",
+                                &cifs_spnego_key_acl);
        revert_creds(saved_cred);
 
 #ifdef CONFIG_CIFS_DEBUG2
 
        keyring = keyring_alloc(".cifs_spnego",
                                GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
-                               (KEY_POS_ALL & ~KEY_POS_SETATTR) |
-                               KEY_USR_VIEW | KEY_USR_READ,
+                               &cifs_spnego_keyring_acl,
                                KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
        if (IS_ERR(keyring)) {
                ret = PTR_ERR(keyring);
 
 #include "cifsproto.h"
 #include "cifs_debug.h"
 
+static struct key_acl cifs_idmap_key_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .nr_ace = 2,
+       .possessor_viewable = true,
+       .aces = {
+               KEY_POSSESSOR_ACE(KEY_ACE_VIEW | KEY_ACE_SEARCH | KEY_ACE_READ),
+               KEY_OWNER_ACE(KEY_ACE_VIEW),
+       }
+};
+
+static struct key_acl cifs_idmap_keyring_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .nr_ace = 2,
+       .aces = {
+               KEY_POSSESSOR_ACE(KEY_ACE_SEARCH | KEY_ACE_WRITE),
+               KEY_OWNER_ACE(KEY_ACE_VIEW | KEY_ACE_READ),
+       }
+};
+
 /* security id for everyone/world system group */
 static const struct cifs_sid sid_everyone = {
        1, 1, {0, 0, 0, 0, 0, 1}, {0} };
 
        rc = 0;
        saved_cred = override_creds(root_cred);
-       sidkey = request_key(&cifs_idmap_key_type, desc, "");
+       sidkey = request_key(&cifs_idmap_key_type, desc, "",
+                            &cifs_idmap_key_acl);
        if (IS_ERR(sidkey)) {
                rc = -EINVAL;
                cifs_dbg(FYI, "%s: Can't map %cid %u to a SID\n",
                return -ENOMEM;
 
        saved_cred = override_creds(root_cred);
-       sidkey = request_key(&cifs_idmap_key_type, sidstr, "");
+       sidkey = request_key(&cifs_idmap_key_type, sidstr, "",
+                            &cifs_idmap_key_acl);
        if (IS_ERR(sidkey)) {
                rc = -EINVAL;
                cifs_dbg(FYI, "%s: Can't map SID %s to a %cid\n",
 
        keyring = keyring_alloc(".cifs_idmap",
                                GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
-                               (KEY_POS_ALL & ~KEY_POS_SETATTR) |
-                               KEY_USR_VIEW | KEY_USR_READ,
+                               &cifs_idmap_keyring_acl,
                                KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
        if (IS_ERR(keyring)) {
                ret = PTR_ERR(keyring);
 
        }
 
        cifs_dbg(FYI, "%s: desc=%s\n", __func__, desc);
-       key = request_key(&key_type_logon, desc, "");
+       key = request_key(&key_type_logon, desc, "", NULL);
        if (IS_ERR(key)) {
                if (!ses->domainName) {
                        cifs_dbg(FYI, "domainName is NULL\n");
                /* didn't work, try to find a domain key */
                sprintf(desc, "cifs:d:%s", ses->domainName);
                cifs_dbg(FYI, "%s: desc=%s\n", __func__, desc);
-               key = request_key(&key_type_logon, desc, "");
+               key = request_key(&key_type_logon, desc, "", NULL);
                if (IS_ERR(key)) {
                        rc = PTR_ERR(key);
                        goto out_err;
 
        if (!description)
                return ERR_PTR(-ENOMEM);
 
-       key = request_key(&key_type_logon, description, NULL);
+       key = request_key(&key_type_logon, description, NULL, NULL);
        kfree(description);
        if (IS_ERR(key))
                return key;
 
 
 static inline struct key *ecryptfs_get_encrypted_key(char *sig)
 {
-       return request_key(&key_type_encrypted, sig, NULL);
+       return request_key(&key_type_encrypted, sig, NULL, NULL);
 }
 
 #else
 
 {
        int rc = 0;
 
-       (*auth_tok_key) = request_key(&key_type_user, sig, NULL);
+       (*auth_tok_key) = request_key(&key_type_user, sig, NULL, NULL);
        if (!(*auth_tok_key) || IS_ERR(*auth_tok_key)) {
                (*auth_tok_key) = ecryptfs_get_encrypted_key(sig);
                if (!(*auth_tok_key) || IS_ERR(*auth_tok_key)) {
 
        const char *buf;
        int len;
 
-       key = request_key(&key_type_user, "fscache:objlist", NULL);
+       key = request_key(&key_type_user, "fscache:objlist", NULL, NULL);
        if (IS_ERR(key))
                goto no_config;
 
 
        const struct cred       *cred;
 };
 
+static struct key_acl nfs_idmap_key_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .nr_ace = 2,
+       .possessor_viewable = true,
+       .aces = {
+               KEY_POSSESSOR_ACE(KEY_ACE_VIEW | KEY_ACE_SEARCH | KEY_ACE_READ),
+               KEY_OWNER_ACE(KEY_ACE_VIEW),
+       }
+};
+
+static struct key_acl nfs_idmap_keyring_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .nr_ace = 2,
+       .aces = {
+               KEY_POSSESSOR_ACE(KEY_ACE_SEARCH | KEY_ACE_WRITE),
+               KEY_OWNER_ACE(KEY_ACE_VIEW | KEY_ACE_READ),
+       }
+};
+
 static struct user_namespace *idmap_userns(const struct idmap *idmap)
 {
        if (idmap && idmap->cred)
 
        keyring = keyring_alloc(".id_resolver",
                                GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
-                               (KEY_POS_ALL & ~KEY_POS_SETATTR) |
-                               KEY_USR_VIEW | KEY_USR_READ,
+                               &nfs_idmap_keyring_acl,
                                KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
        if (IS_ERR(keyring)) {
                ret = PTR_ERR(keyring);
                return ERR_PTR(ret);
 
        if (!idmap->cred || idmap->cred->user_ns == &init_user_ns)
-               rkey = request_key(&key_type_id_resolver, desc, "");
+               rkey = request_key(&key_type_id_resolver, desc, "",
+                                  &nfs_idmap_key_acl);
        if (IS_ERR(rkey)) {
                mutex_lock(&idmap->idmap_mutex);
                rkey = request_key_with_auxdata(&key_type_id_resolver_legacy,
-                                               desc, NULL, "", 0, idmap);
+                                               desc, NULL, "", 0, idmap,
+                                               &nfs_idmap_key_acl);
                mutex_unlock(&idmap->idmap_mutex);
        }
        if (!IS_ERR(rkey))
        }
 
        rcu_read_lock();
-       rkey->perm |= KEY_USR_VIEW;
-
        ret = key_validate(rkey);
        if (ret < 0)
                goto out_up;
 
        snprintf(hmac_name, CRYPTO_MAX_ALG_NAME, "hmac(%s)",
                 c->auth_hash_name);
 
-       keyring_key = request_key(&key_type_logon, c->auth_key_name, NULL);
+       keyring_key = request_key(&key_type_logon, c->auth_key_name, NULL, NULL);
 
        if (IS_ERR(keyring_key)) {
                ubifs_err(c, "Failed to request key: %ld",
 
 /* key handle serial number */
 typedef int32_t key_serial_t;
 
-/* key handle permissions mask */
-typedef uint32_t key_perm_t;
-
 struct key;
 struct net;
 
 #ifdef CONFIG_KEYS
 
-#undef KEY_DEBUGGING
+#include <linux/keyctl.h>
 
-#define KEY_POS_VIEW   0x01000000      /* possessor can view a key's attributes */
-#define KEY_POS_READ   0x02000000      /* possessor can read key payload / view keyring */
-#define KEY_POS_WRITE  0x04000000      /* possessor can update key payload / add link to keyring */
-#define KEY_POS_SEARCH 0x08000000      /* possessor can find a key in search / search a keyring */
-#define KEY_POS_LINK   0x10000000      /* possessor can create a link to a key/keyring */
-#define KEY_POS_SETATTR        0x20000000      /* possessor can set key attributes */
-#define KEY_POS_ALL    0x3f000000
-
-#define KEY_USR_VIEW   0x00010000      /* user permissions... */
-#define KEY_USR_READ   0x00020000
-#define KEY_USR_WRITE  0x00040000
-#define KEY_USR_SEARCH 0x00080000
-#define KEY_USR_LINK   0x00100000
-#define KEY_USR_SETATTR        0x00200000
-#define KEY_USR_ALL    0x003f0000
-
-#define KEY_GRP_VIEW   0x00000100      /* group permissions... */
-#define KEY_GRP_READ   0x00000200
-#define KEY_GRP_WRITE  0x00000400
-#define KEY_GRP_SEARCH 0x00000800
-#define KEY_GRP_LINK   0x00001000
-#define KEY_GRP_SETATTR        0x00002000
-#define KEY_GRP_ALL    0x00003f00
-
-#define KEY_OTH_VIEW   0x00000001      /* third party permissions... */
-#define KEY_OTH_READ   0x00000002
-#define KEY_OTH_WRITE  0x00000004
-#define KEY_OTH_SEARCH 0x00000008
-#define KEY_OTH_LINK   0x00000010
-#define KEY_OTH_SETATTR        0x00000020
-#define KEY_OTH_ALL    0x0000003f
-
-#define KEY_PERM_UNDEF 0xffffffff
+#undef KEY_DEBUGGING
 
 struct seq_file;
 struct user_struct;
        void                    *data[4];
 };
 
+struct key_ace {
+       unsigned int            type;
+       unsigned int            perm;
+       union {
+               kuid_t          uid;
+               kgid_t          gid;
+               unsigned int    subject_id;
+       };
+};
+
+struct key_acl {
+       refcount_t              usage;
+       unsigned short          nr_ace;
+       bool                    possessor_viewable;
+       struct rcu_head         rcu;
+       struct key_ace          aces[];
+};
+
+#define KEY_POSSESSOR_ACE(perms) {                     \
+               .type = KEY_ACE_SUBJ_STANDARD,          \
+               .perm = perms,                          \
+               .subject_id = KEY_ACE_POSSESSOR         \
+       }
+
+#define KEY_OWNER_ACE(perms) {                         \
+               .type = KEY_ACE_SUBJ_STANDARD,          \
+               .perm = perms,                          \
+               .subject_id = KEY_ACE_OWNER             \
+       }
+
 /*****************************************************************************/
 /*
  * key reference with possession attribute handling
        struct rw_semaphore     sem;            /* change vs change sem */
        struct key_user         *user;          /* owner of this key */
        void                    *security;      /* security data for this key */
+       struct key_acl          __rcu *acl;
        union {
                time64_t        expiry;         /* time at which key expires (or 0) */
                time64_t        revoked_at;     /* time at which key was revoked */
        time64_t                last_used_at;   /* last time used for LRU keyring discard */
        kuid_t                  uid;
        kgid_t                  gid;
-       key_perm_t              perm;           /* access permissions */
        unsigned short          quotalen;       /* length added to quota */
        unsigned short          datalen;        /* payload data length
                                                 * - may not match RCU dereferenced payload
 #define KEY_FLAG_ROOT_CAN_INVAL        7       /* set if key can be invalidated by root without permission */
 #define KEY_FLAG_KEEP          8       /* set if key should not be removed */
 #define KEY_FLAG_UID_KEYRING   9       /* set if key is a user or user session keyring */
+#define KEY_FLAG_HAS_ACL       10      /* Set if KEYCTL_SETACL called on key */
 
        /* the key type and key description string
         * - the desc is used to match a key against search criteria
                             const char *desc,
                             kuid_t uid, kgid_t gid,
                             const struct cred *cred,
-                            key_perm_t perm,
+                            struct key_acl *acl,
                             unsigned long flags,
                             struct key_restriction *restrict_link);
 
 extern struct key *request_key_tag(struct key_type *type,
                                   const char *description,
                                   struct key_tag *domain_tag,
-                                  const char *callout_info);
+                                  const char *callout_info,
+                                  struct key_acl *acl);
 
 extern struct key *request_key_rcu(struct key_type *type,
                                   const char *description,
                                            struct key_tag *domain_tag,
                                            const void *callout_info,
                                            size_t callout_len,
-                                           void *aux);
+                                           void *aux,
+                                           struct key_acl *acl);
 
 /**
  * request_key - Request a key and wait for construction
  * @type: Type of key.
  * @description: The searchable description of the key.
  * @callout_info: The data to pass to the instantiation upcall (or NULL).
+ * @acl: The ACL to attach to a new key (or NULL).
  *
  * As for request_key_tag(), but with the default global domain tag.
  */
 static inline struct key *request_key(struct key_type *type,
                                      const char *description,
-                                     const char *callout_info)
+                                     const char *callout_info,
+                                     struct key_acl *acl)
 {
-       return request_key_tag(type, description, NULL, callout_info);
+       return request_key_tag(type, description, NULL, callout_info, acl);
 }
 
 #ifdef CONFIG_NET
  * @description: The searchable description of the key.
  * @net: The network namespace that is the key's domain of operation.
  * @callout_info: The data to pass to the instantiation upcall (or NULL).
+ * @acl: The ACL to attach to a new key (or NULL).
  *
  * As for request_key() except that it does not add the returned key to a
  * keyring if found, new keys are always allocated in the user's quota, the
  * Furthermore, it then works as wait_for_key_construction() to wait for the
  * completion of keys undergoing construction with a non-interruptible wait.
  */
-#define request_key_net(type, description, net, callout_info) \
-       request_key_tag(type, description, net->key_domain, callout_info);
+#define request_key_net(type, description, net, callout_info, acl)     \
+       request_key_tag(type, description, net->key_domain, callout_info, acl);
 #endif /* CONFIG_NET */
 
 extern int wait_for_key_construction(struct key *key, bool intr);
                                      const char *description,
                                      const void *payload,
                                      size_t plen,
-                                     key_perm_t perm,
+                                     struct key_acl *acl,
                                      unsigned long flags);
 
 extern int key_update(key_ref_t key,
 
 extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
                                 const struct cred *cred,
-                                key_perm_t perm,
+                                struct key_acl *acl,
                                 unsigned long flags,
                                 struct key_restriction *restrict_link,
                                 struct key *dest);
 extern void key_set_timeout(struct key *, unsigned);
 
 extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags,
-                                key_perm_t perm);
+                                u32 desired_perm);
 extern void key_free_user_ns(struct user_namespace *);
 
 /*
  * The permissions required on a key that we're looking up.
  */
-#define        KEY_NEED_VIEW   0x01    /* Require permission to view attributes */
-#define        KEY_NEED_READ   0x02    /* Require permission to read content */
-#define        KEY_NEED_WRITE  0x04    /* Require permission to update / modify */
-#define        KEY_NEED_SEARCH 0x08    /* Require permission to search (keyring) or find (key) */
-#define        KEY_NEED_LINK   0x10    /* Require permission to link */
-#define        KEY_NEED_SETATTR 0x20   /* Require permission to change attributes */
-#define        KEY_NEED_ALL    0x3f    /* All the above permissions */
+#define        KEY_NEED_VIEW   0x001   /* Require permission to view attributes */
+#define        KEY_NEED_READ   0x002   /* Require permission to read content */
+#define        KEY_NEED_WRITE  0x004   /* Require permission to update / modify */
+#define        KEY_NEED_SEARCH 0x008   /* Require permission to search (keyring) or find (key) */
+#define        KEY_NEED_LINK   0x010   /* Require permission to link */
+#define        KEY_NEED_SETSEC 0x020   /* Require permission to set owner, group, ACL */
+#define        KEY_NEED_INVAL  0x040   /* Require permission to invalidate key */
+#define        KEY_NEED_REVOKE 0x080   /* Require permission to revoke key */
+#define        KEY_NEED_JOIN   0x100   /* Require permission to join keyring as session */
+#define        KEY_NEED_CLEAR  0x200   /* Require permission to clear a keyring */
+#define KEY_NEED_ALL   0x3ff
+
+#define OLD_KEY_NEED_SETATTR 0x20 /* Used to be Require permission to change attributes */
+
+extern struct key_acl internal_key_acl;
+extern struct key_acl internal_keyring_acl;
+extern struct key_acl internal_writable_keyring_acl;
 
 static inline short key_read_state(const struct key *key)
 {
 
 
 #include <linux/types.h>
 
+/*
+ * Keyring permission grant definitions
+ */
+enum key_ace_subject_type {
+       KEY_ACE_SUBJ_STANDARD   = 0,    /* subject is one of key_ace_standard_subject */
+       nr__key_ace_subject_type
+};
+
+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 */
+       nr__key_ace_standard_subject
+};
+
+#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 */
+#define KEY_ACE__PERMS         0xffffffff
+
+/*
+ * Old-style permissions mask, deprecated in favour of ACL.
+ */
+#define KEY_POS_VIEW   0x01000000      /* possessor can view a key's attributes */
+#define KEY_POS_READ   0x02000000      /* possessor can read key payload / view keyring */
+#define KEY_POS_WRITE  0x04000000      /* possessor can update key payload / add link to keyring */
+#define KEY_POS_SEARCH 0x08000000      /* possessor can find a key in search / search a keyring */
+#define KEY_POS_LINK   0x10000000      /* possessor can create a link to a key/keyring */
+#define KEY_POS_SETATTR        0x20000000      /* possessor can set key attributes */
+#define KEY_POS_ALL    0x3f000000
+
+#define KEY_USR_VIEW   0x00010000      /* user permissions... */
+#define KEY_USR_READ   0x00020000
+#define KEY_USR_WRITE  0x00040000
+#define KEY_USR_SEARCH 0x00080000
+#define KEY_USR_LINK   0x00100000
+#define KEY_USR_SETATTR        0x00200000
+#define KEY_USR_ALL    0x003f0000
+
+#define KEY_GRP_VIEW   0x00000100      /* group permissions... */
+#define KEY_GRP_READ   0x00000200
+#define KEY_GRP_WRITE  0x00000400
+#define KEY_GRP_SEARCH 0x00000800
+#define KEY_GRP_LINK   0x00001000
+#define KEY_GRP_SETATTR        0x00002000
+#define KEY_GRP_ALL    0x00003f00
+
+#define KEY_OTH_VIEW   0x00000001      /* third party permissions... */
+#define KEY_OTH_READ   0x00000002
+#define KEY_OTH_WRITE  0x00000004
+#define KEY_OTH_SEARCH 0x00000008
+#define KEY_OTH_LINK   0x00000010
+#define KEY_OTH_SETATTR        0x00000020
+#define KEY_OTH_ALL    0x0000003f
+
 /* special process keyring shortcut IDs */
 #define KEY_SPEC_THREAD_KEYRING                -1      /* - key ID for thread-specific keyring */
 #define KEY_SPEC_PROCESS_KEYRING       -2      /* - key ID for process-specific keyring */
 
                else
                        key = key_ref_to_ptr(kref);
        } else {
-               key = request_key(&key_type_user, name, NULL);
+               key = request_key(&key_type_user, name, NULL, NULL);
        }
        if (IS_ERR(key)) {
                pr_err("key not found, id: %s\n", name);
 
        int err = 0;
        struct ceph_crypto_key *ckey;
 
-       ukey = request_key(&key_type_ceph, name, NULL);
+       ukey = request_key(&key_type_ceph, name, NULL, NULL);
        if (IS_ERR(ukey)) {
                /* request_key errors don't map nicely to mount(2)
                   errors; don't even try, but still printk */
 
 
 #define        DNS_ERRORNO_OPTION      "dnserror"
 
+static struct key_acl dns_keyring_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .nr_ace = 2,
+       .aces = {
+               KEY_POSSESSOR_ACE(KEY_ACE_SEARCH | KEY_ACE_WRITE),
+               KEY_OWNER_ACE(KEY_ACE_VIEW | KEY_ACE_READ | KEY_ACE_CLEAR),
+       }
+};
+
 /*
  * Preparse instantiation data for a dns_resolver key.
  *
 
        keyring = keyring_alloc(".dns_resolver",
                                GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
-                               (KEY_POS_ALL & ~KEY_POS_SETATTR) |
-                               KEY_USR_VIEW | KEY_USR_READ,
+                               &dns_keyring_acl,
                                KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
        if (IS_ERR(keyring)) {
                ret = PTR_ERR(keyring);
 
 
 #include "internal.h"
 
+static struct key_acl dns_key_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .nr_ace = 2,
+       .possessor_viewable = true,
+       .aces = {
+               KEY_POSSESSOR_ACE(KEY_ACE_VIEW | KEY_ACE_SEARCH | KEY_ACE_READ),
+               KEY_OWNER_ACE(KEY_ACE_VIEW | KEY_ACE_INVAL),
+       }
+};
+
 /**
  * dns_query - Query the DNS
  * @net: The network namespace to operate in.
         * add_key() to preinstall malicious redirections
         */
        saved_cred = override_creds(dns_resolver_cache);
-       rkey = request_key_net(&key_type_dns_resolver, desc, net, options);
+       rkey = request_key_net(&key_type_dns_resolver, desc, net, options,
+                              &dns_key_acl);
        revert_creds(saved_cred);
        kfree(desc);
        if (IS_ERR(rkey)) {
 
        down_read(&rkey->sem);
        set_bit(KEY_FLAG_ROOT_CAN_INVAL, &rkey->flags);
-       rkey->perm |= KEY_USR_VIEW;
-
        ret = key_validate(rkey);
        if (ret < 0)
                goto put;
 
 #include <keys/user-type.h>
 #include "ar-internal.h"
 
+static struct key_acl rxrpc_null_key_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .nr_ace = 1,
+       .aces = {
+               KEY_POSSESSOR_ACE(KEY_ACE_SEARCH | KEY_ACE_READ),
+       }
+};
+
 static int rxrpc_vet_description_s(const char *);
 static int rxrpc_preparse(struct key_preparsed_payload *);
 static int rxrpc_preparse_s(struct key_preparsed_payload *);
        if (IS_ERR(description))
                return PTR_ERR(description);
 
-       key = request_key_net(&key_type_rxrpc, description, sock_net(&rx->sk), NULL);
+       key = request_key_net(&key_type_rxrpc, description, sock_net(&rx->sk),
+                             NULL, NULL);
        if (IS_ERR(key)) {
                kfree(description);
                _leave(" = %ld", PTR_ERR(key));
        if (IS_ERR(description))
                return PTR_ERR(description);
 
-       key = request_key_net(&key_type_keyring, description, sock_net(&rx->sk), NULL);
+       key = request_key_net(&key_type_keyring, description, sock_net(&rx->sk),
+                             NULL, NULL);
        if (IS_ERR(key)) {
                kfree(description);
                _leave(" = %ld", PTR_ERR(key));
        _enter("");
 
        key = key_alloc(&key_type_rxrpc, "x",
-                       GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, 0,
+                       GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
+                       &internal_key_acl,
                        KEY_ALLOC_NOT_IN_QUOTA, NULL);
        if (IS_ERR(key)) {
                _leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key));
 
        key = key_alloc(&key_type_rxrpc, keyname,
                        GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
-                       KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA, NULL);
+                       &rxrpc_null_key_acl, KEY_ALLOC_NOT_IN_QUOTA, NULL);
        if (IS_ERR(key))
                return key;
 
 
 
                key = key_create_or_update(make_key_ref(builtin_regdb_keys, 1),
                                           "asymmetric", NULL, p, plen,
-                                          ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
-                                           KEY_USR_VIEW | KEY_USR_READ),
+                                          &internal_key_acl, 
                                           KEY_ALLOC_NOT_IN_QUOTA |
                                           KEY_ALLOC_BUILT_IN |
                                           KEY_ALLOC_BYPASS_RESTRICTION);
        builtin_regdb_keys =
                keyring_alloc(".builtin_regdb_keys",
                              KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
-                             ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
-                             KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH),
+                             &internal_keyring_acl, 
                              KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
        if (IS_ERR(builtin_regdb_keys))
                return PTR_ERR(builtin_regdb_keys);
 
 
        if (!keyring[id]) {
                keyring[id] =
-                       request_key(&key_type_keyring, keyring_name[id], NULL);
+                       request_key(&key_type_keyring, keyring_name[id],
+                                   NULL, NULL);
                if (IS_ERR(keyring[id])) {
                        int err = PTR_ERR(keyring[id]);
                        pr_err("no %s keyring: %d\n", keyring_name[id], err);
        return -EOPNOTSUPP;
 }
 
-static int __integrity_init_keyring(const unsigned int id, key_perm_t perm,
+static int __integrity_init_keyring(const unsigned int id, struct key_acl *acl,
                                    struct key_restriction *restriction)
 {
        const struct cred *cred = current_cred();
        int err = 0;
 
        keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0),
-                                   KGIDT_INIT(0), cred, perm,
+                                   KGIDT_INIT(0), cred, acl,
                                    KEY_ALLOC_NOT_IN_QUOTA, restriction, NULL);
        if (IS_ERR(keyring[id])) {
                err = PTR_ERR(keyring[id]);
 int __init integrity_init_keyring(const unsigned int id)
 {
        struct key_restriction *restriction;
-       key_perm_t perm;
-
-       perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW
-               | KEY_USR_READ | KEY_USR_SEARCH;
+       struct key_acl *acl = &internal_keyring_acl;
 
        if (id == INTEGRITY_KEYRING_PLATFORM) {
                restriction = NULL;
                return -ENOMEM;
 
        restriction->check = restrict_link_to_ima;
-       perm |= KEY_USR_WRITE;
+       acl = &internal_writable_keyring_acl;
 
 out:
-       return __integrity_init_keyring(id, perm, restriction);
+       return __integrity_init_keyring(id, acl, restriction);
 }
 
-int __init integrity_add_key(const unsigned int id, const void *data,
-                            off_t size, key_perm_t perm)
+static int __init integrity_add_key(const unsigned int id, const void *data,
+                                   off_t size, struct key_acl *acl)
 {
        key_ref_t key;
        int rc = 0;
                return -EINVAL;
 
        key = key_create_or_update(make_key_ref(keyring[id], 1), "asymmetric",
-                                  NULL, data, size, perm,
+                                  NULL, data, size, acl ?: &internal_key_acl,
                                   KEY_ALLOC_NOT_IN_QUOTA);
        if (IS_ERR(key)) {
                rc = PTR_ERR(key);
        void *data;
        loff_t size;
        int rc;
-       key_perm_t perm;
 
        rc = kernel_read_file_from_path(path, &data, &size, 0,
                                        READING_X509_CERTIFICATE);
                return rc;
        }
 
-       perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ;
-
        pr_info("Loading X.509 certificate: %s\n", path);
-       rc = integrity_add_key(id, (const void *)data, size, perm);
+       rc = integrity_add_key(id, data, size, NULL);
 
        vfree(data);
        return rc;
 }
 
 int __init integrity_load_cert(const unsigned int id, const char *source,
-                              const void *data, size_t len, key_perm_t perm)
+                              const void *data, size_t len, struct key_acl *acl)
 {
        if (!data)
                return -EINVAL;
 
        pr_info("Loading X.509 certificate: %s\n", source);
-       return integrity_add_key(id, data, len, perm);
+       return integrity_add_key(id, data, len, acl);
 }
 
                else
                        key = key_ref_to_ptr(kref);
        } else {
-               key = request_key(&key_type_asymmetric, name, NULL);
+               key = request_key(&key_type_asymmetric, name, NULL, NULL);
        }
 
        if (IS_ERR(key)) {
 
        struct encrypted_key_payload *ekp;
        int rc;
 
-       evm_key = request_key(&key_type_encrypted, EVMKEY, NULL);
+       evm_key = request_key(&key_type_encrypted, EVMKEY, NULL, NULL);
        if (IS_ERR(evm_key))
                return -ENOENT;
 
 
 #include <keys/system_keyring.h>
 
 
+static struct key_acl integrity_blacklist_keyring_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .nr_ace = 2,
+       .aces = {
+               KEY_POSSESSOR_ACE(KEY_ACE_SEARCH | KEY_ACE_WRITE),
+               KEY_OWNER_ACE(KEY_ACE_VIEW | KEY_ACE_READ | KEY_ACE_WRITE | KEY_ACE_SEARCH),
+       }
+};
+
 struct key *ima_blacklist_keyring;
 
 /*
 
        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,
+                               &integrity_blacklist_keyring_acl,
                                KEY_ALLOC_NOT_IN_QUOTA,
                                restriction, NULL);
 
 
 #include <linux/key.h>
 #include <linux/audit.h>
 
+struct key_acl;
+
 /* iint action cache flags */
 #define IMA_MEASURE            0x00000001
 #define IMA_MEASURED           0x00000002
 int __init integrity_init_keyring(const unsigned int id);
 int __init integrity_load_x509(const unsigned int id, const char *path);
 int __init integrity_load_cert(const unsigned int id, const char *source,
-                              const void *data, size_t len, key_perm_t perm);
+                              const void *data, size_t len, struct key_acl *acl);
 #else
 
 static inline int integrity_digsig_verify(const unsigned int id,
 static inline int __init integrity_load_cert(const unsigned int id,
                                             const char *source,
                                             const void *data, size_t len,
-                                            key_perm_t perm)
+                                            struct key_acl *acl)
 {
        return 0;
 }
 
 #include <linux/slab.h>
 #include "../integrity.h"
 
+static struct key_acl platform_key_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .nr_ace = 2,
+       .aces = {
+               KEY_POSSESSOR_ACE(KEY_ACE_SEARCH | KEY_ACE_READ),
+               KEY_OWNER_ACE(KEY_ACE_VIEW),
+       }
+};
+
 /**
  * add_to_platform_keyring - Add to platform keyring without validation.
  * @source: Source of key
 void __init add_to_platform_keyring(const char *source, const void *data,
                                    size_t len)
 {
-       key_perm_t perm;
        int rc;
 
-       perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW;
-
        rc = integrity_load_cert(INTEGRITY_KEYRING_PLATFORM, source, data, len,
-                                perm);
+                                &platform_key_acl);
        if (rc)
                pr_info("Error adding keys to platform keyring %s\n", source);
 }
 
        const struct user_key_payload *upayload;
        struct key *ukey;
 
-       ukey = request_key(&key_type_user, master_desc, NULL);
+       ukey = request_key(&key_type_user, master_desc, NULL, NULL);
        if (IS_ERR(ukey))
                goto error;
 
 
        struct trusted_key_payload *tpayload;
        struct key *tkey;
 
-       tkey = request_key(&key_type_trusted, trusted_desc, NULL);
+       tkey = request_key(&key_type_trusted, trusted_desc, NULL, NULL);
        if (IS_ERR(tkey))
                goto error;
 
 
 
                key_user_put(key->user);
                key_put_tag(key->domain_tag);
+               key_put_acl(rcu_access_pointer(key->acl));
                kfree(key->description);
 
                memzero_explicit(key, sizeof(*key));
                        if (key->type == key_gc_dead_keytype) {
                                gc_state |= KEY_GC_FOUND_DEAD_KEY;
                                set_bit(KEY_FLAG_DEAD, &key->flags);
-                               key->perm = 0;
                                goto skip_dead_key;
                        } else if (key->type == &key_type_keyring &&
                                   key->restrict_link) {
 
 extern spinlock_t key_serial_lock;
 extern struct mutex key_construction_mutex;
 extern wait_queue_head_t request_key_conswq;
+extern struct key_acl default_key_acl;
+extern struct key_acl joinable_keyring_acl;
 
 extern void key_set_index_key(struct keyring_index_key *index_key);
+
 extern struct key_type *key_type_lookup(const char *type);
 extern void key_type_put(struct key_type *ktype);
 
                                        const void *callout_info,
                                        size_t callout_len,
                                        void *aux,
+                                       struct key_acl *acl,
                                        struct key *dest_keyring,
                                        unsigned long flags);
 
 
 extern int key_task_permission(const key_ref_t key_ref,
                               const struct cred *cred,
-                              key_perm_t perm);
+                              u32 desired_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);
 
 /*
  * Check to see whether permission is granted to use a key in the desired way.
                                  const char __user *, key_serial_t);
 extern long keyctl_read_key(key_serial_t, char __user *, size_t);
 extern long keyctl_chown_key(key_serial_t, uid_t, gid_t);
-extern long keyctl_setperm_key(key_serial_t, key_perm_t);
+extern long keyctl_setperm_key(key_serial_t, unsigned int);
 extern long keyctl_instantiate_key(key_serial_t, const void __user *,
                                   size_t, key_serial_t);
 extern long keyctl_negate_key(key_serial_t, unsigned, key_serial_t);
 
  * @uid: The owner of the new key.
  * @gid: The group ID for the new key's group permissions.
  * @cred: The credentials specifying UID namespace.
- * @perm: The permissions mask of the new key.
+ * @acl: The ACL to attach to the new key.
  * @flags: Flags specifying quota properties.
  * @restrict_link: Optional link restriction for new keyrings.
  *
  */
 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,
+                     struct key_acl *acl, unsigned long flags,
                      struct key_restriction *restrict_link)
 {
        struct key_user *user = NULL;
        desclen = strlen(desc);
        quotalen = desclen + 1 + type->def_datalen;
 
+       if (!acl)
+               acl = &default_key_acl;
+
        /* get hold of the key tracking for this user */
        user = key_user_lookup(uid);
        if (!user)
        key->datalen = type->def_datalen;
        key->uid = uid;
        key->gid = gid;
-       key->perm = perm;
+       refcount_inc(&acl->usage);
+       rcu_assign_pointer(key->acl, acl);
        key->restrict_link = restrict_link;
        key->last_used_at = ktime_get_real_seconds();
 
  * @description: The searchable description for the key.
  * @payload: The data to use to instantiate or update the key.
  * @plen: The length of @payload.
- * @perm: The permissions mask for a new key.
+ * @acl: The ACL to attach if a key is created.
  * @flags: The quota flags for a new key.
  *
  * Search the destination keyring for a key of the same description and if one
                               const char *description,
                               const void *payload,
                               size_t plen,
-                              key_perm_t perm,
+                              struct key_acl *acl,
                               unsigned long flags)
 {
        struct keyring_index_key index_key = {
                        goto found_matching_key;
        }
 
-       /* if the client doesn't provide, decide on the permissions we want */
-       if (perm == KEY_PERM_UNDEF) {
-               perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR;
-               perm |= KEY_USR_VIEW;
-
-               if (index_key.type->read)
-                       perm |= KEY_POS_READ;
-
-               if (index_key.type == &key_type_keyring ||
-                   index_key.type->update)
-                       perm |= KEY_POS_WRITE;
-       }
-
        /* allocate a new key */
        key = key_alloc(index_key.type, index_key.description,
-                       cred->fsuid, cred->fsgid, cred, perm, flags, NULL);
+                       cred->fsuid, cred->fsgid, cred, acl, flags, NULL);
        if (IS_ERR(key)) {
                key_ref = ERR_CAST(key);
                goto error_link_end;
 
        /* create or update the requested key and add it to the target
         * keyring */
        key_ref = key_create_or_update(keyring_ref, type, description,
-                                      payload, plen, KEY_PERM_UNDEF,
-                                      KEY_ALLOC_IN_QUOTA);
+                                      payload, plen, NULL, KEY_ALLOC_IN_QUOTA);
        if (!IS_ERR(key_ref)) {
                ret = key_ref_to_ptr(key_ref)->serial;
                key_ref_put(key_ref);
 
        /* do the search */
        key = request_key_and_link(ktype, description, NULL, callout_info,
-                                  callout_len, NULL, key_ref_to_ptr(dest_ref),
+                                  callout_len, NULL, NULL,
+                                  key_ref_to_ptr(dest_ref),
                                   KEY_ALLOC_IN_QUOTA);
        if (IS_ERR(key)) {
                ret = PTR_ERR(key);
        struct key *key;
        long ret;
 
-       key_ref = lookup_user_key(id, 0, KEY_NEED_WRITE);
+       key_ref = lookup_user_key(id, 0, KEY_NEED_REVOKE);
        if (IS_ERR(key_ref)) {
                ret = PTR_ERR(key_ref);
-               if (ret != -EACCES)
-                       goto error;
-               key_ref = lookup_user_key(id, 0, KEY_NEED_SETATTR);
-               if (IS_ERR(key_ref)) {
-                       ret = PTR_ERR(key_ref);
-                       goto error;
-               }
+               goto error;
        }
 
        key = key_ref_to_ptr(key_ref);
 
        kenter("%d", id);
 
-       key_ref = lookup_user_key(id, 0, KEY_NEED_SEARCH);
+       key_ref = lookup_user_key(id, 0, KEY_NEED_INVAL);
        if (IS_ERR(key_ref)) {
                ret = PTR_ERR(key_ref);
 
        struct key *keyring;
        long ret;
 
-       keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE);
+       keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_CLEAR);
        if (IS_ERR(keyring_ref)) {
                ret = PTR_ERR(keyring_ref);
 
                         size_t buflen)
 {
        struct key *key, *instkey;
+       unsigned int perm;
        key_ref_t key_ref;
        char *infobuf;
        long ret;
        key = key_ref_to_ptr(key_ref);
        desclen = strlen(key->description);
 
+       rcu_read_lock();
+       perm = key_acl_to_perm(rcu_dereference(key->acl));
+       rcu_read_unlock();
+
        /* calculate how much information we're going to return */
        ret = -ENOMEM;
        infobuf = kasprintf(GFP_KERNEL,
                            key->type->name,
                            from_kuid_munged(current_user_ns(), key->uid),
                            from_kgid_munged(current_user_ns(), key->gid),
-                           key->perm);
+                           perm);
        if (!infobuf)
                goto error2;
        infolen = strlen(infobuf);
                goto error;
 
        key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL,
-                                 KEY_NEED_SETATTR);
+                                 KEY_NEED_SETSEC);
        if (IS_ERR(key_ref)) {
                ret = PTR_ERR(key_ref);
                goto error;
  * the key need not be fully instantiated yet.  If the caller does not have
  * sysadmin capability, it may only change the permission on keys that it owns.
  */
-long keyctl_setperm_key(key_serial_t id, key_perm_t perm)
+long keyctl_setperm_key(key_serial_t id, unsigned int perm)
 {
+       struct key_acl *acl;
        struct key *key;
        key_ref_t key_ref;
        long ret;
+       int nr, i, j;
 
-       ret = -EINVAL;
        if (perm & ~(KEY_POS_ALL | KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL))
-               goto error;
+               return -EINVAL;
+
+       nr = 0;
+       if (perm & KEY_POS_ALL) nr++;
+       if (perm & KEY_USR_ALL) nr++;
+       if (perm & KEY_GRP_ALL) nr++;
+       if (perm & KEY_OTH_ALL) nr++;
 
        key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL,
-                                 KEY_NEED_SETATTR);
+                                 KEY_NEED_SETSEC);
        if (IS_ERR(key_ref)) {
                ret = PTR_ERR(key_ref);
                goto error;
 
        key = key_ref_to_ptr(key_ref);
 
-       /* make the changes with the locks held to prevent chown/chmod races */
-       ret = -EACCES;
-       down_write(&key->sem);
+       ret = -EOPNOTSUPP;
+       if (test_bit(KEY_FLAG_HAS_ACL, &key->flags))
+               goto error_key;
 
-       /* if we're not the sysadmin, we can only change a key that we own */
-       if (capable(CAP_SYS_ADMIN) || uid_eq(key->uid, current_fsuid())) {
-               key->perm = perm;
-               ret = 0;
+       ret = -ENOMEM;
+       acl = kzalloc(struct_size(acl, aces, nr), GFP_KERNEL);
+       if (!acl)
+               goto error_key;
+
+       refcount_set(&acl->usage, 1);
+       acl->nr_ace = nr;
+       j = 0;
+       for (i = 0; i < 4; i++) {
+               struct key_ace *ace = &acl->aces[j];
+               unsigned int subset = (perm >> (i * 8)) & KEY_OTH_ALL;
+
+               if (!subset)
+                       continue;
+               ace->type = KEY_ACE_SUBJ_STANDARD;
+               ace->subject_id = KEY_ACE_EVERYONE + i;
+               ace->perm = subset;
+               if (subset & (KEY_OTH_WRITE | KEY_OTH_SETATTR))
+                       ace->perm |= KEY_ACE_REVOKE;
+               if (subset & KEY_OTH_SEARCH)
+                       ace->perm |= KEY_ACE_INVAL;
+               if (key->type == &key_type_keyring) {
+                       if (subset & KEY_OTH_SEARCH)
+                               ace->perm |= KEY_ACE_JOIN;
+                       if (subset & KEY_OTH_WRITE)
+                               ace->perm |= KEY_ACE_CLEAR;
+               }
+               j++;
        }
 
+       /* make the changes with the locks held to prevent chown/chmod races */
+       down_write(&key->sem);
+       ret = key_set_acl(key, acl);
        up_write(&key->sem);
+error_key:
        key_put(key);
 error:
        return ret;
        long ret;
 
        key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL,
-                                 KEY_NEED_SETATTR);
+                                 KEY_NEED_SETSEC);
        if (IS_ERR(key_ref)) {
                /* setting the timeout on a key under construction is permitted
                 * if we have the authorisation token handy */
  * Attempt to install the calling process's session keyring on the process's
  * parent process.
  *
- * The keyring must exist and must grant the caller LINK permission, and the
+ * The keyring must exist and must grant the caller JOIN permission, and the
  * parent process must be single-threaded and must have the same effective
  * ownership as this process and mustn't be SUID/SGID.
  *
        struct cred *cred;
        int ret;
 
-       keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_NEED_LINK);
+       keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_NEED_JOIN);
        if (IS_ERR(keyring_r))
                return PTR_ERR(keyring_r);
 
        char *restriction = NULL;
        long ret;
 
-       key_ref = lookup_user_key(id, 0, KEY_NEED_SETATTR);
+       key_ref = lookup_user_key(id, 0, KEY_NEED_SETSEC);
        if (IS_ERR(key_ref))
                return PTR_ERR(key_ref);
 
 
        case KEYCTL_SETPERM:
                return keyctl_setperm_key((key_serial_t) arg2,
-                                         (key_perm_t) arg3);
+                                         (unsigned int)arg3);
 
        case KEYCTL_INSTANTIATE:
                return keyctl_instantiate_key((key_serial_t) arg2,
 
        return ret;
 }
 
-/*
- * Allocate a keyring and link into the destination keyring.
+/**
+ * keyring_alloc - Allocate a keyring and link into the destination
+ * @description: The key description to allow the key to be searched out.
+ * @uid: The owner of the new key.
+ * @gid: The group ID for the new key's group permissions.
+ * @cred: The credentials specifying UID namespace.
+ * @acl: The ACL to attach to the new key.
+ * @flags: Flags specifying quota properties.
+ * @restrict_link: Optional link restriction for new keyrings.
+ * @dest: Destination keyring.
  */
 struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
-                         const struct cred *cred, key_perm_t perm,
+                         const struct cred *cred, struct key_acl *acl,
                          unsigned long flags,
                          struct key_restriction *restrict_link,
                          struct key *dest)
        int ret;
 
        keyring = key_alloc(&key_type_keyring, description,
-                           uid, gid, cred, perm, flags, restrict_link);
+                           uid, gid, cred, acl, flags, restrict_link);
        if (!IS_ERR(keyring)) {
                ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL);
                if (ret < 0) {
 /*
  * Find a keyring with the specified name.
  *
- * Only keyrings that have nonzero refcount, are not revoked, and are owned by a
- * user in the current user namespace are considered.  If @uid_keyring is %true,
- * the keyring additionally must have been allocated as a user or user session
- * keyring; otherwise, it must grant Search permission directly to the caller.
+ * Only keyrings that have nonzero refcount, are not revoked, and are owned by
+ * a user in the current user namespace are considered.  If @uid_keyring is
+ * %true, the keyring additionally must have been allocated as a user or user
+ * session keyring; otherwise, it must grant JOIN permission directly to the
+ * caller (ie. not through possession).
  *
  * Returns a pointer to the keyring with the keyring's refcount having being
  * incremented on success.  -ENOKEY is returned if a key could not be found.
                                continue;
                } else {
                        if (key_permission(make_key_ref(keyring, 0),
-                                          KEY_NEED_SEARCH) < 0)
+                                          KEY_NEED_JOIN) < 0)
                                continue;
                }
 
 
 
 #include <linux/export.h>
 #include <linux/security.h>
+#include <linux/user_namespace.h>
+#include <linux/uaccess.h>
 #include "internal.h"
 
+struct key_acl default_key_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .nr_ace = 2,
+       .possessor_viewable = true,
+       .aces = {
+               KEY_POSSESSOR_ACE(KEY_ACE__PERMS & ~KEY_ACE_JOIN),
+               KEY_OWNER_ACE(KEY_ACE_VIEW),
+       }
+};
+EXPORT_SYMBOL(default_key_acl);
+
+struct key_acl joinable_keyring_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .nr_ace = 2,
+       .possessor_viewable = true,
+       .aces   = {
+               KEY_POSSESSOR_ACE(KEY_ACE__PERMS & ~KEY_ACE_JOIN),
+               KEY_OWNER_ACE(KEY_ACE_VIEW | KEY_ACE_READ | KEY_ACE_LINK | KEY_ACE_JOIN),
+       }
+};
+EXPORT_SYMBOL(joinable_keyring_acl);
+
+struct key_acl internal_key_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .nr_ace = 2,
+       .aces = {
+               KEY_POSSESSOR_ACE(KEY_ACE_SEARCH),
+               KEY_OWNER_ACE(KEY_ACE_VIEW | KEY_ACE_READ | KEY_ACE_SEARCH),
+       }
+};
+EXPORT_SYMBOL(internal_key_acl);
+
+struct key_acl internal_keyring_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .nr_ace = 2,
+       .aces = {
+               KEY_POSSESSOR_ACE(KEY_ACE_SEARCH),
+               KEY_OWNER_ACE(KEY_ACE_VIEW | KEY_ACE_READ | KEY_ACE_SEARCH),
+       }
+};
+EXPORT_SYMBOL(internal_keyring_acl);
+
+struct key_acl internal_writable_keyring_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .nr_ace = 2,
+       .aces = {
+               KEY_POSSESSOR_ACE(KEY_ACE_SEARCH | KEY_ACE_WRITE),
+               KEY_OWNER_ACE(KEY_ACE_VIEW | KEY_ACE_READ | KEY_ACE_WRITE | KEY_ACE_SEARCH),
+       }
+};
+EXPORT_SYMBOL(internal_writable_keyring_acl);
+
 /**
  * key_task_permission - Check a key can be used
  * @key_ref: The key to check.
  * @cred: The credentials to use.
- * @perm: The permissions to check for.
+ * @desired_perm: The permission to check for.
  *
  * Check to see whether permission is granted to use a key in the desired way,
  * but permit the security modules to override.
  * permissions bits or the LSM check.
  */
 int key_task_permission(const key_ref_t key_ref, const struct cred *cred,
-                       unsigned perm)
+                       unsigned int desired_perm)
 {
-       struct key *key;
-       key_perm_t kperm;
-       int ret;
+       const struct key_acl *acl;
+       const struct key *key;
+       unsigned int allow = 0;
+       int i;
+
+       BUILD_BUG_ON(KEY_NEED_VIEW      != KEY_ACE_VIEW         ||
+                    KEY_NEED_READ      != KEY_ACE_READ         ||
+                    KEY_NEED_WRITE     != KEY_ACE_WRITE        ||
+                    KEY_NEED_SEARCH    != KEY_ACE_SEARCH       ||
+                    KEY_NEED_LINK      != KEY_ACE_LINK         ||
+                    KEY_NEED_SETSEC    != KEY_ACE_SET_SECURITY ||
+                    KEY_NEED_INVAL     != KEY_ACE_INVAL        ||
+                    KEY_NEED_REVOKE    != KEY_ACE_REVOKE       ||
+                    KEY_NEED_JOIN      != KEY_ACE_JOIN         ||
+                    KEY_NEED_CLEAR     != KEY_ACE_CLEAR);
 
        key = key_ref_to_ptr(key_ref);
 
-       /* use the second 8-bits of permissions for keys the caller owns */
-       if (uid_eq(key->uid, cred->fsuid)) {
-               kperm = key->perm >> 16;
-               goto use_these_perms;
-       }
+       rcu_read_lock();
 
-       /* use the third 8-bits of permissions for keys the caller has a group
-        * membership in common with */
-       if (gid_valid(key->gid) && key->perm & KEY_GRP_ALL) {
-               if (gid_eq(key->gid, cred->fsgid)) {
-                       kperm = key->perm >> 8;
-                       goto use_these_perms;
-               }
+       acl = rcu_dereference(key->acl);
+       if (!acl || acl->nr_ace == 0)
+               goto no_access_rcu;
+
+       for (i = 0; i < acl->nr_ace; i++) {
+               const struct key_ace *ace = &acl->aces[i];
 
-               ret = groups_search(cred->group_info, key->gid);
-               if (ret) {
-                       kperm = key->perm >> 8;
-                       goto use_these_perms;
+               switch (ace->type) {
+               case KEY_ACE_SUBJ_STANDARD:
+                       switch (ace->subject_id) {
+                       case KEY_ACE_POSSESSOR:
+                               if (is_key_possessed(key_ref))
+                                       allow |= ace->perm;
+                               break;
+                       case KEY_ACE_OWNER:
+                               if (uid_eq(key->uid, cred->fsuid))
+                                       allow |= ace->perm;
+                               break;
+                       case KEY_ACE_GROUP:
+                               if (gid_valid(key->gid)) {
+                                       if (gid_eq(key->gid, cred->fsgid))
+                                               allow |= ace->perm;
+                                       else if (groups_search(cred->group_info, key->gid))
+                                               allow |= ace->perm;
+                               }
+                               break;
+                       case KEY_ACE_EVERYONE:
+                               allow |= ace->perm;
+                               break;
+                       }
+                       break;
                }
        }
 
-       /* otherwise use the least-significant 8-bits */
-       kperm = key->perm;
-
-use_these_perms:
+       rcu_read_unlock();
 
-       /* use the top 8-bits of permissions for keys the caller possesses
-        * - possessor permissions are additive with other permissions
-        */
-       if (is_key_possessed(key_ref))
-               kperm |= key->perm >> 24;
+       if (!(allow & desired_perm))
+               goto no_access;
 
-       kperm = kperm & perm & KEY_NEED_ALL;
+       return security_key_permission(key_ref, cred, desired_perm);
 
-       if (kperm != perm)
-               return -EACCES;
-
-       /* let LSM be the final arbiter */
-       return security_key_permission(key_ref, cred, perm);
+no_access_rcu:
+       rcu_read_unlock();
+no_access:
+       return -EACCES;
 }
 EXPORT_SYMBOL(key_task_permission);
 
        return 0;
 }
 EXPORT_SYMBOL(key_validate);
+
+/*
+ * Roughly render an ACL to an old-style permissions mask.  We cannot
+ * accurately render what the ACL, particularly if it has ACEs that represent
+ * subjects outside of { poss, user, group, other }.
+ */
+unsigned int key_acl_to_perm(const struct key_acl *acl)
+{
+       unsigned int perm = 0, tperm;
+       int i;
+
+       BUILD_BUG_ON(KEY_OTH_VIEW       != KEY_ACE_VIEW         ||
+                    KEY_OTH_READ       != KEY_ACE_READ         ||
+                    KEY_OTH_WRITE      != KEY_ACE_WRITE        ||
+                    KEY_OTH_SEARCH     != KEY_ACE_SEARCH       ||
+                    KEY_OTH_LINK       != KEY_ACE_LINK         ||
+                    KEY_OTH_SETATTR    != KEY_ACE_SET_SECURITY);
+
+       if (!acl || acl->nr_ace == 0)
+               return 0;
+
+       for (i = 0; i < acl->nr_ace; i++) {
+               const struct key_ace *ace = &acl->aces[i];
+
+               switch (ace->type) {
+               case KEY_ACE_SUBJ_STANDARD:
+                       tperm = ace->perm & KEY_OTH_ALL;
+
+                       /* Invalidation and joining were allowed by SEARCH */
+                       if (ace->perm & (KEY_ACE_INVAL | KEY_ACE_JOIN))
+                               tperm |= KEY_OTH_SEARCH;
+
+                       /* Revocation was allowed by either SETATTR or WRITE */
+                       if ((ace->perm & KEY_ACE_REVOKE) && !(tperm & KEY_OTH_SETATTR))
+                               tperm |= KEY_OTH_WRITE;
+
+                       /* Clearing was allowed by WRITE */
+                       if (ace->perm & KEY_ACE_CLEAR)
+                               tperm |= KEY_OTH_WRITE;
+
+                       switch (ace->subject_id) {
+                       case KEY_ACE_POSSESSOR:
+                               perm |= tperm << 24;
+                               break;
+                       case KEY_ACE_OWNER:
+                               perm |= tperm << 16;
+                               break;
+                       case KEY_ACE_GROUP:
+                               perm |= tperm << 8;
+                               break;
+                       case KEY_ACE_EVERYONE:
+                               perm |= tperm << 0;
+                               break;
+                       }
+               }
+       }
+
+       return perm;
+}
+
+/*
+ * 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);
+}
+
+/*
+ * Try to set the ACL.  This either attaches or discards the proposed ACL.
+ */
+long key_set_acl(struct key *key, struct key_acl *acl)
+{
+       int i;
+
+       /* If we're not the sysadmin, we can only change a key that we own. */
+       if (!capable(CAP_SYS_ADMIN) && !uid_eq(key->uid, current_fsuid())) {
+               key_put_acl(acl);
+               return -EACCES;
+       }
+
+       for (i = 0; i < acl->nr_ace; i++) {
+               const struct key_ace *ace = &acl->aces[i];
+               if (ace->type == KEY_ACE_SUBJ_STANDARD &&
+                   ace->subject_id == KEY_ACE_POSSESSOR) {
+                       if (ace->perm & KEY_ACE_VIEW)
+                               acl->possessor_viewable = true;
+                       break;
+               }
+       }
+
+       rcu_swap_protected(key->acl, acl, lockdep_is_held(&key->sem));
+       key_put_acl(acl);
+       return 0;
+}
 
 
 unsigned persistent_keyring_expiry = 3 * 24 * 3600; /* Expire after 3 days of non-use */
 
+static struct key_acl persistent_register_keyring_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .nr_ace = 2,
+       .aces = {
+               KEY_POSSESSOR_ACE(KEY_ACE_SEARCH | KEY_ACE_WRITE),
+               KEY_OWNER_ACE(KEY_ACE_VIEW | KEY_ACE_READ),
+       }
+};
+
+static struct key_acl persistent_keyring_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .nr_ace = 2,
+       .possessor_viewable = true,
+       .aces = {
+               KEY_POSSESSOR_ACE(KEY_ACE_VIEW | KEY_ACE_READ | KEY_ACE_WRITE |
+                                 KEY_ACE_SEARCH | KEY_ACE_LINK |
+                                 KEY_ACE_CLEAR | KEY_ACE_INVAL),
+               KEY_OWNER_ACE(KEY_ACE_VIEW | KEY_ACE_READ),
+       }
+};
+
 /*
  * Create the persistent keyring register for the current user namespace.
  *
        struct key *reg = keyring_alloc(".persistent_register",
                                        KUIDT_INIT(0), KGIDT_INIT(0),
                                        current_cred(),
-                                       ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
-                                        KEY_USR_VIEW | KEY_USR_READ),
+                                       &persistent_register_keyring_acl,
                                        KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
        if (IS_ERR(reg))
                return PTR_ERR(reg);
 
        persistent = keyring_alloc(index_key->description,
                                   uid, INVALID_GID, current_cred(),
-                                  ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
-                                   KEY_USR_VIEW | KEY_USR_READ),
+                                  &persistent_keyring_acl,
                                   KEY_ALLOC_NOT_IN_QUOTA, NULL,
                                   ns->persistent_keyring_register);
        if (IS_ERR(persistent))
 
 }
 
 static void *proc_keys_start(struct seq_file *p, loff_t *_pos)
+       __acquires(rcu)
        __acquires(key_serial_lock)
 {
        key_serial_t pos = *_pos;
        struct key *key;
 
+       rcu_read_lock();
        spin_lock(&key_serial_lock);
 
        if (*_pos > INT_MAX)
 
 static void proc_keys_stop(struct seq_file *p, void *v)
        __releases(key_serial_lock)
+       __releases(rcu)
 {
        spin_unlock(&key_serial_lock);
+       rcu_read_unlock();
 }
 
 static int proc_keys_show(struct seq_file *m, void *v)
 {
+       const struct key_acl *acl;
        struct rb_node *_p = v;
        struct key *key = rb_entry(_p, struct key, serial_node);
        unsigned long flags;
        time64_t now, expiry;
        char xbuf[16];
        short state;
+       bool check_pos;
        u64 timo;
        int rc;
 
                                           KEYRING_SEARCH_RECURSE),
        };
 
-       key_ref = make_key_ref(key, 0);
+       acl = rcu_dereference(key->acl);
+       check_pos = acl->possessor_viewable;
 
        /* determine if the key is possessed by this process (a test we can
         * skip if the key does not indicate the possessor can view it
         */
-       if (key->perm & KEY_POS_VIEW) {
-               rcu_read_lock();
+       key_ref = make_key_ref(key, 0);
+       if (check_pos) {
                skey_ref = search_cred_keyrings_rcu(&ctx);
-               rcu_read_unlock();
                if (!IS_ERR(skey_ref)) {
                        key_ref_put(skey_ref);
                        key_ref = make_key_ref(key, 1);
        /* check whether the current task is allowed to view the key */
        rc = key_task_permission(key_ref, ctx.cred, KEY_NEED_VIEW);
        if (rc < 0)
-               return 0;
+               goto out;
 
        now = ktime_get_real_seconds();
 
-       rcu_read_lock();
-
        /* come up with a suitable timeout value */
        expiry = READ_ONCE(key->expiry);
        if (expiry == 0) {
                   showflag(flags, 'i', KEY_FLAG_INVALIDATED),
                   refcount_read(&key->usage),
                   xbuf,
-                  key->perm,
+                  key_acl_to_perm(acl),
                   from_kuid_munged(seq_user_ns(m), key->uid),
                   from_kgid_munged(seq_user_ns(m), key->gid),
                   key->type->name);
                key->type->describe(key, m);
        seq_putc(m, '\n');
 
-       rcu_read_unlock();
+out:
        return 0;
 }
 
 
        .uid            = GLOBAL_ROOT_UID,
 };
 
+static struct key_acl user_reg_keyring_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .possessor_viewable = true,
+       .nr_ace = 2,
+       .aces = {
+               KEY_POSSESSOR_ACE(KEY_ACE_WRITE | KEY_ACE_SEARCH),
+               KEY_OWNER_ACE(KEY_ACE_VIEW | KEY_ACE_READ),
+       }
+};
+
+static struct key_acl user_keyring_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .possessor_viewable = true,
+       .nr_ace = 2,
+       .aces = {
+               KEY_POSSESSOR_ACE(KEY_ACE_VIEW | KEY_ACE_READ | KEY_ACE_WRITE |
+                                 KEY_ACE_SEARCH | KEY_ACE_LINK),
+               KEY_OWNER_ACE(KEY_ACE__PERMS & ~(KEY_ACE_JOIN | KEY_ACE_SET_SECURITY)),
+       }
+};
+
+static struct key_acl session_keyring_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .possessor_viewable = true,
+       .nr_ace = 2,
+       .aces = {
+               KEY_POSSESSOR_ACE(KEY_ACE__PERMS & ~KEY_ACE_JOIN),
+               KEY_OWNER_ACE(KEY_ACE_VIEW | KEY_ACE_READ),
+       }
+};
+
+static struct key_acl thread_and_process_keyring_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .possessor_viewable = true,
+       .nr_ace = 2,
+       .aces = {
+               KEY_POSSESSOR_ACE(KEY_ACE__PERMS & ~(KEY_ACE_JOIN | KEY_ACE_SET_SECURITY)),
+               KEY_OWNER_ACE(KEY_ACE_VIEW),
+       }
+};
+
 /*
  * Get or create a user register keyring.
  */
        if (!reg_keyring) {
                reg_keyring = keyring_alloc(".user_reg",
                                            user_ns->owner, INVALID_GID,
-                                           &init_cred,
-                                           KEY_POS_WRITE | KEY_POS_SEARCH |
-                                           KEY_USR_VIEW | KEY_USR_READ,
-                                           0,
-                                           NULL, NULL);
+                                           &init_cred, &user_reg_keyring_acl,
+                                           0, NULL, NULL);
                if (!IS_ERR(reg_keyring))
                        smp_store_release(&user_ns->user_keyring_register,
                                          reg_keyring);
        const struct cred *cred = current_cred();
        struct user_namespace *user_ns = current_user_ns();
        struct key *reg_keyring, *uid_keyring, *session_keyring;
-       key_perm_t user_keyring_perm;
        key_ref_t uid_keyring_r, session_keyring_r;
        uid_t uid = from_kuid(user_ns, cred->user->uid);
        char buf[20];
        int ret;
 
-       user_keyring_perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL;
-
        kenter("%u", uid);
 
        reg_keyring = get_user_register(user_ns);
        kdebug("_uid %p", uid_keyring_r);
        if (uid_keyring_r == ERR_PTR(-EAGAIN)) {
                uid_keyring = keyring_alloc(buf, cred->user->uid, INVALID_GID,
-                                           cred, user_keyring_perm,
+                                           cred, &user_keyring_acl,
                                            KEY_ALLOC_UID_KEYRING |
                                            KEY_ALLOC_IN_QUOTA,
                                            NULL, reg_keyring);
        kdebug("_uid_ses %p", session_keyring_r);
        if (session_keyring_r == ERR_PTR(-EAGAIN)) {
                session_keyring = keyring_alloc(buf, cred->user->uid, INVALID_GID,
-                                               cred, user_keyring_perm,
+                                               cred, &user_keyring_acl,
                                                KEY_ALLOC_UID_KEYRING |
                                                KEY_ALLOC_IN_QUOTA,
                                                NULL, NULL);
                return 0;
 
        keyring = keyring_alloc("_tid", new->uid, new->gid, new,
-                               KEY_POS_ALL | KEY_USR_VIEW,
+                               &thread_and_process_keyring_acl,
                                KEY_ALLOC_QUOTA_OVERRUN,
                                NULL, NULL);
        if (IS_ERR(keyring))
                return 0;
 
        keyring = keyring_alloc("_pid", new->uid, new->gid, new,
-                               KEY_POS_ALL | KEY_USR_VIEW,
+                               &thread_and_process_keyring_acl,
                                KEY_ALLOC_QUOTA_OVERRUN,
                                NULL, NULL);
        if (IS_ERR(keyring))
                        flags = KEY_ALLOC_IN_QUOTA;
 
                keyring = keyring_alloc("_ses", cred->uid, cred->gid, cred,
-                                       KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ,
-                                       flags, NULL, NULL);
+                                       &session_keyring_acl, flags, NULL, NULL);
                if (IS_ERR(keyring))
                        return PTR_ERR(keyring);
        } else {
  * returned key reference.
  */
 key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
-                         key_perm_t perm)
+                         unsigned int desired_perm)
 {
        struct keyring_search_context ctx = {
                .match_data.cmp         = lookup_user_key_possessed,
                case -ERESTARTSYS:
                        goto invalid_key;
                default:
-                       if (perm)
+                       if (desired_perm)
                                goto invalid_key;
                case 0:
                        break;
                }
-       } else if (perm) {
+       } else if (desired_perm) {
                ret = key_validate(key);
                if (ret < 0)
                        goto invalid_key;
                goto invalid_key;
 
        /* check the permissions */
-       ret = key_task_permission(key_ref, ctx.cred, perm);
-       if (ret < 0)
-               goto invalid_key;
+       if (desired_perm) {
+               ret = key_task_permission(key_ref, ctx.cred, desired_perm);
+               if (ret < 0)
+                       goto invalid_key;
+       }
 
        key->last_used_at = ktime_get_real_seconds();
 
        if (PTR_ERR(keyring) == -ENOKEY) {
                /* not found - try and create a new one */
                keyring = keyring_alloc(
-                       name, old->uid, old->gid, old,
-                       KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_LINK,
+                       name, old->uid, old->gid, old, &joinable_keyring_acl,
                        KEY_ALLOC_IN_QUOTA, NULL, NULL);
                if (IS_ERR(keyring)) {
                        ret = PTR_ERR(keyring);
                        goto error2;
                }
+               goto no_perm_test;
        } else if (IS_ERR(keyring)) {
                ret = PTR_ERR(keyring);
                goto error2;
                goto error3;
        }
 
+       ret = key_task_permission(make_key_ref(keyring, false), old,
+                                 KEY_NEED_JOIN);
+       if (ret < 0)
+               goto error3;
+
+no_perm_test:
        /* we've got a keyring - now to install it */
        ret = install_session_keyring_to_cred(new, keyring);
        if (ret < 0)
 
 
        cred = get_current_cred();
        keyring = keyring_alloc(desc, cred->fsuid, cred->fsgid, cred,
-                               KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ,
-                               KEY_ALLOC_QUOTA_OVERRUN, NULL, NULL);
+                               NULL, KEY_ALLOC_QUOTA_OVERRUN, NULL, NULL);
        put_cred(cred);
        if (IS_ERR(keyring)) {
                ret = PTR_ERR(keyring);
                               struct key *dest_keyring,
                               unsigned long flags,
                               struct key_user *user,
+                              struct key_acl *acl,
                               struct key **_key)
 {
        struct assoc_array_edit *edit = NULL;
        struct key *key;
-       key_perm_t perm;
        key_ref_t key_ref;
        int ret;
 
        *_key = NULL;
        mutex_lock(&user->cons_lock);
 
-       perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR;
-       perm |= KEY_USR_VIEW;
-       if (ctx->index_key.type->read)
-               perm |= KEY_POS_READ;
-       if (ctx->index_key.type == &key_type_keyring ||
-           ctx->index_key.type->update)
-               perm |= KEY_POS_WRITE;
-
        key = key_alloc(ctx->index_key.type, ctx->index_key.description,
                        ctx->cred->fsuid, ctx->cred->fsgid, ctx->cred,
-                       perm, flags, NULL);
+                       acl, flags, NULL);
        if (IS_ERR(key))
                goto alloc_failed;
 
                                          const char *callout_info,
                                          size_t callout_len,
                                          void *aux,
+                                         struct key_acl *acl,
                                          struct key *dest_keyring,
                                          unsigned long flags)
 {
                goto error_put_dest_keyring;
        }
 
-       ret = construct_alloc_key(ctx, dest_keyring, flags, user, &key);
+       ret = construct_alloc_key(ctx, dest_keyring, flags, user, acl, &key);
        key_user_put(user);
 
        if (ret == 0) {
  * @callout_info: The data to pass to the instantiation upcall (or NULL).
  * @callout_len: The length of callout_info.
  * @aux: Auxiliary data for the upcall.
+ * @acl: The ACL to attach if a new key is created.
  * @dest_keyring: Where to cache the key.
  * @flags: Flags to key_alloc().
  *
                                 const void *callout_info,
                                 size_t callout_len,
                                 void *aux,
+                                struct key_acl *acl,
                                 struct key *dest_keyring,
                                 unsigned long flags)
 {
                        goto error_free;
 
                key = construct_key_and_link(&ctx, callout_info, callout_len,
-                                            aux, dest_keyring, flags);
+                                            aux, acl, dest_keyring, flags);
        }
 
 error_free:
  * @description: The searchable description of the key.
  * @domain_tag: The domain in which the key operates.
  * @callout_info: The data to pass to the instantiation upcall (or NULL).
+ * @acl: The ACL to attach if a new key is created.
  *
  * As for request_key_and_link() except that it does not add the returned key
  * to a keyring if found, new keys are always allocated in the user's quota,
 struct key *request_key_tag(struct key_type *type,
                            const char *description,
                            struct key_tag *domain_tag,
-                           const char *callout_info)
+                           const char *callout_info,
+                           struct key_acl *acl)
 {
        struct key *key;
        size_t callout_len = 0;
                callout_len = strlen(callout_info);
        key = request_key_and_link(type, description, domain_tag,
                                   callout_info, callout_len,
-                                  NULL, NULL, KEY_ALLOC_IN_QUOTA);
+                                  NULL, acl, NULL, KEY_ALLOC_IN_QUOTA);
        if (!IS_ERR(key)) {
                ret = wait_for_key_construction(key, false);
                if (ret < 0) {
  * @callout_info: The data to pass to the instantiation upcall (or NULL).
  * @callout_len: The length of callout_info.
  * @aux: Auxiliary data for the upcall.
+ * @acl: The ACL to attach if a new key is created.
  *
  * As for request_key_and_link() except that it does not add the returned key
  * to a keyring if found and new keys are always allocated in the user's quota.
                                     struct key_tag *domain_tag,
                                     const void *callout_info,
                                     size_t callout_len,
-                                    void *aux)
+                                    void *aux,
+                                    struct key_acl *acl)
 {
        struct key *key;
        int ret;
 
        key = request_key_and_link(type, description, domain_tag,
                                   callout_info, callout_len,
-                                  aux, NULL, KEY_ALLOC_IN_QUOTA);
+                                  aux, acl, NULL, KEY_ALLOC_IN_QUOTA);
        if (!IS_ERR(key)) {
                ret = wait_for_key_construction(key, false);
                if (ret < 0) {
 
 static void request_key_auth_destroy(struct key *);
 static long request_key_auth_read(const struct key *, char __user *, size_t);
 
+static struct key_acl request_key_auth_acl = {
+       .usage  = REFCOUNT_INIT(1),
+       .nr_ace = 2,
+       .possessor_viewable = true,
+       .aces = {
+               KEY_POSSESSOR_ACE(KEY_ACE_VIEW | KEY_ACE_READ | KEY_ACE_SEARCH |
+                                 KEY_ACE_LINK),
+               KEY_OWNER_ACE(KEY_ACE_VIEW),
+       }
+};
+
 /*
  * The request-key authorisation key type definition.
  */
 
        authkey = key_alloc(&key_type_request_key_auth, desc,
                            cred->fsuid, cred->fsgid, cred,
-                           KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH | KEY_POS_LINK |
-                           KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA, NULL);
+                           &request_key_auth_acl,
+                           KEY_ALLOC_NOT_IN_QUOTA, NULL);
        if (IS_ERR(authkey)) {
                ret = PTR_ERR(authkey);
                goto error_free_rka;
 
 {
        struct key *key;
        struct key_security_struct *ksec;
+       unsigned oldstyle_perm;
        u32 sid;
 
        /* if no specific permissions are requested, we skip the
        if (perm == 0)
                return 0;
 
+       oldstyle_perm = perm & (KEY_NEED_VIEW | KEY_NEED_READ | KEY_NEED_WRITE |
+                               KEY_NEED_SEARCH | KEY_NEED_LINK);
+       if (perm & KEY_NEED_SETSEC)
+               oldstyle_perm |= OLD_KEY_NEED_SETATTR;
+       if (perm & KEY_NEED_INVAL)
+               oldstyle_perm |= KEY_NEED_SEARCH;
+       if (perm & KEY_NEED_REVOKE && !(perm & OLD_KEY_NEED_SETATTR))
+               oldstyle_perm |= KEY_NEED_WRITE;
+       if (perm & KEY_NEED_JOIN)
+               oldstyle_perm |= KEY_NEED_SEARCH;
+       if (perm & KEY_NEED_CLEAR)
+               oldstyle_perm |= KEY_NEED_WRITE;
+
        sid = cred_sid(cred);
 
        key = key_ref_to_ptr(key_ref);
        ksec = key->security;
 
        return avc_has_perm(&selinux_state,
-                           sid, ksec->sid, SECCLASS_KEY, perm, NULL);
+                           sid, ksec->sid, SECCLASS_KEY, oldstyle_perm, NULL);
 }
 
 static int selinux_key_getsecurity(struct key *key, char **_buffer)
 
 #endif
        if (perm & (KEY_NEED_READ | KEY_NEED_SEARCH | KEY_NEED_VIEW))
                request |= MAY_READ;
-       if (perm & (KEY_NEED_WRITE | KEY_NEED_LINK | KEY_NEED_SETATTR))
+       if (perm & (KEY_NEED_WRITE | KEY_NEED_LINK | KEY_NEED_SETSEC |
+                   KEY_NEED_INVAL | KEY_NEED_REVOKE | KEY_NEED_CLEAR))
                request |= MAY_WRITE;
        rc = smk_access(tkp, keyp->security, request, &ad);
        rc = smk_bu_note("key access", tkp, keyp->security, request, rc);