Skip to content

Commit 5294bac

Browse files
thomas-cedenomicah-morton
authored andcommitted
LSM: SafeSetID: Add GID security policy handling
The SafeSetID LSM has functionality for restricting setuid() calls based on its configured security policies. This patch adds the analogous functionality for setgid() calls. This is mostly a copy-and-paste change with some code deduplication, plus slight modifications/name changes to the policy-rule-related structs (now contain GID rules in addition to the UID ones) and some type generalization since SafeSetID now needs to deal with kgid_t and kuid_t types. Signed-off-by: Thomas Cedeno <thomascedeno@google.com> Signed-off-by: Micah Morton <mortonm@chromium.org>
1 parent 111767c commit 5294bac

4 files changed

Lines changed: 329 additions & 118 deletions

File tree

Documentation/admin-guide/LSM/SafeSetID.rst

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ SafeSetID
33
=========
44
SafeSetID is an LSM module that gates the setid family of syscalls to restrict
55
UID/GID transitions from a given UID/GID to only those approved by a
6-
system-wide whitelist. These restrictions also prohibit the given UIDs/GIDs
6+
system-wide allowlist. These restrictions also prohibit the given UIDs/GIDs
77
from obtaining auxiliary privileges associated with CAP_SET{U/G}ID, such as
8-
allowing a user to set up user namespace UID mappings.
8+
allowing a user to set up user namespace UID/GID mappings.
99

1010

1111
Background
@@ -98,10 +98,21 @@ Directions for use
9898
==================
9999
This LSM hooks the setid syscalls to make sure transitions are allowed if an
100100
applicable restriction policy is in place. Policies are configured through
101-
securityfs by writing to the safesetid/add_whitelist_policy and
102-
safesetid/flush_whitelist_policies files at the location where securityfs is
103-
mounted. The format for adding a policy is '<UID>:<UID>', using literal
104-
numbers, such as '123:456'. To flush the policies, any write to the file is
105-
sufficient. Again, configuring a policy for a UID will prevent that UID from
106-
obtaining auxiliary setid privileges, such as allowing a user to set up user
107-
namespace UID mappings.
101+
securityfs by writing to the safesetid/uid_allowlist_policy and
102+
safesetid/gid_allowlist_policy files at the location where securityfs is
103+
mounted. The format for adding a policy is '<UID>:<UID>' or '<GID>:<GID>',
104+
using literal numbers, and ending with a newline character such as '123:456\n'.
105+
Writing an empty string "" will flush the policy. Again, configuring a policy
106+
for a UID/GID will prevent that UID/GID from obtaining auxiliary setid
107+
privileges, such as allowing a user to set up user namespace UID/GID mappings.
108+
109+
Note on GID policies and setgroups()
110+
==================
111+
In v5.9 we are adding support for limiting CAP_SETGID privileges as was done
112+
previously for CAP_SETUID. However, for compatibility with common sandboxing
113+
related code conventions in userspace, we currently allow arbitrary
114+
setgroups() calls for processes with CAP_SETGID restrictions. Until we add
115+
support in a future release for restricting setgroups() calls, these GID
116+
policies add no meaningful security. setgroups() restrictions will be enforced
117+
once we have the policy checking code in place, which will rely on GID policy
118+
configuration code added in v5.9.

security/safesetid/lsm.c

Lines changed: 143 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,36 @@
2424
/* Flag indicating whether initialization completed */
2525
int safesetid_initialized;
2626

27-
struct setuid_ruleset __rcu *safesetid_setuid_rules;
27+
struct setid_ruleset __rcu *safesetid_setuid_rules;
28+
struct setid_ruleset __rcu *safesetid_setgid_rules;
29+
2830

