Changeset View
Standalone View
sys/kern/kern_prot.c
Show First 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | |||||
#include <security/mac/mac_framework.h> | #include <security/mac/mac_framework.h> | ||||
static MALLOC_DEFINE(M_CRED, "cred", "credentials"); | static MALLOC_DEFINE(M_CRED, "cred", "credentials"); | ||||
SYSCTL_NODE(_security, OID_AUTO, bsd, CTLFLAG_RW, 0, "BSD security policy"); | SYSCTL_NODE(_security, OID_AUTO, bsd, CTLFLAG_RW, 0, "BSD security policy"); | ||||
static void crsetgroups_locked(struct ucred *cr, int ngrp, | static void crsetgroups_locked(struct ucred *cr, int ngrp, | ||||
gid_t *groups); | 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_ | #ifndef _SYS_SYSPROTO_H_ | ||||
struct getpid_args { | struct getpid_args { | ||||
int dummy; | int dummy; | ||||
}; | }; | ||||
#endif | #endif | ||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
int | int | ||||
▲ Show 20 Lines • Show All 116 Lines • ▼ Show 20 Lines | struct getuid_args { | ||||
int dummy; | int dummy; | ||||
}; | }; | ||||
#endif | #endif | ||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
int | int | ||||
sys_getuid(struct thread *td, struct getuid_args *uap) | sys_getuid(struct thread *td, struct getuid_args *uap) | ||||
{ | { | ||||
if (TD_IS_SUGID(td)) { | |||||
PROC_LOCK(td->td_proc); | |||||
td->td_retval[0] = td->td_proc->p_ucred->cr_ruid; | |||||
rwatson: Is this change safe without holding the process lock? Historically, p_ucred hasn't been a… | |||||
#if defined(COMPAT_43) | |||||
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; | td->td_retval[0] = td->td_ucred->cr_ruid; | ||||
#if defined(COMPAT_43) | #if defined(COMPAT_43) | ||||
td->td_retval[1] = td->td_ucred->cr_uid; | td->td_retval[1] = td->td_ucred->cr_uid; | ||||
#endif | #endif | ||||
} | |||||
return (0); | return (0); | ||||
} | } | ||||
#ifndef _SYS_SYSPROTO_H_ | #ifndef _SYS_SYSPROTO_H_ | ||||
struct geteuid_args { | struct geteuid_args { | ||||
int dummy; | int dummy; | ||||
}; | }; | ||||
#endif | #endif | ||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
int | int | ||||
sys_geteuid(struct thread *td, struct geteuid_args *uap) | sys_geteuid(struct thread *td, struct geteuid_args *uap) | ||||
{ | { | ||||
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; | td->td_retval[0] = td->td_ucred->cr_uid; | ||||
Not Done Inline ActionsIs this change safe without holding the process lock? Historically, p_ucred hasn't been a stable pointer without it. rwatson: Is this change safe without holding the process lock? Historically, p_ucred hasn't been a… | |||||
return (0); | return (0); | ||||
} | } | ||||
#ifndef _SYS_SYSPROTO_H_ | #ifndef _SYS_SYSPROTO_H_ | ||||
struct getgid_args { | struct getgid_args { | ||||
int dummy; | int dummy; | ||||
}; | }; | ||||
#endif | #endif | ||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
int | int | ||||
sys_getgid(struct thread *td, struct getgid_args *uap) | sys_getgid(struct thread *td, struct getgid_args *uap) | ||||
{ | { | ||||
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; | td->td_retval[0] = td->td_ucred->cr_rgid; | ||||
Not Done Inline ActionsIs this change safe without holding the process lock? Historically, p_ucred hasn't been a stable pointer without it. rwatson: Is this change safe without holding the process lock? Historically, p_ucred hasn't been a… | |||||
#if defined(COMPAT_43) | #if defined(COMPAT_43) | ||||
td->td_retval[1] = td->td_ucred->cr_groups[0]; | td->td_retval[1] = td->td_ucred->cr_groups; | ||||
#endif | #endif | ||||
} | |||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Get effective group ID. The "egid" is groups[0], and could be obtained | * 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 | * via getgroups. This syscall exists because it is somewhat painful to do | ||||
* correctly in a library function. | * correctly in a library function. | ||||
*/ | */ | ||||
#ifndef _SYS_SYSPROTO_H_ | #ifndef _SYS_SYSPROTO_H_ | ||||
struct getegid_args { | struct getegid_args { | ||||
int dummy; | int dummy; | ||||
}; | }; | ||||
#endif | #endif | ||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
int | int | ||||
sys_getegid(struct thread *td, struct getegid_args *uap) | sys_getegid(struct thread *td, struct getegid_args *uap) | ||||
{ | { | ||||
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]; | td->td_retval[0] = td->td_ucred->cr_groups[0]; | ||||
Not Done Inline ActionsIs this change safe without holding the process lock? Historically, p_ucred hasn't been a stable pointer without it. rwatson: Is this change safe without holding the process lock? Historically, p_ucred hasn't been a… | |||||
return (0); | return (0); | ||||
} | } | ||||
#ifndef _SYS_SYSPROTO_H_ | #ifndef _SYS_SYSPROTO_H_ | ||||
struct getgroups_args { | struct getgroups_args { | ||||
u_int gidsetsize; | u_int gidsetsize; | ||||
gid_t *gidset; | gid_t *gidset; | ||||
}; | }; | ||||
#endif | #endif | ||||
int | int | ||||
sys_getgroups(struct thread *td, struct getgroups_args *uap) | 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); | |||||
Done Inline ActionsThis cannot be legit. kern_getgroups performs copyout which can block indefinitely. Unfortunately the current routines don't provide any warning even with debug enabled. I may add something to change it. There are probably other places with the same problem. mjg: This cannot be legit. kern_getgroups performs copyout which can block indefinitely. | |||||
} 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; | u_int ngrp; | ||||
int error; | int error; | ||||
cred = td->td_ucred; | |||||
ngrp = cred->cr_ngroups; | ngrp = cred->cr_ngroups; | ||||
Not Done Inline ActionsIs this change safe without holding the process lock? Historically, p_ucred hasn't been a stable pointer without it. rwatson: Is this change safe without holding the process lock? Historically, p_ucred hasn't been a… | |||||
if (uap->gidsetsize == 0) { | if (uap->gidsetsize == 0) { | ||||
error = 0; | error = 0; | ||||
goto out; | goto out; | ||||
} | } | ||||
if (uap->gidsetsize < ngrp) | if (uap->gidsetsize < ngrp) | ||||
return (EINVAL); | return (EINVAL); | ||||
▲ Show 20 Lines • Show All 231 Lines • ▼ Show 20 Lines | #endif | ||||
/* We are using privs. */ | /* We are using privs. */ | ||||
priv_check_cred(oldcred, PRIV_CRED_SETUID) == 0) | priv_check_cred(oldcred, PRIV_CRED_SETUID) == 0) | ||||
#endif | #endif | ||||
{ | { | ||||
/* | /* | ||||
* Set the real uid and transfer proc count to new user. | * Set the real uid and transfer proc count to new user. | ||||
*/ | */ | ||||
if (uid != oldcred->cr_ruid) { | if (uid != oldcred->cr_ruid) { | ||||
change_ruid(newcred, uip); | change_ruid(newcred, uip, 1); | ||||
setsugid(p); | setsugid(p); | ||||
} | } | ||||
/* | /* | ||||
* Set saved uid | * Set saved uid | ||||
* | * | ||||
* XXX always set saved uid even if not _POSIX_SAVED_IDS, as | * XXX always set saved uid even if not _POSIX_SAVED_IDS, as | ||||
* the security of seteuid() depends on it. B.4.2.2 says it | * the security of seteuid() depends on it. B.4.2.2 says it | ||||
* is important that we should do this. | * is important that we should do this. | ||||
▲ Show 20 Lines • Show All 237 Lines • ▼ Show 20 Lines | struct setgroups_args { | ||||
u_int gidsetsize; | u_int gidsetsize; | ||||
gid_t *gidset; | gid_t *gidset; | ||||
}; | }; | ||||
#endif | #endif | ||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
int | int | ||||
sys_setgroups(struct thread *td, struct setgroups_args *uap) | 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 smallgroups[XU_NGROUPS]; | ||||
gid_t *groups; | gid_t *groups; | ||||
u_int gidsetsize; | u_int gidsetsize; | ||||
int error; | int error; | ||||
gidsetsize = uap->gidsetsize; | gidsetsize = uap->gidsetsize; | ||||
if (gidsetsize > ngroups_max + 1) | if (gidsetsize > ngroups_max + 1) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (gidsetsize > XU_NGROUPS) | if (gidsetsize > XU_NGROUPS) | ||||
groups = malloc(gidsetsize * sizeof(gid_t), M_TEMP, M_WAITOK); | groups = malloc(gidsetsize * sizeof(gid_t), M_TEMP, M_WAITOK); | ||||
else | else | ||||
groups = smallgroups; | groups = smallgroups; | ||||
newcred = crget(); | |||||
crextend(newcred, gidsetsize); | |||||
PROC_LOCK(p); | |||||
oldcred = crcopysafe(p, newcred); | |||||
error = copyin(uap->gidset, groups, gidsetsize * sizeof(gid_t)); | 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) | 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) | if (gidsetsize > XU_NGROUPS) | ||||
free(groups, M_TEMP); | free(groups, M_TEMP); | ||||
return (error); | return (error); | ||||
} | } | ||||
int | 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) | |||||
Not Done Inline ActionsIt's very iffy to have a kern_ prefixed function expect a locked process. More importantly though, since such an expectation is now in place, it should assert that it holds. mjg: It's very iffy to have a kern_ prefixed function expect a locked process. More importantly… | |||||
Done Inline ActionsI can add an assert, I could also suffix with '_locked' if that helps jack_gandi.net: I can add an assert, I could also suffix with '_locked' if that helps | |||||
{ | { | ||||
struct proc *p = td->td_proc; | struct proc *p = td->td_proc; | ||||
struct ucred *newcred, *oldcred; | |||||
int error; | int error; | ||||
MPASS(ngrp <= ngroups_max + 1); | MPASS(ngrp <= ngroups_max + 1); | ||||
AUDIT_ARG_GROUPSET(groups, ngrp); | AUDIT_ARG_GROUPSET(groups, ngrp); | ||||
newcred = crget(); | |||||
crextend(newcred, ngrp); | |||||
PROC_LOCK(p); | |||||
oldcred = crcopysafe(p, newcred); | |||||
#ifdef MAC | #ifdef MAC | ||||
error = mac_cred_check_setgroups(oldcred, ngrp, groups); | error = mac_cred_check_setgroups(oldcred, ngrp, groups); | ||||
if (error) | if (error) | ||||
goto fail; | goto out; | ||||
#endif | #endif | ||||
error = priv_check_cred(oldcred, PRIV_CRED_SETGROUPS); | error = priv_check_cred(oldcred, PRIV_CRED_SETGROUPS); | ||||
if (error) | if (error) | ||||
goto fail; | goto out; | ||||
if (ngrp == 0) { | if (ngrp == 0) { | ||||
/* | /* | ||||
* setgroups(0, NULL) is a legitimate way of clearing the | * setgroups(0, NULL) is a legitimate way of clearing the | ||||
* groups vector on non-BSD systems (which generally do not | * groups vector on non-BSD systems (which generally do not | ||||
* have the egid in the groups[0]). We risk security holes | * have the egid in the groups[0]). We risk security holes | ||||
* when running non-BSD software if we do not do the same. | * when running non-BSD software if we do not do the same. | ||||
*/ | */ | ||||
newcred->cr_ngroups = 1; | newcred->cr_ngroups = 1; | ||||
} else { | } else { | ||||
crsetgroups_locked(newcred, ngrp, groups); | crsetgroups_locked(newcred, ngrp, groups); | ||||
} | } | ||||
setsugid(p); | setsugid(p); | ||||
proc_set_cred(p, newcred); | |||||
PROC_UNLOCK(p); | |||||
crfree(oldcred); | |||||
return (0); | |||||
fail: | out: | ||||
PROC_UNLOCK(p); | |||||
crfree(newcred); | |||||
return (error); | return (error); | ||||
} | } | ||||
#ifndef _SYS_SYSPROTO_H_ | #ifndef _SYS_SYSPROTO_H_ | ||||
struct setreuid_args { | struct setreuid_args { | ||||
uid_t ruid; | uid_t ruid; | ||||
uid_t euid; | uid_t euid; | ||||
}; | }; | ||||
Show All 31 Lines | if (((ruid != (uid_t)-1 && ruid != oldcred->cr_ruid && | ||||
(error = priv_check_cred(oldcred, PRIV_CRED_SETREUID)) != 0) | (error = priv_check_cred(oldcred, PRIV_CRED_SETREUID)) != 0) | ||||
goto fail; | goto fail; | ||||
if (euid != (uid_t)-1 && oldcred->cr_uid != euid) { | if (euid != (uid_t)-1 && oldcred->cr_uid != euid) { | ||||
change_euid(newcred, euip); | change_euid(newcred, euip); | ||||
setsugid(p); | setsugid(p); | ||||
} | } | ||||
if (ruid != (uid_t)-1 && oldcred->cr_ruid != ruid) { | if (ruid != (uid_t)-1 && oldcred->cr_ruid != ruid) { | ||||
change_ruid(newcred, ruip); | change_ruid(newcred, ruip, 1); | ||||
setsugid(p); | setsugid(p); | ||||
} | } | ||||
if ((ruid != (uid_t)-1 || newcred->cr_uid != newcred->cr_ruid) && | if ((ruid != (uid_t)-1 || newcred->cr_uid != newcred->cr_ruid) && | ||||
newcred->cr_svuid != newcred->cr_uid) { | newcred->cr_svuid != newcred->cr_uid) { | ||||
change_svuid(newcred, newcred->cr_uid); | change_svuid(newcred, newcred->cr_uid); | ||||
setsugid(p); | setsugid(p); | ||||
} | } | ||||
proc_set_cred(p, newcred); | proc_set_cred(p, newcred); | ||||
▲ Show 20 Lines • Show All 107 Lines • ▼ Show 20 Lines | sys_setresuid(struct thread *td, struct setresuid_args *uap) | ||||
AUDIT_ARG_RUID(ruid); | AUDIT_ARG_RUID(ruid); | ||||
AUDIT_ARG_SUID(suid); | AUDIT_ARG_SUID(suid); | ||||
newcred = crget(); | newcred = crget(); | ||||
euip = uifind(euid); | euip = uifind(euid); | ||||
ruip = uifind(ruid); | ruip = uifind(ruid); | ||||
PROC_LOCK(p); | PROC_LOCK(p); | ||||
oldcred = crcopysafe(p, newcred); | 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 | #ifdef MAC | ||||
error = mac_cred_check_setresuid(oldcred, ruid, euid, suid); | error = mac_cred_check_setresuid(oldcred, ruid, euid, suid); | ||||
if (error) | if (error) | ||||
goto fail; | return (error); | ||||
#endif | #endif | ||||
if (((ruid != (uid_t)-1 && ruid != oldcred->cr_ruid && | if (((ruid != (uid_t)-1 && ruid != oldcred->cr_ruid && | ||||
ruid != oldcred->cr_svuid && | ruid != oldcred->cr_svuid && | ||||
ruid != oldcred->cr_uid) || | ruid != oldcred->cr_uid) || | ||||
(euid != (uid_t)-1 && euid != oldcred->cr_ruid && | (euid != (uid_t)-1 && euid != oldcred->cr_ruid && | ||||
euid != oldcred->cr_svuid && | euid != oldcred->cr_svuid && | ||||
euid != oldcred->cr_uid) || | euid != oldcred->cr_uid) || | ||||
(suid != (uid_t)-1 && suid != oldcred->cr_ruid && | (suid != (uid_t)-1 && suid != oldcred->cr_ruid && | ||||
suid != oldcred->cr_svuid && | suid != oldcred->cr_svuid && | ||||
suid != oldcred->cr_uid)) && | suid != oldcred->cr_uid)) && | ||||
(error = priv_check_cred(oldcred, PRIV_CRED_SETRESUID)) != 0) | (error = priv_check_cred(oldcred, PRIV_CRED_SETRESUID)) != 0) | ||||
goto fail; | return (error); | ||||
if (euid != (uid_t)-1 && oldcred->cr_uid != euid) { | if (euid != (uid_t)-1 && oldcred->cr_uid != euid) { | ||||
change_euid(newcred, euip); | change_euid(newcred, euip); | ||||
setsugid(p); | setsugid(td->td_proc); | ||||
} | } | ||||
if (ruid != (uid_t)-1 && oldcred->cr_ruid != ruid) { | if (ruid != (uid_t)-1 && oldcred->cr_ruid != ruid) { | ||||
change_ruid(newcred, ruip); | change_ruid(newcred, ruip, chgproccnt); | ||||
setsugid(p); | setsugid(td->td_proc); | ||||
} | } | ||||
if (suid != (uid_t)-1 && oldcred->cr_svuid != suid) { | if (suid != (uid_t)-1 && oldcred->cr_svuid != suid) { | ||||
change_svuid(newcred, 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: | return (0); | ||||
PROC_UNLOCK(p); | |||||
uifree(ruip); | |||||
uifree(euip); | |||||
crfree(newcred); | |||||
return (error); | |||||
} | } | ||||
/* | /* | ||||
* setresgid(rgid, egid, sgid) is like setregid except control over the saved | * setresgid(rgid, egid, sgid) is like setregid except control over the saved | ||||
* gid is explicit. | * gid is explicit. | ||||
*/ | */ | ||||
#ifndef _SYS_SYSPROTO_H_ | #ifndef _SYS_SYSPROTO_H_ | ||||
struct setresgid_args { | struct setresgid_args { | ||||
▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | struct getresuid_args { | ||||
uid_t *euid; | uid_t *euid; | ||||
uid_t *suid; | uid_t *suid; | ||||
}; | }; | ||||
#endif | #endif | ||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
int | int | ||||
sys_getresuid(struct thread *td, struct getresuid_args *uap) | 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; | int error1 = 0, error2 = 0, error3 = 0; | ||||
cred = td->td_ucred; | |||||
if (uap->ruid) | if (uap->ruid) | ||||
Not Done Inline ActionsIs this change safe without holding the process lock? Historically, p_ucred hasn't been a stable pointer without it. rwatson: Is this change safe without holding the process lock? Historically, p_ucred hasn't been a… | |||||
error1 = copyout(&cred->cr_ruid, | error1 = copyout(&cred->cr_ruid, | ||||
uap->ruid, sizeof(cred->cr_ruid)); | uap->ruid, sizeof(cred->cr_ruid)); | ||||
if (uap->euid) | if (uap->euid) | ||||
error2 = copyout(&cred->cr_uid, | error2 = copyout(&cred->cr_uid, | ||||
uap->euid, sizeof(cred->cr_uid)); | uap->euid, sizeof(cred->cr_uid)); | ||||
if (uap->suid) | if (uap->suid) | ||||
error3 = copyout(&cred->cr_svuid, | error3 = copyout(&cred->cr_svuid, | ||||
uap->suid, sizeof(cred->cr_svuid)); | uap->suid, sizeof(cred->cr_svuid)); | ||||
return (error1 ? error1 : error2 ? error2 : error3); | return (error1 ? error1 : error2 ? error2 : error3); | ||||
} | } | ||||
#ifndef _SYS_SYSPROTO_H_ | #ifndef _SYS_SYSPROTO_H_ | ||||
struct getresgid_args { | struct getresgid_args { | ||||
gid_t *rgid; | gid_t *rgid; | ||||
gid_t *egid; | gid_t *egid; | ||||
gid_t *sgid; | gid_t *sgid; | ||||
}; | }; | ||||
#endif | #endif | ||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
int | int | ||||
sys_getresgid(struct thread *td, struct getresgid_args *uap) | 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; | int error1 = 0, error2 = 0, error3 = 0; | ||||
cred = td->td_ucred; | |||||
if (uap->rgid) | if (uap->rgid) | ||||
Not Done Inline ActionsIs this change safe without holding the process lock? Historically, p_ucred hasn't been a stable pointer without it. rwatson: Is this change safe without holding the process lock? Historically, p_ucred hasn't been a… | |||||
error1 = copyout(&cred->cr_rgid, | error1 = copyout(&cred->cr_rgid, | ||||
uap->rgid, sizeof(cred->cr_rgid)); | uap->rgid, sizeof(cred->cr_rgid)); | ||||
if (uap->egid) | if (uap->egid) | ||||
error2 = copyout(&cred->cr_groups[0], | error2 = copyout(&cred->cr_groups[0], | ||||
uap->egid, sizeof(cred->cr_groups[0])); | uap->egid, sizeof(cred->cr_groups[0])); | ||||
if (uap->sgid) | if (uap->sgid) | ||||
error3 = copyout(&cred->cr_svgid, | error3 = copyout(&cred->cr_svgid, | ||||
uap->sgid, sizeof(cred->cr_svgid)); | uap->sgid, sizeof(cred->cr_svgid)); | ||||
▲ Show 20 Lines • Show All 1,005 Lines • ▼ Show 20 Lines | |||||
* Change a process's real uid. | * Change a process's real uid. | ||||
* Side effects: newcred->cr_ruid will be updated, newcred->cr_ruidinfo | * Side effects: newcred->cr_ruid will be updated, newcred->cr_ruidinfo | ||||
* will be updated, and the old and new cr_ruidinfo proc | * will be updated, and the old and new cr_ruidinfo proc | ||||
* counts will be updated. | * counts will be updated. | ||||
* References: newcred must be an exclusive credential reference for the | * References: newcred must be an exclusive credential reference for the | ||||
* duration of the call. | * duration of the call. | ||||
*/ | */ | ||||
void | void | ||||
change_ruid(struct ucred *newcred, struct uidinfo *ruip) | change_ruid(struct ucred *newcred, struct uidinfo *ruip, int do_chgproccnt) | ||||
{ | { | ||||
if (do_chgproccnt != 0) | |||||
(void)chgproccnt(newcred->cr_ruidinfo, -1, 0); | (void)chgproccnt(newcred->cr_ruidinfo, -1, 0); | ||||
newcred->cr_ruid = ruip->ui_uid; | newcred->cr_ruid = ruip->ui_uid; | ||||
uihold(ruip); | uihold(ruip); | ||||
uifree(newcred->cr_ruidinfo); | uifree(newcred->cr_ruidinfo); | ||||
newcred->cr_ruidinfo = ruip; | newcred->cr_ruidinfo = ruip; | ||||
if (do_chgproccnt != 0) | |||||
(void)chgproccnt(newcred->cr_ruidinfo, 1, 0); | (void)chgproccnt(newcred->cr_ruidinfo, 1, 0); | ||||
} | } | ||||
/*- | /*- | ||||
* Change a process's real gid. | * Change a process's real gid. | ||||
* Side effects: newcred->cr_rgid will be updated. | * Side effects: newcred->cr_rgid will be updated. | ||||
* References: newcred must be an exclusive credential reference for the | * References: newcred must be an exclusive credential reference for the | ||||
* duration of the call. | * duration of the call. | ||||
*/ | */ | ||||
Show All 23 Lines | |||||
* References: newcred must be an exclusive credential reference for the | * References: newcred must be an exclusive credential reference for the | ||||
* duration of the call. | * duration of the call. | ||||
*/ | */ | ||||
void | void | ||||
change_svgid(struct ucred *newcred, gid_t svgid) | change_svgid(struct ucred *newcred, gid_t svgid) | ||||
{ | { | ||||
newcred->cr_svgid = svgid; | 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)) { | |||||
Not Done Inline ActionsIs this change safe without holding the process lock? Historically, p_ucred hasn't been a stable pointer without it. rwatson: Is this change safe without holding the process lock? Historically, p_ucred hasn't been a… | |||||
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; | |||||
Not Done Inline ActionsIs this change safe without holding the process lock? Historically, p_ucred hasn't been a stable pointer without it. rwatson: Is this change safe without holding the process lock? Historically, p_ucred hasn't been a… | |||||
int gidsetlen = 0; | |||||
int error; | |||||
const int zero = 0; | |||||
error = copyin(uap->gidsetlen, &gidsetlen, sizeof(int)); | |||||
if (error != 0) | |||||
Done Inline ActionsPersonal style thing, I'd make this sizeof(*uap->uid) so the size is the declared size of the destination. brooks: Personal style thing, I'd make this `sizeof(*uap->uid)` so the size is the declared size of the… | |||||
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); | |||||
Done Inline Actionselse not needed. brooks: `else` not needed. | |||||
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)); | |||||
Not Done Inline ActionsIs this change safe without holding the process lock? Historically, p_ucred hasn't been a stable pointer without it. rwatson: Is this change safe without holding the process lock? Historically, p_ucred hasn't been a… | |||||
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); | |||||
} | } |
Is this change safe without holding the process lock? Historically, p_ucred hasn't been a stable pointer without it.