Merge tag 'keys-acl-20190703' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowel...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 9 Jul 2019 02:56:57 +0000 (19:56 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 9 Jul 2019 02:56:57 +0000 (19:56 -0700)
Pull keyring ACL support from David Howells:
 "This changes the permissions model used by keys and keyrings to be
  based on an internal ACL by the following means:

   - Replace the permissions mask internally with an ACL that contains a
     list of ACEs, each with a specific subject with a permissions mask.
     Potted default ACLs are available for new keys and keyrings.

     ACE subjects can be macroised to indicate the UID and GID specified
     on the key (which remain). Future commits will be able to add
     additional subject types, such as specific UIDs or domain
     tags/namespaces.

     Also split a number of permissions to give finer control. Examples
     include splitting the revocation permit from the change-attributes
     permit, thereby allowing someone to be granted permission to revoke
     a key without allowing them to change the owner; also the ability
     to join a keyring is split from the ability to link to it, thereby
     stopping a process accessing a keyring by joining it and thus
     acquiring use of possessor permits.

   - Provide a keyctl to allow the granting or denial of one or more
     permits to a specific subject. Direct access to the ACL is not
     granted, and the ACL cannot be viewed"

* tag 'keys-acl-20190703' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
  keys: Provide KEYCTL_GRANT_PERMISSION
  keys: Replace uid/gid/perm permissions checking with an ACL

32 files changed:
1  2 
certs/blacklist.c
certs/system_keyring.c
fs/afs/security.c
fs/cifs/connect.c
fs/ecryptfs/ecryptfs_kernel.h
fs/ecryptfs/keystore.c
fs/fscache/object-list.c
include/linux/key.h
lib/digsig.c
net/ceph/ceph_common.c
net/rxrpc/key.c
security/integrity/digsig.c
security/integrity/digsig_asymmetric.c
security/integrity/evm/evm_crypto.c
security/integrity/ima/ima_mok.c
security/integrity/integrity.h
security/keys/compat.c
security/keys/encrypted-keys/encrypted.c
security/keys/encrypted-keys/masterkey_trusted.c
security/keys/gc.c
security/keys/internal.h
security/keys/key.c
security/keys/keyctl.c
security/keys/keyring.c
security/keys/permission.c
security/keys/persistent.c
security/keys/proc.c
security/keys/process_keys.c
security/keys/request_key.c
security/keys/request_key_auth.c
security/selinux/hooks.c
security/smack/smack_lsm.c

diff --combined certs/blacklist.c
index ec00bf337eb673b28bced4f1521d9d0a007a0c2e,39de9d68b21e2861b64d8a59505aeb491c20b073..93d70b885f8ea38980be751357909a6264558ae3
@@@ -1,8 -1,12 +1,8 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* System hash blacklist.
   *
   * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public Licence
 - * as published by the Free Software Foundation; either version
 - * 2 of the Licence, or (at your option) any later version.
   */
  
  #define pr_fmt(fmt) "blacklist: "fmt
@@@ -89,8 -93,7 +89,7 @@@ int mark_hash_blacklisted(const char *h
                                   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)) {
@@@ -149,9 -152,7 +148,7 @@@ static int __init blacklist_init(void
                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);
diff --combined certs/system_keyring.c
index 1eba08a1af82733dc84b08fcdd8797d022922329,2873a4ce282813e2e218dd76eb890d9f16b06852..57be78b5fdfc30fd77b76ce29d00d4b63b12cfe4
@@@ -1,8 -1,12 +1,8 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* System trusted keyring for trusted public keys
   *
   * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public Licence
 - * as published by the Free Software Foundation; either version
 - * 2 of the Licence, or (at your option) any later version.
   */
  
  #include <linux/export.h>
@@@ -99,9 -103,7 +99,7 @@@ static __init int system_trusted_keyrin
        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))
@@@ -163,8 -162,7 +158,7 @@@ static __init int load_system_certifica
                                           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);
diff --combined fs/afs/security.c
index 71e71c07568f6f0675662ae976a0061c7132b8c7,3185898237b213e5d14021ef595e8d57cbe42039..8866703b2e6c061f7aa63c6a08dc7e8ae6625801
@@@ -1,8 -1,12 +1,8 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* AFS security handling
   *
   * Copyright (C) 2007, 2017 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License
 - * as published by the Free Software Foundation; either version
 - * 2 of the License, or (at your option) any later version.
   */
  
  #include <linux/init.h>
@@@ -28,7 -32,7 +28,7 @@@ struct key *afs_request_key(struct afs_
  
        _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));
