From nobody Thu Apr 23 21:13:24 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4g1pjQ0ZjPz6b5y4 for ; Thu, 23 Apr 2026 21:13:30 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R13" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4g1pjP6YM0z42dw for ; Thu, 23 Apr 2026 21:13:29 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1776978809; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=/k7KGOYMe74syOPYdT/NkTb6KJMx6R7olZGN13J/AJ8=; b=SZ3Wd1U7KPo49o+6biaJ5IEwf79zQutnVcrT+aBlRdcazQddmzB0/nJvj7mp2+iUbMzvcA jo5kHA/FDrsdECNKD9yLAbR1dcqbWve19L3Sj6Dthmilc+p67W65pROfouiqi8zgAiQjJG zFjcF2JnWSluC32mnaIN8PusBkae07ZrRr+NVtZntneowVS950Wml6netYZjzR+a0NSidc uWqdY9wAVeFjf/oDLap/WJaY7+VLSHZrwmU0NivHPLA0X7Yuij3JCr1/+OEkKYWx2eWI5k 3YX+6ECltLLA+g78BbAPEjIa+oM3AXoV1eLPJ7wc6OZGOAa8JWFF/+XhAADxWA== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1776978809; a=rsa-sha256; cv=none; b=lKgVyaYxSLcfHb1cvIzHIgVHLYiI3gbon5PyQ2hliq+15NteHXX5UE3M4zZz77UMFt4THm HyOxK5aVMWFiV0JY4m8RnVSu15crPjPnQOzsf/t/5F1cFnZ7kcJTh6qkMt06ugNKuyfhXS fsgPUEKONZMx8dy5S9ORTvo9CiMlj2SAPYFSLSvpHLUjB/zqVuAnU1zuEhyjOISNA4PJSD snknw/eBhnvpjQk2SKkKukoEg+ptECBMMzmHNStOd0r+rsBcgX8jgEjhFbBwATpp8waSov M6mU/Kn9cYcwa+/ndN1C6iSY70kZjtbz5A7Aj/WZkIiBUeTZlvyueoJSxTUYig== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1776978809; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=/k7KGOYMe74syOPYdT/NkTb6KJMx6R7olZGN13J/AJ8=; b=W2WpyVNxqMBN/ilKhPuDRLwLl7bj78yHwTCWhOzGAsSmi0hMEFTo08tBgB8ikbJcRDOFjZ MqKFyqi4+10dMZQsHkg3wak5s4ZQWfoUKBUveFerMif2rZ+pr85Self8+A1Mjp+PI1IMeW +ixZD9+zLk9jgVXtUzHwUa5U0PXOx3UKT+pO9bNhsUkPKi8Vm7Z/5lHLOkYQN8Mky5hKz4 SL+pijB0BQB5HrQwiahDvon8URI/QnXct0nhbwWU1R37SmBzVvoDu3i53nRRPI0XAvxaai XPiKpuXsERlfoEQu1HsJKAT3YT7X8w33+MbvlCttGTECFcnZzRRxWWztBi/9dQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4g1pjP690TzZrD for ; Thu, 23 Apr 2026 21:13:29 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 30ff7 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Thu, 23 Apr 2026 21:13:24 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kyle Evans Subject: git: b675ff8eedc9 - main - mac_seeotheruids: allow specificgid to be a list of groups List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kevans X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: b675ff8eedc9ac93cdf1cfe33185b7a1a027df37 Auto-Submitted: auto-generated Date: Thu, 23 Apr 2026 21:13:24 +0000 Message-Id: <69ea8b74.30ff7.599b6a18@gitrepo.freebsd.org> The branch main has been updated by kevans: URL: https://cgit.FreeBSD.org/src/commit/?id=b675ff8eedc9ac93cdf1cfe33185b7a1a027df37 commit b675ff8eedc9ac93cdf1cfe33185b7a1a027df37 Author: Kyle Evans AuthorDate: 2026-03-01 03:42:25 +0000 Commit: Kyle Evans 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 #include +#include +#include #include #include #include +#include #include #include #include @@ -59,6 +62,9 @@ #include +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,