Index: include/pthread_np.h =================================================================== --- include/pthread_np.h +++ include/pthread_np.h @@ -71,6 +71,9 @@ int pthread_switch_add_np(pthread_switch_routine_t); int pthread_switch_delete_np(pthread_switch_routine_t); int pthread_timedjoin_np(pthread_t, void **, const struct timespec *); +int pthread_getcred_np(uid_t *, int *, gid_t *); +int pthread_setcred_np(uid_t , int , gid_t *); +int pthread_revertcred_np(void); __END_DECLS #endif Index: include/unistd.h =================================================================== --- include/unistd.h +++ include/unistd.h @@ -585,6 +585,9 @@ int undelete(const char *); int unwhiteout(const char *); void *valloc(size_t); /* obsoleted by malloc() */ +int setcred(uid_t, int, const gid_t *); +int getcred(uid_t *, int *, gid_t *); +int revertcred(void); #ifndef _OPTRESET_DECLARED #define _OPTRESET_DECLARED Index: lib/libc/include/namespace.h =================================================================== --- lib/libc/include/namespace.h +++ lib/libc/include/namespace.h @@ -138,6 +138,7 @@ #define pthread_getaffinity_np _pthread_getaffinity_np #define pthread_getconcurrency _pthread_getconcurrency #define pthread_getcpuclockid _pthread_getcpuclockid +#define pthread_getcred_np _pthread_getcred_np #define pthread_getprio _pthread_getprio #define pthread_getschedparam _pthread_getschedparam #define pthread_getspecific _pthread_getspecific @@ -172,6 +173,7 @@ #define pthread_once _pthread_once #define pthread_resume_all_np _pthread_resume_all_np #define pthread_resume_np _pthread_resume_np +#define pthread_revertcred_np _pthread_revertcred_np #define pthread_rwlock_destroy _pthread_rwlock_destroy #define pthread_rwlock_init _pthread_rwlock_init #define pthread_rwlock_rdlock _pthread_rwlock_rdlock @@ -191,6 +193,7 @@ #define pthread_setcancelstate _pthread_setcancelstate #define pthread_setcanceltype _pthread_setcanceltype #define pthread_setconcurrency _pthread_setconcurrency +#define pthread_setcred_np _pthread_setcred_np #define pthread_setprio _pthread_setprio #define pthread_setschedparam _pthread_setschedparam #define pthread_setspecific _pthread_setspecific Index: lib/libc/include/un-namespace.h =================================================================== --- lib/libc/include/un-namespace.h +++ lib/libc/include/un-namespace.h @@ -119,6 +119,7 @@ #undef pthread_getaffinity_np #undef pthread_getconcurrency #undef pthread_getcpuclockid +#undef pthread_getcred_np #undef pthread_getprio #undef pthread_getschedparam #undef pthread_getspecific @@ -153,6 +154,7 @@ #undef pthread_once #undef pthread_resume_all_np #undef pthread_resume_np +#undef pthread_revertcred_np #undef pthread_rwlock_destroy #undef pthread_rwlock_init #undef pthread_rwlock_rdlock @@ -172,6 +174,7 @@ #undef pthread_setcancelstate #undef pthread_setcanceltype #undef pthread_setconcurrency +#undef pthread_setcred_np #undef pthread_setprio #undef pthread_setschedparam #undef pthread_setspecific Index: lib/libc/sys/Makefile.inc =================================================================== --- lib/libc/sys/Makefile.inc +++ lib/libc/sys/Makefile.inc @@ -297,6 +297,7 @@ semget.2 \ semop.2 \ send.2 \ + setcred.2 \ setfib.2 \ sendfile.2 \ setgroups.2 \ @@ -463,6 +464,8 @@ select.2 FD_ZERO.3 MLINKS+=send.2 sendmsg.2 \ send.2 sendto.2 +MLINKS+=setcred.2 getcred.2 \ + setcred.2 revertcred.2 MLINKS+=setpgid.2 setpgrp.2 MLINKS+=setresuid.2 getresgid.2 \ setresuid.2 getresuid.2 \ Index: lib/libc/sys/Symbol.map =================================================================== --- lib/libc/sys/Symbol.map +++ lib/libc/sys/Symbol.map @@ -406,6 +406,9 @@ fhlinkat; fhreadlink; getfhat; + getcred; + setcred; + revertcred; }; FBSDprivate_1.0 { Index: lib/libc/sys/setcred.2 =================================================================== --- /dev/null +++ lib/libc/sys/setcred.2 @@ -0,0 +1,119 @@ +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.\" Copyright (c) 2019 Gandi +.\" +.\" 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 AUTHOR 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 AUTHOR 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. +.\" +.\" $FreeBSD$ +.\" +.Dd January 23, 2019 +.Dt SETCRED 2 +.Os +.Sh NAME +.Nm setcred , +.Nm getcred , +.Nm revertcred +.Nd control per-thread user credentials +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In unistd.h +.In sys/param.h +.Ft int +.Fn setcred "uid_t uid" "int gidsetlen" "const gid_t *gidset" +.Ft int +.Fn getcred "uid_t *uid" "int *gidsetlen" "const gid_t *gidset" +.Ft int +.Fn revertcred "void" +.Sh Description +The +.Fn setcred +system call sets the real, effective, and saved UIDs and GIDs, +along with supplementary group access list of the current thread. +.Pp +The +.Fa uid +parameter can be any UID. +The +.Fa gidsetlen +parameter indicates the number of entries in the array and must be no more +than NGROUPS. The +.Fa gidset +parameter is an array of GIDs, the first being the primary GID to set. +This call will replace all supplementary groups in the credential. +.Pp +The +.Fn getcred +system call retrieves the real user ID of the calling thread and stores it in +.Fa uid . +It also retrieves the current group access list of the calling thread and +stores it in +.Fa gidset . +The gidsetlen argument indicates the number of entries that may be placed in +gidset. +.Pp +The +.Fn getcred +system call sets this variable to the actual number of groups returned in +gidset, even on error. +.Pp +The +.Fn revertcred +system call reverts the thread's credential, including the real, effective and +saved UIDs and GIDs, to the per-process credential. +.Pp +These system calls may only be called if the process has super-user privileges. +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +The +.Fn revertcred +system call will only fail if: +.Bl -tag -width Er +.It Bq Er EPERM +The user is not the super user. +.El +.Pp +In addition to the error returned by +.Fn revertcred , +the +.Fn setcred +and +.Fn getcred +system calls may fail if: +.Bl -tag -width Er +.It Bq Er EINVAL +The value of +.Fa gidsetlen +is not valid. +.It Bq Er ENOENT +The per-thread credential has not yet been explicitly set with +.Fn setcred , +or it has been reverted with +.Fn revertcred . +.It Bq Er ERANGE +The incoming +.Fa gidsetlen +is less than the number of groups in the credential. +.It Bq Er EFAULT +One of the parameters points to an invalid address. +.El Index: lib/libthr/pthread.map =================================================================== --- lib/libthr/pthread.map +++ lib/libthr/pthread.map @@ -325,3 +325,9 @@ FBSD_1.5 { pthread_get_name_np; }; + +FBSD_1.6 { + pthread_getcred_np; + pthread_setcred_np; + pthread_revertcred_np; +}; Index: lib/libthr/thread/Makefile.inc =================================================================== --- lib/libthr/thread/Makefile.inc +++ lib/libthr/thread/Makefile.inc @@ -14,6 +14,7 @@ thr_cond.c \ thr_condattr.c \ thr_create.c \ + thr_cred_np.c \ thr_ctrdtr.c \ thr_detach.c \ thr_equal.c \ Index: lib/libthr/thread/thr_cred_np.c =================================================================== --- /dev/null +++ lib/libthr/thread/thr_cred_np.c @@ -0,0 +1,58 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2018 Gandi + * + * 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 AUTHOR 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 AUTHOR 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include +#include +#include +#include "un-namespace.h" + +__weak_reference(_pthread_getcred_np, pthread_getcred_np); +__weak_reference(_pthread_setcred_np, pthread_setcred_np); +__weak_reference(_pthread_revertcred_np, pthread_revertcred_np); + +int _pthread_getcred_np(uid_t *uid, int *gidsetlen, gid_t *gidset) +{ + + return (getcred(uid, gidsetlen, gidset)); +} + +int _pthread_setcred_np(uid_t uid, int gidsetlen, gid_t *gidset) +{ + + return (setcred(uid, gidsetlen, gidset)); +} + +int _pthread_revertcred_np(void) +{ + + return (revertcred()); +} Index: share/man/man3/Makefile =================================================================== --- share/man/man3/Makefile +++ share/man/man3/Makefile @@ -275,6 +275,7 @@ pthread_rwlock_wrlock.3 \ pthread_schedparam.3 \ pthread_self.3 \ + pthread_setcred_np.3 \ pthread_set_name_np.3 \ pthread_setspecific.3 \ pthread_sigmask.3 \ @@ -339,6 +340,8 @@ PTHREAD_MLINKS+=pthread_rwlock_wrlock.3 pthread_rwlock_trywrlock.3 PTHREAD_MLINKS+=pthread_schedparam.3 pthread_getschedparam.3 \ pthread_schedparam.3 pthread_setschedparam.3 +PTHREAD_MLINKS+=pthread_setcred_np.3 pthread_getcred_np.3 \ + pthread_setcred_np.3 pthread_revertcred_np.3 PTHREAD_MLINKS+=pthread_set_name_np.3 pthread_get_name_np.3 PTHREAD_MLINKS+=pthread_spin_init.3 pthread_spin_destroy.3 \ pthread_spin_lock.3 pthread_spin_trylock.3 \ Index: share/man/man3/pthread_setcred_np.3 =================================================================== --- /dev/null +++ share/man/man3/pthread_setcred_np.3 @@ -0,0 +1,124 @@ +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.\" Copyright (c) 2019 Gandi +.\" +.\" 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 AUTHOR 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 AUTHOR 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. +.\" +.\" $FreeBSD$ +.\" +.Dd January 23, 2019 +.Dt pthread_setcred_np 3 +.Os +.Sh NAME +.Nm pthread_setcred_np , +.Nm pthread_getcred_np , +.Nm pthread_revertcred_np +.Nd control per-thread user credentials +.Sh LIBRARY +.Lb libpthread +.Sh SYNOPSIS +.In pthread_np.h +.In sys/param.h +.Ft int +.Fn pthread_setcred_np "uid_t uid" "int gidsetlen" "const gid_t *gidset" +.Ft int +.Fn pthread_getcred_np "uid_t *uid" "int *gidsetlen" "const gid_t *gidset" +.Ft int +.Fn pthread_revertcred_np "void" +.Sh Description +The +.Fn pthread_setcred_np +function sets the real, effective, and saved UIDs and GIDs, +along with supplementary group access list of the current thread. +.Pp +The +.Fa uid +parameter can be any UID. The +.Fa gidsetlen +parameter indicates the number of entries in the array and must be no more +than NGROUPS. The +.Fa gidset +parameter is an array of GIDs, the first being the primary GID to set. +This call will replace all supplementary groups in the credential. +.Pp +The +.Fn pthread_getcred_np +function retrieves the real user ID of the calling thread and stores it in +.Fa uid . +It also retrieves the current group access list of the calling thread and +stores it in +.Fa gidset . +The gidsetlen argument indicates the number of entries that may be placed in +gidset. +.Pp +The +.Fn pthread_getcred_np +sets this variable to the actual number of groups returned in +gidset, even on error. +.Pp +The +.Fn pthread_revertcred_np +function reverts the thread's credential, including the real, effective and +saved UIDs and GIDs, to the per-process credential. +.Pp +These functions may only be called if the process has super-user privileges. +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +The +.Fn pthread_revertcred_np +function will only fail if: +.Bl -tag -width Er +.It Bq Er EPERM +The user is not the super user. +.El +.Pp +In addition to the error returned by +.Fn pthread_revertcred_np , +the +.Fn pthread_setcred_np +and +.Fn pthread_getcred_np +functions calls may fail if: +.Bl -tag -width Er +.It Bq Er EINVAL +The value of +.Fa gidsetlen +is not valid. +.It Bq Er ENOENT +The per-thread credential has not yet been explicitly set with +.Fn pthread_setcred_np , +or it has been reverted with +.Fn pthread_revertcred_np . +.It Bq Er ERANGE +The incoming +.Fa gidsetlen +is less than the number of groups in the credential. +.It Bq Er EFAULT +One of the parameters points to an invalid address. +.El +.Sh STANDARDS +These functions are non standard extensions. +.Sh SEE ALSO +.Xr setcred 2 , +.Xr getcred 2 , +.Xr revertcred 2 , Index: sys/bsm/audit_kevents.h =================================================================== --- sys/bsm/audit_kevents.h +++ sys/bsm/audit_kevents.h @@ -644,6 +644,9 @@ #define AUE_SETLOGINCLASS 43238 /* FreeBSD-specific. */ #define AUE_POSIX_FADVISE 43239 /* FreeBSD-specific. */ #define AUE_SCTP_GENERIC_SENDMSG_IOV 43240 /* FreeBSD-specific. */ +#define AUE_GETCRED 43263 /* FreeBSD-specific. */ +#define AUE_SETCRED 43264 /* FreeBSD-specific. */ +#define AUE_REVERTCRED 43265 /* FreeBSD-specific. */ /* * Darwin BSM uses a number of AUE_O_* definitions, which are aliased to the Index: sys/compat/freebsd32/syscalls.master =================================================================== --- sys/compat/freebsd32/syscalls.master +++ sys/compat/freebsd32/syscalls.master @@ -1145,5 +1145,10 @@ const char *to); } 567 AUE_NULL NOPROTO { int fhreadlink( struct fhandle *fhp, char *buf, \ size_t bufsize); } +568 AUE_SETCRED NOPROTO { int setcred( uid_t uid, int gidsetlen, \ + const gid_t *gidset); } +569 AUE_GETCRED NOPROTO { int getcred( uid_t *uid, int *gidsetlen, \ + gid_t *gidset); } +570 AUE_REVERTCRED NOPROTO { int revertcred(void); } ; vim: syntax=off Index: sys/ddb/db_ps.c =================================================================== --- sys/ddb/db_ps.c +++ sys/ddb/db_ps.c @@ -257,7 +257,11 @@ void *wchan; if (all) { - db_printf("%6d ", td->td_tid); + db_printf("%6d ", td->td_tid); + if ((td->td_flags & TDP_SUGID) != 0) + db_printf("%5d ", td->td_ucred->cr_ruid); + else + db_printf(" "); switch (td->td_state) { case TDS_RUNNING: snprintf(state, sizeof(state), "Run"); @@ -340,6 +344,7 @@ struct lock_object *lock; bool comma; int delta; + int i; /* Determine which thread to examine. */ if (have_addr) @@ -350,6 +355,15 @@ db_printf("Thread %d at %p:\n", td->td_tid, td); db_printf(" proc (pid %d): %p\n", td->td_proc->p_pid, td->td_proc); + if ((td->td_flags & TDP_SUGID) != 0) { + db_printf(" uid: %d gids: ", td->td_ucred->cr_uid); + for (i = 0; i < td->td_ucred->cr_ngroups; i++) { + db_printf("%d", td->td_ucred->cr_groups[i]); + if (i < (td->td_ucred->cr_ngroups - 1)) + db_printf(", "); + } + db_printf("\n"); + } if (td->td_name[0] != '\0') db_printf(" name: %s\n", td->td_name); db_printf(" stack: %p-%p\n", (void *)td->td_kstack, Index: sys/fs/unionfs/union_subr.c =================================================================== --- sys/fs/unionfs/union_subr.c +++ sys/fs/unionfs/union_subr.c @@ -772,7 +772,7 @@ */ chgproccnt(cred->cr_ruidinfo, 1, 0); change_euid(cred, rootinfo); - change_ruid(cred, rootinfo); + change_ruid(cred, rootinfo, 1); change_svuid(cred, (uid_t)0); uifree(rootinfo); cnp->cn_cred = cred; Index: sys/kern/kern_jail.c =================================================================== --- sys/kern/kern_jail.c +++ sys/kern/kern_jail.c @@ -2402,6 +2402,15 @@ oldcred = crcopysafe(p, newcred); newcred->cr_prison = pr; proc_set_cred(p, newcred); + FOREACH_THREAD_IN_PROC(p, td) { + thread_lock(td); + if (TD_IS_SUGID(td)) { + prison_free(td->td_ucred->cr_prison); + prison_hold(pr); + td->td_ucred->cr_prison = pr; + } + thread_unlock(td); + } setsugid(p); #ifdef RACCT racct_proc_ucred_changed(p, oldcred, newcred); Index: sys/kern/kern_loginclass.c =================================================================== --- sys/kern/kern_loginclass.c +++ sys/kern/kern_loginclass.c @@ -229,6 +229,15 @@ oldcred = crcopysafe(p, newcred); newcred->cr_loginclass = newlc; proc_set_cred(p, newcred); + FOREACH_THREAD_IN_PROC(p, td) { + thread_lock(td); + if (TD_IS_SUGID(td)) { + loginclass_free(td->td_ucred->cr_loginclass); + loginclass_hold(newlc); + td->td_ucred->cr_loginclass = newlc; + } + thread_unlock(td); + } #ifdef RACCT racct_proc_ucred_changed(p, oldcred, newcred); crhold(newcred); Index: sys/kern/kern_prot.c =================================================================== --- sys/kern/kern_prot.c +++ sys/kern/kern_prot.c @@ -87,6 +87,17 @@ static void crsetgroups_locked(struct ucred *cr, int ngrp, gid_t *groups); +static int kern_setresuid(struct thread *td, + struct ucred *newcred, struct ucred *oldcred, + uid_t ruid, uid_t euid, uid_t suid, + struct uidinfo *euip, struct uidinfo *ruip, + int chgproccnt); +static int kern_getgroups(struct thread *td, struct getgroups_args *uap, + struct ucred *cred); +static int kern_getresuid(struct thread *td, struct getresuid_args *uap, + struct ucred *cred); +static int kern_getresgid(struct thread *td, struct getresgid_args *uap, + struct ucred *cred); #ifndef _SYS_SYSPROTO_H_ struct getpid_args { @@ -219,10 +230,19 @@ sys_getuid(struct thread *td, struct getuid_args *uap) { - td->td_retval[0] = td->td_ucred->cr_ruid; + if (TD_IS_SUGID(td)) { + PROC_LOCK(td->td_proc); + td->td_retval[0] = td->td_proc->p_ucred->cr_ruid; #if defined(COMPAT_43) - td->td_retval[1] = td->td_ucred->cr_uid; + td->td_retval[1] = td->td_proc->p_ucred->cr_uid; #endif + PROC_UNLOCK(td->td_proc); + } else { + td->td_retval[0] = td->td_ucred->cr_ruid; +#if defined(COMPAT_43) + td->td_retval[1] = td->td_ucred->cr_uid; +#endif + } return (0); } @@ -236,7 +256,12 @@ sys_geteuid(struct thread *td, struct geteuid_args *uap) { - td->td_retval[0] = td->td_ucred->cr_uid; + if (TD_IS_SUGID(td)) { + PROC_LOCK(td->td_proc); + td->td_retval[0] = td->td_proc->p_ucred->cr_uid; + PROC_UNLOCK(td->td_proc); + } else + td->td_retval[0] = td->td_ucred->cr_uid; return (0); } @@ -250,10 +275,19 @@ sys_getgid(struct thread *td, struct getgid_args *uap) { - td->td_retval[0] = td->td_ucred->cr_rgid; + if (TD_IS_SUGID(td)) { + PROC_LOCK(td->td_proc); + td->td_retval[0] = td->td_proc->p_ucred->cr_rgid; +#if defined(COMPAT_43) + td->td_retval[1] = td->td_proc->p_ucred->cr_groups; +#endif + PROC_UNLOCK(td->td_proc); + } else { + td->td_retval[0] = td->td_ucred->cr_rgid; #if defined(COMPAT_43) - td->td_retval[1] = td->td_ucred->cr_groups[0]; + td->td_retval[1] = td->td_ucred->cr_groups; #endif + } return (0); } @@ -272,7 +306,12 @@ sys_getegid(struct thread *td, struct getegid_args *uap) { - td->td_retval[0] = td->td_ucred->cr_groups[0]; + if (TD_IS_SUGID(td)) { + PROC_LOCK(td->td_proc); + td->td_retval[0] = td->td_proc->p_ucred->cr_groups[0]; + PROC_UNLOCK(td->td_proc); + } else + td->td_retval[0] = td->td_ucred->cr_groups[0]; return (0); } @@ -285,11 +324,23 @@ int sys_getgroups(struct thread *td, struct getgroups_args *uap) { - struct ucred *cred; + int error; + + if (TD_IS_SUGID(td)) { + PROC_LOCK(td->td_proc); + error = kern_getgroups(td, uap, td->td_proc->p_ucred); + PROC_UNLOCK(td->td_proc); + } else + error = kern_getgroups(td, uap, td->td_ucred); + return (error); +} + +static int __always_inline +kern_getgroups(struct thread *td, struct getgroups_args *uap, struct ucred *cred) +{ u_int ngrp; int error; - cred = td->td_ucred; ngrp = cred->cr_ngroups; if (uap->gidsetsize == 0) { @@ -538,7 +589,7 @@ * Set the real uid and transfer proc count to new user. */ if (uid != oldcred->cr_ruid) { - change_ruid(newcred, uip); + change_ruid(newcred, uip, 1); setsugid(p); } /* @@ -792,6 +843,8 @@ int sys_setgroups(struct thread *td, struct setgroups_args *uap) { + struct ucred *newcred, *oldcred; + struct proc *p = td->td_proc; gid_t smallgroups[XU_NGROUPS]; gid_t *groups; u_int gidsetsize; @@ -806,38 +859,42 @@ else groups = smallgroups; + newcred = crget(); + crextend(newcred, gidsetsize); + PROC_LOCK(p); + oldcred = crcopysafe(p, newcred); error = copyin(uap->gidset, groups, gidsetsize * sizeof(gid_t)); + if (error != 0) + goto out; + error = kern_setgroups(td, newcred, oldcred, gidsetsize, groups); if (error == 0) - error = kern_setgroups(td, gidsetsize, groups); - + proc_set_cred(p, newcred); +out: + PROC_UNLOCK(p); + crfree(error ? newcred : oldcred); if (gidsetsize > XU_NGROUPS) free(groups, M_TEMP); return (error); } int -kern_setgroups(struct thread *td, u_int ngrp, gid_t *groups) +kern_setgroups(struct thread *td, struct ucred *newcred, struct ucred *oldcred, + u_int ngrp, gid_t *groups) { struct proc *p = td->td_proc; - struct ucred *newcred, *oldcred; int error; MPASS(ngrp <= ngroups_max + 1); AUDIT_ARG_GROUPSET(groups, ngrp); - newcred = crget(); - crextend(newcred, ngrp); - PROC_LOCK(p); - oldcred = crcopysafe(p, newcred); - #ifdef MAC error = mac_cred_check_setgroups(oldcred, ngrp, groups); if (error) - goto fail; + goto out; #endif error = priv_check_cred(oldcred, PRIV_CRED_SETGROUPS); if (error) - goto fail; + goto out; if (ngrp == 0) { /* @@ -851,14 +908,8 @@ crsetgroups_locked(newcred, ngrp, groups); } setsugid(p); - proc_set_cred(p, newcred); - PROC_UNLOCK(p); - crfree(oldcred); - return (0); -fail: - PROC_UNLOCK(p); - crfree(newcred); +out: return (error); } @@ -906,7 +957,7 @@ setsugid(p); } if (ruid != (uid_t)-1 && oldcred->cr_ruid != ruid) { - change_ruid(newcred, ruip); + change_ruid(newcred, ruip, 1); setsugid(p); } if ((ruid != (uid_t)-1 || newcred->cr_uid != newcred->cr_ruid) && @@ -1030,10 +1081,45 @@ PROC_LOCK(p); oldcred = crcopysafe(p, newcred); + error = kern_setresuid(td, newcred, oldcred, ruid, euid, suid, + euip, ruip, 1); + if (error != 0) + goto fail; + proc_set_cred(p, newcred); +#ifdef RACCT + racct_proc_ucred_changed(p, oldcred, newcred); + crhold(newcred); +#endif + PROC_UNLOCK(p); +#ifdef RCTL + rctl_proc_ucred_changed(p, newcred); + crfree(newcred); +#endif + uifree(ruip); + uifree(euip); + crfree(oldcred); + return (0); + +fail: + PROC_UNLOCK(p); + uifree(ruip); + uifree(euip); + crfree(newcred); + return (error); + +} + +static int __always_inline +kern_setresuid(struct thread *td, struct ucred *newcred, struct ucred *oldcred, + uid_t ruid, uid_t euid, uid_t suid, + struct uidinfo *euip, struct uidinfo *ruip, int chgproccnt) +{ + int error; + #ifdef MAC error = mac_cred_check_setresuid(oldcred, ruid, euid, suid); if (error) - goto fail; + return (error); #endif if (((ruid != (uid_t)-1 && ruid != oldcred->cr_ruid && @@ -1046,42 +1132,22 @@ suid != oldcred->cr_svuid && suid != oldcred->cr_uid)) && (error = priv_check_cred(oldcred, PRIV_CRED_SETRESUID)) != 0) - goto fail; + return (error); if (euid != (uid_t)-1 && oldcred->cr_uid != euid) { change_euid(newcred, euip); - setsugid(p); + setsugid(td->td_proc); } if (ruid != (uid_t)-1 && oldcred->cr_ruid != ruid) { - change_ruid(newcred, ruip); - setsugid(p); + change_ruid(newcred, ruip, chgproccnt); + setsugid(td->td_proc); } if (suid != (uid_t)-1 && oldcred->cr_svuid != suid) { change_svuid(newcred, suid); - setsugid(p); + setsugid(td->td_proc); } - proc_set_cred(p, newcred); -#ifdef RACCT - racct_proc_ucred_changed(p, oldcred, newcred); - crhold(newcred); -#endif - PROC_UNLOCK(p); -#ifdef RCTL - rctl_proc_ucred_changed(p, newcred); - crfree(newcred); -#endif - uifree(ruip); - uifree(euip); - crfree(oldcred); - return (0); - -fail: - PROC_UNLOCK(p); - uifree(ruip); - uifree(euip); - crfree(newcred); - return (error); + return (0); } /* @@ -1166,10 +1232,22 @@ int sys_getresuid(struct thread *td, struct getresuid_args *uap) { - struct ucred *cred; + int error; + + if (TD_IS_SUGID(td)) { + PROC_LOCK(td->td_proc); + error = kern_getresuid(td, uap, td->td_proc->p_ucred); + PROC_UNLOCK(td->td_proc); + } else + error = kern_getresuid(td, uap, td->td_ucred); + return (error); +} + +static int __always_inline +kern_getresuid(struct thread *td, struct getresuid_args *uap, struct ucred *cred) +{ int error1 = 0, error2 = 0, error3 = 0; - cred = td->td_ucred; if (uap->ruid) error1 = copyout(&cred->cr_ruid, uap->ruid, sizeof(cred->cr_ruid)); @@ -1193,10 +1271,22 @@ int sys_getresgid(struct thread *td, struct getresgid_args *uap) { - struct ucred *cred; + int error; + + if (TD_IS_SUGID(td)) { + PROC_LOCK(td->td_proc); + error = kern_getresgid(td, uap, td->td_proc->p_ucred); + PROC_UNLOCK(td->td_proc); + } else + error = kern_getresgid(td, uap, td->td_ucred); + return (error); +} + +static int __always_inline +kern_getresgid(struct thread *td, struct getresgid_args *uap, struct ucred *cred) +{ int error1 = 0, error2 = 0, error3 = 0; - cred = td->td_ucred; if (uap->rgid) error1 = copyout(&cred->cr_rgid, uap->rgid, sizeof(cred->cr_rgid)); @@ -2219,15 +2309,16 @@ * duration of the call. */ void -change_ruid(struct ucred *newcred, struct uidinfo *ruip) +change_ruid(struct ucred *newcred, struct uidinfo *ruip, int do_chgproccnt) { - - (void)chgproccnt(newcred->cr_ruidinfo, -1, 0); + if (do_chgproccnt != 0) + (void)chgproccnt(newcred->cr_ruidinfo, -1, 0); newcred->cr_ruid = ruip->ui_uid; uihold(ruip); uifree(newcred->cr_ruidinfo); newcred->cr_ruidinfo = ruip; - (void)chgproccnt(newcred->cr_ruidinfo, 1, 0); + if (do_chgproccnt != 0) + (void)chgproccnt(newcred->cr_ruidinfo, 1, 0); } /*- @@ -2268,3 +2359,129 @@ newcred->cr_svgid = svgid; } + +/* + * Set credentials. + */ +#ifndef _SYS_SYSPROTO_H_ +struct setcred_args { + uid_t uid; + int gidsetlen; + const gid_t *gidset; +}; +#endif +/* ARGSUSED */ +int +sys_setcred(struct thread *td, struct setcred_args *uap) +{ + struct ucred *newcred, *oldcred; + struct uidinfo *uip; + gid_t smallgroups[XU_NGROUPS]; + gid_t *groups; + int gidsetlen; + int error; + + gidsetlen = uap->gidsetlen; + if (gidsetlen > ngroups_max + 1) + return (EINVAL); + AUDIT_ARG_UID(uap->uid); + if (TD_IS_SUGID(td)) { + PROC_LOCK(td->td_proc); + error = priv_check_cred(td->td_proc->p_ucred, PRIV_CRED_SETUID); + PROC_UNLOCK(td->td_proc); + } else + error = priv_check_cred(td->td_ucred, PRIV_CRED_SETUID); + if (error != 0) + return (error); + oldcred = td->td_ucred; + newcred = crdup(oldcred); + crextend(newcred, gidsetlen); + uip = uifind(uap->uid); + if (gidsetlen > XU_NGROUPS) + groups = malloc(gidsetlen * sizeof(gid_t), M_TEMP, M_WAITOK); + else + groups = smallgroups; + error = copyin(uap->gidset, groups, gidsetlen * sizeof(gid_t)); + if (error != 0) + goto out; + + PROC_LOCK(td->td_proc); + error = kern_setresuid(td, newcred, td->td_proc->p_ucred, uap->uid, + uap->uid, uap->uid, uip, uip, 0); + if (error != 0) + goto unlock; + error = kern_setgroups(td, newcred, td->td_proc->p_ucred, gidsetlen, + groups); + if (error != 0) + goto unlock; + td->td_ucred = newcred; + td->td_flags |= TDP_SUGID; + +unlock: + PROC_UNLOCK(td->td_proc); +out: + if (gidsetlen > XU_NGROUPS) + free(groups, M_TEMP); + uifree(uip); + crfree(error ? newcred : oldcred); + return (error); +} + +/* + * Get credentials. + */ +#ifndef _SYS_SYSPROTO_H_ +struct getcred_args { + uid_t *uid; + int *gidsetlen; + gid_t *gidset; +}; +#endif +/* ARGSUSED */ +int +sys_getcred(struct thread *td, struct getcred_args *uap) +{ + struct ucred *cred; + int gidsetlen = 0; + int error; + const int zero = 0; + + error = copyin(uap->gidsetlen, &gidsetlen, sizeof(int)); + if (error != 0) + return (error); + error = copyout(&zero, uap->gidsetlen, sizeof(*uap->gidsetlen)); + if (error != 0) + return (error); + if (!TD_IS_SUGID(td)) + return (ENOENT); + PROC_LOCK(td->td_proc); + error = priv_check_cred(td->td_proc->p_ucred, PRIV_CRED_SETUID); + PROC_UNLOCK(td->td_proc); + if (error != 0) + return (error); + cred = td->td_ucred; + error = copyout(&cred->cr_ruid, uap->uid, sizeof(*uap->uid)); + if (gidsetlen < cred->cr_ngroups) + return (ERANGE); + error = copyout(cred->cr_groups, uap->gidset, + cred->cr_ngroups * sizeof(*uap->gidset)); + if (error != 0) + return (error); + error = copyout(&cred->cr_ngroups, uap->gidsetlen, + sizeof(*uap->gidsetlen)); + return (error); +} + +int +sys_revertcred(struct thread *td, struct revertcred_args *uap) +{ + int error; + + error = priv_check_cred(td->td_proc->p_ucred, PRIV_CRED_SETUID); + if (error != 0) + return (error); + /* We need to untaint the thread to restore the process credentials */ + td->td_flags &= ~TDP_SUGID; + thread_cow_update(td); + return (0); +} Index: sys/kern/kern_thread.c =================================================================== --- sys/kern/kern_thread.c +++ sys/kern/kern_thread.c @@ -488,7 +488,7 @@ oldcred = NULL; oldlimit = NULL; PROC_LOCK(p); - if (td->td_ucred != p->p_ucred) { + if (td->td_ucred != p->p_ucred && !TD_IS_SUGID(td)) { oldcred = td->td_ucred; td->td_ucred = crhold(p->p_ucred); } Index: sys/kern/sys_capability.c =================================================================== --- sys/kern/sys_capability.c +++ sys/kern/sys_capability.c @@ -113,6 +113,13 @@ oldcred = crcopysafe(p, newcred); newcred->cr_flags |= CRED_FLAG_CAPMODE; proc_set_cred(p, newcred); + FOREACH_THREAD_IN_PROC(p, td) { + thread_lock(td); + if (TD_IS_SUGID(td)) { + td->td_ucred->cr_flags |= CRED_FLAG_CAPMODE; + } + thread_unlock(td); + } PROC_UNLOCK(p); crfree(oldcred); return (0); Index: sys/kern/syscalls.master =================================================================== --- sys/kern/syscalls.master +++ sys/kern/syscalls.master @@ -3167,6 +3167,23 @@ size_t bufsize ); } +568 AUE_GETCRED STD { + int getcred( + _Out_ uid_t *uid, + _Inout_ int *gidsetlen, + _Out_writes_(*gidsetlen) gid_t *gidset + ); + } +569 AUE_SETCRED STD { + int setcred( + uid_t uid, + int gidsetlen, + _In_reads_(gidsetlen) const gid_t *gidset + ); + } +570 AUE_REVERTCRED STD { + int revertcred(void); + } ; Please copy any additions and changes to the following compatability tables: ; sys/compat/freebsd32/syscalls.master Index: sys/security/audit/audit_bsm.c =================================================================== --- sys/security/audit/audit_bsm.c +++ sys/security/audit/audit_bsm.c @@ -1466,6 +1466,29 @@ } break; + case AUE_SETCRED: + if (ARG_IS_VALID(kar, ARG_UID)) { + tok = au_to_arg32(1, "uid", ar->ar_arg_uid); + kau_write(rec, tok); + } + if (ARG_IS_VALID(kar, ARG_GROUPSET)) { + for(ctr = 0; ctr < ar->ar_arg_groups.gidset_size; ctr++) + { + tok = au_to_arg32(1, "gidset", + ar->ar_arg_groups.gidset[ctr]); + kau_write(rec, tok); + } + } + break; + + case AUE_GETCRED: + case AUE_REVERTCRED: + + /* + * Header, subject, and return tokens added at end. + */ + break; + case AUE_SETLOGIN: if (ARG_IS_VALID(kar, ARG_LOGIN)) { tok = au_to_text(ar->ar_arg_login); Index: sys/security/audit/audit_syscalls.c =================================================================== --- sys/security/audit/audit_syscalls.c +++ sys/security/audit/audit_syscalls.c @@ -494,6 +494,15 @@ newcred->cr_audit.ai_mask.am_failure = udata.au_aupinfo.ap_mask.am_failure; proc_set_cred(tp, newcred); + FOREACH_THREAD_IN_PROC(td->td_proc, td) { + thread_lock(td); + if (TD_IS_SUGID(td)) { + audit_cred_destroy(td->td_ucred); + audit_cred_copy(td->td_proc->p_ucred, + td->td_ucred); + } + thread_unlock(td); + } PROC_UNLOCK(tp); crfree(oldcred); break; @@ -633,6 +642,14 @@ goto fail; newcred->cr_audit.ai_auid = id; proc_set_cred(td->td_proc, newcred); + FOREACH_THREAD_IN_PROC(td->td_proc, td) { + thread_lock(td); + if (TD_IS_SUGID(td)) { + audit_cred_destroy(td->td_ucred); + audit_cred_copy(td->td_proc->p_ucred, td->td_ucred); + } + thread_unlock(td); + } PROC_UNLOCK(td->td_proc); crfree(oldcred); return (0); @@ -704,6 +721,14 @@ newcred->cr_audit.ai_termid.at_port = ai.ai_termid.port; newcred->cr_audit.ai_termid.at_type = AU_IPv4; proc_set_cred(td->td_proc, newcred); + FOREACH_THREAD_IN_PROC(td->td_proc, td) { + thread_lock(td); + if (TD_IS_SUGID(td)) { + audit_cred_destroy(td->td_ucred); + audit_cred_copy(td->td_proc->p_ucred, td->td_ucred); + } + thread_unlock(td); + } PROC_UNLOCK(td->td_proc); crfree(oldcred); return (0); @@ -761,6 +786,14 @@ goto fail; newcred->cr_audit = aia; proc_set_cred(td->td_proc, newcred); + FOREACH_THREAD_IN_PROC(td->td_proc, td) { + thread_lock(td); + if (TD_IS_SUGID(td)) { + audit_cred_destroy(td->td_ucred); + audit_cred_copy(td->td_proc->p_ucred, td->td_ucred); + } + thread_unlock(td); + } PROC_UNLOCK(td->td_proc); crfree(oldcred); return (0); Index: sys/security/mac/mac_syscalls.c =================================================================== --- sys/security/mac/mac_syscalls.c +++ sys/security/mac/mac_syscalls.c @@ -214,6 +214,13 @@ crcopy(newcred, oldcred); mac_cred_relabel(newcred, intlabel); proc_set_cred(p, newcred); + FOREACH_THREAD_IN_PROC(p, td) { + thread_lock(td); + if (TD_IS_SUGID(td)) { + mac_cred_relabel(td->td_ucred, intlabel); + } + thread_unlock(td); + } PROC_UNLOCK(p); crfree(oldcred); Index: sys/security/mac_lomac/mac_lomac.c =================================================================== --- sys/security/mac_lomac/mac_lomac.c +++ sys/security/mac_lomac/mac_lomac.c @@ -2256,6 +2256,14 @@ crhold(newcred); lomac_copy(&subj->mac_lomac, SLOT(newcred->cr_label)); proc_set_cred(p, newcred); + FOREACH_THREAD_IN_PROC(p, td) { + thread_lock(td); + if (TD_IS_SUGID(td)) { + lomac_copy(&subj->mac_lomac, + SLOT(td->td_ucred->cr_label)); + } + thread_unlock(td); + } crfree(oldcred); dodrop = 1; out: Index: sys/sys/proc.h =================================================================== --- sys/sys/proc.h +++ sys/sys/proc.h @@ -492,6 +492,7 @@ #define TDP_UIOHELD 0x10000000 /* Current uio has pages held in td_ma */ #define TDP_FORKING 0x20000000 /* Thread is being created through fork() */ #define TDP_EXECVMSPC 0x40000000 /* Execve destroyed old vmspace */ +#define TDP_SUGID 0x80000000 /* Thread is tainted by setcred(2) */ /* * Reasons that the current thread can not be run yet. @@ -514,7 +515,8 @@ #define TD_CAN_RUN(td) ((td)->td_state == TDS_CAN_RUN) #define TD_IS_INHIBITED(td) ((td)->td_state == TDS_INHIBITED) #define TD_ON_UPILOCK(td) ((td)->td_flags & TDF_UPIBLOCKED) -#define TD_IS_IDLETHREAD(td) ((td)->td_flags & TDF_IDLETD) +#define TD_IS_IDLETHREAD(td) ((td)->td_flags & TDF_IDLETD) +#define TD_IS_SUGID(td) ((td)->td_pflags & TDP_SUGID) #define KTDSTATE(td) \ (((td)->td_inhibitors & TDI_SLEEPING) != 0 ? "sleep" : \ Index: sys/sys/syscallsubr.h =================================================================== --- sys/sys/syscallsubr.h +++ sys/sys/syscallsubr.h @@ -238,7 +238,8 @@ 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, u_int ngrp, gid_t *groups); +int kern_setgroups(struct thread *td, struct ucred *newcred, + struct ucred *oldcred, u_int ngrp, gid_t *groups); int kern_setitimer(struct thread *, u_int, struct itimerval *, struct itimerval *); int kern_setrlimit(struct thread *, u_int, struct rlimit *); Index: sys/sys/ucred.h =================================================================== --- sys/sys/ucred.h +++ sys/sys/ucred.h @@ -101,7 +101,7 @@ void change_egid(struct ucred *newcred, gid_t egid); void change_euid(struct ucred *newcred, struct uidinfo *euip); void change_rgid(struct ucred *newcred, gid_t rgid); -void change_ruid(struct ucred *newcred, struct uidinfo *ruip); +void change_ruid(struct ucred *newcred, struct uidinfo *ruip, int do_chgproccnt); void change_svgid(struct ucred *newcred, gid_t svgid); void change_svuid(struct ucred *newcred, uid_t svuid); void crcopy(struct ucred *dest, struct ucred *src);