aboutsummaryrefslogtreecommitdiff
path: root/sys/kern/kern_prot.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/kern_prot.c')
-rw-r--r--sys/kern/kern_prot.c126
1 files changed, 81 insertions, 45 deletions
diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c
index 0ca42d640767..81099aa7d28d 100644
--- a/sys/kern/kern_prot.c
+++ b/sys/kern/kern_prot.c
@@ -291,11 +291,6 @@ sys_getgid(struct thread *td, struct getgid_args *uap)
return (0);
}
-/*
- * Get effective group ID. The "egid" is groups[0], and could be obtained
- * via getgroups. This syscall exists because it is somewhat painful to do
- * correctly in a library function.
- */
#ifndef _SYS_SYSPROTO_H_
struct getegid_args {
int dummy;
@@ -427,7 +422,7 @@ again:
* pid must be in same session (EPERM)
* pid can't have done an exec (EACCES)
* if pgid != pid
- * there must exist some pid in same session having pgid (EPERM)
+ * there must exist some pid in same session having pgid (EPERM)
* pid must not be session leader (EPERM)
*/
#ifndef _SYS_SYSPROTO_H_
@@ -608,8 +603,8 @@ user_setcred(struct thread *td, const u_int flags,
if (error != 0)
return (error);
/* These fields have exactly the same sizes and positions. */
- memcpy(&wcred, &wcred32, &wcred32.setcred32_copy_end -
- &wcred32.setcred32_copy_start);
+ memcpy(&wcred, &wcred32, __rangeof(struct setcred32,
+ setcred32_copy_start, setcred32_copy_end));
/* Remaining fields are pointers and need PTRIN*(). */
PTRIN_CP(wcred32, wcred, sc_supp_groups);
PTRIN_CP(wcred32, wcred, sc_label);
@@ -701,7 +696,7 @@ kern_setcred(struct thread *const td, const u_int flags,
gid_t *groups = NULL;
gid_t smallgroups[CRED_SMALLGROUPS_NB];
int error;
- bool cred_set;
+ bool cred_set = false;
/* Bail out on unrecognized flags. */
if (flags & ~SETCREDF_MASK)
@@ -837,24 +832,50 @@ kern_setcred(struct thread *const td, const u_int flags,
if (error != 0)
goto unlock_finish;
+#ifdef RACCT
/*
- * Set the new credentials, noting that they have changed.
+ * Hold a reference to 'new_cred', as we need to call some functions on
+ * it after proc_set_cred_enforce_proc_lim().
*/
+ crhold(new_cred);
+#endif
+
+ /* Set the new credentials. */
cred_set = proc_set_cred_enforce_proc_lim(p, new_cred);
if (cred_set) {
setsugid(p);
+#ifdef RACCT
+ /* Adjust RACCT counters. */
+ racct_proc_ucred_changed(p, old_cred, new_cred);
+#endif
to_free_cred = old_cred;
MPASS(error == 0);
- } else
+ } else {
+#ifdef RACCT
+ /* Matches the crhold() just before the containing 'if'. */
+ crfree(new_cred);
+#endif
error = EAGAIN;
+ }
unlock_finish:
PROC_UNLOCK(p);
+
/*
* Part 3: After releasing the process lock, we perform cleanups and
* finishing operations.
*/
+#ifdef RACCT
+ if (cred_set) {
+#ifdef RCTL
+ rctl_proc_ucred_changed(p, new_cred);
+#endif
+ /* Paired with the crhold() above. */
+ crfree(new_cred);
+ }
+#endif
+
#ifdef MAC
if (mac_set_proc_data != NULL)
mac_set_proc_finish(td, proc_label_set, mac_set_proc_data);
@@ -981,14 +1002,19 @@ sys_setuid(struct thread *td, struct setuid_args *uap)
change_euid(newcred, uip);
setsugid(p);
}
- /*
- * This also transfers the proc count to the new user.
- */
- proc_set_cred(p, newcred);
+
#ifdef RACCT
racct_proc_ucred_changed(p, oldcred, newcred);
+#endif
+#ifdef RCTL
crhold(newcred);
#endif
+ /*
+ * Takes over 'newcred''s reference, so 'newcred' must not be used
+ * besides this point except on RCTL where we took an additional
+ * reference above.
+ */
+ proc_set_cred(p, newcred);
PROC_UNLOCK(p);
#ifdef RCTL
rctl_proc_ucred_changed(p, newcred);
@@ -1392,11 +1418,18 @@ sys_setreuid(struct thread *td, struct setreuid_args *uap)
change_svuid(newcred, newcred->cr_uid);
setsugid(p);
}
- proc_set_cred(p, newcred);
#ifdef RACCT
racct_proc_ucred_changed(p, oldcred, newcred);
+#endif
+#ifdef RCTL
crhold(newcred);
#endif
+ /*
+ * Takes over 'newcred''s reference, so 'newcred' must not be used
+ * besides this point except on RCTL where we took an additional
+ * reference above.
+ */
+ proc_set_cred(p, newcred);
PROC_UNLOCK(p);
#ifdef RCTL
rctl_proc_ucred_changed(p, newcred);
@@ -1538,11 +1571,18 @@ sys_setresuid(struct thread *td, struct setresuid_args *uap)
change_svuid(newcred, suid);
setsugid(p);
}
- proc_set_cred(p, newcred);
#ifdef RACCT
racct_proc_ucred_changed(p, oldcred, newcred);
+#endif
+#ifdef RCTL
crhold(newcred);
#endif
+ /*
+ * Takes over 'newcred''s reference, so 'newcred' must not be used
+ * besides this point except on RCTL where we took an additional
+ * reference above.
+ */
+ proc_set_cred(p, newcred);
PROC_UNLOCK(p);
#ifdef RCTL
rctl_proc_ucred_changed(p, newcred);
@@ -1803,12 +1843,6 @@ groupmember(gid_t gid, const struct ucred *cred)
bool
realgroupmember(gid_t gid, const struct ucred *cred)
{
- /*
- * Although the equality test on 'cr_rgid' below doesn't access
- * 'cr_groups', we check for the latter's length here as we assume that,
- * if 'cr_ngroups' is 0, the passed 'struct ucred' is invalid, and
- * 'cr_rgid' may not have been filled.
- */
groups_check_positive_len(cred->cr_ngroups);
if (gid == cred->cr_rgid)
@@ -1896,19 +1930,22 @@ SYSCTL_INT(_security_bsd, OID_AUTO, see_other_gids, CTLFLAG_RW,
static int
cr_canseeothergids(struct ucred *u1, struct ucred *u2)
{
- if (!see_other_gids) {
- if (realgroupmember(u1->cr_rgid, u2))
- return (0);
+ if (see_other_gids)
+ return (0);
- for (int i = 1; i < u1->cr_ngroups; i++)
- if (realgroupmember(u1->cr_groups[i], u2))
- return (0);
+ /* Restriction in force. */
- if (priv_check_cred(u1, PRIV_SEEOTHERGIDS) != 0)
- return (ESRCH);
- }
+ if (realgroupmember(u1->cr_rgid, u2))
+ return (0);
- return (0);
+ for (int i = 0; i < u1->cr_ngroups; i++)
+ if (realgroupmember(u1->cr_groups[i], u2))
+ return (0);
+
+ if (priv_check_cred(u1, PRIV_SEEOTHERGIDS) == 0)
+ return (0);
+
+ return (ESRCH);
}
/*
@@ -2276,6 +2313,7 @@ cr_xids_subset(struct ucred *active_cred, struct ucred *obj_cred)
}
}
grpsubset = grpsubset &&
+ groupmember(obj_cred->cr_gid, active_cred) &&
groupmember(obj_cred->cr_rgid, active_cred) &&
groupmember(obj_cred->cr_svgid, active_cred);
@@ -2769,7 +2807,7 @@ cru2xt(struct thread *td, struct xucred *xcr)
* 'enforce_proc_lim' being true and if no new process can be accounted to the
* new real UID because of the current limit (see the inner comment for more
* details) and the caller does not have privilege (PRIV_PROC_LIMIT) to override
- * that.
+ * that. In this case, the reference to 'newcred' is not taken over.
*/
static bool
_proc_set_cred(struct proc *p, struct ucred *newcred, bool enforce_proc_lim)
@@ -2778,10 +2816,6 @@ _proc_set_cred(struct proc *p, struct ucred *newcred, bool enforce_proc_lim)
MPASS(oldcred != NULL);
PROC_LOCK_ASSERT(p, MA_OWNED);
- KASSERT(newcred->cr_users == 0, ("%s: users %d not 0 on cred %p",
- __func__, newcred->cr_users, newcred));
- KASSERT(newcred->cr_ref == 1, ("%s: ref %ld not 1 on cred %p",
- __func__, newcred->cr_ref, newcred));
if (newcred->cr_ruidinfo != oldcred->cr_ruidinfo) {
/*
@@ -2807,8 +2841,10 @@ _proc_set_cred(struct proc *p, struct ucred *newcred, bool enforce_proc_lim)
__func__, oldcred->cr_users, oldcred));
oldcred->cr_users--;
mtx_unlock(&oldcred->cr_mtx);
+ mtx_lock(&newcred->cr_mtx);
+ newcred->cr_users++;
+ mtx_unlock(&newcred->cr_mtx);
p->p_ucred = newcred;
- newcred->cr_users = 1;
PROC_UPDATE_COW(p);
if (newcred->cr_ruidinfo != oldcred->cr_ruidinfo)
(void)chgproccnt(oldcred->cr_ruidinfo, -1, 0);
@@ -2921,8 +2957,8 @@ crextend(struct ucred *cr, int n)
* Normalizes a set of groups to be applied to a 'struct ucred'.
*
* Normalization ensures that the supplementary groups are sorted in ascending
- * order and do not contain duplicates. This allows group_is_supplementary
- * to do a binary search.
+ * order and do not contain duplicates. This allows group_is_supplementary() to
+ * do a binary search.
*/
static void
groups_normalize(int *ngrp, gid_t *groups)
@@ -2985,9 +3021,9 @@ crsetgroups_internal(struct ucred *cr, int ngrp, const gid_t *groups)
* Copy groups in to a credential after expanding it if required.
*
* 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 if
+ * before with 'ngrp' or greater). Truncates the list to 'ngroups_max' if
* it is too large. Array 'groups' doesn't need to be sorted. 'ngrp' must be
- * strictly positive.
+ * positive.
*/
void
crsetgroups(struct ucred *cr, int ngrp, const gid_t *groups)
@@ -3018,8 +3054,8 @@ crsetgroups(struct ucred *cr, int ngrp, const gid_t *groups)
* Same as crsetgroups() but sets the effective GID as well.
*
* This function ensures that an effective GID is always present in credentials.
- * An empty array will only set the effective GID to the default_egid, while a
- * non-empty array will peel off groups[0] to set as the effective GID and use
+ * An empty array will only set the effective GID to 'default_egid', while
+ * a non-empty array will peel off groups[0] to set as the effective GID and use
* the remainder, if any, as supplementary groups.
*/
void