diff options
author | rwatson <rwatson@FreeBSD.org> | 2000-08-30 04:49:09 +0000 |
---|---|---|
committer | rwatson <rwatson@FreeBSD.org> | 2000-08-30 04:49:09 +0000 |
commit | 3dc6d2b9ea10286037cd4515ed45fda5febc40b1 (patch) | |
tree | e3a19c7ec35fd1d6bb7787fde34f55c068367f74 /sys/kern | |
parent | 496b1356ffbfd16fcaefb3e6e21904cd576650f2 (diff) | |
download | FreeBSD-src-3dc6d2b9ea10286037cd4515ed45fda5febc40b1.zip FreeBSD-src-3dc6d2b9ea10286037cd4515ed45fda5febc40b1.tar.gz |
o Centralize inter-process access control, introducing:
int p_can(p1, p2, operation, privused)
which allows specification of subject process, object process,
inter-process operation, and an optional call-by-reference privused
flag, allowing the caller to determine if privilege was required
for the call to succeed. This allows jail, kern.ps_showallprocs and
regular credential-based interaction checks to occur in one block of
code. Possible operations are P_CAN_SEE, P_CAN_SCHED, P_CAN_KILL,
and P_CAN_DEBUG. p_can currently breaks out as a wrapper to a
series of static function checks in kern_prot, which should not
be invoked directly.
o Commented out capabilities entries are included for some checks.
o Update most inter-process authorization to make use of p_can() instead
of manual checks, PRISON_CHECK(), P_TRESPASS(), and
kern.ps_showallprocs.
o Modify suser{,_xxx} to use const arguments, as it no longer modifies
process flags due to the disabling of ASU.
o Modify some checks/errors in procfs so that ENOENT is returned instead
of ESRCH, further improving concealment of processes that should not
be visible to other processes. Also introduce new access checks to
improve hiding of processes for procfs_lookup(), procfs_getattr(),
procfs_readdir(). Correct a bug reported by bp concerning not
handling the CREATE case in procfs_lookup(). Remove volatile flag in
procfs that caused apparently spurious qualifier warnigns (approved by
bde).
o Add comment noting that ktrace() has not been updated, as its access
control checks are different from ptrace(), whereas they should
probably be the same. Further discussion should happen on this topic.
Reviewed by: bde, green, phk, freebsd-security, others
Approved by: bde
Obtained from: TrustedBSD Project
Diffstat (limited to 'sys/kern')
-rw-r--r-- | sys/kern/kern_event.c | 2 | ||||
-rw-r--r-- | sys/kern/kern_ktrace.c | 2 | ||||
-rw-r--r-- | sys/kern/kern_proc.c | 12 | ||||
-rw-r--r-- | sys/kern/kern_prot.c | 152 | ||||
-rw-r--r-- | sys/kern/kern_resource.c | 21 | ||||
-rw-r--r-- | sys/kern/kern_sig.c | 2 | ||||
-rw-r--r-- | sys/kern/sys_process.c | 14 |
7 files changed, 167 insertions, 38 deletions
diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c index 74d5fa7..0444d7e 100644 --- a/sys/kern/kern_event.c +++ b/sys/kern/kern_event.c @@ -200,7 +200,7 @@ filt_procattach(struct knote *kn) p = pfind(kn->kn_id); if (p == NULL) return (ESRCH); - if (! PRISON_CHECK(curproc, p)) + if (p_can(curproc, p, P_CAN_SEE, NULL)) return (EACCES); kn->kn_ptr.p_proc = p; diff --git a/sys/kern/kern_ktrace.c b/sys/kern/kern_ktrace.c index b0530f9..ef82c6c 100644 --- a/sys/kern/kern_ktrace.c +++ b/sys/kern/kern_ktrace.c @@ -513,6 +513,8 @@ ktrwrite(vp, kth, uio) * root previously set the tracing status on the target process, and * so, only root may further change it. * + * XXX: These checks are stronger than for ptrace() + * * TODO: check groups. use caller effective gid. */ static int diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index 4a4282a..ebff074 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -55,7 +55,7 @@ MALLOC_DEFINE(M_SESSION, "session", "session header"); static MALLOC_DEFINE(M_PROC, "proc", "Proc structures"); MALLOC_DEFINE(M_SUBPROC, "subproc", "Proc sub-structures"); -static int ps_showallprocs = 1; +int ps_showallprocs = 1; SYSCTL_INT(_kern, OID_AUTO, ps_showallprocs, CTLFLAG_RW, &ps_showallprocs, 0, ""); @@ -586,7 +586,7 @@ sysctl_kern_proc(SYSCTL_HANDLER_ARGS) p = pfind((pid_t)name[0]); if (!p) return (0); - if (!PRISON_CHECK(curproc, p)) + if (p_can(curproc, p, P_CAN_SEE, NULL)) return (0); error = sysctl_out_proc(p, req, 0); return (error); @@ -611,9 +611,9 @@ sysctl_kern_proc(SYSCTL_HANDLER_ARGS) p = LIST_FIRST(&zombproc); for (; p != 0; p = LIST_NEXT(p, p_list)) { /* - * Show a user only their processes. + * Show a user only appropriate processes. */ - if ((!ps_showallprocs) && p_trespass(curproc, p)) + if (p_can(curproc, p, P_CAN_SEE, NULL)) continue; /* * Skip embryonic processes. @@ -655,7 +655,7 @@ sysctl_kern_proc(SYSCTL_HANDLER_ARGS) break; } - if (!PRISON_CHECK(curproc, p)) + if (p_can(curproc, p, P_CAN_SEE, NULL)) continue; error = sysctl_out_proc(p, req, doingzomb); @@ -688,7 +688,7 @@ sysctl_kern_proc_args(SYSCTL_HANDLER_ARGS) if (!p) return (0); - if ((!ps_argsopen) && p_trespass(curproc, p)) + if ((!ps_argsopen) && p_can(curproc, p, P_CAN_SEE, NULL)) return (0); if (req->newptr && curproc != p) diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c index 33007e7..0750466 100644 --- a/sys/kern/kern_prot.c +++ b/sys/kern/kern_prot.c @@ -945,15 +945,15 @@ SYSCTL_INT(_kern, OID_AUTO, suser_permitted, CTLFLAG_RW, &suser_permitted, 0, */ int suser(p) - struct proc *p; + const struct proc *p; { return suser_xxx(0, p, 0); } int suser_xxx(cred, proc, flag) - struct ucred *cred; - struct proc *proc; + const struct ucred *cred; + const struct proc *proc; int flag; { if (!suser_permitted) @@ -971,31 +971,165 @@ suser_xxx(cred, proc, flag) return (0); } -/* - * Return zero if p1 can fondle p2, return errno (EPERM/ESRCH) otherwise. - */ +static int +p_cansee(const struct proc *p1, const struct proc *p2, int *privused) +{ -int -p_trespass(struct proc *p1, struct proc *p2) + if (privused != NULL) + *privused = 0; + + if (!PRISON_CHECK(p1, p2)) + return (ESRCH); + + if (!ps_showallprocs && (p1->p_ucred->cr_uid != p2->p_ucred->cr_uid) && + suser_xxx(NULL, p1, PRISON_ROOT)) + return (ESRCH); + + return (0); +} + +static int +p_cankill(const struct proc *p1, const struct proc *p2, int *privused) +{ + + if (privused != NULL) + *privused = 0; + + if (p1 == p2) + return (0); + + if (!PRISON_CHECK(p1, p2)) + return (ESRCH); + + if (p1->p_cred->p_ruid == p2->p_cred->p_ruid) + return (0); + if (p1->p_ucred->cr_uid == p2->p_cred->p_ruid) + return (0); + /* + * XXX should a process be able to affect another process + * acting as the same uid (i.e., a userland nfsd or the like?) + */ + if (p1->p_cred->p_ruid == p2->p_ucred->cr_uid) + return (0); + if (p1->p_ucred->cr_uid == p2->p_ucred->cr_uid) + return (0); + + if (!suser_xxx(0, p1, PRISON_ROOT)) { + if (privused != NULL) + *privused = 1; + return (0); + } + +#ifdef CAPABILITIES + if (!cap_check_xxx(0, p1, CAP_KILL, PRISON_ROOT)) { + if (privused != NULL) + *privused = 1; + return (0); + } +#endif + + return (EPERM); +} + +static int +p_cansched(const struct proc *p1, const struct proc *p2, int *privused) { + if (privused != NULL) + *privused = 0; + if (p1 == p2) return (0); + if (!PRISON_CHECK(p1, p2)) return (ESRCH); + if (p1->p_cred->p_ruid == p2->p_cred->p_ruid) return (0); if (p1->p_ucred->cr_uid == p2->p_cred->p_ruid) return (0); + /* + * XXX should a process be able to affect another process + * acting as the same uid (i.e., a userland nfsd or the like?) + */ if (p1->p_cred->p_ruid == p2->p_ucred->cr_uid) return (0); if (p1->p_ucred->cr_uid == p2->p_ucred->cr_uid) return (0); - if (!suser_xxx(0, p1, PRISON_ROOT)) + + if (!suser_xxx(0, p1, PRISON_ROOT)) { + if (privused != NULL) + *privused = 1; + return (0); + } + +#ifdef CAPABILITIES + if (!cap_check_xxx(0, p1, CAP_SYS_NICE, PRISON_ROOT)) { + if (privused != NULL) + *privused = 1; return (0); + } +#endif + return (EPERM); } +static int +p_candebug(const struct proc *p1, const struct proc *p2, int *privused) +{ + int error; + + if (privused != NULL) + *privused = 0; + + /* XXX it is authorized, but semantics don't permit it */ + if (p1 == p2) + return (0); + + if (!PRISON_CHECK(p1, p2)) + return (ESRCH); + + /* not owned by you, has done setuid (unless you're root) */ + /* add a CAP_SYS_PTRACE here? */ + if ((p1->p_cred->p_ruid != p2->p_cred->p_ruid) || + (p2->p_flag & P_SUGID)) { + if ((error = suser_xxx(0, p1, PRISON_ROOT))) + return (error); + if (privused != NULL) + *privused = 1; + } + + /* can't trace init when securelevel > 0 */ + if (securelevel > 0 && p2->p_pid == 1) + return (EPERM); + + return (0); +} + +int +p_can(const struct proc *p1, const struct proc *p2, int operation, + int *privused) +{ + + switch(operation) { + case P_CAN_SEE: + return (p_cansee(p1, p2, privused)); + + case P_CAN_KILL: + return (p_cankill(p1, p2, privused)); + + case P_CAN_SCHED: + return (p_cansched(p1, p2, privused)); + + case P_CAN_DEBUG: + return (p_candebug(p1, p2, privused)); + + default: + panic("p_can: invalid operation"); + } +} + + /* * Allocate a zeroed cred structure. */ diff --git a/sys/kern/kern_resource.c b/sys/kern/kern_resource.c index 5af09c9..b3c2ec6 100644 --- a/sys/kern/kern_resource.c +++ b/sys/kern/kern_resource.c @@ -88,7 +88,7 @@ getpriority(curp, uap) p = pfind(uap->who); if (p == 0) break; - if (!PRISON_CHECK(curp, p)) + if (p_can(curp, p, P_CAN_SEE, NULL)) break; low = p->p_nice; break; @@ -101,7 +101,7 @@ getpriority(curp, uap) else if ((pg = pgfind(uap->who)) == NULL) break; LIST_FOREACH(p, &pg->pg_members, p_pglist) { - if ((PRISON_CHECK(curp, p) && p->p_nice < low)) + if (!p_can(curp, p, P_CAN_SEE, NULL) && p->p_nice < low) low = p->p_nice; } break; @@ -111,7 +111,7 @@ getpriority(curp, uap) if (uap->who == 0) uap->who = curp->p_ucred->cr_uid; LIST_FOREACH(p, &allproc, p_list) - if (PRISON_CHECK(curp, p) && + if (!p_can(curp, p, P_CAN_SEE, NULL) && p->p_ucred->cr_uid == uap->who && p->p_nice < low) low = p->p_nice; @@ -151,7 +151,7 @@ setpriority(curp, uap) p = pfind(uap->who); if (p == 0) break; - if (!PRISON_CHECK(curp, p)) + if (p_can(curp, p, P_CAN_SEE, NULL)) break; error = donice(curp, p, uap->prio); found++; @@ -165,7 +165,7 @@ setpriority(curp, uap) else if ((pg = pgfind(uap->who)) == NULL) break; LIST_FOREACH(p, &pg->pg_members, p_pglist) { - if (PRISON_CHECK(curp, p)) { + if (!p_can(curp, p, P_CAN_SEE, NULL)) { error = donice(curp, p, uap->prio); found++; } @@ -178,7 +178,7 @@ setpriority(curp, uap) uap->who = curp->p_ucred->cr_uid; LIST_FOREACH(p, &allproc, p_list) if (p->p_ucred->cr_uid == uap->who && - PRISON_CHECK(curp, p)) { + !p_can(curp, p, P_CAN_SEE, NULL)) { error = donice(curp, p, uap->prio); found++; } @@ -197,9 +197,10 @@ donice(curp, chgp, n) register struct proc *curp, *chgp; register int n; { + int error; - if (p_trespass(curp, chgp) != 0) - return (EPERM); + if ((error = p_can(curp, chgp, P_CAN_SCHED, NULL))) + return (error); if (n > PRIO_MAX) n = PRIO_MAX; if (n < PRIO_MIN) @@ -250,8 +251,8 @@ rtprio(curp, uap) case RTP_LOOKUP: return (copyout(&p->p_rtprio, uap->rtp, sizeof(struct rtprio))); case RTP_SET: - if (p_trespass(curp, p) != 0) - return (EPERM); + if ((error = p_can(curp, p, P_CAN_SCHED, NULL))) + return (error); /* disallow setting rtprio in most cases if not superuser */ if (suser(curp) != 0) { /* can't set someone else's */ diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index db3f46f..a2ff2ef 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -97,7 +97,7 @@ SYSCTL_INT(_kern, KERN_LOGSIGEXIT, logsigexit, CTLFLAG_RW, * Can process p, with pcred pc, send the signal sig to process q? */ #define CANSIGNAL(p, q, sig) \ - (!p_trespass(p, q) || \ + (!p_can(p, q, P_CAN_KILL, NULL) || \ ((sig) == SIGCONT && (q)->p_session == (p)->p_session)) /* diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c index 4740476..0292961 100644 --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -217,7 +217,7 @@ ptrace(curp, uap) if ((p = pfind(uap->pid)) == NULL) return ESRCH; } - if (!PRISON_CHECK(curp, p)) + if (p_can(curp, p, P_CAN_SEE, NULL)) return (ESRCH); /* @@ -237,16 +237,8 @@ ptrace(curp, uap) if (p->p_flag & P_TRACED) return EBUSY; - /* not owned by you, has done setuid (unless you're root) */ - if ((p->p_cred->p_ruid != curp->p_cred->p_ruid) || - (p->p_flag & P_SUGID)) { - if ((error = suser(curp)) != 0) - return error; - } - - /* can't trace init when securelevel > 0 */ - if (securelevel > 0 && p->p_pid == 1) - return EPERM; + if ((error = p_can(curp, p, P_CAN_DEBUG, NULL))) + return error; /* OK */ break; |