Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 23 Apr 2026 21:13:24 +0000
From:      Kyle Evans <kevans@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: b675ff8eedc9 - main - mac_seeotheruids: allow specificgid to be a list of groups
Message-ID:  <69ea8b74.30ff7.599b6a18@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by kevans:

URL: https://cgit.FreeBSD.org/src/commit/?id=b675ff8eedc9ac93cdf1cfe33185b7a1a027df37

commit b675ff8eedc9ac93cdf1cfe33185b7a1a027df37
Author:     Kyle Evans <kevans@FreeBSD.org>
AuthorDate: 2026-03-01 03:42:25 +0000
Commit:     Kyle Evans <kevans@FreeBSD.org>
CommitDate: 2026-04-23 21:13:11 +0000

    mac_seeotheruids: allow specificgid to be a list of groups
    
    The specificgid functionality has historically allowed only a single
    group to be exempt, but in practice one might want a few services to
    be exempt for reasons.  From a security perspective, we probably don't
    want to encourage unrelated users to be grouped together solely for
    this purpose, as that creates one point of shared access that could be
    used for nefarious purposes.
    
    Normalize the group list as we do cr_groups to allow for linear matching
    rather than quadratic, we just need to account for the differences in
    FreeBSD 15.0+ where cr_groups is entirely supplementary groups vs.
    earlier versions, where cr_groups[0] is the egid and the rest is
    sorted.
    
    Reviewed by:    csjp, des (earlier version)
    Sponsored by:   Klara, Inc.
    Differential Revision:  https://reviews.freebsd.org/D56592
---
 share/man/man4/mac_seeotheruids.4                |   4 +-
 sys/security/mac_seeotheruids/mac_seeotheruids.c | 174 +++++++++++++++++++++--
 2 files changed, 166 insertions(+), 12 deletions(-)

diff --git a/share/man/man4/mac_seeotheruids.4 b/share/man/man4/mac_seeotheruids.4
index 5b1718bf83d4..04f67ebb7ea3 100644
--- a/share/man/man4/mac_seeotheruids.4
+++ b/share/man/man4/mac_seeotheruids.4
@@ -28,7 +28,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd July 25, 2015
+.Dd Februrary 26, 2026
 .Dt MAC_SEEOTHERUIDS 4
 .Os
 .Sh NAME
@@ -80,7 +80,7 @@ set the sysctl OID
 .Va security.mac.seeotheruids.specificgid_enabled
 to 1, and
 .Va security.mac.seeotheruids.specificgid
-to the group ID to be exempted.
+to the list of group IDs to be exempted.
 .Ss Label Format
 No labels are defined for
 .Nm .
diff --git a/sys/security/mac_seeotheruids/mac_seeotheruids.c b/sys/security/mac_seeotheruids/mac_seeotheruids.c
index 9cd2e0f3c0fc..a112a904fa72 100644
--- a/sys/security/mac_seeotheruids/mac_seeotheruids.c
+++ b/sys/security/mac_seeotheruids/mac_seeotheruids.c
@@ -45,9 +45,12 @@
 
 #include <sys/param.h>
 #include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
 #include <sys/module.h>
 #include <sys/priv.h>
 #include <sys/proc.h>
+#include <sys/rmlock.h>
 #include <sys/systm.h>
 #include <sys/socket.h>
 #include <sys/socketvar.h>
@@ -59,6 +62,9 @@
 
 #include <security/mac/mac_policy.h>
 
+static MALLOC_DEFINE(M_SEEOTHERUIDS, "mac_seeotheruids",
+    "mac_seeotheruids(4) security module");
+
 static SYSCTL_NODE(_security_mac, OID_AUTO, seeotheruids,
     CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
     "TrustedBSD mac_seeotheruids policy controls");
@@ -94,13 +100,116 @@ SYSCTL_INT(_security_mac_seeotheruids, OID_AUTO, specificgid_enabled,
     CTLFLAG_RW, &specificgid_enabled, 0, "Make an exception for credentials "
     "with a specific gid as their real primary group id or group set");
 
