From 32ba8e7385d01e5974e7162de7ff0f7c872fabcf Mon Sep 17 00:00:00 2001 From: Taylor R Campbell 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 #include #include +#include #include @@ -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 4c37c4097f48..c19903393b47 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -2783,9 +2783,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 +__KERNEL_RCSID(1, "$NetBSD: sun8i_crypto.c,v 1.30 2022/03/19 11:37:05 riastradh Exp $"); + +#include +#include + +#include +#include +#include +#include +#include +#include + +#else /* !_KERNEL */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +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 +#include + +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 15699ab37ed1..cb7b61b10253 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_ */