tech-kern archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: Changing NGROUPS_MAX to 1024?
> Date: Sat, 11 Apr 2026 11:22:29 -0700
> From: Konrad Schroder <perseant%hhhh.org@localhost>
>
> At ${WORK} our organizational identity provider assigns quite a number
> of groups to each person---many more than 16---which causes users not
> being able to authenticate to SaMBa running on a NetBSD host. To
> address this, I've been running for quite a while with a patch to set
> NGROUPS_MAX to 1024, with a compatibility setting to 16 for use in NFS.
>
> Is this something that we'd like to have in base? I haven't tested the
> NFS functionality widely (all our systems are patched), but last time I
> tried it between a patched and an unpatched system, it seemed to work.
> Is there anything else I should be looking at aside from NFS? Any other
> thoughts?
Yes, we should raise NGROUPS_MAX by a lot.
I drafted a patch a few years ago with the goals:
1. to keep a logarithmic bound on time to test group membership,
2. to avoid incurring costs on processes that aren't in many groups,
3. to share copies of large group arrays rather than waste kmem, and
4. to preserve the _first_ 16 groups as distinguished for nfs or other
compatibility,
so it uses a reference-counted array of group ids, with those after
the first 16 sorted for binary search lookup, of up to 65536 groups
per credential. It might also be worthwhile to intern large arrays
themselves so equivalent setgroups() calls share the existing memory.
But I didn't get around to finishing it.
I'm not attached to this way of doing things -- I'm just sharing prior
work and design considerations in case they're helpful.
From e608365d79a18df1c970570844767b86f36e949e Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Sat, 26 Mar 2022 19:27:58 +0000
Subject: [PATCH] WIP: bump NGROUPS_MAX to 65536
Use a sorted array of gids for supplemental groups past the 16th.
This currently leaves NGROUPS at 16, the legacy number. Some things
still use stack allocations of length NGROUPS_MAX -- need to nix
those.
---
sys/kern/files.kern | 1 +
sys/kern/groupset.h | 1 +
sys/kern/kern_auth.c | 144 ++++++-----
sys/kern/kern_proc.c | 6 +-
sys/kern/subr_groupset.c | 406 ++++++++++++++++++++++++++++++
sys/miscfs/procfs/procfs_status.c | 4 +-
sys/miscfs/umapfs/umap_subr.c | 9 +-
sys/sys/groupset.h | 52 ++++
sys/sys/kauth.h | 3 +-
sys/sys/param.h | 2 +-
sys/sys/syslimits.h | 2 +-
sys/sys/ucred.h | 2 +-
12 files changed, 557 insertions(+), 75 deletions(-)
create mode 120000 sys/kern/groupset.h
create mode 100644 sys/kern/subr_groupset.c
create mode 100644 sys/sys/groupset.h
diff --git a/sys/kern/files.kern b/sys/kern/files.kern
index 03eb05ec2588..c708a0c182b4 100644
--- a/sys/kern/files.kern
+++ b/sys/kern/files.kern
@@ -122,6 +122,7 @@ file kern/subr_evcnt.c kern
file kern/subr_exec_fd.c kern
file kern/subr_extent.c kern
file kern/subr_fault.c fault
+file kern/subr_groupset.c kern
file kern/subr_hash.c kern
file kern/subr_humanize.c kern
file kern/subr_interrupt.c kern
diff --git a/sys/kern/groupset.h b/sys/kern/groupset.h
new file mode 120000
index 000000000000..1a94c0a1a0c8
--- /dev/null
+++ b/sys/kern/groupset.h
@@ -0,0 +1 @@
+../sys/groupset.h
\ No newline at end of file
diff --git a/sys/kern/kern_auth.c b/sys/kern/kern_auth.c
index 9c8634e00883..5cd42215b341 100644
--- a/sys/kern/kern_auth.c
+++ b/sys/kern/kern_auth.c
@@ -44,6 +44,7 @@ __KERNEL_RCSID(0, "$NetBSD: kern_auth.c,v 1.82 2023/02/24 11:02:27 riastradh Exp
#include <sys/atomic.h>
#include <sys/specificdata.h>
#include <sys/vnode.h>
+#include <sys/groupset.h>
#include <secmodel/secmodel.h>
@@ -113,7 +114,7 @@ kauth_cred_alloc(void)
cred->cr_gid = 0;
cred->cr_egid = 0;
cred->cr_svgid = 0;
- cred->cr_ngroups = 0;
+ cred->cr_groups = NULL;
specificdata_init(kauth_domain, &cred->cr_sd);
kauth_cred_hook(cred, KAUTH_CRED_INIT, NULL, NULL);
@@ -171,10 +172,8 @@ kauth_cred_clone1(kauth_cred_t from, kauth_cred_t to, bool copy_groups)
to->cr_gid = from->cr_gid;
to->cr_egid = from->cr_egid;
to->cr_svgid = from->cr_svgid;
- if (copy_groups) {
- to->cr_ngroups = from->cr_ngroups;
- memcpy(to->cr_groups, from->cr_groups, sizeof(to->cr_groups));
- }
+ if (copy_groups)
+ to->cr_groups = groupset_clone(from->cr_groups);
kauth_cred_hook(from, KAUTH_CRED_COPY, to, NULL);
}
@@ -382,22 +381,14 @@ kauth_cred_setsvgid(kauth_cred_t cred, gid_t gid)
int
kauth_cred_ismember_gid(kauth_cred_t cred, gid_t gid, int *resultp)
{
- uint32_t i;
KASSERT(cred != NULL);
KASSERT(cred != NOCRED);
KASSERT(cred != FSCRED);
KASSERT(resultp != NULL);
- *resultp = 0;
-
- for (i = 0; i < cred->cr_ngroups; i++)
- if (cred->cr_groups[i] == gid) {
- *resultp = 1;
- break;
- }
-
- return (0);
+ *resultp = groupset_member(cred->cr_groups, gid);
+ return 0;
}
int
@@ -422,11 +413,12 @@ kauth_cred_groupmember(kauth_cred_t cred, gid_t gid)
u_int
kauth_cred_ngroups(kauth_cred_t cred)
{
+
KASSERT(cred != NULL);
KASSERT(cred != NOCRED);
KASSERT(cred != FSCRED);
- return (cred->cr_ngroups);
+ return groupset_ngroups(cred->cr_groups);
}
/*
@@ -435,12 +427,30 @@ kauth_cred_ngroups(kauth_cred_t cred)
gid_t
kauth_cred_group(kauth_cred_t cred, u_int idx)
{
+
KASSERT(cred != NULL);
KASSERT(cred != NOCRED);
KASSERT(cred != FSCRED);
- KASSERT(idx < cred->cr_ngroups);
+ KASSERT(idx < kauth_cred_ngroups(cred));
+
+ return groupset_nth(cred->cr_groups, idx);
+}
+
+static int
+copyingids(gid_t *kgids, unsigned n, void *cookie)
+{
+ const gid_t *ugids = cookie;
- return (cred->cr_groups[idx]);
+ return copyin(ugids, kgids, n*sizeof(ugids[0]));
+}
+
+static int
+memcpyingids(gid_t *dstgids, unsigned n, void *cookie)
+{
+ const gid_t *srcgids = cookie;
+
+ memcpy(dstgids, srcgids, n*sizeof(srcgids[0]));
+ return 0;
}
/* XXX elad: gmuid is unused for now. */
@@ -448,33 +458,15 @@ int
kauth_cred_setgroups(kauth_cred_t cred, const gid_t *grbuf, size_t len,
uid_t gmuid, enum uio_seg seg)
{
- int error = 0;
KASSERT(cred != NULL);
KASSERT(cred != NOCRED);
KASSERT(cred != FSCRED);
KASSERT(cred->cr_refcnt == 1);
- if (len > __arraycount(cred->cr_groups))
- return EINVAL;
-
- if (len) {
- if (seg == UIO_SYSSPACE) {
- memcpy(cred->cr_groups, grbuf,
- len * sizeof(cred->cr_groups[0]));
- } else {
- error = copyin(grbuf, cred->cr_groups,
- len * sizeof(cred->cr_groups[0]));
- if (error != 0)
- len = 0;
- }
- }
- memset(cred->cr_groups + len, 0xff,
- sizeof(cred->cr_groups) - (len * sizeof(cred->cr_groups[0])));
-
- cred->cr_ngroups = len;
-
- return error;
+ return groupset_create(len,
+ seg == UIO_SYSSPACE ? memcpyingids : copyingids, __UNCONST(grbuf),
+ &cred->cr_groups);
}
/* This supports sys_setgroups() */
@@ -507,20 +499,37 @@ kauth_proc_setgroups(struct lwp *l, kauth_cred_t ncred)
return 0;
}
+static int
+copyoutgids(const gid_t *kgids, unsigned n, void *cookie)
+{
+ gid_t *ugids = cookie;
+
+ return copyout(kgids, ugids, n*sizeof(ugids[0]));
+}
+
+static int
+memcpyoutgids(const gid_t *srcgids, unsigned n, void *cookie)
+{
+ gid_t *dstgids = cookie;
+
+ memcpy(dstgids, srcgids, n*sizeof(srcgids[0]));
+ return 0;
+}
+
int
kauth_cred_getgroups(kauth_cred_t cred, gid_t *grbuf, size_t len,
enum uio_seg seg)
{
+ unsigned ngroups;
+
KASSERT(cred != NULL);
- if (len > cred->cr_ngroups)
+ if (len > groupset_ngroups(cred->cr_groups))
return EINVAL;
+ ngroups = len;
- if (seg == UIO_USERSPACE)
- return copyout(cred->cr_groups, grbuf, sizeof(*grbuf) * len);
- memcpy(grbuf, cred->cr_groups, sizeof(*grbuf) * len);
-
- return 0;
+ return groupset_export(cred->cr_groups, &ngroups,
+ seg == UIO_SYSSPACE ? memcpyoutgids : copyoutgids, grbuf);
}
int
@@ -615,14 +624,16 @@ kauth_cred_getrefcnt(kauth_cred_t cred)
* Convert userland credentials (struct uucred) to kauth_cred_t.
* XXX: For NFS & puffs
*/
-void
+void
kauth_uucred_to_cred(kauth_cred_t cred, const struct uucred *uuc)
-{
+{
+ int error;
+
KASSERT(cred != NULL);
KASSERT(cred != NOCRED);
KASSERT(cred != FSCRED);
KASSERT(uuc != NULL);
-
+
cred->cr_refcnt = 1;
cred->cr_uid = uuc->cr_uid;
cred->cr_euid = uuc->cr_uid;
@@ -630,29 +641,33 @@ kauth_uucred_to_cred(kauth_cred_t cred, const struct uucred *uuc)
cred->cr_gid = uuc->cr_gid;
cred->cr_egid = uuc->cr_gid;
cred->cr_svgid = uuc->cr_gid;
- cred->cr_ngroups = uimin(uuc->cr_ngroups, NGROUPS);
- kauth_cred_setgroups(cred, __UNCONST(uuc->cr_groups),
- cred->cr_ngroups, -1, UIO_SYSSPACE);
+ error = kauth_cred_setgroups(cred, __UNCONST(uuc->cr_groups),
+ MIN(uuc->cr_ngroups, NGROUPS_MAX), /*gmuid*/-1, UIO_SYSSPACE);
+ KASSERTMSG(error == 0, "error=%d", error);
}
/*
* Convert kauth_cred_t to userland credentials (struct uucred).
* XXX: For NFS & puffs
*/
-void
+void
kauth_cred_to_uucred(struct uucred *uuc, const kauth_cred_t cred)
-{
+{
+ int ng;
+ int error;
+
KASSERT(cred != NULL);
KASSERT(cred != NOCRED);
KASSERT(cred != FSCRED);
KASSERT(uuc != NULL);
- int ng;
- ng = uimin(cred->cr_ngroups, NGROUPS);
- uuc->cr_uid = cred->cr_euid;
- uuc->cr_gid = cred->cr_egid;
+ ng = MIN(groupset_ngroups(cred->cr_groups),
+ __arraycount(uuc->cr_groups));
+ uuc->cr_uid = cred->cr_euid;
+ uuc->cr_gid = cred->cr_egid;
uuc->cr_ngroups = ng;
- kauth_cred_getgroups(cred, uuc->cr_groups, ng, UIO_SYSSPACE);
+ error = kauth_cred_getgroups(cred, uuc->cr_groups, ng, UIO_SYSSPACE);
+ KASSERTMSG(error == 0, "error=%d", error);
}
/*
@@ -669,7 +684,7 @@ kauth_cred_uucmp(kauth_cred_t cred, const struct uucred *uuc)
if (cred->cr_euid == uuc->cr_uid &&
cred->cr_egid == uuc->cr_gid &&
- cred->cr_ngroups == (uint32_t)uuc->cr_ngroups) {
+ kauth_cred_ngroups(cred) == (uint32_t)uuc->cr_ngroups) {
int i;
/* Check if all groups from uuc appear in cred. */
@@ -694,17 +709,22 @@ kauth_cred_uucmp(kauth_cred_t cred, const struct uucred *uuc)
void
kauth_cred_toucred(kauth_cred_t cred, struct ki_ucred *uc)
{
+ int ng;
+ int error;
+
KASSERT(cred != NULL);
KASSERT(cred != NOCRED);
KASSERT(cred != FSCRED);
KASSERT(uc != NULL);
+ ng = MIN(groupset_ngroups(cred->cr_groups),
+ __arraycount(uc->cr_groups));
uc->cr_ref = cred->cr_refcnt;
uc->cr_uid = cred->cr_euid;
uc->cr_gid = cred->cr_egid;
- uc->cr_ngroups = uimin(cred->cr_ngroups, __arraycount(uc->cr_groups));
- memcpy(uc->cr_groups, cred->cr_groups,
- uc->cr_ngroups * sizeof(uc->cr_groups[0]));
+ uc->cr_ngroups = ng;
+ error = kauth_cred_getgroups(cred, uc->cr_groups, ng, UIO_SYSSPACE);
+ KASSERTMSG(error == 0, "error=%d", error);
}
/*
diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c
index a6b2acf07925..88a3a718b0cc 100644
--- a/sys/kern/kern_proc.c
+++ b/sys/kern/kern_proc.c
@@ -2793,9 +2793,9 @@ fill_kproc2(struct proc *p, struct kinfo_proc2 *ki, bool zombie, bool allowaddr)
ki->p_rgid = kauth_cred_getgid(p->p_cred);
ki->p_svuid = kauth_cred_getsvuid(p->p_cred);
ki->p_svgid = kauth_cred_getsvgid(p->p_cred);
- ki->p_ngroups = kauth_cred_ngroups(p->p_cred);
- kauth_cred_getgroups(p->p_cred, ki->p_groups,
- uimin(ki->p_ngroups, sizeof(ki->p_groups) / sizeof(ki->p_groups[0])),
+ ki->p_ngroups = MIN(kauth_cred_ngroups(p->p_cred),
+ __arraycount(ki->p_groups));
+ kauth_cred_getgroups(p->p_cred, ki->p_groups, ki->p_ngroups,
UIO_SYSSPACE);
ki->p_uticks = p->p_uticks;
diff --git a/sys/kern/subr_groupset.c b/sys/kern/subr_groupset.c
new file mode 100644
index 000000000000..77a0e8461ad2
--- /dev/null
+++ b/sys/kern/subr_groupset.c
@@ -0,0 +1,406 @@
+/* $NetBSD$ */
+
+/*-
+ * Copyright (c) 2022 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * groupset -- Simple abstraction for sets of gids
+ *
+ * A groupset is mostly an unordered set of integers. However, it also
+ * remembers the first sixteen groups separately from the rest -- this
+ * is for NFS, which limits group memberships to sixteen. We call
+ * these `legacy groups', and we remember the _first_ sixteen groups as
+ * the legacy groups (and, just in case, we also preserve the order of
+ * the first sixteen groups).
+ */
+
+#ifdef _KERNEL
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(1, "$NetBSD: sun8i_crypto.c,v 1.30 2022/03/19 11:37:05 riastradh Exp $");
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <sys/atomic.h>
+#include <sys/errno.h>
+#include <sys/groupset.h>
+#include <sys/kmem.h>
+#include <sys/null.h>
+#include <sys/syslimits.h>
+
+#else /* !_KERNEL */
+
+#include <sys/atomic.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#define membar_acquire() membar_enter()
+#define membar_release() membar_exit()
+
+#define _KERNEL
+#include "groupset.h"
+#undef _KERNEL
+
+#define KASSERT assert
+
+#undef NGROUPS_MAX
+#define NGROUPS_MAX 65536
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+#define KM_SLEEP 0
+static inline void *
+kmem_alloc(size_t n, int flags)
+{
+ void *p;
+
+ (void)flags;
+
+ p = malloc(n);
+ if (p == NULL)
+ err(1, "malloc");
+ return p;
+}
+static inline void
+kmem_free(void *p, size_t n)
+{
+
+ (void)n;
+
+ free(p);
+}
+
+#endif /* _KERNEL */
+
+#define NGROUPS_LEGACY 16
+
+struct groupset {
+ volatile uint32_t refcnt;
+ uint32_t ngids;
+ gid_t gids[];
+};
+
+#ifndef _KERNEL
+#define kheapsort(b, n, s, cmp, tmp) ((void)(tmp), qsort(b, n, s, cmp))
+#endif
+
+static int
+cmpgid(const void *va, const void *vb)
+{
+ const gid_t *a = va;
+ const gid_t *b = vb;
+
+ if (*a < *b)
+ return -1;
+ if (*a > *b)
+ return +1;
+ return 0;
+}
+
+int
+groupset_create(unsigned ngroups, int (*copyingids)(gid_t *, unsigned, void *),
+ void *cookie, struct groupset **G_ret)
+{
+ struct groupset *G = NULL;
+ size_t sz;
+ unsigned i;
+ int error;
+
+ if (ngroups == 0) {
+ error = 0;
+ goto out;
+ }
+ if (ngroups > NGROUPS_MAX) {
+ error = EINVAL;
+ goto out;
+ }
+
+ __CTASSERT(NGROUPS_MAX <=
+ (SIZE_MAX - offsetof(struct groupset, gids))/sizeof(G->gids[0]));
+ sz = offsetof(struct groupset, gids[ngroups]);
+ G = kmem_alloc(sz, KM_SLEEP);
+ G->refcnt = 1;
+ G->ngids = ngroups;
+ error = (*copyingids)(G->gids, ngroups, cookie);
+ if (error)
+ goto out;
+ for (i = 0; i < ngroups; i++) {
+ if (G->gids[i] > GID_MAX) {
+ error = EINVAL;
+ goto out;
+ }
+ }
+ if (ngroups > NGROUPS_LEGACY) {
+ gid_t tmp;
+
+ kheapsort(G->gids + NGROUPS_LEGACY, ngroups - NGROUPS_LEGACY,
+ sizeof(G->gids[0]), cmpgid, &tmp);
+ }
+
+out: if (error && G != NULL) {
+ kmem_free(G, sz);
+ G = NULL;
+ }
+ if (error == 0)
+ *G_ret = G;
+ return error;
+}
+
+struct groupset *
+groupset_clone(struct groupset *G)
+{
+
+ if (G == NULL)
+ return NULL;
+
+ /*
+ * Reference count is limited by the maximum number of
+ * processes, roughly, so this can't overflow to 2^31.
+ */
+ atomic_inc_32(&G->refcnt);
+
+ return G;
+}
+
+void
+groupset_destroy(struct groupset *G)
+{
+
+ if (G == NULL)
+ return;
+
+ membar_release();
+ if (atomic_dec_32_nv(&G->refcnt) > 0)
+ return;
+ membar_acquire();
+
+ kmem_free(G, offsetof(struct groupset, gids[G->ngids]));
+}
+
+unsigned
+groupset_ngroups(const struct groupset *G)
+{
+
+ if (G == NULL)
+ return 0;
+
+ __CTASSERT(NGROUPS_MAX <= UINT_MAX);
+ return G->ngids;
+}
+
+int
+groupset_export(const struct groupset *G,
+ int (*copyoutgids)(const gid_t *, unsigned, void *),
+ void *cookie)
+{
+
+ if (G == NULL)
+ return 0;
+
+ return (*copyoutgids)(G->gids, G->ngids, cookie);
+}
+
+gid_t
+groupset_nth(const struct groupset *G, unsigned n)
+{
+
+ KASSERT(n < G->ngids);
+ return G->gids[n];
+}
+
+int
+groupset_member(const struct groupset *G, gid_t gid)
+{
+ unsigned i, start, end;
+
+ if (G == NULL)
+ return 0;
+
+ for (i = 0; i < MIN(G->ngids, NGROUPS_LEGACY); i++) {
+ if (G->gids[i] == gid)
+ return 1;
+ }
+ if (G->ngids <= NGROUPS_LEGACY)
+ return 0;
+
+ /* binary search */
+ start = NGROUPS_LEGACY;
+ end = G->ngids;
+ while (start < end) {
+ i = start + (end - start)/2;
+ if (gid < G->gids[i])
+ end = i;
+ else if (gid > G->gids[i])
+ start = i + 1;
+ else
+ return 1;
+ }
+ return 0;
+}
+
+#ifdef TEST_GROUPSET
+
+#include <stdio.h>
+
+static int
+user_copyingids(gid_t *kgids, unsigned ngids, void *cookie)
+{
+ const gid_t *ugids = cookie;
+
+ if (ugids == NULL)
+ return EFAULT;
+ memcpy(kgids, ugids, ngids*sizeof(ugids[0]));
+ return 0;
+}
+
+static int
+user_copyoutgids(const gid_t *kgids, unsigned ngids, void *cookie)
+{
+ gid_t *ugids = cookie;
+
+ if (ugids == NULL)
+ return EFAULT;
+ memcpy(ugids, kgids, ngids*sizeof(ugids[0]));
+ return 0;
+}
+
+int
+main(void)
+{
+ gid_t gids[] = {
+ 0, 1, 32766, 1000, 1023, 1024, 1022, 65535,
+ 65536, 65537, 2, 3, 5, 4, 2147483647, 12345,
+ /* extra groups start here */
+ 47402, 34710, 53130, 52514, 1810323505, 318441710, 757203521,
+ 1820495206, 59781, 26299, 2130707744, 518372768, 11768, 870,
+ 18182968, 45421, 51556, 11331, 838558838, 49789, 1775643299,
+ 1586865971, 54207, 15045, 48361, 2582, 1444333234, 333088441,
+ 720434092, 542622081, 430193300, 223508133, 17069, 1949211901,
+ };
+ gid_t notingids = 56122;
+ gid_t copy[__arraycount(gids) + 1];
+ struct groupset *G, *G1;
+ unsigned i, j, ngroups;
+ int error;
+
+ printf("legacy groups:");
+ for (i = 0; i < NGROUPS_LEGACY; i++)
+ printf(" %u,", gids[i]);
+ printf("\n");
+
+ printf("groupset_create(NGROUPS_MAX + 1, ...) = %d\n",
+ groupset_create(NGROUPS_MAX + 1, user_copyingids, gids, &G));
+ printf("groupset_create(-1, gids) = %d\n",
+ groupset_create(-1, user_copyingids, gids, &G));
+ printf("groupset_create(1, (gid_t[]){GID_MAX+1}) = %d\n",
+ groupset_create(1, user_copyingids, (gid_t[]){GID_MAX+1}, &G));
+
+ for (i = 0; i < __arraycount(gids); i++) {
+ printf("\n# %u\n", i);
+
+ error = groupset_create(i, user_copyingids, NULL, &G);
+ printf("create group set from bad address: %d\n", error);
+
+ error = groupset_create(i, user_copyingids, gids, &G);
+ if (error) {
+ printf("create good group set: %d\n", error);
+ continue;
+ }
+ for (j = 0; j < i; j++) {
+ if (!groupset_member(G, gids[j]))
+ printf("missing: %u\n", (unsigned)gids[j]);
+ }
+ if (groupset_member(G, notingids))
+ printf("extraneous: %u\n", (unsigned)notingids);
+
+ ngroups = groupset_ngroups(G);
+ printf("ngroups = %u\n", ngroups);
+ printf("groups:");
+ for (j = 0; j < ngroups; j++)
+ printf(" %u,", groupset_nth(G, j));
+ printf("\n");
+
+ memset(copy, 0xff, sizeof(copy));
+ error = groupset_export(G, user_copyoutgids, NULL);
+ printf("export group set to bad address: %d\n", error);
+ error = groupset_export(G, user_copyoutgids, copy);
+ if (error) {
+ printf("export group set (legacy): error=%d\n", error);
+ continue;
+ }
+ printf("groups:");
+ for (j = 0; j < groupset_ngroups(G); j++)
+ printf(" %u,", copy[j]);
+ printf("\n");
+ printf("pad: 0x%x\n", copy[j + 1]);
+
+ G1 = groupset_clone(G);
+ memset(copy, 0xff, sizeof(copy));
+ ngroups = groupset_ngroups(G1);
+ error = groupset_export(G1, user_copyoutgids, copy);
+ if (error) {
+ printf("export group set (clone): error=%d\n", error);
+ continue;
+ }
+ printf("ngroups (clone) = %u\n", ngroups);
+ printf("groups:");
+ for (j = 0; j < ngroups; j++)
+ printf(" %u,", copy[j]);
+ printf("\n");
+ printf("pad: 0x%x\n", copy[j + 1]);
+ groupset_destroy(G1);
+
+ memset(copy, 0xff, sizeof(copy));
+ ngroups = groupset_ngroups(G);
+ error = groupset_export(G, user_copyoutgids, copy);
+ if (error) {
+ printf("export group set (all): error=%d\n", error);
+ continue;
+ }
+ printf("ngroups (all) = %u\n", ngroups);
+ printf("groups:");
+ for (j = 0; j < ngroups; j++)
+ printf(" %u,", copy[j]);
+ printf("\n");
+ printf("pad: 0x%x\n", copy[j + 1]);
+ groupset_destroy(G);
+ }
+
+ printf("\nok\n");
+
+ fflush(stdout);
+ return ferror(stdout);
+}
+
+#endif
diff --git a/sys/miscfs/procfs/procfs_status.c b/sys/miscfs/procfs/procfs_status.c
index 149521ac74a8..8495f519307f 100644
--- a/sys/miscfs/procfs/procfs_status.c
+++ b/sys/miscfs/procfs/procfs_status.c
@@ -101,7 +101,7 @@ procfs_status_netbsd(struct lwp *l, struct uio *uio)
int pid, ppid, pgid, sid;
u_int i;
char psbuf[256+MAXHOSTNAMELEN]; /* XXX - conservative */
- uint16_t ngroups;
+ unsigned ngroups;
mutex_enter(&proc_lock);
@@ -209,7 +209,7 @@ procfs_status_linux(struct lwp *l, struct uio *uio)
int pid, ppid;
u_int i;
char psbuf[256+MAXHOSTNAMELEN]; /* XXX - conservative */
- uint16_t ngroups;
+ unsigned ngroups;
mutex_enter(&proc_lock);
mutex_enter(p->p_lock);
diff --git a/sys/miscfs/umapfs/umap_subr.c b/sys/miscfs/umapfs/umap_subr.c
index 7633c5879e2c..75f74b4fbf45 100644
--- a/sys/miscfs/umapfs/umap_subr.c
+++ b/sys/miscfs/umapfs/umap_subr.c
@@ -137,8 +137,8 @@ umap_mapids(struct mount *v_mount, kauth_cred_t credp)
uid_t uid;
gid_t gid;
u_long (*usermap)[2], (*groupmap)[2];
- gid_t groups[NGROUPS];
- uint16_t ngroups;
+ gid_t stackgroups[NGROUPS], *groups = stackgroups;
+ unsigned ngroups;
if (credp == NOCRED || credp == FSCRED)
return;
@@ -174,6 +174,8 @@ umap_mapids(struct mount *v_mount, kauth_cred_t credp)
structure. */
ngroups = kauth_cred_ngroups(credp);
+ if (ngroups > __arraycount(stackgroups))
+ groups = kmem_alloc(ngroups * sizeof(groups[0]), KM_SLEEP);
for (i = 0; i < ngroups; i++) {
/* XXX elad: can't we just skip cases where gid == -1? */
groups[i] = kauth_cred_group(credp, i);
@@ -184,6 +186,7 @@ umap_mapids(struct mount *v_mount, kauth_cred_t credp)
else
groups[i] = NULLGROUP;
}
-
kauth_cred_setgroups(credp, groups, ngroups, -1, UIO_SYSSPACE);
+ if (groups != stackgroups)
+ kmem_free(groups, ngroups * sizeof(groups[0]));
}
diff --git a/sys/sys/groupset.h b/sys/sys/groupset.h
new file mode 100644
index 000000000000..4fa9d9e9e491
--- /dev/null
+++ b/sys/sys/groupset.h
@@ -0,0 +1,52 @@
+/* $NetBSD$ */
+
+/*-
+ * Copyright (c) 2022 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_GROUPSET_H_
+#define _SYS_GROUPSET_H_
+
+#ifndef _KERNEL
+#error Don't touch this -- it's pure kernel!
+#endif
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+struct groupset;
+
+int groupset_create(unsigned, int (*)(gid_t *, unsigned, void *), void *,
+ struct groupset **);
+struct groupset *groupset_clone(struct groupset *);
+void groupset_destroy(struct groupset *);
+
+unsigned groupset_ngroups(const struct groupset *) __pure;
+int groupset_export(const struct groupset *,
+ int (*)(const gid_t *, unsigned, void *), void *);
+gid_t groupset_nth(const struct groupset *, unsigned) __pure;
+int groupset_member(const struct groupset *, gid_t) __pure;
+
+#endif /* _SYS_GROUPSET_H_ */
diff --git a/sys/sys/kauth.h b/sys/sys/kauth.h
index 2368a5f931e3..5bbe09884b2f 100644
--- a/sys/sys/kauth.h
+++ b/sys/sys/kauth.h
@@ -86,8 +86,7 @@ struct kauth_cred {
gid_t cr_gid; /* group id */
gid_t cr_egid; /* effective group id */
gid_t cr_svgid; /* saved effective group id */
- u_int cr_ngroups; /* number of groups */
- gid_t cr_groups[NGROUPS]; /* group memberships */
+ struct groupset *cr_groups; /* group memberships */
specificdata_reference cr_sd; /* specific data */
};
diff --git a/sys/sys/param.h b/sys/sys/param.h
index 6313435c1696..3ae59b9e5296 100644
--- a/sys/sys/param.h
+++ b/sys/sys/param.h
@@ -119,7 +119,7 @@
/* DEPRECATED: use LOGIN_NAME_MAX instead. */
#define MAXLOGNAME (LOGIN_NAME_MAX - 1) /* max login name length */
#define NCARGS ARG_MAX /* max bytes for an exec function */
-#define NGROUPS NGROUPS_MAX /* max number groups */
+#define NGROUPS 16 /* max number legacy groups */
#define NOGROUP 65535 /* marker for empty group set member */
#define MAXHOSTNAMELEN 256 /* max hostname size */
diff --git a/sys/sys/syslimits.h b/sys/sys/syslimits.h
index f4af0fdd19c4..00e214e75d71 100644
--- a/sys/sys/syslimits.h
+++ b/sys/sys/syslimits.h
@@ -52,7 +52,7 @@
#define MAX_INPUT 255 /* max bytes in terminal input */
#define NAME_MAX 511 /* max bytes in a file name, must be */
/* kept in sync with MAXNAMLEN */
-#define NGROUPS_MAX 16 /* max supplemental group id's */
+#define NGROUPS_MAX 65536 /* max supplemental group id's */
#define UID_MAX 2147483647U /* max value for a uid_t (2^31-2) */
#ifndef OPEN_MAX
#define OPEN_MAX 128 /* max open files per process */
diff --git a/sys/sys/ucred.h b/sys/sys/ucred.h
index 1b2af303d966..6bfecea96382 100644
--- a/sys/sys/ucred.h
+++ b/sys/sys/ucred.h
@@ -50,7 +50,7 @@ struct uucred {
uid_t cr_uid; /* effective user id */
gid_t cr_gid; /* effective group id */
short cr_ngroups; /* number of groups */
- gid_t cr_groups[NGROUPS_MAX]; /* groups */
+ gid_t cr_groups[16]; /* groups */
};
#endif /* !_SYS_UCRED_H_ */
--
2.51.1
Home |
Main Index |
Thread Index |
Old Index