diff options
Diffstat (limited to 'sys/kern/kern_prot.c')
| -rw-r--r-- | sys/kern/kern_prot.c | 90 |
1 files changed, 70 insertions, 20 deletions
diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c index 4ce571db20fd..8c21107068f5 100644 --- a/sys/kern/kern_prot.c +++ b/sys/kern/kern_prot.c @@ -53,10 +53,13 @@ #include <sys/proc.h> #include <sys/malloc.h> #include <sys/pioctl.h> +#include <sys/resourcevar.h> #include <sys/sysctl.h> static MALLOC_DEFINE(M_CRED, "cred", "credentials"); +static void change_ruid(struct proc *p, uid_t ruid); + #ifndef _SYS_SYSPROTO_H_ struct getpid_args { int dummy; @@ -427,18 +430,16 @@ setuid(p, uap) #endif { /* - * Transfer proc count to new user. + * Set the real uid and transfer proc count to new user. */ if (uid != pc->p_ruid) { - (void)chgproccnt(pc->p_ruid, -1, 0); - (void)chgproccnt(uid, 1, 0); + change_ruid(p, uid); + setsugid(p); } /* * Set real uid */ if (uid != pc->p_ruid) { - pc->p_ruid = uid; - setsugid(p); } /* * Set saved uid @@ -458,8 +459,7 @@ setuid(p, uap) * Copy credentials so other references do not see our changes. */ if (pc->pc_ucred->cr_uid != uid) { - pc->pc_ucred = crcopy(pc->pc_ucred); - pc->pc_ucred->cr_uid = uid; + change_euid(p, uid); setsugid(p); } return (0); @@ -490,8 +490,7 @@ seteuid(p, uap) * not see our changes. */ if (pc->pc_ucred->cr_uid != euid) { - pc->pc_ucred = crcopy(pc->pc_ucred); - pc->pc_ucred->cr_uid = euid; + change_euid(p, euid); setsugid(p); } return (0); @@ -674,14 +673,11 @@ setreuid(p, uap) return (error); if (euid != (uid_t)-1 && pc->pc_ucred->cr_uid != euid) { - pc->pc_ucred = crcopy(pc->pc_ucred); - pc->pc_ucred->cr_uid = euid; + change_euid(p, euid); setsugid(p); } if (ruid != (uid_t)-1 && pc->p_ruid != ruid) { - (void)chgproccnt(pc->p_ruid, -1, 0); - (void)chgproccnt(ruid, 1, 0); - pc->p_ruid = ruid; + change_ruid(p, ruid); setsugid(p); } if ((ruid != (uid_t)-1 || pc->pc_ucred->cr_uid != pc->p_ruid) && @@ -767,14 +763,11 @@ setresuid(p, uap) (error = suser_xxx(0, p, PRISON_ROOT)) != 0) return (error); if (euid != (uid_t)-1 && pc->pc_ucred->cr_uid != euid) { - pc->pc_ucred = crcopy(pc->pc_ucred); - pc->pc_ucred->cr_uid = euid; + change_euid(p, euid); setsugid(p); } if (ruid != (uid_t)-1 && pc->p_ruid != ruid) { - (void)chgproccnt(pc->p_ruid, -1, 0); - (void)chgproccnt(ruid, 1, 0); - pc->p_ruid = ruid; + change_ruid(p, ruid); setsugid(p); } if (suid != (uid_t)-1 && pc->p_svuid != suid) { @@ -1157,8 +1150,16 @@ void crfree(cr) struct ucred *cr; { - if (--cr->cr_ref == 0) + if (--cr->cr_ref == 0) { + /* + * Some callers of crget(), such as nfs_statfs(), + * allocate a temporary credential, but don't + * allocate a uidinfo structure. + */ + if (cr->cr_uidinfo != NULL) + uifree(cr->cr_uidinfo); FREE((caddr_t)cr, M_CRED); + } } /* @@ -1174,6 +1175,7 @@ crcopy(cr) return (cr); newcr = crget(); *newcr = *cr; + uihold(newcr->cr_uidinfo); crfree(cr); newcr->cr_ref = 1; return (newcr); @@ -1190,6 +1192,7 @@ crdup(cr) newcr = crget(); *newcr = *cr; + uihold(newcr->cr_uidinfo); newcr->cr_ref = 1; return (newcr); } @@ -1253,3 +1256,50 @@ setsugid(p) if (!(p->p_pfsflags & PF_ISUGID)) p->p_stops = 0; } + +/* + * Helper function to change the effective uid of a process + */ +void +change_euid(p, euid) + struct proc *p; + uid_t euid; +{ + struct pcred *pc; + struct uidinfo *uip; + + pc = p->p_cred; + /* + * crcopy is essentially a NOP if ucred has a reference count + * of 1, which is true if it has already been copied. + */ + pc->pc_ucred = crcopy(pc->pc_ucred); + uip = pc->pc_ucred->cr_uidinfo; + pc->pc_ucred->cr_uid = euid; + pc->pc_ucred->cr_uidinfo = uifind(euid); + uifree(uip); +} + +/* + * Helper function to change the real uid of a process + * + * The per-uid process count for this process is transfered from + * the old uid to the new uid. + */ +static void +change_ruid(p, ruid) + struct proc *p; + uid_t ruid; +{ + struct pcred *pc; + struct uidinfo *uip; + + pc = p->p_cred; + (void)chgproccnt(pc->p_uidinfo, -1, 0); + uip = pc->p_uidinfo; + /* It is assumed that pcred is not shared between processes */ + pc->p_ruid = ruid; + pc->p_uidinfo = uifind(ruid); + (void)chgproccnt(pc->p_uidinfo, 1, 0); + uifree(uip); +} |
