Merge tag 'safesetid-6.0' of https://github.com/micah-morton/linux
[linux-2.6-microblaze.git] / security / safesetid / lsm.c
index 963f4ad..e806739 100644 (file)
@@ -97,15 +97,9 @@ static int safesetid_security_capable(const struct cred *cred,
                return 0;
 
        /*
-        * If CAP_SET{U/G}ID is currently used for a setid() syscall, we want to
-        * let it go through here; the real security check happens later, in the
-        * task_fix_set{u/g}id hook.
-         *
-         * NOTE:
-         * Until we add support for restricting setgroups() calls, GID security
-         * policies offer no meaningful security since we always return 0 here
-         * when called from within the setgroups() syscall and there is no
-         * additional hook later on to enforce security policies for setgroups().
+        * If CAP_SET{U/G}ID is currently used for a setid or setgroups syscall, we
+        * want to let it go through here; the real security check happens later, in
+        * the task_fix_set{u/g}id or task_fix_setgroups hooks.
         */
        if ((opts & CAP_OPT_INSETID) != 0)
                return 0;
@@ -241,9 +235,36 @@ static int safesetid_task_fix_setgid(struct cred *new,
        return -EACCES;
 }
 
+static int safesetid_task_fix_setgroups(struct cred *new, const struct cred *old)
+{
+       int i;
+
+       /* Do nothing if there are no setgid restrictions for our old RGID. */
+       if (setid_policy_lookup((kid_t){.gid = old->gid}, INVALID_ID, GID) == SIDPOL_DEFAULT)
+               return 0;
+
+       get_group_info(new->group_info);
+       for (i = 0; i < new->group_info->ngroups; i++) {
+               if (!id_permitted_for_cred(old, (kid_t){.gid = new->group_info->gid[i]}, GID)) {
+                       put_group_info(new->group_info);
+                       /*
+                        * Kill this process to avoid potential security vulnerabilities
+                        * that could arise from a missing allowlist entry preventing a
+                        * privileged process from dropping to a lesser-privileged one.
+                        */
+                       force_sig(SIGKILL);
+                       return -EACCES;
+               }
+       }
+
+       put_group_info(new->group_info);
+       return 0;
+}
+
 static struct security_hook_list safesetid_security_hooks[] = {
        LSM_HOOK_INIT(task_fix_setuid, safesetid_task_fix_setuid),
        LSM_HOOK_INIT(task_fix_setgid, safesetid_task_fix_setgid),
+       LSM_HOOK_INIT(task_fix_setgroups, safesetid_task_fix_setgroups),
        LSM_HOOK_INIT(capable, safesetid_security_capable)
 };