12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2973c9f4fSDavid Howells /* Key permission checking
3468ed2b0SDavid Howells *
4468ed2b0SDavid Howells * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
5468ed2b0SDavid Howells * Written by David Howells (dhowells@redhat.com)
6468ed2b0SDavid Howells */
7468ed2b0SDavid Howells
8876979c9SPaul Gortmaker #include <linux/export.h>
929db9190SDavid Howells #include <linux/security.h>
10468ed2b0SDavid Howells #include "internal.h"
11468ed2b0SDavid Howells
12d84f4f99SDavid Howells /**
13d84f4f99SDavid Howells * key_task_permission - Check a key can be used
14973c9f4fSDavid Howells * @key_ref: The key to check.
15973c9f4fSDavid Howells * @cred: The credentials to use.
16*8c0637e9SDavid Howells * @need_perm: The permission required.
17d84f4f99SDavid Howells *
18d84f4f99SDavid Howells * Check to see whether permission is granted to use a key in the desired way,
19d84f4f99SDavid Howells * but permit the security modules to override.
20d84f4f99SDavid Howells *
21973c9f4fSDavid Howells * The caller must hold either a ref on cred or must hold the RCU readlock.
22973c9f4fSDavid Howells *
23973c9f4fSDavid Howells * Returns 0 if successful, -EACCES if access is denied based on the
24973c9f4fSDavid Howells * permissions bits or the LSM check.
25468ed2b0SDavid Howells */
key_task_permission(const key_ref_t key_ref,const struct cred * cred,enum key_need_perm need_perm)26d84f4f99SDavid Howells int key_task_permission(const key_ref_t key_ref, const struct cred *cred,
27*8c0637e9SDavid Howells enum key_need_perm need_perm)
28468ed2b0SDavid Howells {
29028db3e2SLinus Torvalds struct key *key;
30*8c0637e9SDavid Howells key_perm_t kperm, mask;
31028db3e2SLinus Torvalds int ret;
32468ed2b0SDavid Howells
33*8c0637e9SDavid Howells switch (need_perm) {
34*8c0637e9SDavid Howells default:
35*8c0637e9SDavid Howells WARN_ON(1);
36*8c0637e9SDavid Howells return -EACCES;
37*8c0637e9SDavid Howells case KEY_NEED_UNLINK:
38*8c0637e9SDavid Howells case KEY_SYSADMIN_OVERRIDE:
39*8c0637e9SDavid Howells case KEY_AUTHTOKEN_OVERRIDE:
40*8c0637e9SDavid Howells case KEY_DEFER_PERM_CHECK:
41*8c0637e9SDavid Howells goto lsm;
42*8c0637e9SDavid Howells
43*8c0637e9SDavid Howells case KEY_NEED_VIEW: mask = KEY_OTH_VIEW; break;
44*8c0637e9SDavid Howells case KEY_NEED_READ: mask = KEY_OTH_READ; break;
45*8c0637e9SDavid Howells case KEY_NEED_WRITE: mask = KEY_OTH_WRITE; break;
46*8c0637e9SDavid Howells case KEY_NEED_SEARCH: mask = KEY_OTH_SEARCH; break;
47*8c0637e9SDavid Howells case KEY_NEED_LINK: mask = KEY_OTH_LINK; break;
48*8c0637e9SDavid Howells case KEY_NEED_SETATTR: mask = KEY_OTH_SETATTR; break;
49*8c0637e9SDavid Howells }
50*8c0637e9SDavid Howells
51468ed2b0SDavid Howells key = key_ref_to_ptr(key_ref);
52468ed2b0SDavid Howells
53028db3e2SLinus Torvalds /* use the second 8-bits of permissions for keys the caller owns */
54028db3e2SLinus Torvalds if (uid_eq(key->uid, cred->fsuid)) {
55028db3e2SLinus Torvalds kperm = key->perm >> 16;
56028db3e2SLinus Torvalds goto use_these_perms;
57028db3e2SLinus Torvalds }
58468ed2b0SDavid Howells
59028db3e2SLinus Torvalds /* use the third 8-bits of permissions for keys the caller has a group
60028db3e2SLinus Torvalds * membership in common with */
61028db3e2SLinus Torvalds if (gid_valid(key->gid) && key->perm & KEY_GRP_ALL) {
62028db3e2SLinus Torvalds if (gid_eq(key->gid, cred->fsgid)) {
63028db3e2SLinus Torvalds kperm = key->perm >> 8;
64028db3e2SLinus Torvalds goto use_these_perms;
65028db3e2SLinus Torvalds }
66468ed2b0SDavid Howells
67028db3e2SLinus Torvalds ret = groups_search(cred->group_info, key->gid);
68028db3e2SLinus Torvalds if (ret) {
69028db3e2SLinus Torvalds kperm = key->perm >> 8;
70028db3e2SLinus Torvalds goto use_these_perms;
71028db3e2SLinus Torvalds }
72028db3e2SLinus Torvalds }
73468ed2b0SDavid Howells
74028db3e2SLinus Torvalds /* otherwise use the least-significant 8-bits */
75028db3e2SLinus Torvalds kperm = key->perm;
76028db3e2SLinus Torvalds
77028db3e2SLinus Torvalds use_these_perms:
78028db3e2SLinus Torvalds
79028db3e2SLinus Torvalds /* use the top 8-bits of permissions for keys the caller possesses
80028db3e2SLinus Torvalds * - possessor permissions are additive with other permissions
81028db3e2SLinus Torvalds */
827ab501dbSDavid Howells if (is_key_possessed(key_ref))
83028db3e2SLinus Torvalds kperm |= key->perm >> 24;
847ab501dbSDavid Howells
85*8c0637e9SDavid Howells if ((kperm & mask) != mask)
8629db9190SDavid Howells return -EACCES;
87028db3e2SLinus Torvalds
88028db3e2SLinus Torvalds /* let LSM be the final arbiter */
89*8c0637e9SDavid Howells lsm:
90*8c0637e9SDavid Howells return security_key_permission(key_ref, cred, need_perm);
91a8b17ed0SDavid Howells }
92468ed2b0SDavid Howells EXPORT_SYMBOL(key_task_permission);
93b5f545c8SDavid Howells
94973c9f4fSDavid Howells /**
95973c9f4fSDavid Howells * key_validate - Validate a key.
96973c9f4fSDavid Howells * @key: The key to be validated.
97973c9f4fSDavid Howells *
98fd75815fSDavid Howells * Check that a key is valid, returning 0 if the key is okay, -ENOKEY if the
99fd75815fSDavid Howells * key is invalidated, -EKEYREVOKED if the key's type has been removed or if
100fd75815fSDavid Howells * the key has been revoked or -EKEYEXPIRED if the key has expired.
101b5f545c8SDavid Howells */
key_validate(const struct key * key)102b404aef7SDavid Howells int key_validate(const struct key *key)
103b5f545c8SDavid Howells {
1041823d475SEric Biggers unsigned long flags = READ_ONCE(key->flags);
105074d5898SBaolin Wang time64_t expiry = READ_ONCE(key->expiry);
106b5f545c8SDavid Howells
107fd75815fSDavid Howells if (flags & (1 << KEY_FLAG_INVALIDATED))
108b404aef7SDavid Howells return -ENOKEY;
109fd75815fSDavid Howells
110b5f545c8SDavid Howells /* check it's still accessible */
111fd75815fSDavid Howells if (flags & ((1 << KEY_FLAG_REVOKED) |
112fd75815fSDavid Howells (1 << KEY_FLAG_DEAD)))
113b404aef7SDavid Howells return -EKEYREVOKED;
114b5f545c8SDavid Howells
115b5f545c8SDavid Howells /* check it hasn't expired */
1161823d475SEric Biggers if (expiry) {
117074d5898SBaolin Wang if (ktime_get_real_seconds() >= expiry)
118b404aef7SDavid Howells return -EKEYEXPIRED;
119b5f545c8SDavid Howells }
120b5f545c8SDavid Howells
121b404aef7SDavid Howells return 0;
122a8b17ed0SDavid Howells }
123b5f545c8SDavid Howells EXPORT_SYMBOL(key_validate);
124