diff --git a/lib/libc/sys/Symbol.map b/lib/libc/sys/Symbol.map index d6126fd952f..506d88d46c3 100644 --- a/lib/libc/sys/Symbol.map +++ b/lib/libc/sys/Symbol.map @@ -427,6 +427,7 @@ FBSD_1.7 { FBSD_1.8 { getrlimitusage; kcmp; + setcred; }; FBSDprivate_1.0 { diff --git a/sys/bsm/audit_kevents.h b/sys/bsm/audit_kevents.h index d06381837aa..0f110d5f9dd 100644 --- a/sys/bsm/audit_kevents.h +++ b/sys/bsm/audit_kevents.h @@ -662,6 +662,7 @@ #define AUE_AIO_READV 43268 /* FreeBSD-specific. */ #define AUE_FSPACECTL 43269 /* FreeBSD-specific. */ #define AUE_TIMERFD 43270 /* FreeBSD/Linux. */ +#define AUE_SETCRED 43271 /* FreeBSD-specific. */ /* * Darwin BSM uses a number of AUE_O_* definitions, which are aliased to the diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c index c4872373735..7d945cfb6de 100644 --- a/sys/compat/freebsd32/freebsd32_misc.c +++ b/sys/compat/freebsd32/freebsd32_misc.c @@ -86,6 +86,7 @@ #include #include #include +#include #include #include #include @@ -115,6 +116,7 @@ #endif #include +#include #include #include @@ -4172,3 +4174,10 @@ ofreebsd32_sethostid(struct thread *td, struct ofreebsd32_sethostid_args *uap) sizeof(hostid), NULL, 0)); } #endif + +int +freebsd32_setcred(struct thread *td, struct freebsd32_setcred_args *uap) +{ + /* Last argument is 'is_32bit'. */ + return (user_setcred(td, uap->flags, uap->wcred, uap->size, true)); +} diff --git a/sys/compat/freebsd32/freebsd32_proto.h b/sys/compat/freebsd32/freebsd32_proto.h index 50448b6dce1..ea72f0e57ac 100644 --- a/sys/compat/freebsd32/freebsd32_proto.h +++ b/sys/compat/freebsd32/freebsd32_proto.h @@ -694,6 +694,11 @@ struct freebsd32_timerfd_settime_args { char new_value_l_[PADL_(const struct itimerspec32 *)]; const struct itimerspec32 * new_value; char new_value_r_[PADR_(const struct itimerspec32 *)]; char old_value_l_[PADL_(struct itimerspec32 *)]; struct itimerspec32 * old_value; char old_value_r_[PADR_(struct itimerspec32 *)]; }; +struct freebsd32_setcred_args { + char flags_l_[PADL_(u_int)]; u_int flags; char flags_r_[PADR_(u_int)]; + char wcred_l_[PADL_(const struct setcred32 *)]; const struct setcred32 * wcred; char wcred_r_[PADR_(const struct setcred32 *)]; + char size_l_[PADL_(size_t)]; size_t size; char size_r_[PADR_(size_t)]; +}; int freebsd32_wait4(struct thread *, struct freebsd32_wait4_args *); int freebsd32_ptrace(struct thread *, struct freebsd32_ptrace_args *); int freebsd32_recvmsg(struct thread *, struct freebsd32_recvmsg_args *); @@ -811,6 +816,7 @@ int freebsd32_aio_writev(struct thread *, struct freebsd32_aio_writev_args *); int freebsd32_aio_readv(struct thread *, struct freebsd32_aio_readv_args *); int freebsd32_timerfd_gettime(struct thread *, struct freebsd32_timerfd_gettime_args *); int freebsd32_timerfd_settime(struct thread *, struct freebsd32_timerfd_settime_args *); +int freebsd32_setcred(struct thread *, struct freebsd32_setcred_args *); #ifdef COMPAT_43 @@ -1306,6 +1312,7 @@ int freebsd11_freebsd32_fstatat(struct thread *, struct freebsd11_freebsd32_fsta #define FREEBSD32_SYS_AUE_freebsd32_aio_readv AUE_AIO_READV #define FREEBSD32_SYS_AUE_freebsd32_timerfd_gettime AUE_TIMERFD #define FREEBSD32_SYS_AUE_freebsd32_timerfd_settime AUE_TIMERFD +#define FREEBSD32_SYS_AUE_freebsd32_setcred AUE_SETCRED #undef PAD_ #undef PADL_ diff --git a/sys/compat/freebsd32/freebsd32_syscall.h b/sys/compat/freebsd32/freebsd32_syscall.h index 01c1a5c515d..6aef20fb023 100644 --- a/sys/compat/freebsd32/freebsd32_syscall.h +++ b/sys/compat/freebsd32/freebsd32_syscall.h @@ -508,4 +508,5 @@ #define FREEBSD32_SYS_freebsd32_timerfd_settime 587 #define FREEBSD32_SYS_kcmp 588 #define FREEBSD32_SYS_getrlimitusage 589 -#define FREEBSD32_SYS_MAXSYSCALL 590 +#define FREEBSD32_SYS_freebsd32_setcred 591 +#define FREEBSD32_SYS_MAXSYSCALL 592 diff --git a/sys/compat/freebsd32/freebsd32_syscalls.c b/sys/compat/freebsd32/freebsd32_syscalls.c index 6d50f7c0362..1501c0a78f0 100644 --- a/sys/compat/freebsd32/freebsd32_syscalls.c +++ b/sys/compat/freebsd32/freebsd32_syscalls.c @@ -595,4 +595,6 @@ const char *freebsd32_syscallnames[] = { "freebsd32_timerfd_settime", /* 587 = freebsd32_timerfd_settime */ "kcmp", /* 588 = kcmp */ "getrlimitusage", /* 589 = getrlimitusage */ + "#590", /* 590 = fchroot */ + "freebsd32_setcred", /* 591 = freebsd32_setcred */ }; diff --git a/sys/compat/freebsd32/freebsd32_sysent.c b/sys/compat/freebsd32/freebsd32_sysent.c index 3d491642747..5c5cbe96712 100644 --- a/sys/compat/freebsd32/freebsd32_sysent.c +++ b/sys/compat/freebsd32/freebsd32_sysent.c @@ -651,4 +651,6 @@ struct sysent freebsd32_sysent[] = { { .sy_narg = AS(freebsd32_timerfd_settime_args), .sy_call = (sy_call_t *)freebsd32_timerfd_settime, .sy_auevent = AUE_TIMERFD, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 587 = freebsd32_timerfd_settime */ { .sy_narg = AS(kcmp_args), .sy_call = (sy_call_t *)sys_kcmp, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 588 = kcmp */ { .sy_narg = AS(getrlimitusage_args), .sy_call = (sy_call_t *)sys_getrlimitusage, .sy_auevent = AUE_NULL, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 589 = getrlimitusage */ + { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 590 = fchroot */ + { .sy_narg = AS(freebsd32_setcred_args), .sy_call = (sy_call_t *)freebsd32_setcred, .sy_auevent = AUE_SETCRED, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 591 = freebsd32_setcred */ }; diff --git a/sys/compat/freebsd32/freebsd32_systrace_args.c b/sys/compat/freebsd32/freebsd32_systrace_args.c index 8f7d9edadfe..c5b16c12edd 100644 --- a/sys/compat/freebsd32/freebsd32_systrace_args.c +++ b/sys/compat/freebsd32/freebsd32_systrace_args.c @@ -3391,6 +3391,15 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) *n_args = 3; break; } + /* freebsd32_setcred */ + case 591: { + struct freebsd32_setcred_args *p = params; + uarg[a++] = p->flags; /* u_int */ + uarg[a++] = (intptr_t)p->wcred; /* const struct setcred32 * */ + uarg[a++] = p->size; /* size_t */ + *n_args = 3; + break; + } default: *n_args = 0; break; @@ -9159,6 +9168,22 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) break; }; break; + /* freebsd32_setcred */ + case 591: + switch (ndx) { + case 0: + p = "u_int"; + break; + case 1: + p = "userland const struct setcred32 *"; + break; + case 2: + p = "size_t"; + break; + default: + break; + }; + break; default: break; }; @@ -11057,6 +11082,11 @@ systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) if (ndx == 0 || ndx == 1) p = "int"; break; + /* freebsd32_setcred */ + case 591: + if (ndx == 0 || ndx == 1) + p = "int"; + break; default: break; }; diff --git a/sys/kern/init_sysent.c b/sys/kern/init_sysent.c index e740d6ef1b7..fa266016559 100644 --- a/sys/kern/init_sysent.c +++ b/sys/kern/init_sysent.c @@ -650,4 +650,6 @@ struct sysent sysent[] = { { .sy_narg = AS(timerfd_settime_args), .sy_call = (sy_call_t *)sys_timerfd_settime, .sy_auevent = AUE_TIMERFD, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 587 = timerfd_settime */ { .sy_narg = AS(kcmp_args), .sy_call = (sy_call_t *)sys_kcmp, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 588 = kcmp */ { .sy_narg = AS(getrlimitusage_args), .sy_call = (sy_call_t *)sys_getrlimitusage, .sy_auevent = AUE_NULL, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 589 = getrlimitusage */ + { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 590 = fchroot */ + { .sy_narg = AS(setcred_args), .sy_call = (sy_call_t *)sys_setcred, .sy_auevent = AUE_SETCRED, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 591 = setcred */ }; diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c index 103b44cc00b..99cf8f731c4 100644 --- a/sys/kern/kern_jail.c +++ b/sys/kern/kern_jail.c @@ -3939,6 +3939,7 @@ prison_priv_check(struct ucred *cred, int priv) * Allow jailed processes to manipulate process UNIX * credentials in any way they see fit. */ + case PRIV_CRED_SETCRED: case PRIV_CRED_SETUID: case PRIV_CRED_SETEUID: case PRIV_CRED_SETGID: diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c index 3f1d28376d4..31652a338e8 100644 --- a/sys/kern/kern_prot.c +++ b/sys/kern/kern_prot.c @@ -49,6 +49,7 @@ #include #include +#include #include #include #include @@ -75,6 +76,10 @@ #include #include +#ifdef MAC +#include +#endif + #ifdef REGRESSION FEATURE(regression, "Kernel support for interfaces necessary for regression testing (SECURITY RISK!)"); @@ -484,6 +489,365 @@ done: return (error); } +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)); +} + +/* + * Final storage for groups (including the effective GID) will be returned via + * 'groups'. '*groups' must be NULL on input, and if not equal to 'smallgroups' + * on output, must be freed (M_TEMP) *even if* an error is returned. + */ +static int +kern_setcred_copyin_supp_groups(struct setcred *const wcred, + const u_int flags, gid_t *const smallgroups, gid_t **const groups) +{ + MPASS(*groups == NULL); + + if (flags & SETCREDF_SUPP_GROUPS) { + int error; + + /* + * Check for the limit for number of groups right now in order + * to limit the amount of bytes to copy. + */ + if (wcred->sc_supp_groups_nb > ngroups_max) + return (EINVAL); + + /* + * Since we are going to be copying the supplementary groups + * from userland, make room also for the effective GID right + * now, to avoid having to allocate and copy again the + * supplementary groups. + */ + *groups = wcred->sc_supp_groups_nb < CRED_SMALLGROUPS_NB ? + smallgroups : malloc((wcred->sc_supp_groups_nb + 1) * + sizeof(*groups), M_TEMP, M_WAITOK); + + error = copyin(wcred->sc_supp_groups, *groups + 1, + wcred->sc_supp_groups_nb * sizeof(*groups)); + if (error != 0) + return (error); + wcred->sc_supp_groups = *groups + 1; + } else { + wcred->sc_supp_groups_nb = 0; + wcred->sc_supp_groups = NULL; + } + + return (0); +} + +int +user_setcred(struct thread *td, const u_int flags, + const void *const uwcred, const size_t size, bool is_32bit) +{ + struct setcred wcred; +#ifdef MAC + struct mac mac; + /* Pointer to 'struct mac' or 'struct mac32'. */ + void *umac; +#endif + gid_t smallgroups[CRED_SMALLGROUPS_NB]; + gid_t *groups = NULL; + int error; + + /* + * As the only point of this wrapper function is to copyin() from + * userland, we only interpret the data pieces we need to perform this + * operation and defer further sanity checks to kern_setcred(), except + * that we redundantly check here that no unknown flags have been + * passed. + */ + if ((flags & ~SETCREDF_MASK) != 0) + return (EINVAL); + +#ifdef COMPAT_FREEBSD32 + if (is_32bit) { + struct setcred32 wcred32; + + if (size != sizeof(wcred32)) + return (EINVAL); + error = copyin(uwcred, &wcred32, sizeof(wcred32)); + 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); + /* Remaining fields are pointers and need PTRIN*(). */ + PTRIN_CP(wcred32, wcred, sc_supp_groups); + PTRIN_CP(wcred32, wcred, sc_label); + } else +#endif /* COMPAT_FREEBSD32 */ + { + if (size != sizeof(wcred)) + return (EINVAL); + error = copyin(uwcred, &wcred, sizeof(wcred)); + if (error != 0) + return (error); + } +#ifdef MAC + umac = wcred.sc_label; +#endif + /* Also done on !MAC as a defensive measure. */ + wcred.sc_label = NULL; + + /* + * Copy supplementary groups as needed. There is no specific + * alternative for 32-bit compatibility as 'gid_t' has the same size + * everywhere. + */ + error = kern_setcred_copyin_supp_groups(&wcred, flags, smallgroups, + &groups); + if (error != 0) + goto free_groups; + +#ifdef MAC + if ((flags & SETCREDF_MAC_LABEL) != 0) { +#ifdef COMPAT_FREEBSD32 + if (is_32bit) + error = mac_label_copyin32(umac, &mac, NULL); + else +#endif + error = mac_label_copyin(umac, &mac, NULL); + if (error != 0) + goto free_groups; + wcred.sc_label = &mac; + } +#endif + + error = kern_setcred(td, flags, &wcred, groups); + +#ifdef MAC + if (wcred.sc_label != NULL) + free_copied_label(wcred.sc_label); +#endif + +free_groups: + if (groups != smallgroups) + free(groups, M_TEMP); + + return (error); +} + +#ifndef _SYS_SYSPROTO_H_ +struct setcred_args { + u_int flags; /* Flags. */ + const struct setcred *wcred; + size_t size; /* Passed 'setcred' structure length. */ +}; +#endif +/* ARGSUSED */ +int +sys_setcred(struct thread *td, struct setcred_args *uap) +{ + return (user_setcred(td, uap->flags, uap->wcred, uap->size, false)); +} + +/* + * CAUTION: This function normalizes groups in 'wcred'. + * + * If 'preallocated_groups' is non-NULL, it must be an already allocated array + * of size 'wcred->sc_supp_groups_nb + 1', with the supplementary groups + * starting at index 1, and 'wcred->sc_supp_groups' then must point to the first + * supplementary group. + */ +int +kern_setcred(struct thread *const td, const u_int flags, + struct setcred *const wcred, gid_t *preallocated_groups) +{ + struct proc *const p = td->td_proc; + struct ucred *new_cred, *old_cred, *to_free_cred; + struct uidinfo *uip = NULL, *ruip = NULL; +#ifdef MAC + void *mac_set_proc_data = NULL; + bool proc_label_set = false; +#endif + gid_t *groups = NULL; + gid_t smallgroups[CRED_SMALLGROUPS_NB]; + int error; + bool cred_set; + + /* Bail out on unrecognized flags. */ + if (flags & ~SETCREDF_MASK) + return (EINVAL); + + /* + * Part 1: We allocate and perform preparatory operations with no locks. + */ + + if (flags & SETCREDF_SUPP_GROUPS) { + if (wcred->sc_supp_groups_nb > ngroups_max) + return (EINVAL); + if (preallocated_groups != NULL) { + groups = preallocated_groups; + MPASS(preallocated_groups + 1 == wcred->sc_supp_groups); + } else { + groups = wcred->sc_supp_groups_nb < CRED_SMALLGROUPS_NB ? + smallgroups : + malloc((wcred->sc_supp_groups_nb + 1) * + sizeof(*groups), M_TEMP, M_WAITOK); + memcpy(groups + 1, wcred->sc_supp_groups, + wcred->sc_supp_groups_nb * sizeof(*groups)); + } + } + + if (flags & SETCREDF_MAC_LABEL) { +#ifdef MAC + error = mac_set_proc_prepare(td, wcred->sc_label, + &mac_set_proc_data); + if (error != 0) + goto free_groups; +#else + error = ENOTSUP; + goto free_groups; +#endif + } + + if (flags & SETCREDF_UID) { + AUDIT_ARG_EUID(wcred->sc_uid); + uip = uifind(wcred->sc_uid); + } + if (flags & SETCREDF_RUID) { + AUDIT_ARG_RUID(wcred->sc_ruid); + ruip = uifind(wcred->sc_ruid); + } + if (flags & SETCREDF_SVUID) + AUDIT_ARG_SUID(wcred->sc_svuid); + + if (flags & SETCREDF_GID) + AUDIT_ARG_EGID(wcred->sc_gid); + if (flags & SETCREDF_RGID) + AUDIT_ARG_RGID(wcred->sc_rgid); + if (flags & SETCREDF_SVGID) + AUDIT_ARG_SGID(wcred->sc_svgid); + if (flags & SETCREDF_SUPP_GROUPS) { + int ngrp = wcred->sc_supp_groups_nb; + + /* + * Output the raw supplementary groups array for better + * traceability. + */ + AUDIT_ARG_GROUPSET(groups + 1, ngrp); + ++ngrp; + groups_normalize(&ngrp, groups); + wcred->sc_supp_groups_nb = ngrp - 1; + } + + /* + * We first completely build the new credentials and only then pass them + * to MAC along with the old ones so that modules can check whether the + * requested transition is allowed. + */ + new_cred = crget(); + to_free_cred = new_cred; + if (flags & SETCREDF_SUPP_GROUPS) + crextend(new_cred, wcred->sc_supp_groups_nb + 1); + +#ifdef MAC + mac_cred_setcred_enter(); +#endif + + /* + * Part 2: We grab the process lock as to have a stable view of its + * current credentials, and prepare a copy of them with the requested + * changes applied under that lock. + */ + + PROC_LOCK(p); + old_cred = crcopysafe(p, new_cred); + + /* + * Change user IDs. + */ + if (flags & SETCREDF_UID) + change_euid(new_cred, uip); + if (flags & SETCREDF_RUID) + change_ruid(new_cred, ruip); + if (flags & SETCREDF_SVUID) + change_svuid(new_cred, wcred->sc_svuid); + + /* + * Change groups. + * + * crsetgroups_internal() changes both the effective and supplementary + * ones. + */ + if (flags & SETCREDF_SUPP_GROUPS) { + groups[0] = flags & SETCREDF_GID ? wcred->sc_gid : + new_cred->cr_gid; + crsetgroups_internal(new_cred, wcred->sc_supp_groups_nb + 1, + groups); + } else if (flags & SETCREDF_GID) + change_egid(new_cred, wcred->sc_gid); + if (flags & SETCREDF_RGID) + change_rgid(new_cred, wcred->sc_rgid); + if (flags & SETCREDF_SVGID) + change_svgid(new_cred, wcred->sc_svgid); + +#ifdef MAC + /* + * Change the MAC label. + */ + if (flags & SETCREDF_MAC_LABEL) { + error = mac_set_proc_core(td, new_cred, mac_set_proc_data); + if (error != 0) + goto unlock_finish; + proc_label_set = true; + } + + /* + * MAC security modules checks. + */ + error = mac_cred_check_setcred(flags, old_cred, new_cred); + if (error != 0) + goto unlock_finish; +#endif + /* + * Privilege check. + */ + error = priv_check_cred(old_cred, PRIV_CRED_SETCRED); + if (error != 0) + goto unlock_finish; + + /* + * Set the new credentials, noting that they have changed. + */ + cred_set = proc_set_cred_enforce_proc_lim(p, new_cred); + if (cred_set) { + setsugid(p); + to_free_cred = old_cred; + MPASS(error == 0); + } else + error = EAGAIN; + +unlock_finish: + PROC_UNLOCK(p); + /* + * Part 3: After releasing the process lock, we perform cleanups and + * finishing operations. + */ + +#ifdef MAC + if (mac_set_proc_data != NULL) + mac_set_proc_finish(td, proc_label_set, mac_set_proc_data); + mac_cred_setcred_exit(); +#endif + crfree(to_free_cred); + if (uip != NULL) + uifree(uip); + if (ruip != NULL) + uifree(ruip); +free_groups: + if (groups != preallocated_groups && groups != smallgroups) + free(groups, M_TEMP); /* Deals with 'groups' being NULL. */ + return (error); +} + /* * Use the clause in B.4.2.2 that allows setuid/setgid to be 4.2/4.3BSD * compatible. It says that setting the uid/gid to euid/egid is a special @@ -859,15 +1223,6 @@ sys_setgroups(struct thread *td, struct setgroups_args *uap) return (error); } -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)); -} - /* * CAUTION: This function normalizes 'groups', possibly also changing the value * of '*ngrpp' as a consequence. diff --git a/sys/kern/syscalls.c b/sys/kern/syscalls.c index f21cdd66b3c..ac2984e339f 100644 --- a/sys/kern/syscalls.c +++ b/sys/kern/syscalls.c @@ -595,4 +595,6 @@ const char *syscallnames[] = { "timerfd_settime", /* 587 = timerfd_settime */ "kcmp", /* 588 = kcmp */ "getrlimitusage", /* 589 = getrlimitusage */ + "#590", /* 590 = fchroot */ + "setcred", /* 591 = setcred */ }; diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index 825ab1a4536..a52f4bd88b8 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -3354,5 +3354,13 @@ _Out_ rlim_t *res ); } +590 AUE_NULL UNIMPL fchroot +591 AUE_SETCRED STD|CAPENABLED { + int setcred( + u_int flags, + _In_reads_bytes_(size) _Contains_ptr_ const struct setcred *wcred, + size_t size + ); + } ; vim: syntax=off diff --git a/sys/kern/systrace_args.c b/sys/kern/systrace_args.c index dd2f7a43ee7..dca61ab5fca 100644 --- a/sys/kern/systrace_args.c +++ b/sys/kern/systrace_args.c @@ -3478,6 +3478,15 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) *n_args = 3; break; } + /* setcred */ + case 591: { + struct setcred_args *p = params; + uarg[a++] = p->flags; /* u_int */ + uarg[a++] = (intptr_t)p->wcred; /* const struct setcred * */ + uarg[a++] = p->size; /* size_t */ + *n_args = 3; + break; + } default: *n_args = 0; break; @@ -9304,6 +9313,22 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) break; }; break; + /* setcred */ + case 591: + switch (ndx) { + case 0: + p = "u_int"; + break; + case 1: + p = "userland const struct setcred *"; + break; + case 2: + p = "size_t"; + break; + default: + break; + }; + break; default: break; }; @@ -11292,6 +11317,11 @@ systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) if (ndx == 0 || ndx == 1) p = "int"; break; + /* setcred */ + case 591: + if (ndx == 0 || ndx == 1) + p = "int"; + break; default: break; }; diff --git a/sys/security/mac/mac_cred.c b/sys/security/mac/mac_cred.c index 304265b783f..5066de27717 100644 --- a/sys/security/mac/mac_cred.c +++ b/sys/security/mac/mac_cred.c @@ -209,6 +209,53 @@ mac_cred_check_relabel(struct ucred *cred, struct label *newlabel) return (error); } +/* + * Entry hook for setcred(). + * + * Called with no lock held by setcred() so that MAC modules may allocate memory + * in preparation for checking privileges. A call to this hook is always + * followed by a matching call to mac_cred_setcred_exit(). Between these two, + * setcred() may or may not call mac_cred_check_setcred(). + */ +void +mac_cred_setcred_enter(void) +{ + MAC_POLICY_PERFORM_NOSLEEP(cred_setcred_enter); +} + +MAC_CHECK_PROBE_DEFINE3(cred_check_setcred, "unsigned int", "struct ucred *", + "struct ucred *"); + +/* + * Check hook for setcred(). + * + * When called, the current process' lock is held. It thus cannot perform + * memory allocations, which must be done in advance in + * mac_cred_setcred_enter(). It *MUST NOT* tamper with the process' lock. + */ +int +mac_cred_check_setcred(u_int flags, const struct ucred *old_cred, + struct ucred *new_cred) +{ + int error; + + MAC_POLICY_CHECK_NOSLEEP(cred_check_setcred, flags, old_cred, new_cred); + MAC_CHECK_PROBE3(cred_check_setcred, error, flags, old_cred, new_cred); + + return (error); +} + +/* + * Exit hook for setcred(). + * + * Called with no lock held, exactly once per call to mac_cred_setcred_enter(). + */ +void +mac_cred_setcred_exit(void) +{ + MAC_POLICY_PERFORM_NOSLEEP(cred_setcred_exit); +} + MAC_CHECK_PROBE_DEFINE2(cred_check_setuid, "struct ucred *", "uid_t"); int diff --git a/sys/security/mac/mac_framework.h b/sys/security/mac/mac_framework.h index 644028bde47..99bb11be879 100644 --- a/sys/security/mac/mac_framework.h +++ b/sys/security/mac/mac_framework.h @@ -72,6 +72,7 @@ struct mbuf; struct mount; struct msg; struct msqid_kernel; +struct pipepair; struct proc; struct semid_kernel; struct shmfd; @@ -80,7 +81,6 @@ struct sockaddr; struct socket; struct sysctl_oid; struct sysctl_req; -struct pipepair; struct thread; struct timespec; struct ucred; @@ -115,6 +115,10 @@ int mac_cred_check_setaudit(struct ucred *cred, struct auditinfo *ai); int mac_cred_check_setaudit_addr(struct ucred *cred, struct auditinfo_addr *aia); int mac_cred_check_setauid(struct ucred *cred, uid_t auid); +void mac_cred_setcred_enter(void); +int mac_cred_check_setcred(u_int flags, const struct ucred *old_cred, + struct ucred *new_cred); +void mac_cred_setcred_exit(void); int mac_cred_check_setegid(struct ucred *cred, gid_t egid); int mac_cred_check_seteuid(struct ucred *cred, uid_t euid); int mac_cred_check_setgid(struct ucred *cred, gid_t gid); diff --git a/sys/security/mac/mac_policy.h b/sys/security/mac/mac_policy.h index 084684e5749..66e48906080 100644 --- a/sys/security/mac/mac_policy.h +++ b/sys/security/mac/mac_policy.h @@ -144,6 +144,10 @@ typedef int (*mpo_cred_check_setaudit_t)(struct ucred *cred, typedef int (*mpo_cred_check_setaudit_addr_t)(struct ucred *cred, struct auditinfo_addr *aia); typedef int (*mpo_cred_check_setauid_t)(struct ucred *cred, uid_t auid); +typedef void (*mpo_cred_setcred_enter_t)(void); +typedef int (*mpo_cred_check_setcred_t)(u_int flags, + const struct ucred *old_cred, struct ucred *new_cred); +typedef void (*mpo_cred_setcred_exit_t)(void); typedef int (*mpo_cred_check_setegid_t)(struct ucred *cred, gid_t egid); typedef int (*mpo_cred_check_seteuid_t)(struct ucred *cred, uid_t euid); typedef int (*mpo_cred_check_setgid_t)(struct ucred *cred, gid_t gid); @@ -720,6 +724,9 @@ struct mac_policy_ops { mpo_cred_check_setaudit_t mpo_cred_check_setaudit; mpo_cred_check_setaudit_addr_t mpo_cred_check_setaudit_addr; mpo_cred_check_setauid_t mpo_cred_check_setauid; + mpo_cred_setcred_enter_t mpo_cred_setcred_enter; + mpo_cred_check_setcred_t mpo_cred_check_setcred; + mpo_cred_setcred_exit_t mpo_cred_setcred_exit; mpo_cred_check_setuid_t mpo_cred_check_setuid; mpo_cred_check_seteuid_t mpo_cred_check_seteuid; mpo_cred_check_setgid_t mpo_cred_check_setgid; @@ -1033,8 +1040,9 @@ struct mac_policy_conf { * 3 7.x * 4 8.x * 5 14.x + * 6 15.x */ -#define MAC_VERSION 5 +#define MAC_VERSION 6 #define MAC_POLICY_SET(mpops, mpname, mpfullname, mpflags, privdata_wanted) \ static struct mac_policy_conf mpname##_mac_policy_conf = { \ diff --git a/sys/security/mac_stub/mac_stub.c b/sys/security/mac_stub/mac_stub.c index c602c639ec9..a3b0dd01a76 100644 --- a/sys/security/mac_stub/mac_stub.c +++ b/sys/security/mac_stub/mac_stub.c @@ -222,6 +222,23 @@ stub_cred_check_setauid(struct ucred *cred, uid_t auid) return (0); } +static void +stub_cred_setcred_enter(void) +{ +} + +static int +stub_cred_check_setcred(u_int flags, const struct ucred *old_cred, + struct ucred *new_cred) +{ + return (0); +} + +static void +stub_cred_setcred_exit(void) +{ +} + static int stub_cred_check_setegid(struct ucred *cred, gid_t egid) { @@ -1688,6 +1705,9 @@ static struct mac_policy_ops stub_ops = .mpo_cred_check_setaudit = stub_cred_check_setaudit, .mpo_cred_check_setaudit_addr = stub_cred_check_setaudit_addr, .mpo_cred_check_setauid = stub_cred_check_setauid, + .mpo_cred_setcred_enter = stub_cred_setcred_enter, + .mpo_cred_check_setcred = stub_cred_check_setcred, + .mpo_cred_setcred_exit = stub_cred_setcred_exit, .mpo_cred_check_setegid = stub_cred_check_setegid, .mpo_cred_check_seteuid = stub_cred_check_seteuid, .mpo_cred_check_setgid = stub_cred_check_setgid, diff --git a/sys/security/mac_test/mac_test.c b/sys/security/mac_test/mac_test.c index 7a6a76ce23c..890b8328055 100644 --- a/sys/security/mac_test/mac_test.c +++ b/sys/security/mac_test/mac_test.c @@ -257,6 +257,32 @@ test_cred_check_setauid(struct ucred *cred, uid_t auid) return (0); } +COUNTER_DECL(cred_setcred_enter); +static void +test_cred_setcred_enter(void) +{ + COUNTER_INC(cred_setcred_enter); +} + +COUNTER_DECL(cred_check_setcred); +static int +test_cred_check_setcred(u_int flags, const struct ucred *old_cred, + struct ucred *new_cred) +{ + LABEL_CHECK(old_cred->cr_label, MAGIC_CRED); + LABEL_CHECK(new_cred->cr_label, MAGIC_CRED); + COUNTER_INC(cred_check_setcred); + + return (0); +} + +COUNTER_DECL(cred_setcred_exit); +static void +test_cred_setcred_exit(void) +{ + COUNTER_INC(cred_setcred_exit); +} + COUNTER_DECL(cred_check_setegid); static int test_cred_check_setegid(struct ucred *cred, gid_t egid) @@ -3033,6 +3059,9 @@ static struct mac_policy_ops test_ops = .mpo_cred_check_setaudit = test_cred_check_setaudit, .mpo_cred_check_setaudit_addr = test_cred_check_setaudit_addr, .mpo_cred_check_setauid = test_cred_check_setauid, + .mpo_cred_setcred_enter = test_cred_setcred_enter, + .mpo_cred_check_setcred = test_cred_check_setcred, + .mpo_cred_setcred_exit = test_cred_setcred_exit, .mpo_cred_check_seteuid = test_cred_check_seteuid, .mpo_cred_check_setegid = test_cred_check_setegid, .mpo_cred_check_setgid = test_cred_check_setgid, diff --git a/sys/sys/priv.h b/sys/sys/priv.h index cfec8345a4b..0dec7842793 100644 --- a/sys/sys/priv.h +++ b/sys/sys/priv.h @@ -105,7 +105,8 @@ #define PRIV_CRED_SETRESGID 58 /* setresgid. */ #define PRIV_SEEOTHERGIDS 59 /* Exempt bsd.seeothergids. */ #define PRIV_SEEOTHERUIDS 60 /* Exempt bsd.seeotheruids. */ -#define PRIV_SEEJAILPROC 61 /* Exempt from bsd.see_jail_proc. */ +#define PRIV_SEEJAILPROC 61 /* Exempt from bsd.see_jail_proc. */ +#define PRIV_CRED_SETCRED 62 /* setcred. */ /* * Debugging privileges. diff --git a/sys/sys/syscall.h b/sys/sys/syscall.h index 2283d5bf999..2d5c9f2378e 100644 --- a/sys/sys/syscall.h +++ b/sys/sys/syscall.h @@ -526,4 +526,5 @@ #define SYS_timerfd_settime 587 #define SYS_kcmp 588 #define SYS_getrlimitusage 589 -#define SYS_MAXSYSCALL 590 +#define SYS_setcred 591 +#define SYS_MAXSYSCALL 592 diff --git a/sys/sys/syscall.mk b/sys/sys/syscall.mk index 901f8138456..a22d36937c1 100644 --- a/sys/sys/syscall.mk +++ b/sys/sys/syscall.mk @@ -431,4 +431,5 @@ MIASM = \ timerfd_gettime.o \ timerfd_settime.o \ kcmp.o \ - getrlimitusage.o + getrlimitusage.o \ + setcred.o diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h index 52d2035cc96..4450a28dc4d 100644 --- a/sys/sys/syscallsubr.h +++ b/sys/sys/syscallsubr.h @@ -313,6 +313,8 @@ int kern_select(struct thread *td, int nd, fd_set *fd_in, fd_set *fd_ou, 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_setcred(struct thread *const td, const u_int flags, + struct setcred *const wcred, gid_t *preallocated_groups); int kern_setgroups(struct thread *td, int *ngrpp, gid_t *groups); int kern_setitimer(struct thread *, u_int, struct itimerval *, struct itimerval *); diff --git a/sys/sys/sysproto.h b/sys/sys/sysproto.h index f96d541aa03..42de669977c 100644 --- a/sys/sys/sysproto.h +++ b/sys/sys/sysproto.h @@ -1888,6 +1888,11 @@ struct getrlimitusage_args { char flags_l_[PADL_(int)]; int flags; char flags_r_[PADR_(int)]; char res_l_[PADL_(rlim_t *)]; rlim_t * res; char res_r_[PADR_(rlim_t *)]; }; +struct setcred_args { + char flags_l_[PADL_(u_int)]; u_int flags; char flags_r_[PADR_(u_int)]; + char wcred_l_[PADL_(const struct setcred *)]; const struct setcred * wcred; char wcred_r_[PADR_(const struct setcred *)]; + char size_l_[PADL_(size_t)]; size_t size; char size_r_[PADR_(size_t)]; +}; int sys_exit(struct thread *, struct exit_args *); int sys_fork(struct thread *, struct fork_args *); int sys_read(struct thread *, struct read_args *); @@ -2290,6 +2295,7 @@ int sys_timerfd_gettime(struct thread *, struct timerfd_gettime_args *); int sys_timerfd_settime(struct thread *, struct timerfd_settime_args *); int sys_kcmp(struct thread *, struct kcmp_args *); int sys_getrlimitusage(struct thread *, struct getrlimitusage_args *); +int sys_setcred(struct thread *, struct setcred_args *); #ifdef COMPAT_43 @@ -3266,6 +3272,7 @@ int freebsd13_swapoff(struct thread *, struct freebsd13_swapoff_args *); #define SYS_AUE_timerfd_settime AUE_TIMERFD #define SYS_AUE_kcmp AUE_NULL #define SYS_AUE_getrlimitusage AUE_NULL +#define SYS_AUE_setcred AUE_SETCRED #undef PAD_ #undef PADL_ diff --git a/sys/sys/ucred.h b/sys/sys/ucred.h index 5aceb28e594..7df55488425 100644 --- a/sys/sys/ucred.h +++ b/sys/sys/ucred.h @@ -34,6 +34,7 @@ #ifndef _SYS_UCRED_H_ #define _SYS_UCRED_H_ +#include #if defined(_KERNEL) || defined(_WANT_UCRED) #include #include @@ -52,6 +53,10 @@ struct loginclass; */ #define CRED_SMALLGROUPS_NB 16 +struct label; +struct prison; +struct uidinfo; + /* * Credentials. * @@ -121,10 +126,73 @@ struct xucred { /* This can be used for both ucred and xucred structures. */ #define cr_gid cr_groups[0] +struct mac; +/* + * Structure to pass as an argument to the setcred() system call. + */ +struct setcred { + uid_t sc_uid; /* effective user id */ + uid_t sc_ruid; /* real user id */ + uid_t sc_svuid; /* saved user id */ + gid_t sc_gid; /* effective group id */ + gid_t sc_rgid; /* real group id */ + gid_t sc_svgid; /* saved group id */ + u_int sc_pad; /* see 32-bit compat structure */ + u_int sc_supp_groups_nb; /* number of supplementary groups */ + gid_t *sc_supp_groups; /* supplementary groups */ + struct mac *sc_label; /* MAC label */ +}; +/* + * Initializer for 'struct setcred' variables. + */ +#define SETCRED_INITIALIZER { -1, -1, -1, -1, -1, -1, 0, 0, NULL, NULL } + +/* + * Flags to setcred(). + */ +#define SETCREDF_UID (1u << 0) +#define SETCREDF_RUID (1u << 1) +#define SETCREDF_SVUID (1u << 2) +#define SETCREDF_GID (1u << 3) +#define SETCREDF_RGID (1u << 4) +#define SETCREDF_SVGID (1u << 5) +#define SETCREDF_SUPP_GROUPS (1u << 6) +#define SETCREDF_MAC_LABEL (1u << 7) + #ifdef _KERNEL -struct proc; +/* + * Masks of the currently valid flags to setcred(). + * + * Please consider reserving some of the high bits in the 'flags' argument for + * versioning when almost all of them are in use. + */ +#define SETCREDF_MASK (SETCREDF_UID | SETCREDF_RUID | SETCREDF_SVUID | \ + SETCREDF_GID | SETCREDF_RGID | SETCREDF_SVGID | SETCREDF_SUPP_GROUPS | \ + SETCREDF_MAC_LABEL) + +struct setcred32 { +#define setcred32_copy_start sc_uid + uid_t sc_uid; + uid_t sc_ruid; + uid_t sc_svuid; + gid_t sc_gid; + gid_t sc_rgid; + gid_t sc_svgid; + u_int sc_pad; + u_int sc_supp_groups_nb; +#define setcred32_copy_end sc_supp_groups + uint32_t sc_supp_groups; /* gid_t [*] */ + uint32_t sc_label; /* struct mac32 [*] */ +}; + struct thread; +/* Common native and 32-bit compatibility entry point. */ +int user_setcred(struct thread *td, const u_int flags, + const void *const uwcred, const size_t size, bool is_32bit); + +struct proc; + struct credbatch { struct ucred *cred; int users; @@ -185,6 +253,13 @@ group_is_primary(const gid_t gid, const struct ucred *const cred) bool group_is_supplementary(const gid_t gid, const struct ucred *const cred); bool groupmember(gid_t gid, const struct ucred *cred); bool realgroupmember(gid_t gid, const struct ucred *cred); + +#else /* !_KERNEL */ + +__BEGIN_DECLS +int setcred(u_int flags, const struct setcred *wcred, size_t size); +__END_DECLS + #endif /* _KERNEL */ #endif /* !_SYS_UCRED_H_ */