diff --combined fs/cifs/connect.c
index 8dd6637a3cbb7887e582652c47cc0735916e0cd4,6e50d3e879486e27397e7f379dde7ca3a31a8f06..93f7082daf526c6aeedfca5f405692459267903d
@@@ -476,7 -476,6 +476,7 @@@ cifs_reconnect(struct TCP_Server_Info *
        spin_lock(&GlobalMid_Lock);
        server->nr_targets = 1;
  #ifdef CONFIG_CIFS_DFS_UPCALL
 +      spin_unlock(&GlobalMid_Lock);
        cifs_sb = find_super_by_tcp(server);
        if (IS_ERR(cifs_sb)) {
                rc = PTR_ERR(cifs_sb);
        }
        cifs_dbg(FYI, "%s: will retry %d target(s)\n", __func__,
                 server->nr_targets);
 +      spin_lock(&GlobalMid_Lock);
  #endif
        if (server->tcpStatus == CifsExiting) {
                /* the demux thread will exit normally
@@@ -2992,7 -2990,7 +2992,7 @@@ cifs_set_cifscreds(struct smb_vol *vol
        }
  
        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;
index 1c1a56be7ea2feb35314419f79aa08ceb12dbd62,6460bd2a4e9de91916be3f555e7898cd68e16a45..67844fe41a61d17ac94ea6c58457c4a858fdb59d
@@@ -1,4 -1,3 +1,4 @@@
 +/* SPDX-License-Identifier: GPL-2.0-or-later */
  /**
   * eCryptfs: Linux filesystem encryption layer
   * Kernel declarations.
@@@ -9,6 -8,21 +9,6 @@@
   *   Author(s): Michael A. Halcrow <mahalcro@us.ibm.com>
   *              Trevor S. Highland <trevor.highland@gmail.com>
   *              Tyler Hicks <tyhicks@ou.edu>
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License as
 - * published by the Free Software Foundation; either version 2 of the
 - * License, or (at your option) any later version.
 - *
 - * This program is distributed in the hope that it will be useful, but
 - * WITHOUT ANY WARRANTY; without even the implied warranty of
 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 - * General Public License for more details.
 - *
 - * You should have received a copy of the GNU General Public License
 - * along with this program; if not, write to the Free Software
 - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 - * 02111-1307, USA.
   */
  
  #ifndef ECRYPTFS_KERNEL_H
@@@ -91,7 -105,7 +91,7 @@@ ecryptfs_get_encrypted_key_payload_data
  
  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
diff --combined fs/ecryptfs/keystore.c
index 9536e592e25a4caa0fd4154671e58f8d466f50af,923a6006cceafa2d4364258635e1732541de6569..ba382f1359181b38391ebb04eda432b6719f345e
@@@ -1,4 -1,3 +1,4 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /**
   * eCryptfs: Linux filesystem encryption layer
   * In-kernel key management code.  Includes functions to parse and
@@@ -9,6 -8,21 +9,6 @@@
   *   Author(s): Michael A. Halcrow <mhalcrow@us.ibm.com>
   *              Michael C. Thompson <mcthomps@us.ibm.com>
   *              Trevor S. Highland <trevor.highland@gmail.com>
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License as
 - * published by the Free Software Foundation; either version 2 of the
 - * License, or (at your option) any later version.
 - *
 - * This program is distributed in the hope that it will be useful, but
 - * WITHOUT ANY WARRANTY; without even the implied warranty of
 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 - * General Public License for more details.
 - *
 - * You should have received a copy of the GNU General Public License
 - * along with this program; if not, write to the Free Software
 - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 - * 02111-1307, USA.
   */
  
  #include <crypto/hash.h>
@@@ -1610,7 -1624,7 +1610,7 @@@ int ecryptfs_keyring_auth_tok_for_sig(s
  {
        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)) {
diff --combined fs/fscache/object-list.c
index 72ebfe578f4098e7e0880ac1ddc65775ce2fa3a0,6a672289e5ec64069095e14274a07b895a747ddf..67b7bda5647a6e5e2639b6f90e0d7c83472ae36b
@@@ -1,8 -1,12 +1,8 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* Global fscache object list maintainer and viewer
   *
   * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public Licence
 - * as published by the Free Software Foundation; either version
 - * 2 of the Licence, or (at your option) any later version.
   */
  
  #define FSCACHE_DEBUG_LEVEL COOKIE
@@@ -317,7 -321,7 +317,7 @@@ static void fscache_objlist_config(stru
        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;
  
diff --combined include/linux/key.h
index 91f391cd272e094d3d74683f08ac226fe8e1ce20,bc4adfd254fea58852e8d0dd93290495e62701c0..6fef6684501f56c86a3686d4cb609be9f76ac29e
@@@ -1,9 -1,14 +1,9 @@@
 +/* SPDX-License-Identifier: GPL-2.0-or-later */
  /* Authentication token and access key management
   *
   * Copyright (C) 2004, 2007 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
   *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License
 - * as published by the Free Software Foundation; either version
 - * 2 of the License, or (at your option) any later version.
 - *
 - *
   * See Documentation/security/keys/core.rst for information on keys/keyrings.
   */
  
  /* 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;
@@@ -113,6 -83,36 +78,36 @@@ union key_payload 
        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
@@@ -179,6 -179,7 +174,7 @@@ struct key 
        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
@@@ -258,7 -259,7 +254,7 @@@ extern struct key *key_alloc(struct key
                             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);
  
@@@ -295,7 -296,8 +291,8 @@@ static inline void key_ref_put(key_ref_
  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,
@@@ -306,21 -308,24 +303,24 @@@ extern struct key *request_key_with_aux
                                            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);
@@@ -352,7 -358,7 +353,7 @@@ extern key_ref_t key_create_or_update(k
                                      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,
@@@ -372,7 -378,7 +373,7 @@@ extern int key_unlink(struct key *keyri
  
  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);
@@@ -405,19 -411,29 +406,29 @@@ static inline key_serial_t key_serial(c
  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)
  {
diff --combined lib/digsig.c
index e0627c3e53b2e77ea3d0835e1b7666e24da5e93d,ce87ca2e092987adb657c020d17b8392d441fb30..ab0800f98eaf06df45fa7820bb547194e149c3aa
@@@ -1,4 -1,3 +1,4 @@@
 +// SPDX-License-Identifier: GPL-2.0-only
  /*
   * Copyright (C) 2011 Nokia Corporation
   * Copyright (C) 2011 Intel Corporation
@@@ -7,6 -6,10 +7,6 @@@
   * Dmitry Kasatkin <dmitry.kasatkin@nokia.com>
   *                 <dmitry.kasatkin@intel.com>
   *
 - * This program is free software; you can redistribute it and/or modify
 - * it under the terms of the GNU General Public License as published by
 - * the Free Software Foundation, version 2 of the License.
 - *
   * File: sign.c
   *    implements signature (RSA) verification
   *    pkcs decoding is based on LibTomCrypt code
@@@ -224,7 -227,7 +224,7 @@@ int digsig_verify(struct key *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);
diff --combined net/ceph/ceph_common.c
index 1c811c74bfc0f3123eb2ad481822aca91590c997,d4af93a35e2b8ebeb7502e3b6c8a1eb70dcbe3b3..38de80d01aae121f7129c55f731e0a92c3bb77a0
@@@ -1,4 -1,3 +1,4 @@@
 +// SPDX-License-Identifier: GPL-2.0-only
  
  #include <linux/ceph/ceph_debug.h>
  #include <linux/backing-dev.h>
@@@ -306,7 -305,7 +306,7 @@@ static int get_secret(struct ceph_crypt
        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 */
diff --combined net/rxrpc/key.c
index 6c3f35fac42d57eeb4449e403c4267ae7bcb9d11,207d621d18c0121e1bf11ecaaee1811dc1e86069..2032f6a8225e5df4c49f821fb501cdad0d5c437c
@@@ -1,9 -1,13 +1,9 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* RxRPC key management
   *
   * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
   *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License
 - * as published by the Free Software Foundation; either version
 - * 2 of the License, or (at your option) any later version.
 - *
   * RxRPC keys should have a description of describing their purpose:
   *    "afs@CAMBRIDGE.REDHAT.COM>
   */
  #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 *);
@@@ -910,7 -922,8 +918,8 @@@ int rxrpc_request_key(struct rxrpc_soc
        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));
@@@ -941,7 -954,8 +950,8 @@@ int rxrpc_server_keyring(struct rxrpc_s
        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));
@@@ -974,7 -988,8 +984,8 @@@ int rxrpc_get_server_data_key(struct rx
        _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));
@@@ -1022,7 -1037,7 +1033,7 @@@ struct key *rxrpc_get_null_key(const ch
  
        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;
  
index 4582bc26770a34a71eb5c9b0c3d11501cc7f9d4e,3bd2cc28f4f5de5c72af84d9949291d114067929..ceb10553a6ba64a093a747bf1f5f1605ba9fecf6
@@@ -1,9 -1,13 +1,9 @@@
 +// SPDX-License-Identifier: GPL-2.0-only
  /*
   * Copyright (C) 2011 Intel Corporation
   *
   * Author:
   * Dmitry Kasatkin <dmitry.kasatkin@intel.com>
 - *
 - * This program is free software; you can redistribute it and/or modify
 - * it under the terms of the GNU General Public License as published by
 - * the Free Software Foundation, version 2 of the License.
 - *
   */
  
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@@ -47,7 -51,8 +47,8 @@@ int integrity_digsig_verify(const unsig
  
        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);
@@@ -148,7 -150,6 +146,6 @@@ int __init integrity_load_x509(const un
        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);
  }
index 55aec161d0e1c9e73cda8fab91ca5442065162d2,a8bd8b2f4fcefd5efab1e12e36ca78ac395b8514..a29df775fdd8db55b17ac9ed27985be33eface11
@@@ -1,9 -1,13 +1,9 @@@
 +// SPDX-License-Identifier: GPL-2.0-only
  /*
   * Copyright (C) 2013 Intel Corporation
   *
   * Author:
   * Dmitry Kasatkin <dmitry.kasatkin@intel.com>
 - *
 - * This program is free software; you can redistribute it and/or modify
 - * it under the terms of the GNU General Public License as published by
 - * the Free Software Foundation, version 2 of the License.
 - *
   */
  
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@@ -53,7 -57,7 +53,7 @@@ static struct key *request_asymmetric_k
                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)) {
index d485f6fc908e50bc5857c39a51ade369a9cfedad,304cb0b21f7a39f1b63efafe601866741fab1bd7..466eebd3b4aa47e96991acde531f0ef06df13755
@@@ -1,4 -1,3 +1,4 @@@
 +// SPDX-License-Identifier: GPL-2.0-only
  /*
   * Copyright (C) 2005-2010 IBM Corporation
   *
@@@ -6,6 -5,10 +6,6 @@@
   * Mimi Zohar <zohar@us.ibm.com>
   * Kylene Hall <kjhall@us.ibm.com>
   *
 - * This program is free software; you can redistribute it and/or modify
 - * it under the terms of the GNU General Public License as published by
 - * the Free Software Foundation, version 2 of the License.
 - *
   * File: evm_crypto.c
   *     Using root's kernel master key (kmk), calculate the HMAC
   */
@@@ -86,9 -89,6 +86,9 @@@ static struct shash_desc *init_desc(cha
                tfm = &hmac_tfm;
                algo = evm_hmac;
        } else {
 +              if (hash_algo >= HASH_ALGO__LAST)
 +                      return ERR_PTR(-EINVAL);
 +
                tfm = &evm_tfm[hash_algo];
                algo = hash_algo_name[hash_algo];
        }
@@@ -356,7 -356,7 +356,7 @@@ int evm_init_key(void
        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;
  
index 36cadadbfba47394ef9e2cee74b11e302107bc01,ce48303cfacce84499a0ec5deca03a07d487861d..b52ae1476ec3f0087d4a82ea2a1b290500e663dc
@@@ -1,9 -1,14 +1,9 @@@
 +// SPDX-License-Identifier: GPL-2.0-only
  /*
   * Copyright (C) 2015 Juniper Networks, Inc.
   *
   * Author:
   * Petko Manolov <petko.manolov@konsulko.com>
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License as
 - * published by the Free Software Foundation, version 2 of the
 - * License.
 - *
   */
  
  #include <linux/export.h>
  #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;
  
  /*
@@@ -35,9 -49,7 +44,7 @@@ __init int ima_mok_init(void
  
        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);
  
index 65377848fbc58149daed46f30274c5fa7721be2a,1c50aff6f65a48a1003244c96496280724ecc4ba..45f4aef83e29e89576221e18e4ec7b09d0f66c20
@@@ -1,9 -1,14 +1,9 @@@
 +/* SPDX-License-Identifier: GPL-2.0-only */
  /*
   * Copyright (C) 2009-2010 IBM Corporation
   *
   * Authors:
   * Mimi Zohar <zohar@us.ibm.com>
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License as
 - * published by the Free Software Foundation, version 2 of the
 - * License.
 - *
   */
  
  #include <linux/types.h>
@@@ -12,6 -17,8 +12,8 @@@
  #include <linux/key.h>
  #include <linux/audit.h>
  
+ struct key_acl;
  /* iint action cache flags */
  #define IMA_MEASURE           0x00000001
  #define IMA_MEASURED          0x00000002
@@@ -149,7 -156,7 +151,7 @@@ int integrity_digsig_verify(const unsig
  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,
@@@ -167,7 -174,7 +169,7 @@@ static inline int integrity_init_keyrin
  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;
  }
diff --combined security/keys/compat.c
index 9bcc404131aa0d30c86045d2b24a03856e939398,1eebb9a237b81449a0284e9e2b4752693841142e..b0e59546e7bd1296c4fe9785d98625a0ad1b9c66
@@@ -1,8 -1,12 +1,8 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* 32-bit compatibility syscall for 64-bit systems
   *
   * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License
 - * as published by the Free Software Foundation; either version
 - * 2 of the License, or (at your option) any later version.
   */
  
  #include <linux/syscalls.h>
@@@ -157,6 -161,8 +157,8 @@@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, opt
  
        case KEYCTL_MOVE:
                return keyctl_keyring_move(arg2, arg3, arg4, arg5);
+       case KEYCTL_GRANT_PERMISSION:
+               return keyctl_grant_permission(arg2, arg3, arg4, arg5);
  
        case KEYCTL_CAPABILITIES:
                return keyctl_capabilities(compat_ptr(arg2), arg3);
index 60720f58cbe015f2639805bce9cc366e13cfb3ee,dc76c60a27a6d0a5764268f3aac31100e1ac16f0..9df560e477c2d90dc290a3ef190f0e24c6d2f13f
@@@ -1,4 -1,3 +1,4 @@@
 +// SPDX-License-Identifier: GPL-2.0-only
  /*
   * Copyright (C) 2010 IBM Corporation
   * Copyright (C) 2010 Politecnico di Torino, Italy
@@@ -8,6 -7,10 +8,6 @@@
   * Mimi Zohar <zohar@us.ibm.com>
   * Roberto Sassu <roberto.sassu@polito.it>
   *
 - * This program is free software; you can redistribute it and/or modify
 - * it under the terms of the GNU General Public License as published by
 - * the Free Software Foundation, version 2 of the License.
 - *
   * See Documentation/security/keys/trusted-encrypted.rst
   */
  
@@@ -304,7 -307,7 +304,7 @@@ static struct key *request_user_key(con
        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;
  
index c68528aa49c6dc3369d3c36beba29687dec059be,3322e7eeafce7d2d8fc09c03f3461e607d2f33ec..d649f2f29475fa2b4fd4cbbf8d5f4082cac3bac5
@@@ -1,4 -1,3 +1,4 @@@
 +// SPDX-License-Identifier: GPL-2.0-only
  /*
   * Copyright (C) 2010 IBM Corporation
   * Copyright (C) 2010 Politecnico di Torino, Italy
@@@ -8,6 -7,10 +8,6 @@@
   * Mimi Zohar <zohar@us.ibm.com>
   * Roberto Sassu <roberto.sassu@polito.it>
   *
 - * This program is free software; you can redistribute it and/or modify
 - * it under the terms of the GNU General Public License as published by
 - * the Free Software Foundation, version 2 of the License.
 - *
   * See Documentation/security/keys/trusted-encrypted.rst
   */
  
@@@ -30,7 -33,7 +30,7 @@@ struct key *request_trusted_key(const c
        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;
  
diff --combined security/keys/gc.c
index 671dd730ecfc9e62395b2a7bf046f76f0f9ad313,3b13fb62827ffe95a4ba2353fae847cd04b43331..48c3e124c2729bcaf198841b381d56b096a6cc89
@@@ -1,8 -1,12 +1,8 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* Key garbage collector
   *
   * Copyright (C) 2009-2011 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public Licence
 - * as published by the Free Software Foundation; either version
 - * 2 of the Licence, or (at your option) any later version.
   */
  
  #include <linux/slab.h>
@@@ -151,6 -155,7 +151,7 @@@ static noinline void key_gc_unused_keys
  
                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));
@@@ -220,7 -225,6 +221,6 @@@ continue_scanning
                        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) {
diff --combined security/keys/internal.h
index c039373488bd940d2a1c6dac919e46e586cd3002,5e27ebdf1937677253ebdbb3ac019885dc9d9bff..e0c5bb8b16859ae089fb328ec16fd205500fd665
@@@ -1,8 -1,12 +1,8 @@@
 +/* SPDX-License-Identifier: GPL-2.0-or-later */
  /* Authentication token and access key management internal defs
   *
   * Copyright (C) 2003-5, 2007 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License
 - * as published by the Free Software Foundation; either version
 - * 2 of the License, or (at your option) any later version.
   */
  
  #ifndef _INTERNAL_H
@@@ -84,8 -88,11 +84,11 @@@ extern struct rb_root key_serial_tree
  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);
  
@@@ -156,6 -163,7 +159,7 @@@ extern struct key *request_key_and_link
                                        const void *callout_info,
                                        size_t callout_len,
                                        void *aux,
+                                       struct key_acl *acl,
                                        struct key *dest_keyring,
                                        unsigned long flags);
  
