2424/* Flag indicating whether initialization completed */
2525int 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
152247static 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
0 commit comments