-static gid_t	specificgid = 0;
-SYSCTL_UINT(_security_mac_seeotheruids, OID_AUTO, specificgid, CTLFLAG_RW,
-    &specificgid, 0, "Specific gid to be exempt from seeotheruids policy");
+static struct rmlock seeotheruids_rmlock;
+RM_SYSINIT_FLAGS(mac_seeotheruids_lock, &seeotheruids_rmlock,
+    "mac_seeotheruids_lock", RM_SLEEPABLE);
+
+static gid_t	*specificgids;
+static size_t	 specificgidcnt;
+
+static int
+gidp_cmp(const void *p1, const void *p2)
+{
+	const gid_t g1 = *(const gid_t *)p1;
+	const gid_t g2 = *(const gid_t *)p2;
+
+	return ((g1 > g2) - (g1 < g2));
+}
+
+static void
+specificgid_normalize(gid_t *gidlist, size_t *ngidp)
+{
+	int ins_idx;
+	gid_t prev_g;
+
+	if (*ngidp < 2)
+		return;
+
+	qsort(gidlist, *ngidp, sizeof(*gidlist), gidp_cmp);
+
+	prev_g = gidlist[0];
+	ins_idx = 1;
+	for (int i = ins_idx; i < *ngidp; ++i) {
+		const gid_t g = gidlist[i];
+
+		if (g != prev_g) {
+			if (i != ins_idx)
+				gidlist[ins_idx] = g;
+			++ins_idx;
+			prev_g = g;
+		}
+	}
+
+	*ngidp = ins_idx;
+}
+
+static int
+specificgid_sysctl(SYSCTL_HANDLER_ARGS)
+{
+	gid_t *newgids = NULL;
+	size_t ingidcnt, newgidcnt = 0;
+	int error;
+
+	/* Allocate our new gid array before we take our non-sleepable lock. */
+	if (req->newptr != NULL) {
+		if (req->newlen % sizeof(gid_t) != 0)
+			return (EINVAL);
+		ingidcnt = newgidcnt = howmany(req->newlen, sizeof(gid_t));
+		newgids = mallocarray(newgidcnt, sizeof(*newgids),
+		    M_SEEOTHERUIDS, M_WAITOK);
+
+		error = SYSCTL_IN(req, newgids, newgidcnt * sizeof(*newgids));
+		if (error != 0) {
+			free(newgids, M_SEEOTHERUIDS);
+			return (error);
+		}
+
+		specificgid_normalize(newgids, &newgidcnt);
+
+		/*
+		 * It might be debatable whether shrinking the allocation is
+		 * worth it, but we'll do it in the off-chance that someone is
+		 * generating specificgid entries from various configuration
+		 * sources that won't de-duplicate.
+		 */
+		if (newgidcnt < ingidcnt) {
+			newgids = realloc(newgids, newgidcnt * sizeof(*newgids),
+			    M_SEEOTHERUIDS, M_WAITOK);
+		}
+	}
+
+	rm_wlock(&seeotheruids_rmlock);
+
+	error = SYSCTL_OUT(req, specificgids,
+	    specificgidcnt * sizeof(*specificgids));
+	if (error == 0 && req->newptr != NULL) {
+		free(specificgids, M_SEEOTHERUIDS);
+
+		specificgids = newgids;
+		specificgidcnt = newgidcnt;
+	} else if (error != 0) {
+		free(newgids, M_SEEOTHERUIDS);
+	}
+
+	rm_wunlock(&seeotheruids_rmlock);
+	return (error);
+}
+SYSCTL_PROC(_security_mac_seeotheruids, OID_AUTO, specificgid,
+    CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, 0, 0,
+    &specificgid_sysctl, "I",
+    "Specific gid(s) to be exempt from seeotheruids policy");
+
+static void
+seeotheruids_destroy(struct mac_policy_conf *mpc __unused)
+{
+	free(specificgids, M_SEEOTHERUIDS);
+}
 
 static int
 seeotheruids_check(struct ucred *cr1, struct ucred *cr2)
 {
+	struct rm_priotracker tracker;
+	int error = ESRCH;
 
 	if (!seeotheruids_enabled)
 		return (0);
@@ -110,12 +219,6 @@ seeotheruids_check(struct ucred *cr1, struct ucred *cr2)
 			return (0);
 	}
 
-	if (specificgid_enabled) {
-		if (cr1->cr_rgid == specificgid ||
-		    groupmember(specificgid, cr1))
-			return (0);
-	}
-
 	if (cr1->cr_ruid == cr2->cr_ruid)
 		return (0);
 
@@ -124,7 +227,57 @@ seeotheruids_check(struct ucred *cr1, struct ucred *cr2)
 			return (0);
 	}
 
-	return (ESRCH);
+	rm_rlock(&seeotheruids_rmlock, &tracker);
+	if (specificgid_enabled && specificgids != NULL) {
+		const gid_t *suppgroups = cr1->cr_groups;
+		size_t nsupp = cr1->cr_ngroups;
+
+#if __FreeBSD_version < 1500056
+		/*
+		 * FreeBSD 15.0 changed the cr_groups layout: earlier versions
+		 * used cr_groups[0] for the effective GID, but that's somewhat
+		 * error-prone when propagated throughout the various parts of
+		 * the system (e.g., setgroups/getgroups).  In older versions,
+		 * we want to hop over the egid.
+		 */
+		suppgroups++;
+		nsupp--;
+#endif
+
+		for (size_t i = 0, s = 0; i < specificgidcnt; i++) {
+			gid_t cgid;
+
+			cgid = specificgids[i];
+			if (cgid == cr1->cr_rgid) {
+				error = 0;
+				break;
+			}
+
+			/*
+			 * specificgids and suppgroups are both sorted
+			 * ascending, so advance past all of the supplemental
+			 * groups that are lower than the specificgid we're
+			 * currently at.
+			 */
+			while (s < nsupp && cgid > suppgroups[s])
+				s++;
+
+			/*
+			 * Out of supplementary groups, but we'll keep checking
+			 * for rgid matches.
+			 */
+			if (s == nsupp)
+				continue;
+
+			if (cgid == suppgroups[s]) {
+				error = 0;
+				break;
+			}
+		}
+	}
+
+	rm_runlock(&seeotheruids_rmlock, &tracker);
+	return (error);
 }
 
 static int
@@ -174,6 +327,7 @@ seeotheruids_socket_check_visible(struct ucred *cred, struct socket *so,
 
 static struct mac_policy_ops seeotheruids_ops =
 {
+	.mpo_destroy = seeotheruids_destroy,
 	.mpo_proc_check_debug = seeotheruids_proc_check_debug,
 	.mpo_proc_check_sched = seeotheruids_proc_check_sched,
 	.mpo_proc_check_signal = seeotheruids_proc_check_signal,


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?69ea8b74.30ff7.599b6a18>