@@@ -179,7 -187,10 +183,10 @@@ extern void key_gc_keytype(struct key_t
  
  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.
@@@ -226,7 -237,7 +233,7 @@@ extern long keyctl_keyring_search(key_s
                                  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);
@@@ -331,6 -342,11 +338,11 @@@ static inline long keyctl_pkey_e_d_s(in
  
  extern long keyctl_capabilities(unsigned char __user *_buffer, size_t buflen);
  
+ extern long keyctl_grant_permission(key_serial_t keyid,
+                                   enum key_ace_subject_type type,
+                                   unsigned int subject,
+                                   unsigned int perm);
  /*
   * Debugging key validation
   */
diff --combined security/keys/key.c
index 764f4c57913e95ee5836e523079f7bdb30f6a695,bb96d6235ea2676c04e5f0edfb5c51e44bc4879a..519211a996e713946c031354b9363bb6774c021a
@@@ -1,8 -1,12 +1,8 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* Basic authentication token and access key management
   *
   * Copyright (C) 2004-2008 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License
 - * as published by the Free Software Foundation; either version
 - * 2 of the License, or (at your option) any later version.
   */
  
  #include <linux/export.h>
@@@ -195,7 -199,7 +195,7 @@@ serial_exists
   * @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();
  
@@@ -787,7 -795,7 +791,7 @@@ error
   * @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
@@@ -810,7 -818,7 +814,7 @@@ key_ref_t key_create_or_update(key_ref_
                               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;
diff --combined security/keys/keyctl.c
index 9b898c9695583da389c4129544081c334f679e26,aa096c4080b2de223b43ceaae04809424a4eeb52..c2dd66d556d4a1612622c0b2e04b4b219e1f79c4
@@@ -1,8 -1,12 +1,8 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* Userspace key control operations
   *
   * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License
 - * as published by the Free Software Foundation; either version
 - * 2 of the License, or (at your option) any later version.
   */
  
  #include <linux/init.h>
@@@ -37,7 -41,8 +37,8 @@@ static const unsigned char keyrings_cap
               KEYCTL_CAPS0_MOVE
               ),
        [1] = (KEYCTL_CAPS1_NS_KEYRING_NAME |
-              KEYCTL_CAPS1_NS_KEY_TAG),
+              KEYCTL_CAPS1_NS_KEY_TAG |
+              KEYCTL_CAPS1_ACL_ALTERABLE),
  };
  
  static int key_get_type_from_user(char *type,
@@@ -130,8 -135,7 +131,7 @@@ SYSCALL_DEFINE5(add_key, const char __u
        /* 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);
@@@ -221,7 -225,8 +221,8 @@@ SYSCALL_DEFINE4(request_key, const cha
  
        /* 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);
@@@ -383,16 -388,10 +384,10 @@@ long keyctl_revoke_key(key_serial_t id
        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);
@@@ -426,7 -425,7 +421,7 @@@ long keyctl_invalidate_key(key_serial_
  
        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);
  
@@@ -471,7 -470,7 +466,7 @@@ long keyctl_keyring_clear(key_serial_t 
        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);
  
@@@ -646,6 -645,7 +641,7 @@@ long keyctl_describe_key(key_serial_t k
                         size_t buflen)
  {
        struct key *key, *instkey;
+       unsigned int perm;
        key_ref_t key_ref;
        char *infobuf;
        long ret;
@@@ -675,6 -675,10 +671,10 @@@ okay
        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);
@@@ -899,7 -903,7 +899,7 @@@ long keyctl_chown_key(key_serial_t id, 
                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;
@@@ -994,18 -998,25 +994,25 @@@ quota_overrun
   * 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;
@@@ -1388,7 -1427,7 +1423,7 @@@ long keyctl_set_timeout(key_serial_t id
        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 */
@@@ -1539,7 -1578,7 +1574,7 @@@ long keyctl_get_security(key_serial_t k
   * 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.
   *
@@@ -1556,7 -1595,7 +1591,7 @@@ long keyctl_session_to_parent(void
        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);
  
@@@ -1658,7 -1697,7 +1693,7 @@@ long keyctl_restrict_keyring(key_serial
        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);
  
@@@ -1764,7 -1803,7 +1799,7 @@@ SYSCALL_DEFINE5(keyctl, int, option, un
  
        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,
                                           (key_serial_t)arg3,
                                           (key_serial_t)arg4,
                                           (unsigned int)arg5);
+       case KEYCTL_GRANT_PERMISSION:
+               return keyctl_grant_permission((key_serial_t)arg2,
+                                              (enum key_ace_subject_type)arg3,
+                                              (unsigned int)arg4,
+                                              (unsigned int)arg5);
  
        case KEYCTL_CAPABILITIES:
                return keyctl_capabilities((unsigned char __user *)arg2, (size_t)arg3);
diff --combined security/keys/keyring.c
index febf36c6ddc5dbc06098dd5913f77fba115f78bf,62fb26c61968687fce7e83f895c00b9c520ddec7..3b5458f23a954f54415ac1bfebc0316a0767f739
@@@ -1,8 -1,12 +1,8 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* Keyring handling
   *
   * Copyright (C) 2004-2005, 2008, 2013 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License
 - * as published by the Free Software Foundation; either version
 - * 2 of the License, or (at your option) any later version.
   */
  
  #include <linux/export.h>
@@@ -515,11 -519,19 +515,19 @@@ static long keyring_read(const struct k
        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) {
@@@ -1132,10 -1144,11 +1140,11 @@@ found
  /*
   * 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.
@@@ -1169,7 -1182,7 +1178,7 @@@ struct key *find_keyring_by_name(const 
                                continue;
                } else {
                        if (key_permission(make_key_ref(keyring, 0),
-                                          KEY_NEED_SEARCH) < 0)
+                                          KEY_NEED_JOIN) < 0)
                                continue;
                }
  
index 085f907b64ac5c4161fe188a599354103ec3ff88,11655a827ba13890b1a5692b6fabef0e151ec8ff..fd8a5dc6910ae2bc6baa0e129461c9096f744bad
@@@ -1,19 -1,77 +1,73 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* Key permission checking
   *
   * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License
 - * as published by the Free Software Foundation; either version
 - * 2 of the License, or (at your option) any later version.
   */
  
  #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;
  
-               ret = groups_search(cred->group_info, key->gid);
-               if (ret) {
-                       kperm = key->perm >> 8;
-                       goto use_these_perms;
+       for (i = 0; i < acl->nr_ace; i++) {
+               const struct key_ace *ace = &acl->aces[i];
+               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);
  
@@@ -104,3 -182,218 +178,218 @@@ int key_validate(const struct key *key
        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;
+ }
+ /*
+  * Allocate a new ACL with an extra ACE slot.
+  */
+ static struct key_acl *key_alloc_acl(const struct key_acl *old_acl, int nr, int skip)
+ {
+       struct key_acl *acl;
+       int nr_ace, i, j = 0;
+       nr_ace = old_acl->nr_ace + nr;
+       if (nr_ace > 16)
+               return ERR_PTR(-EINVAL);
+       acl = kzalloc(struct_size(acl, aces, nr_ace), GFP_KERNEL);
+       if (!acl)
+               return ERR_PTR(-ENOMEM);
+       refcount_set(&acl->usage, 1);
+       acl->nr_ace = nr_ace;
+       for (i = 0; i < old_acl->nr_ace; i++) {
+               if (i == skip)
+                       continue;
+               acl->aces[j] = old_acl->aces[i];
+               j++;
+       }
+       return acl;
+ }
+ /*
+  * Generate the revised ACL.
+  */
+ static long key_change_acl(struct key *key, struct key_ace *new_ace)
+ {
+       struct key_acl *acl, *old;
+       int i;
+       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;
+       if (new_ace->perm == 0)
+               return 0; /* No permissions to remove.  Add deny record? */
+       acl = key_alloc_acl(old, 1, -1);
+       if (IS_ERR(acl))
+               return PTR_ERR(acl);
+       acl->aces[i] = *new_ace;
+       goto change;
+ found_match:
+       if (new_ace->perm == 0)
+               goto delete_ace;
+       if (new_ace->perm == old->aces[i].perm)
+               return 0;
+       acl = key_alloc_acl(old, 0, -1);
+       if (IS_ERR(acl))
+               return PTR_ERR(acl);
+       acl->aces[i].perm = new_ace->perm;
+       goto change;
+ delete_ace:
+       acl = key_alloc_acl(old, -1, i);
+       if (IS_ERR(acl))
+               return PTR_ERR(acl);
+       goto change;
+ change:
+       return key_set_acl(key, acl);
+ }
+ /*
+  * Add, alter or remove (if perm == 0) an ACE in a key's ACL.
+  */
+ long keyctl_grant_permission(key_serial_t keyid,
+                            enum key_ace_subject_type type,
+                            unsigned int subject,
+                            unsigned int perm)
+ {
+       struct key_ace new_ace;
+       struct key *key;
+       key_ref_t key_ref;
+       long ret;
+       new_ace.type = type;
+       new_ace.perm = perm;
+       switch (type) {
+       case KEY_ACE_SUBJ_STANDARD:
+               if (subject >= nr__key_ace_standard_subject)
+                       return -ENOENT;
+               new_ace.subject_id = subject;
+               break;
+       default:
+               return -ENOENT;
+       }
+       key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_NEED_SETSEC);
+       if (IS_ERR(key_ref)) {
+               ret = PTR_ERR(key_ref);
+               goto error;
+       }
+       key = key_ref_to_ptr(key_ref);
+       down_write(&key->sem);
+       /* If we're not the sysadmin, we can only change a key that we own */
+       ret = -EACCES;
+       if (capable(CAP_SYS_ADMIN) || uid_eq(key->uid, current_fsuid()))
+               ret = key_change_acl(key, &new_ace);
+       up_write(&key->sem);
+       key_put(key);
+ error:
+       return ret;
+ }
index 97af230aa4b22b3667e7098558bdff933a7b5bb4,c4c480f630eabfef0835bade4da7bd31cde68310..8171c90d4c9a793abdc86f4bc3ff470582f5a2a9
@@@ -1,8 -1,12 +1,8 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* General persistent per-UID keyrings register
   *
   * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public Licence
 - * as published by the Free Software Foundation; either version
 - * 2 of the Licence, or (at your option) any later version.
   */
  
  #include <linux/user_namespace.h>
  
  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.
   *
@@@ -22,8 -47,7 +43,7 @@@ static int key_create_persistent_regist
        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);
@@@ -56,8 -80,7 +76,7 @@@ static key_ref_t key_create_persistent(
  
        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))
diff --combined security/keys/proc.c
index 415f3f1c2da042f794608522aeddd772fc706011,0056fe2dc39bdd746d7e2757fd1332f32c931999..b394ad1e874b4c4ffceae2a3eb6653ef2b075ae6
@@@ -1,8 -1,12 +1,8 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* procfs files for key database enumeration
   *
   * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License
 - * as published by the Free Software Foundation; either version
 - * 2 of the License, or (at your option) any later version.
   */
  
  #include <linux/init.h>
@@@ -110,11 -114,13 +110,13 @@@ static struct key *find_ge_key(struct s
  }
  
  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)
@@@ -144,12 -150,15 +146,15 @@@ static void *proc_keys_next(struct seq_
  
  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;
  }
  
index 09541de31f2f14def6209b939cd1f3e9b3447c18,ddda8544630dd7dc8c80e54e3947711a1455d2ff..aa3bfcadbc6600c0a2a1eba14704aa0bcd726c19
@@@ -1,8 -1,12 +1,8 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* Manage a process's keyrings
   *
   * Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License
 - * as published by the Free Software Foundation; either version
 - * 2 of the License, or (at your option) any later version.
   */
  
  #include <linux/init.h>
@@@ -32,6 -36,47 +32,47 @@@ struct key_user root_key_user = 
        .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.
   */
@@@ -51,11 -96,8 +92,8 @@@ static struct key *get_user_register(st
        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);
@@@ -77,14 -119,11 +115,11 @@@ int look_up_user_keyrings(struct key **
        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);
@@@ -226,7 -265,7 +261,7 @@@ int install_thread_keyring_to_cred(stru
                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))
