#define KEY_PERM_UNDEF 0xffffffff
 
+/*
+ * The permissions required on a key that we're looking up.
+ */
+enum key_need_perm {
+       KEY_NEED_UNSPECIFIED,   /* Needed permission unspecified */
+       KEY_NEED_VIEW,          /* Require permission to view attributes */
+       KEY_NEED_READ,          /* Require permission to read content */
+       KEY_NEED_WRITE,         /* Require permission to update / modify */
+       KEY_NEED_SEARCH,        /* Require permission to search (keyring) or find (key) */
+       KEY_NEED_LINK,          /* Require permission to link */
+       KEY_NEED_SETATTR,       /* Require permission to change attributes */
+       KEY_NEED_UNLINK,        /* Require permission to unlink key */
+       KEY_SYSADMIN_OVERRIDE,  /* Special: override by CAP_SYS_ADMIN */
+       KEY_AUTHTOKEN_OVERRIDE, /* Special: override by possession of auth token */
+       KEY_DEFER_PERM_CHECK,   /* Special: permission check is deferred */
+};
+
 struct seq_file;
 struct user_struct;
 struct signal_struct;
 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);
+                                enum key_need_perm need_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 */
-
 static inline short key_read_state(const struct key *key)
 {
        /* Barrier versus mark_key_instantiated(). */
 
 
 int security_key_alloc(struct key *key, const struct cred *cred, unsigned long flags);
 void security_key_free(struct key *key);
-int security_key_permission(key_ref_t key_ref,
-                           const struct cred *cred, unsigned perm);
+int security_key_permission(key_ref_t key_ref, const struct cred *cred,
+                           enum key_need_perm need_perm);
 int security_key_getsecurity(struct key *key, char **_buffer);
 
 #else
 
 static inline int security_key_permission(key_ref_t key_ref,
                                          const struct cred *cred,
-                                         unsigned perm)
+                                         enum key_need_perm need_perm)
 {
        return 0;
 }
 
                                      const struct key_match_data *match_data);
 #define KEY_LOOKUP_CREATE      0x01
 #define KEY_LOOKUP_PARTIAL     0x02
-#define KEY_LOOKUP_FOR_UNLINK  0x04
 
 extern long join_session_keyring(const char *name);
 extern void key_change_session_keyring(struct callback_head *twork);
 
 extern int key_task_permission(const key_ref_t key_ref,
                               const struct cred *cred,
-                              key_perm_t perm);
+                              enum key_need_perm need_perm);
 
 static inline void notify_key(struct key *key,
                              enum key_notification_subtype subtype, u32 aux)
 /*
  * Check to see whether permission is granted to use a key in the desired way.
  */
-static inline int key_permission(const key_ref_t key_ref, unsigned perm)
+static inline int key_permission(const key_ref_t key_ref,
+                                enum key_need_perm need_perm)
 {
-       return key_task_permission(key_ref, current_cred(), perm);
+       return key_task_permission(key_ref, current_cred(), need_perm);
 }
 
 extern struct key_type key_type_request_key_auth;
 
 
                /* Root is permitted to invalidate certain special keys */
                if (capable(CAP_SYS_ADMIN)) {
-                       key_ref = lookup_user_key(id, 0, 0);
+                       key_ref = lookup_user_key(id, 0, KEY_SYSADMIN_OVERRIDE);
                        if (IS_ERR(key_ref))
                                goto error;
                        if (test_bit(KEY_FLAG_ROOT_CAN_INVAL,
 
                /* Root is permitted to invalidate certain special keyrings */
                if (capable(CAP_SYS_ADMIN)) {
-                       keyring_ref = lookup_user_key(ringid, 0, 0);
+                       keyring_ref = lookup_user_key(ringid, 0,
+                                                     KEY_SYSADMIN_OVERRIDE);
                        if (IS_ERR(keyring_ref))
                                goto error;
                        if (test_bit(KEY_FLAG_ROOT_CAN_CLEAR,
                goto error;
        }
 
-       key_ref = lookup_user_key(id, KEY_LOOKUP_FOR_UNLINK, 0);
+       key_ref = lookup_user_key(id, KEY_LOOKUP_PARTIAL, KEY_NEED_UNLINK);
        if (IS_ERR(key_ref)) {
                ret = PTR_ERR(key_ref);
                goto error2;
                                key_put(instkey);
                                key_ref = lookup_user_key(keyid,
                                                          KEY_LOOKUP_PARTIAL,
-                                                         0);
+                                                         KEY_AUTHTOKEN_OVERRIDE);
                                if (!IS_ERR(key_ref))
                                        goto okay;
                        }
        size_t key_data_len;
 
        /* find the key first */
-       key_ref = lookup_user_key(keyid, 0, 0);
+       key_ref = lookup_user_key(keyid, 0, KEY_DEFER_PERM_CHECK);
        if (IS_ERR(key_ref)) {
                ret = -ENOKEY;
                goto out;
                                key_put(instkey);
                                key_ref = lookup_user_key(id,
                                                          KEY_LOOKUP_PARTIAL,
-                                                         0);
+                                                         KEY_AUTHTOKEN_OVERRIDE);
                                if (!IS_ERR(key_ref))
                                        goto okay;
                        }
                        return PTR_ERR(instkey);
                key_put(instkey);
 
-               key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, 0);
+               key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL,
+                                         KEY_AUTHTOKEN_OVERRIDE);
                if (IS_ERR(key_ref))
                        return PTR_ERR(key_ref);
        }
 
  * 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.
