Merge tag 'fbdev-v5.2' of git://github.com/bzolnier/linux
[sfrench/cifs-2.6.git] / security / safesetid / lsm.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * SafeSetID Linux Security Module
4  *
5  * Author: Micah Morton <mortonm@chromium.org>
6  *
7  * Copyright (C) 2018 The Chromium OS Authors.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2, as
11  * published by the Free Software Foundation.
12  *
13  */
14
15 #define pr_fmt(fmt) "SafeSetID: " fmt
16
17 #include <linux/hashtable.h>
18 #include <linux/lsm_hooks.h>
19 #include <linux/module.h>
20 #include <linux/ptrace.h>
21 #include <linux/sched/task_stack.h>
22 #include <linux/security.h>
23
24 /* Flag indicating whether initialization completed */
25 int safesetid_initialized;
26
27 #define NUM_BITS 8 /* 128 buckets in hash table */
28
29 static DEFINE_HASHTABLE(safesetid_whitelist_hashtable, NUM_BITS);
30
31 /*
32  * Hash table entry to store safesetid policy signifying that 'parent' user
33  * can setid to 'child' user.
34  */
35 struct entry {
36         struct hlist_node next;
37         struct hlist_node dlist; /* for deletion cleanup */
38         uint64_t parent_kuid;
39         uint64_t child_kuid;
40 };
41
42 static DEFINE_SPINLOCK(safesetid_whitelist_hashtable_spinlock);
43
44 static bool check_setuid_policy_hashtable_key(kuid_t parent)
45 {
46         struct entry *entry;
47
48         rcu_read_lock();
49         hash_for_each_possible_rcu(safesetid_whitelist_hashtable,
50                                    entry, next, __kuid_val(parent)) {
51                 if (entry->parent_kuid == __kuid_val(parent)) {
52                         rcu_read_unlock();
53                         return true;
54                 }
55         }
56         rcu_read_unlock();
57
58         return false;
59 }
60
61 static bool check_setuid_policy_hashtable_key_value(kuid_t parent,
62                                                     kuid_t child)
63 {
64         struct entry *entry;
65
66         rcu_read_lock();
67         hash_for_each_possible_rcu(safesetid_whitelist_hashtable,
68                                    entry, next, __kuid_val(parent)) {
69                 if (entry->parent_kuid == __kuid_val(parent) &&
70                     entry->child_kuid == __kuid_val(child)) {
71                         rcu_read_unlock();
72                         return true;
73                 }
74         }
75         rcu_read_unlock();
76
77         return false;
78 }
79
80 static int safesetid_security_capable(const struct cred *cred,
81                                       struct user_namespace *ns,
82                                       int cap,
83                                       unsigned int opts)
84 {
85         if (cap == CAP_SETUID &&
86             check_setuid_policy_hashtable_key(cred->uid)) {
87                 if (!(opts & CAP_OPT_INSETID)) {
88                         /*
89                          * Deny if we're not in a set*uid() syscall to avoid
90                          * giving powers gated by CAP_SETUID that are related
91                          * to functionality other than calling set*uid() (e.g.
92                          * allowing user to set up userns uid mappings).
93                          */
94                         pr_warn("Operation requires CAP_SETUID, which is not available to UID %u for operations besides approved set*uid transitions",
95                                 __kuid_val(cred->uid));
96                         return -1;
97                 }
98         }
99         return 0;
100 }
101
102 static int check_uid_transition(kuid_t parent, kuid_t child)
103 {
104         if (check_setuid_policy_hashtable_key_value(parent, child))
105                 return 0;
106         pr_warn("UID transition (%d -> %d) blocked",
107                 __kuid_val(parent),
108                 __kuid_val(child));
109         /*
110          * Kill this process to avoid potential security vulnerabilities
111          * that could arise from a missing whitelist entry preventing a
112          * privileged process from dropping to a lesser-privileged one.
113          */
114         force_sig(SIGKILL, current);
115         return -EACCES;
116 }
117
118 /*
119  * Check whether there is either an exception for user under old cred struct to
120  * set*uid to user under new cred struct, or the UID transition is allowed (by
121  * Linux set*uid rules) even without CAP_SETUID.
122  */
123 static int safesetid_task_fix_setuid(struct cred *new,
124                                      const struct cred *old,
125                                      int flags)
126 {
127
128         /* Do nothing if there are no setuid restrictions for this UID. */
129         if (!check_setuid_policy_hashtable_key(old->uid))
130                 return 0;
131
132         switch (flags) {
133         case LSM_SETID_RE:
134                 /*
135                  * Users for which setuid restrictions exist can only set the
136                  * real UID to the real UID or the effective UID, unless an
137                  * explicit whitelist policy allows the transition.
138                  */
139                 if (!uid_eq(old->uid, new->uid) &&
140                         !uid_eq(old->euid, new->uid)) {
141                         return check_uid_transition(old->uid, new->uid);
142                 }
143                 /*
144                  * Users for which setuid restrictions exist can only set the
145                  * effective UID to the real UID, the effective UID, or the
146                  * saved set-UID, unless an explicit whitelist policy allows
147                  * the transition.
148                  */
149                 if (!uid_eq(old->uid, new->euid) &&
150                         !uid_eq(old->euid, new->euid) &&
151                         !uid_eq(old->suid, new->euid)) {
152                         return check_uid_transition(old->euid, new->euid);
153                 }
154                 break;
155         case LSM_SETID_ID:
156                 /*
157                  * Users for which setuid restrictions exist cannot change the
158                  * real UID or saved set-UID unless an explicit whitelist
159                  * policy allows the transition.
160                  */
161                 if (!uid_eq(old->uid, new->uid))
162                         return check_uid_transition(old->uid, new->uid);
163                 if (!uid_eq(old->suid, new->suid))
164                         return check_uid_transition(old->suid, new->suid);
165                 break;
166         case LSM_SETID_RES:
167                 /*
168                  * Users for which setuid restrictions exist cannot change the
169                  * real UID, effective UID, or saved set-UID to anything but
170                  * one of: the current real UID, the current effective UID or
171                  * the current saved set-user-ID unless an explicit whitelist
172                  * policy allows the transition.
173                  */
174                 if (!uid_eq(new->uid, old->uid) &&
175                         !uid_eq(new->uid, old->euid) &&
176                         !uid_eq(new->uid, old->suid)) {
177                         return check_uid_transition(old->uid, new->uid);
178                 }
179                 if (!uid_eq(new->euid, old->uid) &&
180                         !uid_eq(new->euid, old->euid) &&
181                         !uid_eq(new->euid, old->suid)) {
182                         return check_uid_transition(old->euid, new->euid);
183                 }
184                 if (!uid_eq(new->suid, old->uid) &&
185                         !uid_eq(new->suid, old->euid) &&
186                         !uid_eq(new->suid, old->suid)) {
187                         return check_uid_transition(old->suid, new->suid);
188                 }
189                 break;
190         case LSM_SETID_FS:
191                 /*
192                  * Users for which setuid restrictions exist cannot change the
193                  * filesystem UID to anything but one of: the current real UID,
194                  * the current effective UID or the current saved set-UID
195                  * unless an explicit whitelist policy allows the transition.
196                  */
197                 if (!uid_eq(new->fsuid, old->uid)  &&
198                         !uid_eq(new->fsuid, old->euid)  &&
199                         !uid_eq(new->fsuid, old->suid) &&
200                         !uid_eq(new->fsuid, old->fsuid)) {
201                         return check_uid_transition(old->fsuid, new->fsuid);
202                 }
203                 break;
204         default:
205                 pr_warn("Unknown setid state %d\n", flags);
206                 force_sig(SIGKILL, current);
207                 return -EINVAL;
208         }
209         return 0;
210 }
211
212 int add_safesetid_whitelist_entry(kuid_t parent, kuid_t child)
213 {
214         struct entry *new;
215
216         /* Return if entry already exists */
217         if (check_setuid_policy_hashtable_key_value(parent, child))
218                 return 0;
219
220         new = kzalloc(sizeof(struct entry), GFP_KERNEL);
221         if (!new)
222                 return -ENOMEM;
223         new->parent_kuid = __kuid_val(parent);
224         new->child_kuid = __kuid_val(child);
225         spin_lock(&safesetid_whitelist_hashtable_spinlock);
226         hash_add_rcu(safesetid_whitelist_hashtable,
227                      &new->next,
228                      __kuid_val(parent));
229         spin_unlock(&safesetid_whitelist_hashtable_spinlock);
230         return 0;
231 }
232
233 void flush_safesetid_whitelist_entries(void)
234 {
235         struct entry *entry;
236         struct hlist_node *hlist_node;
237         unsigned int bkt_loop_cursor;
238         HLIST_HEAD(free_list);
239
240         /*
241          * Could probably use hash_for_each_rcu here instead, but this should
242          * be fine as well.
243          */
244         spin_lock(&safesetid_whitelist_hashtable_spinlock);
245         hash_for_each_safe(safesetid_whitelist_hashtable, bkt_loop_cursor,
246                            hlist_node, entry, next) {
247                 hash_del_rcu(&entry->next);
248                 hlist_add_head(&entry->dlist, &free_list);
249         }
250         spin_unlock(&safesetid_whitelist_hashtable_spinlock);
251         synchronize_rcu();
252         hlist_for_each_entry_safe(entry, hlist_node, &free_list, dlist) {
253                 hlist_del(&entry->dlist);
254                 kfree(entry);
255         }
256 }
257
258 static struct security_hook_list safesetid_security_hooks[] = {
259         LSM_HOOK_INIT(task_fix_setuid, safesetid_task_fix_setuid),
260         LSM_HOOK_INIT(capable, safesetid_security_capable)
261 };
262
263 static int __init safesetid_security_init(void)
264 {
265         security_add_hooks(safesetid_security_hooks,
266                            ARRAY_SIZE(safesetid_security_hooks), "safesetid");
267
268         /* Report that SafeSetID successfully initialized */
269         safesetid_initialized = 1;
270
271         return 0;
272 }
273
274 DEFINE_LSM(safesetid_security_init) = {
275         .init = safesetid_security_init,
276         .name = "safesetid",
277 };