@@@ -273,7 -312,7 +308,7 @@@ int install_process_keyring_to_cred(str
                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))
@@@ -328,8 -367,7 +363,7 @@@ int install_session_keyring_to_cred(str
                        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 {
@@@ -609,7 -647,7 +643,7 @@@ bool lookup_user_key_possessed(const st
   * 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,
@@@ -784,12 -822,12 +818,12 @@@ try_again
                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();
  
@@@ -868,13 -908,13 +904,13 @@@ long join_session_keyring(const char *n
        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)
index 7325f382dbf43b5660d0f3938e0ce18b737bed6e,64af697a9126d6952b4f156c2151359a318287ff..46c5187ce03fa4483fbe6ea7cd67991be15b5a00
@@@ -1,9 -1,13 +1,9 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* Request a key from userspace
   *
   * Copyright (C) 2004-2007 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
   *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License
 - * as published by the Free Software Foundation; either version
 - * 2 of the License, or (at your option) any later version.
 - *
   * See Documentation/security/keys/request-key.rst
   */
  
@@@ -135,8 -139,7 +135,7 @@@ static int call_sbin_request_key(struc
  
        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);
@@@ -367,11 -370,11 +366,11 @@@ static int construct_alloc_key(struct k
                               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;
  
@@@ -474,6 -469,7 +465,7 @@@ static struct key *construct_key_and_li
                                          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) {
@@@ -534,6 -530,7 +526,7 @@@ error
   * @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().
   *
@@@ -561,6 -558,7 +554,7 @@@ struct key *request_key_and_link(struc
                                 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:
@@@ -678,6 -676,7 +672,7 @@@ EXPORT_SYMBOL(wait_for_key_construction
   * @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) {
@@@ -720,6 -720,7 +716,7 @@@ EXPORT_SYMBOL(request_key_tag)
   * @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.
@@@ -732,14 -733,15 +729,15 @@@ struct key *request_key_with_auxdata(st
                                     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) {
index e73ec040e2509494d3d123ad8c47419b747a33f7,d9146606f54e9eb379c5d6d7fa3cdd215844466a..27e437d94b81c2d9b7505929b4ae90f22cf04d01
@@@ -1,9 -1,13 +1,9 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* Request key authorisation token key definition.
   *
   * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
   *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License
 - * as published by the Free Software Foundation; either version
 - * 2 of the License, or (at your option) any later version.
 - *
   * See Documentation/security/keys/request-key.rst
   */
  
@@@ -24,6 -28,17 +24,17 @@@ static void request_key_auth_revoke(str
  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.
   */
@@@ -210,8 -225,8 +221,8 @@@ struct key *request_key_auth_new(struc
  
        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;
diff --combined security/selinux/hooks.c
index 3ec7ac70c3130568bfb12671a61e7206f8095bb2,b828401dcb70b7a7f13af15575834281195488d8..4ed83d869084830c3ca19a1392d538247c842785
@@@ -1,4 -1,3 +1,4 @@@
 +// SPDX-License-Identifier: GPL-2.0-only
  /*
   *  NSA Security-Enhanced Linux (SELinux) security module
   *
   *  Copyright (C) 2007 Hitachi Software Engineering Co., Ltd.
   *                   Yuichi Nakamura <ynakam@hitachisoft.jp>
   *  Copyright (C) 2016 Mellanox Technologies
 - *
 - *    This program is free software; you can redistribute it and/or modify
 - *    it under the terms of the GNU General Public License version 2,
 - *    as published by the Free Software Foundation.
   */
  
  #include <linux/init.h>
@@@ -1049,24 -1052,15 +1049,24 @@@ static int selinux_add_mnt_opt(const ch
        if (token == Opt_error)
                return -EINVAL;
  
 -      if (token != Opt_seclabel)
 +      if (token != Opt_seclabel) {
                val = kmemdup_nul(val, len, GFP_KERNEL);
 +              if (!val) {
 +                      rc = -ENOMEM;
 +                      goto free_opt;
 +              }
 +      }
        rc = selinux_add_opt(token, val, mnt_opts);
        if (unlikely(rc)) {
                kfree(val);
 -              if (*mnt_opts) {
 -                      selinux_free_mnt_opts(*mnt_opts);
 -                      *mnt_opts = NULL;
 -              }
 +              goto free_opt;
 +      }
 +      return rc;
 +
 +free_opt:
 +      if (*mnt_opts) {
 +              selinux_free_mnt_opts(*mnt_opts);
 +              *mnt_opts = NULL;
        }
        return rc;
  }
@@@ -2622,11 -2616,10 +2622,11 @@@ static int selinux_sb_eat_lsm_opts(cha
        char *from = options;
        char *to = options;
        bool first = true;
 +      int rc;
  
        while (1) {
                int len = opt_len(from);
 -              int token, rc;
 +              int token;
                char *arg = NULL;
  
                token = match_opt_prefix(from, len, &arg);
                                                *q++ = c;
                                }
                                arg = kmemdup_nul(arg, q - arg, GFP_KERNEL);
 +                              if (!arg) {
 +                                      rc = -ENOMEM;
 +                                      goto free_opt;
 +                              }
                        }
                        rc = selinux_add_opt(token, arg, mnt_opts);
                        if (unlikely(rc)) {
                                kfree(arg);
 -                              if (*mnt_opts) {
 -                                      selinux_free_mnt_opts(*mnt_opts);
 -                                      *mnt_opts = NULL;
 -                              }
 -                              return rc;
 +                              goto free_opt;
                        }
                } else {
                        if (!first) {   // copy with preceding comma
        }
        *to = '\0';
        return 0;
 +
 +free_opt:
 +      if (*mnt_opts) {
 +              selinux_free_mnt_opts(*mnt_opts);
 +              *mnt_opts = NULL;
 +      }
 +      return rc;
  }
  
  static int selinux_sb_remount(struct super_block *sb, void *mnt_opts)
@@@ -4651,14 -4637,6 +4651,14 @@@ static int selinux_socket_connect_helpe
        err = sock_has_perm(sk, SOCKET__CONNECT);
        if (err)
                return err;
 +      if (addrlen < offsetofend(struct sockaddr, sa_family))
 +              return -EINVAL;
 +
 +      /* connect(AF_UNSPEC) has special handling, as it is a documented
 +       * way to disconnect the socket
 +       */
 +      if (address->sa_family == AF_UNSPEC)
 +              return 0;
  
        /*
         * If a TCP, DCCP or SCTP socket, check name_connect permission
                 * need to check address->sa_family as it is possible to have
                 * sk->sk_family = PF_INET6 with addr->sa_family = AF_INET.
                 */
 -              if (addrlen < offsetofend(struct sockaddr, sa_family))
 -                      return -EINVAL;
                switch (address->sa_family) {
                case AF_INET:
                        addr4 = (struct sockaddr_in *)address;
@@@ -6351,12 -6331,11 +6351,12 @@@ static int selinux_setprocattr(const ch
        } else if (!strcmp(name, "fscreate")) {
                tsec->create_sid = sid;
        } else if (!strcmp(name, "keycreate")) {
 -              error = avc_has_perm(&selinux_state,
 -                                   mysid, sid, SECCLASS_KEY, KEY__CREATE,
 -                                   NULL);
 -              if (error)
 -                      goto abort_change;
 +              if (sid) {
 +                      error = avc_has_perm(&selinux_state, mysid, sid,
 +                                           SECCLASS_KEY, KEY__CREATE, NULL);
 +                      if (error)
 +                              goto abort_change;
 +              }
                tsec->keycreate_sid = sid;
        } else if (!strcmp(name, "sockcreate")) {
                tsec->sockcreate_sid = sid;
@@@ -6502,6 -6481,7 +6502,7 @@@ static int selinux_key_permission(key_r
  {
        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)
index 4c5e5a438f8bdd77f88d4cdfdf8424c910c444ed,6095dc3565a55a18f47e76ac7a7daa62cbdf082c..50c536cad85b31063e2f1ea7c10287bf88c0b9c2
@@@ -1,4 -1,3 +1,4 @@@
 +// SPDX-License-Identifier: GPL-2.0-only
  /*
   *  Simplified MAC Kernel (smack) security module
   *
   *                Paul Moore <paul@paul-moore.com>
   *  Copyright (C) 2010 Nokia Corporation
   *  Copyright (C) 2011 Intel Corporation.
 - *
 - *    This program is free software; you can redistribute it and/or modify
 - *    it under the terms of the GNU General Public License version 2,
 - *      as published by the Free Software Foundation.
   */
  
  #include <linux/xattr.h>
@@@ -65,7 -68,6 +65,7 @@@ static struct 
        int len;
        int opt;
  } smk_mount_opts[] = {
 +      {"smackfsdef", sizeof("smackfsdef") - 1, Opt_fsdefault},
        A(fsdefault), A(fsfloor), A(fshat), A(fsroot), A(fstransmute)
  };
  #undef A
@@@ -680,12 -682,11 +680,12 @@@ static int smack_fs_context_dup(struct 
  }
  
  static const struct fs_parameter_spec smack_param_specs[] = {
 -      fsparam_string("fsdefault",     Opt_fsdefault),
 -      fsparam_string("fsfloor",       Opt_fsfloor),
 -      fsparam_string("fshat",         Opt_fshat),
 -      fsparam_string("fsroot",        Opt_fsroot),
 -      fsparam_string("fstransmute",   Opt_fstransmute),
 +      fsparam_string("smackfsdef",            Opt_fsdefault),
 +      fsparam_string("smackfsdefault",        Opt_fsdefault),
 +      fsparam_string("smackfsfloor",          Opt_fsfloor),
 +      fsparam_string("smackfshat",            Opt_fshat),
 +      fsparam_string("smackfsroot",           Opt_fsroot),
 +      fsparam_string("smackfstransmute",      Opt_fstransmute),
        {}
  };
  
@@@ -4284,7 -4285,8 +4284,8 @@@ static int smack_key_permission(key_ref
  #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);