2931
/* Compute a decision for a transition from @src to @dst under @policy. */
30-
enum sid_policy_type _setuid_policy_lookup(struct setuid_ruleset *policy,
31-
kuid_t src, kuid_t dst)
32+
enum sid_policy_type _setid_policy_lookup(struct setid_ruleset *policy,
33+
kid_t src, kid_t dst)
3234
{
33-
struct setuid_rule *rule;
35+
struct setid_rule *rule;
3436
enum sid_policy_type result = SIDPOL_DEFAULT;
3537

36-
hash_for_each_possible(policy->rules, rule, next, __kuid_val(src)) {
37-
if (!uid_eq(rule->src_uid, src))
38-
continue;
39-
if (uid_eq(rule->dst_uid, dst))
40-
return SIDPOL_ALLOWED;
38+
if (policy->type == UID) {
39+
hash_for_each_possible(policy->rules, rule, next, __kuid_val(src.uid)) {
40+
if (!uid_eq(rule->src_id.uid, src.uid))
41+
continue;
42+
if (uid_eq(rule->dst_id.uid, dst.uid))
43+
return SIDPOL_ALLOWED;
44+
result = SIDPOL_CONSTRAINED;
45+
}
46+
} else if (policy->type == GID) {
47+
hash_for_each_possible(policy->rules, rule, next, __kgid_val(src.gid)) {
48+
if (!gid_eq(rule->src_id.gid, src.gid))
49+
continue;
50+
if (gid_eq(rule->dst_id.gid, dst.gid)){
51+
return SIDPOL_ALLOWED;
52+
}
53+
result = SIDPOL_CONSTRAINED;
54+
}
55+
} else {
56+
/* Should not reach here, report the ID as contrainsted */
4157
result = SIDPOL_CONSTRAINED;
4258
}
4359
return result;
@@ -47,15 +63,26 @@ enum sid_policy_type _setuid_policy_lookup(struct setuid_ruleset *policy,
4763
* Compute a decision for a transition from @src to @dst under the active
4864
* policy.
4965
*/
50-
static enum sid_policy_type setuid_policy_lookup(kuid_t src, kuid_t dst)
66+
static enum sid_policy_type setid_policy_lookup(kid_t src, kid_t dst, enum setid_type new_type)
5167
{
5268
enum sid_policy_type result = SIDPOL_DEFAULT;
53-
struct setuid_ruleset *pol;
69+
struct setid_ruleset *pol;
5470

5571
rcu_read_lock();
56-
pol = rcu_dereference(safesetid_setuid_rules);
57-
if (pol)
58-
result = _setuid_policy_lookup(pol, src, dst);
72+
if (new_type == UID)
73+
pol = rcu_dereference(safesetid_setuid_rules);
74+
else if (new_type == GID)
75+
pol = rcu_dereference(safesetid_setgid_rules);
76+
else { /* Should not reach here */
77+
result = SIDPOL_CONSTRAINED;
78+
rcu_read_unlock();
79+
return result;
80+
}
81+
82+
if (pol) {
83+
pol->type = new_type;
84+
result = _setid_policy_lookup(pol, src, dst);
85+
}
5986
rcu_read_unlock();
6087
return result;
6188
}
@@ -65,57 +92,101 @@ static int safesetid_security_capable(const struct cred *cred,
6592
int cap,
6693
unsigned int opts)
6794
{
68-
/* We're only interested in CAP_SETUID. */
69-
if (cap != CAP_SETUID)
95+
/* We're only interested in CAP_SETUID and CAP_SETGID. */
96+
if (cap != CAP_SETUID && cap != CAP_SETGID)
7097
return 0;
7198

7299
/*
73-
* If CAP_SETUID is currently used for a set*uid() syscall, we want to
100+
* If CAP_SET{U/G}ID is currently used for a setid() syscall, we want to
74101
* let it go through here; the real security check happens later, in the
75-
* task_fix_setuid hook.
102+
* task_fix_set{u/g}id hook.
103+
*
104+
* NOTE:
105+
* Until we add support for restricting setgroups() calls, GID security
106+
* policies offer no meaningful security since we always return 0 here
107+
* when called from within the setgroups() syscall and there is no
108+
* additional hook later on to enforce security policies for setgroups().
76109
*/
77110
if ((opts & CAP_OPT_INSETID) != 0)
78111
return 0;
79112

80-
/*
81-
* If no policy applies to this task, allow the use of CAP_SETUID for
82-
* other purposes.
83-
*/
84-
if (setuid_policy_lookup(cred->uid, INVALID_UID) == SIDPOL_DEFAULT)
113+
switch (cap) {
114+
case CAP_SETUID:
115+
/*
116+
* If no policy applies to this task, allow the use of CAP_SETUID for
117+
* other purposes.
118+
*/
119+
if (setid_policy_lookup((kid_t)cred->uid, INVALID_ID, UID) == SIDPOL_DEFAULT)
120+
return 0;
121+
/*
122+
* Reject use of CAP_SETUID for functionality other than calling
123+
* set*uid() (e.g. setting up userns uid mappings).
124+
*/
125+
pr_warn("Operation requires CAP_SETUID, which is not available to UID %u for operations besides approved set*uid transitions\n",
126+
__kuid_val(cred->uid));
127+
return -EPERM;
128+
break;
129+
case CAP_SETGID:
130+
/*
131+
* If no policy applies to this task, allow the use of CAP_SETGID for
132+
* other purposes.
133+
*/
134+
if (setid_policy_lookup((kid_t)cred->gid, INVALID_ID, GID) == SIDPOL_DEFAULT)
135+
return 0;
136+
/*
137+
* Reject use of CAP_SETUID for functionality other than calling
138+
* set*gid() (e.g. setting up userns gid mappings).
139+
*/
140+
pr_warn("Operation requires CAP_SETGID, which is not available to GID %u for operations besides approved set*gid transitions\n",
141+
__kuid_val(cred->uid));
142+
return -EPERM;
143+
break;
144+
default:
145+
/* Error, the only capabilities were checking for is CAP_SETUID/GID */
85146
return 0;
86-
87-
/*
88-
* Reject use of CAP_SETUID for functionality other than calling
89-
* set*uid() (e.g. setting up userns uid mappings).
90-
*/
91-
pr_warn("Operation requires CAP_SETUID, which is not available to UID %u for operations besides approved set*uid transitions\n",
92-
__kuid_val(cred->uid));
93-
return -EPERM;
147+
break;
148+
}
149+
return 0;
94150
}
95151

96152
/*
97153
* Check whether a caller with old credentials @old is allowed to switch to
98-
* credentials that contain @new_uid.
154+
* credentials that contain @new_id.
99155
*/
100-
static bool uid_permitted_for_cred(const struct cred *old, kuid_t new_uid)
156+
static bool id_permitted_for_cred(const struct cred *old, kid_t new_id, enum setid_type new_type)
101157
{
102158
bool permitted;
103159

104-
/* If our old creds already had this UID in it, it's fine. */
105-
if (uid_eq(new_uid, old->uid) || uid_eq(new_uid, old->euid) ||
106-
uid_eq(new_uid, old->suid))
107-
return true;
160+
/* If our old creds already had this ID in it, it's fine. */
161+
if (new_type == UID) {
162+
if (uid_eq(new_id.uid, old->uid) || uid_eq(new_id.uid, old->euid) ||
163+
uid_eq(new_id.uid, old->suid))
164+
return true;
165+
} else if (new_type == GID){
166+
if (gid_eq(new_id.gid, old->gid) || gid_eq(new_id.gid, old->egid) ||
167+
gid_eq(new_id.gid, old->sgid))
168+
return true;
169+
} else /* Error, new_type is an invalid type */
170+
return false;
108171

109172
/*
110173
* Transitions to new UIDs require a check against the policy of the old
111174
* RUID.
112175
*/
113176
permitted =
114-
setuid_policy_lookup(old->uid, new_uid) != SIDPOL_CONSTRAINED;
177+
setid_policy_lookup((kid_t)old->uid, new_id, new_type) != SIDPOL_CONSTRAINED;
178+
115179
if (!permitted) {
116-
pr_warn("UID transition ((%d,%d,%d) -> %d) blocked\n",
117-
__kuid_val(old->uid), __kuid_val(old->euid),
118-
__kuid_val(old->suid), __kuid_val(new_uid));
180+
if (new_type == UID) {
181+
pr_warn("UID transition ((%d,%d,%d) -> %d) blocked\n",
182+
__kuid_val(old->uid), __kuid_val(old->euid),
183+
__kuid_val(old->suid), __kuid_val(new_id.uid));
184+
} else if (new_type == GID) {
185+
pr_warn("GID transition ((%d,%d,%d) -> %d) blocked\n",
186+
__kgid_val(old->gid), __kgid_val(old->egid),
187+
__kgid_val(old->sgid), __kgid_val(new_id.gid));
188+
} else /* Error, new_type is an invalid type */
189+
return false;
119190
}
120191
return permitted;
121192
}
@@ -131,18 +202,42 @@ static int safesetid_task_fix_setuid(struct cred *new,
131202
{
132203

133204
/* Do nothing if there are no setuid restrictions for our old RUID. */
134-
if (setuid_policy_lookup(old->uid, INVALID_UID) == SIDPOL_DEFAULT)
205+
if (setid_policy_lookup((kid_t)old->uid, INVALID_ID, UID) == SIDPOL_DEFAULT)
206+
return 0;
207+
208+
if (id_permitted_for_cred(old, (kid_t)new->uid, UID) &&
209+
id_permitted_for_cred(old, (kid_t)new->euid, UID) &&
210+
id_permitted_for_cred(old, (kid_t)new->suid, UID) &&
211+
id_permitted_for_cred(old, (kid_t)new->fsuid, UID))
212+
return 0;
213+
214+
/*
215+
* Kill this process to avoid potential security vulnerabilities
216+
* that could arise from a missing allowlist entry preventing a
217+
* privileged process from dropping to a lesser-privileged one.
218+
*/
219+
force_sig(SIGKILL);
220+
return -EACCES;
221+
}
222+
223+
static int safesetid_task_fix_setgid(struct cred *new,
224+
const struct cred *old,
225+
int flags)
226+
{
227+
228+
/* Do nothing if there are no setgid restrictions for our old RGID. */
229+
if (setid_policy_lookup((kid_t)old->gid, INVALID_ID, GID) == SIDPOL_DEFAULT)
135230
return 0;
136231

137-
if (uid_permitted_for_cred(old, new->uid) &&
138-
uid_permitted_for_cred(old, new->euid) &&
139-
uid_permitted_for_cred(old, new->suid) &&
140-
uid_permitted_for_cred(old, new->fsuid))
232+
if (id_permitted_for_cred(old, (kid_t)new->gid, GID) &&
233+
id_permitted_for_cred(old, (kid_t)new->egid, GID) &&
234+
id_permitted_for_cred(old, (kid_t)new->sgid, GID) &&
235+
id_permitted_for_cred(old, (kid_t)new->fsgid, GID))
141236
return 0;
142237

143238
/*
144239
* Kill this process to avoid potential security vulnerabilities
145-
* that could arise from a missing whitelist entry preventing a
240+
* that could arise from a missing allowlist entry preventing a
146241
* privileged process from dropping to a lesser-privileged one.
147242
*/
148243
force_sig(SIGKILL);
@@ -151,6 +246,7 @@ static int safesetid_task_fix_setuid(struct cred *new,
151246

152247
static struct security_hook_list safesetid_security_hooks[] = {
153248
LSM_HOOK_INIT(task_fix_setuid, safesetid_task_fix_setuid),
249+
LSM_HOOK_INIT(task_fix_setgid, safesetid_task_fix_setgid),
154250
LSM_HOOK_INIT(capable, safesetid_security_capable)
155251
};
156252

security/safesetid/lsm.h

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,27 +27,47 @@ enum sid_policy_type {
2727
SIDPOL_ALLOWED /* target ID explicitly allowed */
2828
};
2929

30+
typedef union {
31+
kuid_t uid;
32+
kgid_t gid;
33+
} kid_t;
34+
35+
enum setid_type {
36+
UID,
37+
GID
38+
};
39+
3040
/*
31-
* Hash table entry to store safesetid policy signifying that 'src_uid'
32-
* can setuid to 'dst_uid'.
41+
* Hash table entry to store safesetid policy signifying that 'src_id'
42+
* can set*id to 'dst_id'.
3343
*/
34-
struct setuid_rule {
44+
struct setid_rule {
3545
struct hlist_node next;
36-
kuid_t src_uid;
37-
kuid_t dst_uid;
46+
kid_t src_id;
47+
kid_t dst_id;
48+
49+
/* Flag to signal if rule is for UID's or GID's */
50+
enum setid_type type;
3851
};
3952

4053
#define SETID_HASH_BITS 8 /* 256 buckets in hash table */
4154

42-
struct setuid_ruleset {
55+
/* Extension of INVALID_UID/INVALID_GID for kid_t type */
56+
#define INVALID_ID (kid_t){.uid = INVALID_UID}
57+
58+
struct setid_ruleset {
4359
DECLARE_HASHTABLE(rules, SETID_HASH_BITS);
4460
char *policy_str;
4561
struct rcu_head rcu;
62+
63+
//Flag to signal if ruleset is for UID's or GID's
64+
enum setid_type type;
4665
};
4766

48-
enum sid_policy_type _setuid_policy_lookup(struct setuid_ruleset *policy,
49-
kuid_t src, kuid_t dst);
67+
enum sid_policy_type _setid_policy_lookup(struct setid_ruleset *policy,
68+
kid_t src, kid_t dst);
5069

51-
extern struct setuid_ruleset __rcu *safesetid_setuid_rules;
70+
extern struct setid_ruleset __rcu *safesetid_setuid_rules;
71+
extern struct setid_ruleset __rcu *safesetid_setgid_rules;
5272

5373
#endif /* _SAFESETID_H */

0 commit comments

Comments
 (0)