+ * @need_perm: The permission required.
  *
  * Check to see whether permission is granted to use a key in the desired way,
  * but permit the security modules to override.
  * permissions bits or the LSM check.
  */
 int key_task_permission(const key_ref_t key_ref, const struct cred *cred,
-                       unsigned perm)
+                       enum key_need_perm need_perm)
 {
        struct key *key;
-       key_perm_t kperm;
+       key_perm_t kperm, mask;
        int ret;
 
+       switch (need_perm) {
+       default:
+               WARN_ON(1);
+               return -EACCES;
+       case KEY_NEED_UNLINK:
+       case KEY_SYSADMIN_OVERRIDE:
+       case KEY_AUTHTOKEN_OVERRIDE:
+       case KEY_DEFER_PERM_CHECK:
+               goto lsm;
+
+       case KEY_NEED_VIEW:     mask = KEY_OTH_VIEW;    break;
+       case KEY_NEED_READ:     mask = KEY_OTH_READ;    break;
+       case KEY_NEED_WRITE:    mask = KEY_OTH_WRITE;   break;
+       case KEY_NEED_SEARCH:   mask = KEY_OTH_SEARCH;  break;
+       case KEY_NEED_LINK:     mask = KEY_OTH_LINK;    break;
+       case KEY_NEED_SETATTR:  mask = KEY_OTH_SETATTR; break;
+       }
+
        key = key_ref_to_ptr(key_ref);
 
        /* use the second 8-bits of permissions for keys the caller owns */
        if (is_key_possessed(key_ref))
                kperm |= key->perm >> 24;
 
-       kperm = kperm & perm & KEY_NEED_ALL;
-
-       if (kperm != perm)
+       if ((kperm & mask) != mask)
                return -EACCES;
 
        /* let LSM be the final arbiter */
-       return security_key_permission(key_ref, cred, perm);
+lsm:
+       return security_key_permission(key_ref, cred, need_perm);
 }
 EXPORT_SYMBOL(key_task_permission);
 
 
  * returned key reference.
  */
 key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
