Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F98822577
D46914.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
7 KB
Referenced Files
None
Subscribers
None
D46914.diff
View Options
diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c
--- a/sys/kern/kern_prot.c
+++ b/sys/kern/kern_prot.c
@@ -89,8 +89,22 @@
"BSD security policy");
static void crfree_final(struct ucred *cr);
-static void crsetgroups_locked(struct ucred *cr, int ngrp,
- gid_t *groups);
+
+static inline void
+groups_check_positive_len(int ngrp)
+{
+ MPASS2(ngrp >= 0, "negative number of groups");
+ MPASS2(ngrp != 0, "at least one group expected (effective GID)");
+}
+static inline void
+groups_check_max_len(int ngrp)
+{
+ MPASS2(ngrp <= ngroups_max + 1, "too many groups");
+}
+
+static void groups_normalize(int *ngrp, gid_t *groups);
+static void crsetgroups_internal(struct ucred *cr, int ngrp,
+ const gid_t *groups);
static int cr_canseeotheruids(struct ucred *u1, struct ucred *u2);
static int cr_canseeothergids(struct ucred *u1, struct ucred *u2);
@@ -835,9 +849,9 @@
error = copyin(uap->gidset, groups, gidsetsize * sizeof(gid_t));
if (error == 0)
- error = kern_setgroups(td, gidsetsize, groups);
+ error = kern_setgroups(td, &gidsetsize, groups);
- if (gidsetsize > CRED_SMALLGROUPS_NB)
+ if (groups != smallgroups)
free(groups, M_TEMP);
return (error);
}
@@ -851,25 +865,38 @@
return (*gp1 > *gp2) - (*gp1 < *gp2);
}
+/*
+ * CAUTION: This function normalizes 'groups', possibly changing the value of
+ * '*ngrpp' as a consequence.
+ */
int
-kern_setgroups(struct thread *td, int ngrp, gid_t *groups)
+kern_setgroups(struct thread *td, int *ngrpp, gid_t *groups)
{
struct proc *p = td->td_proc;
struct ucred *newcred, *oldcred;
- int error;
+ int ngrp, error;
+ ngrp = *ngrpp;
/* Sanity check size. */
if (ngrp < 0 || ngrp > ngroups_max + 1)
return (EINVAL);
AUDIT_ARG_GROUPSET(groups, ngrp);
+ if (ngrp != 0) {
+ /* We allow and treat 0 specially below. */
+ groups_normalize(ngrpp, groups);
+ ngrp = *ngrpp;
+ }
newcred = crget();
crextend(newcred, ngrp);
PROC_LOCK(p);
oldcred = crcopysafe(p, newcred);
#ifdef MAC
- error = mac_cred_check_setgroups(oldcred, ngrp, groups);
+ error = ngrp == 0 ?
+ /* If 'ngrp' is 0, we'll keep just the current effective GID. */
+ mac_cred_check_setgroups(oldcred, 1, oldcred->cr_groups) :
+ mac_cred_check_setgroups(oldcred, ngrp, groups);
if (error)
goto fail;
#endif
@@ -886,9 +913,9 @@
* when running non-BSD software if we do not do the same.
*/
newcred->cr_ngroups = 1;
- } else {
- crsetgroups_locked(newcred, ngrp, groups);
- }
+ } else
+ crsetgroups_internal(newcred, ngrp, groups);
+
setsugid(p);
proc_set_cred(p, newcred);
PROC_UNLOCK(p);
@@ -2150,7 +2177,6 @@
crcopy(struct ucred *dest, struct ucred *src)
{
- KASSERT(dest->cr_ref == 1, ("crcopy of shared ucred"));
bcopy(&src->cr_startcopy, &dest->cr_startcopy,
(unsigned)((caddr_t)&src->cr_endcopy -
(caddr_t)&src->cr_startcopy));
@@ -2285,6 +2311,8 @@
{
int cnt;
+ MPASS2(cr->cr_ref == 1, "'cr_ref' must be 1 (referenced, unshared)");
+
/* Truncate? */
if (n <= cr->cr_agroups)
return;
@@ -2317,53 +2345,115 @@
cr->cr_agroups = cnt;
}
+#ifdef INVARIANTS
+static void
+groups_check_normalized(int ngrp, gid_t *groups)
+{
+ gid_t prev_g;
+
+ groups_check_positive_len(ngrp);
+ groups_check_max_len(ngrp);
+
+ if (ngrp == 1)
+ return;
+
+ prev_g = groups[1];
+ for (int i = 2; i < ngrp; ++i) {
+ const gid_t g = groups[i];
+
+ if (prev_g >= g)
+ panic("%s: groups[%d] (%u) >= groups[%d] (%u)",
+ __func__, i - 1, prev_g, i, g);
+ prev_g = g;
+ }
+}
+#else
+#define groups_check_normalized(...)
+#endif
+
/*
- * Copy groups in to a credential, preserving any necessary invariants.
- * Currently this includes the sorting of all supplemental gids.
- * crextend() must have been called before hand to ensure sufficient
- * space is available.
+ * Normalizes a set of groups to be set in a 'struct ucred'.
+ *
+ * The set of groups is passed as an array that must have at least one element,
+ * which is the effective GID.
+ *
+ * Normalization ensures that elements after the first are sorted and that there
+ * are no duplicates.
*/
static void
-crsetgroups_locked(struct ucred *cr, int ngrp, gid_t *groups)
+groups_normalize(int *ngrp, gid_t *groups)
{
- int i;
- int j;
- gid_t g;
+ gid_t prev_g;
+ int ins_idx;
- KASSERT(cr->cr_agroups >= ngrp, ("cr_ngroups is too small"));
+ groups_check_positive_len(*ngrp);
+ groups_check_max_len(*ngrp);
+
+ if (*ngrp == 1)
+ return;
+
+ qsort(groups + 1, *ngrp - 1, sizeof(*groups), gidp_cmp);
+
+ /* Remove duplicates. */
+ prev_g = groups[1];
+ ins_idx = 2;
+ for (int i = 2; i < *ngrp; ++i) {
+ const gid_t g = groups[i];
+
+ if (g != prev_g) {
+ if (i != ins_idx)
+ groups[ins_idx] = g;
+ ++ins_idx;
+ prev_g = g;
+ }
+ }
+ *ngrp = ins_idx;
+
+ groups_check_normalized(*ngrp, groups);
+}
+
+/*
+ * Internal function copying groups into a credential.
+ *
+ * 'ngrp' must be strictly positive. Either the passed 'groups' array must have
+ * been normalized in advance (see groups_normalize()), else it must be so
+ * before the structure is to be used again.
+ *
+ * This function is suitable to be used under any lock (it doesn't take any lock
+ * itself nor sleep, and in particular doesn't allocate memory). crextend()
+ * must have been called beforehand to ensure sufficient space is available.
+ * See also crsetgroups(), which handles that.
+ */
+static void
+crsetgroups_internal(struct ucred *cr, int ngrp, const gid_t *groups)
+{
+
+ MPASS2(cr->cr_ref == 1, "'cr_ref' must be 1 (referenced, unshared)");
+ MPASS2(cr->cr_agroups >= ngrp, "'cr_agroups' too small");
+ groups_check_positive_len(ngrp);
bcopy(groups, cr->cr_groups, ngrp * sizeof(gid_t));
cr->cr_ngroups = ngrp;
-
- /*
- * Sort all groups except cr_groups[0] to allow groupmember to
- * perform a binary search.
- *
- * XXX: If large numbers of groups become common this should
- * be replaced with shell sort like linux uses or possibly
- * heap sort.
- */
- for (i = 2; i < ngrp; i++) {
- g = cr->cr_groups[i];
- for (j = i-1; j >= 1 && g < cr->cr_groups[j]; j--)
- cr->cr_groups[j + 1] = cr->cr_groups[j];
- cr->cr_groups[j + 1] = g;
- }
}
/*
* Copy groups in to a credential after expanding it if required.
- * Truncate the list to (ngroups_max + 1) if it is too large.
+ *
+ * May sleep in order to allocate memory (except if, e.g., crextend() was called
+ * before with 'ngrp' or greater). Truncates the list to (ngroups_max + 1) if
+ * it is too large. Array 'groups' doesn't need to be sorted. 'ngrp' must be
+ * strictly positive.
*/
void
-crsetgroups(struct ucred *cr, int ngrp, gid_t *groups)
+crsetgroups(struct ucred *cr, int ngrp, const gid_t *groups)
{
+ /* No need to assert here, sub-routines will do so themselves. */
if (ngrp > ngroups_max + 1)
ngrp = ngroups_max + 1;
-
crextend(cr, ngrp);
- crsetgroups_locked(cr, ngrp, groups);
+ crsetgroups_internal(cr, ngrp, groups);
+ groups_normalize(&cr->cr_ngroups, cr->cr_groups);
}
/*
diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h
--- a/sys/sys/syscallsubr.h
+++ b/sys/sys/syscallsubr.h
@@ -320,7 +320,7 @@
fd_set *fd_ex, struct timeval *tvp, int abi_nfdbits);
int kern_sendit(struct thread *td, int s, struct msghdr *mp, int flags,
struct mbuf *control, enum uio_seg segflg);
-int kern_setgroups(struct thread *td, int ngrp, gid_t *groups);
+int kern_setgroups(struct thread *td, int *ngrpp, gid_t *groups);
int kern_setitimer(struct thread *, u_int, struct itimerval *,
struct itimerval *);
int kern_setpriority(struct thread *td, int which, int who, int prio);
diff --git a/sys/sys/ucred.h b/sys/sys/ucred.h
--- a/sys/sys/ucred.h
+++ b/sys/sys/ucred.h
@@ -162,7 +162,7 @@
void crcowfree(struct thread *td);
void cru2x(struct ucred *cr, struct xucred *xcr);
void cru2xt(struct thread *td, struct xucred *xcr);
-void crsetgroups(struct ucred *cr, int n, gid_t *groups);
+void crsetgroups(struct ucred *cr, int ngrp, const gid_t *groups);
/*
* Returns whether gid designates a primary group in cred.
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Oct 5, 9:12 PM (21 h, 46 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
13677685
Default Alt Text
D46914.diff (7 KB)
Attached To
Mode
D46914: crsetgroups(): Improve and factor out groups normalization
Attached
Detach File
Event Timeline
Log In to Comment