-                         key_perm_t perm)
+                         enum key_need_perm need_perm)
 {
        struct keyring_search_context ctx = {
                .match_data.cmp         = lookup_user_key_possessed,
 
        /* unlink does not use the nominated key in any way, so can skip all
         * the permission checks as it is only concerned with the keyring */
-       if (lflags & KEY_LOOKUP_FOR_UNLINK) {
-               ret = 0;
-               goto error;
-       }
-
-       if (!(lflags & KEY_LOOKUP_PARTIAL)) {
-               ret = wait_for_key_construction(key, true);
-               switch (ret) {
-               case -ERESTARTSYS:
-                       goto invalid_key;
-               default:
-                       if (perm)
+       if (need_perm != KEY_NEED_UNLINK) {
+               if (!(lflags & KEY_LOOKUP_PARTIAL)) {
+                       ret = wait_for_key_construction(key, true);
+                       switch (ret) {
+                       case -ERESTARTSYS:
+                               goto invalid_key;
+                       default:
+                               if (need_perm != KEY_AUTHTOKEN_OVERRIDE &&
+                                   need_perm != KEY_DEFER_PERM_CHECK)
+                                       goto invalid_key;
+                       case 0:
+                               break;
+                       }
+               } else if (need_perm != KEY_DEFER_PERM_CHECK) {
+                       ret = key_validate(key);
+                       if (ret < 0)
                                goto invalid_key;
-               case 0:
-                       break;
                }
-       } else if (perm) {
-               ret = key_validate(key);
-               if (ret < 0)
+
+               ret = -EIO;
+               if (!(lflags & KEY_LOOKUP_PARTIAL) &&
+                   key_read_state(key) == KEY_IS_UNINSTANTIATED)
                        goto invalid_key;
        }
 
-       ret = -EIO;
-       if (!(lflags & KEY_LOOKUP_PARTIAL) &&
-           key_read_state(key) == KEY_IS_UNINSTANTIATED)
-               goto invalid_key;
-
        /* check the permissions */
-       ret = key_task_permission(key_ref, ctx.cred, perm);
+       ret = key_task_permission(key_ref, ctx.cred, need_perm);
        if (ret < 0)
                goto invalid_key;
 
 
        call_void_hook(key_free, key);
 }
 
-int security_key_permission(key_ref_t key_ref,
-                           const struct cred *cred, unsigned perm)
+int security_key_permission(key_ref_t key_ref, const struct cred *cred,
+                           enum key_need_perm need_perm)
 {
-       return call_int_hook(key_permission, 0, key_ref, cred, perm);
+       return call_int_hook(key_permission, 0, key_ref, cred, need_perm);
 }
 
 int security_key_getsecurity(struct key *key, char **_buffer)
 
 
 static int selinux_key_permission(key_ref_t key_ref,
                                  const struct cred *cred,
-                                 unsigned perm)
+                                 enum key_need_perm need_perm)
 {
        struct key *key;
        struct key_security_struct *ksec;
-       u32 sid;
+       u32 perm, sid;
 
-       /* if no specific permissions are requested, we skip the
-          permission check. No serious, additional covert channels
-          appear to be created. */
-       if (perm == 0)
+       switch (need_perm) {
+       case KEY_NEED_VIEW:
+               perm = KEY__VIEW;
+               break;
+       case KEY_NEED_READ:
+               perm = KEY__READ;
+               break;
+       case KEY_NEED_WRITE:
+               perm = KEY__WRITE;
+               break;
+       case KEY_NEED_SEARCH:
+               perm = KEY__SEARCH;
+               break;
+       case KEY_NEED_LINK:
+               perm = KEY__LINK;
+               break;
+       case KEY_NEED_SETATTR:
+               perm = KEY__SETATTR;
+               break;
+       case KEY_NEED_UNLINK:
+       case KEY_SYSADMIN_OVERRIDE:
+       case KEY_AUTHTOKEN_OVERRIDE:
+       case KEY_DEFER_PERM_CHECK:
                return 0;
+       default:
+               WARN_ON(1);
+               return -EPERM;
 
-       sid = cred_sid(cred);
+       }
 
+       sid = cred_sid(cred);
        key = key_ref_to_ptr(key_ref);
        ksec = key->security;
 
 
  * smack_key_permission - Smack access on a key
  * @key_ref: gets to the object
  * @cred: the credentials to use
- * @perm: requested key permissions
+ * @need_perm: requested key permission
  *
  * Return 0 if the task has read and write to the object,
  * an error code otherwise
  */
 static int smack_key_permission(key_ref_t key_ref,
-                               const struct cred *cred, unsigned perm)
+                               const struct cred *cred,
+                               enum key_need_perm need_perm)
 {
        struct key *keyp;
        struct smk_audit_info ad;
        /*
         * Validate requested permissions
         */
-       if (perm & ~KEY_NEED_ALL)
+       switch (need_perm) {
+       case KEY_NEED_READ:
+       case KEY_NEED_SEARCH:
+       case KEY_NEED_VIEW:
+               request |= MAY_READ;
+               break;
+       case KEY_NEED_WRITE:
+       case KEY_NEED_LINK:
+       case KEY_NEED_SETATTR:
+               request |= MAY_WRITE;
+               break;
+       case KEY_NEED_UNSPECIFIED:
+       case KEY_NEED_UNLINK:
+       case KEY_SYSADMIN_OVERRIDE:
+       case KEY_AUTHTOKEN_OVERRIDE:
+       case KEY_DEFER_PERM_CHECK:
+               return 0;
+       default:
                return -EINVAL;
+       }
 
        keyp = key_ref_to_ptr(key_ref);
        if (keyp == NULL)
        ad.a.u.key_struct.key = keyp->serial;
        ad.a.u.key_struct.key_desc = keyp->description;
 #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))
-               request |= MAY_WRITE;
        rc = smk_access(tkp, keyp->security, request, &ad);
        rc = smk_bu_note("key access", tkp, keyp->security, request, rc);
        return rc;