1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813 |
- /* $OpenBSD: systrace.c,v 1.75 2015/03/14 03:38:46 jsg Exp $ */
- /*
- * Copyright 2002 Niels Provos <provos@citi.umich.edu>
- * All rights reserved.
- *
- * 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.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Niels Provos.
- * 4. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 <sys/param.h>
- #include <sys/systm.h>
- #include <sys/tree.h>
- #include <sys/malloc.h>
- #include <sys/syscall.h>
- #include <sys/vnode.h>
- #include <sys/errno.h>
- #include <sys/device.h>
- #include <sys/proc.h>
- #include <sys/file.h>
- #include <sys/filedesc.h>
- #include <sys/filio.h>
- #include <sys/signalvar.h>
- #include <sys/rwlock.h>
- #include <sys/pool.h>
- #include <sys/mount.h>
- #include <sys/namei.h>
- #include <sys/poll.h>
- #include <sys/ptrace.h>
- #include <compat/common/compat_util.h>
- #include <dev/systrace.h>
- void systraceattach(int);
- int systraceopen(dev_t, int, int, struct proc *);
- int systraceclose(dev_t, int, int, struct proc *);
- int systraceioctl(dev_t, u_long, caddr_t, int, struct proc *);
- int systracef_read(struct file *, off_t *, struct uio *, struct ucred *);
- int systracef_write(struct file *, off_t *, struct uio *, struct ucred *);
- int systracef_ioctl(struct file *, u_long, caddr_t, struct proc *p);
- int systracef_poll(struct file *, int, struct proc *);
- int systracef_kqfilter(struct file *, struct knote *);
- int systracef_stat(struct file *, struct stat *, struct proc *);
- int systracef_close(struct file *, struct proc *);
- struct str_policy {
- TAILQ_ENTRY(str_policy) next;
- int nr;
- struct emul *emul; /* Is only valid for this emulation */
- int refcount;
- int nsysent;
- u_char *sysent;
- };
- struct str_inject {
- caddr_t kaddr;
- caddr_t uaddr;
- size_t len;
- };
- #define STR_PROC_ONQUEUE 0x01
- #define STR_PROC_WAITANSWER 0x02
- #define STR_PROC_SYSCALLRES 0x04
- #define STR_PROC_REPORT 0x08 /* Report emulation */
- #define STR_PROC_NEEDSEQNR 0x10 /* Answer must quote seqnr */
- #define STR_PROC_SETEUID 0x20 /* Elevate privileges */
- #define STR_PROC_SETEGID 0x40
- struct str_process {
- TAILQ_ENTRY(str_process) next;
- TAILQ_ENTRY(str_process) msg_next;
- struct str_process *firstmsg;
- struct proc *proc;
- pid_t pid;
- struct fsystrace *parent;
- struct str_policy *policy;
- struct systrace_replace *replace;
- char *fname[SYSTR_MAXFNAME];
- size_t nfname;
- int flags;
- short answer;
- short error;
- u_int16_t seqnr; /* expected reply sequence number */
- uid_t seteuid;
- gid_t setegid;
- int isscript;
- char scriptname[MAXPATHLEN];
- struct str_message msg;
- caddr_t sg;
- struct str_inject injects[SYSTR_MAXINJECTS];
- int injectind;
- };
- struct rwlock systrace_lck;
- static __inline void
- systrace_lock(void)
- {
- rw_enter_write(&systrace_lck);
- }
- static __inline void
- systrace_unlock(void)
- {
- rw_exit_write(&systrace_lck);
- }
- /* Needs to be called with fst locked */
- int systrace_attach(struct fsystrace *, pid_t);
- int systrace_detach(struct str_process *);
- int systrace_answer(struct str_process *, struct systrace_answer *);
- int systrace_setscriptname(struct str_process *,
- struct systrace_scriptname *);
- int systrace_prepinject(struct str_process *, struct systrace_inject *);
- int systrace_inject(struct str_process *, int);
- int systrace_io(struct str_process *, struct systrace_io *);
- int systrace_policy(struct fsystrace *, struct systrace_policy *);
- int systrace_preprepl(struct str_process *, struct systrace_replace *);
- int systrace_replace(struct str_process *, size_t, register_t []);
- int systrace_getcwd(struct fsystrace *, struct str_process *, int);
- int systrace_restorecwd(struct fsystrace *, struct proc *);
- int systrace_fname(struct str_process *, caddr_t, size_t);
- void systrace_replacefree(struct str_process *);
- int systrace_processready(struct str_process *);
- struct proc *systrace_find(struct str_process *);
- struct str_process *systrace_findpid(struct fsystrace *fst, pid_t pid);
- void systrace_wakeup(struct fsystrace *);
- void systrace_closepolicy(struct fsystrace *, struct str_policy *);
- void systrace_insert_process(struct fsystrace *, struct proc *,
- struct str_process *);
- struct str_policy *systrace_newpolicy(struct fsystrace *, int);
- int systrace_msg_child(struct fsystrace *, struct str_process *, pid_t);
- int systrace_msg_policyfree(struct fsystrace *, struct str_policy *);
- int systrace_msg_ask(struct fsystrace *, struct str_process *,
- int, size_t, register_t []);
- int systrace_msg_result(struct fsystrace *, struct str_process *,
- int, int, size_t, register_t [], register_t []);
- int systrace_msg_emul(struct fsystrace *, struct str_process *);
- int systrace_msg_ugid(struct fsystrace *, struct str_process *);
- int systrace_make_msg(struct str_process *, int);
- static struct fileops systracefops = {
- systracef_read,
- systracef_write,
- systracef_ioctl,
- systracef_poll,
- systracef_kqfilter,
- systracef_stat,
- systracef_close
- };
- struct pool systr_proc_pl;
- struct pool systr_policy_pl;
- int systrace_debug = 0;
- #define DPRINTF(y) if (systrace_debug) printf y;
- /* ARGSUSED */
- int
- systracef_read(struct file *fp, off_t *poff, struct uio *uio,
- struct ucred *cred)
- {
- struct fsystrace *fst = (struct fsystrace *)fp->f_data;
- struct str_process *process;
- int error = 0;
- if (uio->uio_resid != sizeof(struct str_message))
- return (EINVAL);
- again:
- systrace_lock();
- rw_enter_write(&fst->lock);
- systrace_unlock();
- if ((process = TAILQ_FIRST(&fst->messages)) != NULL) {
- error = uiomove((caddr_t)&process->msg,
- sizeof(struct str_message), uio);
- if (!error) {
- TAILQ_REMOVE(&fst->messages, process, msg_next);
- CLR(process->flags, STR_PROC_ONQUEUE);
- if (SYSTR_MSG_NOPROCESS(process))
- pool_put(&systr_proc_pl, process);
- }
- } else if (TAILQ_FIRST(&fst->processes) == NULL) {
- /* EOF situation */
- ;
- } else {
- if (fp->f_flag & FNONBLOCK)
- error = EAGAIN;
- else {
- rw_exit_write(&fst->lock);
- error = tsleep(fst, PWAIT|PCATCH, "systrrd", 0);
- if (error)
- goto out;
- goto again;
- }
- }
- rw_exit_write(&fst->lock);
- out:
- return (error);
- }
- /* ARGSUSED */
- int
- systracef_write(struct file *fp, off_t *poff, struct uio *uio,
- struct ucred *cred)
- {
- return (EIO);
- }
- #define POLICY_VALID(x) ((x) == SYSTR_POLICY_PERMIT || \
- (x) == SYSTR_POLICY_ASK || \
- (x) == SYSTR_POLICY_NEVER || \
- (x) == SYSTR_POLICY_KILL)
- /* ARGSUSED */
- int
- systracef_ioctl(struct file *fp, u_long cmd, caddr_t data, struct proc *p)
- {
- int ret = 0;
- struct fsystrace *fst = (struct fsystrace *)fp->f_data;
- struct str_process *strp;
- pid_t pid = 0;
- int atfd = -1;
- switch (cmd) {
- case FIONBIO:
- case FIOASYNC:
- return (0);
- case STRIOCDETACH:
- case STRIOCREPORT:
- pid = *(pid_t *)data;
- if (!pid)
- ret = EINVAL;
- break;
- case STRIOCANSWER:
- pid = ((struct systrace_answer *)data)->stra_pid;
- if (!pid)
- ret = EINVAL;
- break;
- case STRIOCIO:
- pid = ((struct systrace_io *)data)->strio_pid;
- if (!pid)
- ret = EINVAL;
- break;
- case STRIOCSCRIPTNAME:
- pid = ((struct systrace_scriptname *)data)->sn_pid;
- if (!pid)
- ret = EINVAL;
- break;
- case STRIOCINJECT:
- pid = ((struct systrace_inject *)data)->stri_pid;
- if (!pid)
- ret = EINVAL;
- break;
- case STRIOCGETCWD: {
- struct systrace_getcwd *gd = (struct systrace_getcwd *)data;
- pid = gd->strgd_pid;
- if (!pid)
- ret = EINVAL;
- atfd = gd->strgd_atfd;
- break;
- }
- case STRIOCATTACH:
- case STRIOCRESCWD:
- case STRIOCPOLICY:
- break;
- case STRIOCREPLACE:
- pid = ((struct systrace_replace *)data)->strr_pid;
- if (!pid)
- ret = EINVAL;
- break;
- default:
- ret = ENOTTY;
- break;
- }
- if (ret)
- return (ret);
- systrace_lock();
- rw_enter_write(&fst->lock);
- systrace_unlock();
- if (pid) {
- strp = systrace_findpid(fst, pid);
- if (strp == NULL) {
- ret = ESRCH;
- goto unlock;
- }
- }
- switch (cmd) {
- case STRIOCATTACH:
- pid = *(pid_t *)data;
- if (!pid)
- ret = EINVAL;
- else
- ret = systrace_attach(fst, pid);
- DPRINTF(("%s: attach to %u: %d\n", __func__, pid, ret));
- break;
- case STRIOCDETACH:
- ret = systrace_detach(strp);
- break;
- case STRIOCREPORT:
- SET(strp->flags, STR_PROC_REPORT);
- break;
- case STRIOCANSWER:
- ret = systrace_answer(strp, (struct systrace_answer *)data);
- break;
- case STRIOCIO:
- ret = systrace_io(strp, (struct systrace_io *)data);
- break;
- case STRIOCSCRIPTNAME:
- ret = systrace_setscriptname(strp,
- (struct systrace_scriptname *)data);
- break;
- case STRIOCINJECT:
- ret = systrace_prepinject(strp, (struct systrace_inject *)data);
- break;
- case STRIOCPOLICY:
- ret = systrace_policy(fst, (struct systrace_policy *)data);
- break;
- case STRIOCREPLACE:
- ret = systrace_preprepl(strp, (struct systrace_replace *)data);
- break;
- case STRIOCRESCWD:
- ret = systrace_restorecwd(fst, p);
- break;
- case STRIOCGETCWD:
- ret = systrace_getcwd(fst, strp, atfd);
- break;
- default:
- ret = ENOTTY;
- break;
- }
- unlock:
- rw_exit_write(&fst->lock);
- return (ret);
- }
- /* ARGSUSED */
- int
- systracef_poll(struct file *fp, int events, struct proc *p)
- {
- struct fsystrace *fst = (struct fsystrace *)fp->f_data;
- int revents = 0;
- if ((events & (POLLIN | POLLRDNORM)) == 0)
- return (0);
- systrace_lock();
- rw_enter_write(&fst->lock);
- systrace_unlock();
- if (!TAILQ_EMPTY(&fst->messages))
- revents = events & (POLLIN | POLLRDNORM);
- else
- selrecord(p, &fst->si);
- rw_exit_write(&fst->lock);
- return (revents);
- }
- /* ARGSUSED */
- int
- systracef_kqfilter(struct file *fp, struct knote *kn)
- {
- return (1);
- }
- /* ARGSUSED */
- int
- systracef_stat(struct file *fp, struct stat *sb, struct proc *p)
- {
- return (EOPNOTSUPP);
- }
- /* ARGSUSED */
- int
- systracef_close(struct file *fp, struct proc *p)
- {
- struct fsystrace *fst = (struct fsystrace *)fp->f_data;
- struct str_process *strp;
- struct str_policy *strpol;
- systrace_lock();
- rw_enter_write(&fst->lock);
- systrace_unlock();
- /* Untrace all processes */
- for (strp = TAILQ_FIRST(&fst->processes); strp;
- strp = TAILQ_FIRST(&fst->processes)) {
- struct proc *q = strp->proc;
- systrace_detach(strp);
- psignal(q, SIGKILL);
- }
- /* Clean up fork and exit messages */
- for (strp = TAILQ_FIRST(&fst->messages); strp;
- strp = TAILQ_FIRST(&fst->messages)) {
- TAILQ_REMOVE(&fst->messages, strp, msg_next);
- pool_put(&systr_proc_pl, strp);
- }
- /* Clean up all policies */
- for (strpol = TAILQ_FIRST(&fst->policies); strpol;
- strpol = TAILQ_FIRST(&fst->policies))
- systrace_closepolicy(fst, strpol);
- /* Release vnodes */
- if (fst->fd_cdir)
- vrele(fst->fd_cdir);
- if (fst->fd_rdir)
- vrele(fst->fd_rdir);
- rw_exit_write(&fst->lock);
- free(fp->f_data, M_XDATA, 0);
- fp->f_data = NULL;
- return (0);
- }
- void
- systraceattach(int n)
- {
- pool_init(&systr_proc_pl, sizeof(struct str_process), 0, 0, 0,
- "strprocpl", NULL);
- pool_init(&systr_policy_pl, sizeof(struct str_policy), 0, 0, 0,
- "strpolpl", NULL);
- rw_init(&systrace_lck, "systrace");
- }
- int
- systraceopen(dev_t dev, int flag, int mode, struct proc *p)
- {
- return (0);
- }
- int
- systraceclose(dev_t dev, int flag, int mode, struct proc *p)
- {
- return (0);
- }
- int
- systraceioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
- {
- struct file *f;
- struct fsystrace *fst = NULL;
- int fd, error;
- switch (cmd) {
- case STRIOCCLONE:
- fst = (struct fsystrace *)malloc(sizeof(struct fsystrace),
- M_XDATA, M_WAITOK | M_ZERO);
- rw_init(&fst->lock, "systrace");
- TAILQ_INIT(&fst->processes);
- TAILQ_INIT(&fst->messages);
- TAILQ_INIT(&fst->policies);
- if (suser(p, 0) == 0)
- fst->issuser = 1;
- fst->p_ruid = p->p_ucred->cr_ruid;
- fst->p_rgid = p->p_ucred->cr_rgid;
- fdplock(p->p_fd);
- error = falloc(p, &f, &fd);
- fdpunlock(p->p_fd);
- if (error) {
- free(fst, M_XDATA, 0);
- return (error);
- }
- f->f_flag = FREAD | FWRITE;
- f->f_type = DTYPE_SYSTRACE;
- f->f_ops = &systracefops;
- f->f_data = (caddr_t) fst;
- *(int *)data = fd;
- FILE_SET_MATURE(f, p);
- break;
- default:
- error = EINVAL;
- break;
- }
- return (error);
- }
- void
- systrace_wakeup(struct fsystrace *fst)
- {
- wakeup((caddr_t)fst);
- selwakeup(&fst->si);
- }
- struct proc *
- systrace_find(struct str_process *strp)
- {
- struct proc *proc;
- if ((proc = pfind(strp->pid)) == NULL)
- return (NULL);
- if (proc != strp->proc)
- return (NULL);
- if (!ISSET(proc->p_flag, P_SYSTRACE))
- return (NULL);
- return (proc);
- }
- void
- systrace_exit(struct proc *proc)
- {
- struct str_process *strp;
- struct fsystrace *fst;
- systrace_lock();
- strp = proc->p_systrace;
- if (strp != NULL) {
- fst = strp->parent;
- rw_enter_write(&fst->lock);
- systrace_unlock();
- /* Insert Exit message */
- systrace_msg_child(fst, strp, -1);
- systrace_detach(strp);
- proc->p_systrace = NULL;
- rw_exit_write(&fst->lock);
- } else
- systrace_unlock();
- atomic_clearbits_int(&proc->p_flag, P_SYSTRACE);
- }
- struct str_process *
- systrace_getproc(void)
- {
- struct str_process *newstrp;
- newstrp = pool_get(&systr_proc_pl, PR_WAITOK|PR_ZERO);
- newstrp->firstmsg = pool_get(&systr_proc_pl, PR_WAITOK|PR_ZERO);
- return (newstrp);
- }
- void
- systrace_freeproc(struct str_process *strp)
- {
- if (strp->firstmsg)
- pool_put(&systr_proc_pl, strp->firstmsg);
- pool_put(&systr_proc_pl, strp);
- }
- void
- systrace_fork(struct proc *oldproc, struct proc *p, struct str_process *newstrp)
- {
- struct str_process *oldstrp, *strp;
- struct fsystrace *fst;
- systrace_lock();
- oldstrp = oldproc->p_systrace;
- if (oldstrp == NULL) {
- systrace_unlock();
- systrace_freeproc(newstrp);
- return;
- }
- fst = oldstrp->parent;
- rw_enter_write(&fst->lock);
- systrace_unlock();
- systrace_insert_process(fst, p, newstrp);
- if ((strp = systrace_findpid(fst, p->p_pid)) == NULL)
- panic("systrace_fork");
- /* Reference policy */
- if ((strp->policy = oldstrp->policy) != NULL)
- strp->policy->refcount++;
- /* Insert fork message */
- systrace_msg_child(fst, oldstrp, p->p_pid);
- rw_exit_write(&fst->lock);
- }
- #define REACQUIRE_LOCK do { \
- systrace_lock(); \
- strp = p->p_systrace; \
- if (strp == NULL) { \
- systrace_unlock(); \
- return (error); \
- } \
- fst = strp->parent; \
- rw_enter_write(&fst->lock); \
- systrace_unlock(); \
- } while (0)
- int
- systrace_redirect(int code, struct proc *p, void *v, register_t *retval)
- {
- struct process *pr = p->p_p;
- struct sysent *callp;
- struct str_process *strp;
- struct str_policy *strpolicy;
- struct fsystrace *fst = NULL;
- struct emul *oldemul;
- int policy, error = 0, report = 0, maycontrol = 0, issuser = 0;
- uid_t old_ruid = p->p_ucred->cr_ruid;
- gid_t old_rgid = p->p_ucred->cr_rgid;
- systrace_lock();
- strp = p->p_systrace;
- if (strp == NULL) {
- systrace_unlock();
- return (EINVAL);
- }
- if (code < 0 || code >= pr->ps_emul->e_nsysent) {
- systrace_unlock();
- return (EINVAL);
- }
- KASSERT(strp->proc == p);
- fst = strp->parent;
- rw_enter_write(&fst->lock);
- systrace_unlock();
- /*
- * We can not monitor a SUID process unless we are root,
- * but we wait until it executes something unprivileged.
- * A non-root user may only monitor if the real uid and
- * real gid match the monitored process. Changing the
- * uid or gid causes PS_SUGID to be set.
- */
- if (fst->issuser) {
- maycontrol = 1;
- issuser = 1;
- } else if (!ISSET(pr->ps_flags, PS_SUGID | PS_SUGIDEXEC)) {
- maycontrol = fst->p_ruid == old_ruid &&
- fst->p_rgid == old_rgid;
- }
- if (!maycontrol) {
- policy = SYSTR_POLICY_PERMIT;
- } else {
- /* Find out current policy */
- if ((strpolicy = strp->policy) == NULL)
- policy = SYSTR_POLICY_ASK;
- else {
- if (code >= strpolicy->nsysent)
- policy = SYSTR_POLICY_NEVER;
- else
- policy = strpolicy->sysent[code];
- }
- }
- callp = pr->ps_emul->e_sysent + code;
- /* Fast-path */
- if (policy != SYSTR_POLICY_ASK) {
- if (policy != SYSTR_POLICY_PERMIT &&
- policy != SYSTR_POLICY_KILL) {
- if (policy > 0)
- error = policy;
- else
- error = EPERM;
- }
- systrace_replacefree(strp);
- rw_exit_write(&fst->lock);
- if (policy == SYSTR_POLICY_KILL) {
- error = EPERM;
- DPRINTF(("systrace: pid %u killed on syscall %d\n",
- pr->ps_pid, code));
- psignal(p, SIGKILL);
- } else if (policy == SYSTR_POLICY_PERMIT)
- error = (*callp->sy_call)(p, v, retval);
- return (error);
- }
- /*
- * Reset our stackgap allocation. Note that when resetting
- * the stackgap allocation, we expect to get the same address
- * base; i.e. that stackgap_init() is idempotent.
- */
- systrace_inject(strp, 0 /* Just reset internal state */);
- strp->sg = stackgap_init(p);
- /* Puts the current process to sleep, return unlocked */
- error = systrace_msg_ask(fst, strp, code, callp->sy_argsize, v);
- /* lock has been released in systrace_msg_ask() */
- if (error)
- return (error);
- /* We might have detached by now for some reason */
- systrace_lock();
- if ((strp = p->p_systrace) == NULL) {
- systrace_unlock();
- return (error);
- }
- fst = strp->parent;
- rw_enter_write(&fst->lock);
- systrace_unlock();
- if (strp->answer == SYSTR_POLICY_NEVER) {
- error = strp->error;
- systrace_replacefree(strp);
- goto out_unlock;
- }
- if (ISSET(strp->flags, STR_PROC_SYSCALLRES)) {
- CLR(strp->flags, STR_PROC_SYSCALLRES);
- report = 1;
- }
- error = systrace_inject(strp, 1/* Perform copies */);
- /* Replace the arguments if necessary */
- if (!error && strp->replace != NULL)
- error = systrace_replace(strp, callp->sy_argsize, v);
- if (error)
- goto out_unlock;
- oldemul = pr->ps_emul;
-
- /* Elevate privileges as desired */
- if (issuser && ISSET(strp->flags, STR_PROC_SETEUID|STR_PROC_SETEGID)) {
- struct ucred *uc, *newcred;
- uc = p->p_ucred;
- newcred = NULL;
- if (ISSET(strp->flags, STR_PROC_SETEUID) &&
- uc->cr_uid != strp->seteuid) {
- newcred = crdup(uc);
- newcred->cr_uid = strp->seteuid;
- }
- if (ISSET(strp->flags, STR_PROC_SETEGID) &&
- uc->cr_gid != strp->setegid) {
- if (newcred == NULL)
- newcred = crdup(uc);
- newcred->cr_gid = strp->setegid;
- }
- if (newcred != NULL) {
- p->p_ucred = newcred;
- crfree(uc);
- atomic_setbits_int(&pr->ps_flags, PS_SUGID);
- }
- }
- rw_exit_write(&fst->lock);
-
- error = (*callp->sy_call)(p, v, retval);
- /* reset the credentials on the thread */
- refreshcreds(p);
- /* Return to old privileges */
- systrace_lock();
- if ((strp = p->p_systrace) == NULL) {
- systrace_unlock();
- return (error);
- }
- systrace_replacefree(strp);
- if (ISSET(pr->ps_flags, PS_SUGID | PS_SUGIDEXEC)) {
- if ((fst = strp->parent) == NULL || !fst->issuser) {
- systrace_unlock();
- return (error);
- }
- }
- /* Report change in emulation */
- /* See if we should force a report */
- if (ISSET(strp->flags, STR_PROC_REPORT)) {
- CLR(strp->flags, STR_PROC_REPORT);
- oldemul = NULL;
- }
- /* Acquire lock */
- fst = strp->parent;
- rw_enter_write(&fst->lock);
- systrace_unlock();
- if (pr->ps_emul != oldemul) {
- /* Old policy is without meaning now */
- if (strp->policy) {
- systrace_closepolicy(fst, strp->policy);
- strp->policy = NULL;
- }
- systrace_msg_emul(fst, strp);
- REACQUIRE_LOCK;
- }
- /* Report if effective uid or gid changed */
- if (old_ruid != pr->ps_ucred->cr_ruid ||
- old_rgid != pr->ps_ucred->cr_rgid) {
- systrace_msg_ugid(fst, strp);
- REACQUIRE_LOCK;
- }
- /* Report result from system call */
- if (report) {
- systrace_msg_result(fst, strp, error, code,
- callp->sy_argsize, v, retval);
- /* not locked */
- goto out;
- }
- out_unlock:
- rw_exit_write(&fst->lock);
- out:
- return (error);
- }
- /* Called with fst locked */
- int
- systrace_answer(struct str_process *strp, struct systrace_answer *ans)
- {
- int error = 0;
- DPRINTF(("%s: %u: policy %d\n", __func__,
- ans->stra_pid, ans->stra_policy));
- if (!POLICY_VALID(ans->stra_policy)) {
- error = EINVAL;
- goto out;
- }
- /* Check if answer is in sync with us */
- if (ans->stra_seqnr != strp->seqnr) {
- error = ESRCH;
- goto out;
- }
- if ((error = systrace_processready(strp)) != 0)
- goto out;
- strp->answer = ans->stra_policy;
- strp->error = ans->stra_error;
- if (!strp->error)
- strp->error = EPERM;
- if (ISSET(ans->stra_flags, SYSTR_FLAGS_RESULT))
- SET(strp->flags, STR_PROC_SYSCALLRES);
- /* See if we should elevate privileges for this system call */
- if (ISSET(ans->stra_flags, SYSTR_FLAGS_SETEUID)) {
- SET(strp->flags, STR_PROC_SETEUID);
- strp->seteuid = ans->stra_seteuid;
- }
- if (ISSET(ans->stra_flags, SYSTR_FLAGS_SETEGID)) {
- SET(strp->flags, STR_PROC_SETEGID);
- strp->setegid = ans->stra_setegid;
- }
- /* Clearing the flag indicates to the process that it woke up */
- CLR(strp->flags, STR_PROC_WAITANSWER);
- wakeup(strp);
- out:
- return (error);
- }
- int
- systrace_setscriptname(struct str_process *strp, struct systrace_scriptname *ans)
- {
- strlcpy(strp->scriptname,
- ans->sn_scriptname, sizeof(strp->scriptname));
- return (0);
- }
- int
- systrace_inject(struct str_process *strp, int docopy)
- {
- int ind, ret = 0;
- for (ind = 0; ind < strp->injectind; ind++) {
- struct str_inject *inject = &strp->injects[ind];
- if (!ret && docopy &&
- copyout(inject->kaddr, inject->uaddr, inject->len))
- ret = EINVAL;
- free(inject->kaddr, M_XDATA, 0);
- }
- strp->injectind = 0;
- return (ret);
- }
- int
- systrace_prepinject(struct str_process *strp, struct systrace_inject *inj)
- {
- caddr_t udata, kaddr = NULL;
- int ret = 0;
- struct str_inject *inject;
- if (strp->injectind >= SYSTR_MAXINJECTS)
- return (ENOBUFS);
- udata = stackgap_alloc(&strp->sg, inj->stri_len);
- if (udata == NULL)
- return (ENOMEM);
- /*
- * We have infact forced a maximum length on stri_len because
- * of the stackgap.
- */
- kaddr = malloc(inj->stri_len, M_XDATA, M_WAITOK);
- ret = copyin(inj->stri_addr, kaddr, inj->stri_len);
- if (ret) {
- free(kaddr, M_XDATA, 0);
- return (ret);
- }
- inject = &strp->injects[strp->injectind++];
- inject->kaddr = kaddr;
- inject->uaddr = inj->stri_addr = udata;
- inject->len = inj->stri_len;
- return (0);
- }
- int
- systrace_policy(struct fsystrace *fst, struct systrace_policy *pol)
- {
- struct str_policy *strpol;
- struct str_process *strp;
- switch(pol->strp_op) {
- case SYSTR_POLICY_NEW:
- DPRINTF(("%s: new, ents %d\n", __func__,
- pol->strp_maxents));
- if (pol->strp_maxents <= 0 || pol->strp_maxents > 1024)
- return (EINVAL);
- strpol = systrace_newpolicy(fst, pol->strp_maxents);
- if (strpol == NULL)
- return (ENOBUFS);
- pol->strp_num = strpol->nr;
- break;
- case SYSTR_POLICY_ASSIGN:
- DPRINTF(("%s: %d -> pid %d\n", __func__,
- pol->strp_num, pol->strp_pid));
- /* Find right policy by number */
- TAILQ_FOREACH(strpol, &fst->policies, next)
- if (strpol->nr == pol->strp_num)
- break;
- if (strpol == NULL)
- return (EINVAL);
- strp = systrace_findpid(fst, pol->strp_pid);
- if (strp == NULL)
- return (EINVAL);
- /* Check that emulation matches */
- if (strpol->emul && strpol->emul != strp->proc->p_p->ps_emul)
- return (EINVAL);
- if (strp->policy)
- systrace_closepolicy(fst, strp->policy);
- strp->policy = strpol;
- /* LRU for policy use */
- TAILQ_REMOVE(&fst->policies, strpol, next);
- TAILQ_INSERT_TAIL(&fst->policies, strpol, next);
- strpol->refcount++;
- /* Record emulation for this policy */
- if (strpol->emul == NULL)
- strpol->emul = strp->proc->p_p->ps_emul;
- break;
- case SYSTR_POLICY_MODIFY:
- DPRINTF(("%s: %d: code %d -> policy %d\n", __func__,
- pol->strp_num, pol->strp_code, pol->strp_policy));
- if (!POLICY_VALID(pol->strp_policy))
- return (EINVAL);
- TAILQ_FOREACH(strpol, &fst->policies, next)
- if (strpol->nr == pol->strp_num)
- break;
- if (strpol == NULL)
- return (EINVAL);
- if (pol->strp_code < 0 || pol->strp_code >= strpol->nsysent)
- return (EINVAL);
- strpol->sysent[pol->strp_code] = pol->strp_policy;
- break;
- default:
- return (EINVAL);
- }
- return (0);
- }
- int
- systrace_processready(struct str_process *strp)
- {
- if (ISSET(strp->flags, STR_PROC_ONQUEUE))
- return (EBUSY);
- if (!ISSET(strp->flags, STR_PROC_WAITANSWER))
- return (EBUSY);
- if (strp->proc->p_stat != SSLEEP)
- return (EBUSY);
- return (0);
- }
- int
- systrace_getcwd(struct fsystrace *fst, struct str_process *strp, int atfd)
- {
- struct filedesc *myfdp, *fdp;
- struct vnode *dvp, *odvp;
- int error;
- DPRINTF(("%s: %d\n", __func__, strp->pid));
- error = systrace_processready(strp);
- if (error)
- return (error);
- myfdp = curproc->p_fd;
- fdp = strp->proc->p_fd;
- if (myfdp == NULL || fdp == NULL)
- return (EINVAL);
- if (atfd == AT_FDCWD)
- dvp = fdp->fd_cdir;
- else {
- struct file *fp = fd_getfile(fdp, atfd);
- if (fp == NULL || fp->f_type != DTYPE_VNODE)
- return (EINVAL);
- dvp = (struct vnode *)fp->f_data;
- if (dvp->v_type != VDIR)
- return (EINVAL);
- }
- /* Is there a STRIOCGETCWD currently in effect? */
- if (fst->fd_pid == 0) {
- /* nope: just save the current values */
- fst->fd_cdir = myfdp->fd_cdir;
- fst->fd_rdir = myfdp->fd_rdir;
- } else {
- /* yep: carefully release the current values */
- odvp = myfdp->fd_rdir;
- myfdp->fd_rdir = fst->fd_rdir;
- if (odvp != NULL)
- vrele(odvp);
- odvp = myfdp->fd_cdir;
- myfdp->fd_cdir = fst->fd_cdir;
- if (odvp != NULL)
- vrele(odvp);
- }
- fst->fd_pid = strp->pid;
- if ((myfdp->fd_cdir = dvp) != NULL)
- vref(myfdp->fd_cdir);
- if ((myfdp->fd_rdir = fdp->fd_rdir) != NULL)
- vref(myfdp->fd_rdir);
- return (0);
- }
- int
- systrace_restorecwd(struct fsystrace *fst, struct proc *p)
- {
- struct filedesc *fdp;
- struct vnode *rvp, *cvp;
- if (!fst->fd_pid)
- return (EINVAL);
- fdp = p->p_fd;
- /*
- * Restore original root and current directories and release the
- * ones from the other process.
- */
- rvp = fdp->fd_rdir;
- cvp = fdp->fd_cdir;
- fdp->fd_rdir = fst->fd_rdir;
- fdp->fd_cdir = fst->fd_cdir;
- fst->fd_cdir = fst->fd_rdir = NULL;
- if (rvp != NULL)
- vrele(rvp);
- if (cvp != NULL)
- vrele(cvp);
- /* Note that we are normal again */
- fst->fd_pid = 0;
- return (0);
- }
- int
- systrace_io(struct str_process *strp, struct systrace_io *io)
- {
- struct proc *p = curproc, *t = strp->proc;
- struct uio uio;
- struct iovec iov;
- int error = 0;
- DPRINTF(("%s: %u: %p(%lu)\n", __func__,
- io->strio_pid, io->strio_offs, (u_long)io->strio_len));
- switch (io->strio_op) {
- case SYSTR_READ:
- uio.uio_rw = UIO_READ;
- break;
- case SYSTR_WRITE:
- uio.uio_rw = UIO_WRITE;
- break;
- default:
- return (EINVAL);
- }
- error = systrace_processready(strp);
- if (error)
- goto out;
- iov.iov_base = io->strio_addr;
- iov.iov_len = io->strio_len;
- uio.uio_iov = &iov;
- uio.uio_iovcnt = 1;
- uio.uio_offset = (off_t)(u_long)io->strio_offs;
- uio.uio_resid = io->strio_len;
- uio.uio_segflg = UIO_USERSPACE;
- uio.uio_procp = p;
- error = process_domem(p, t, &uio, PT_WRITE_I);
- io->strio_len -= uio.uio_resid;
- out:
- return (error);
- }
- int
- systrace_attach(struct fsystrace *fst, pid_t pid)
- {
- int error = 0;
- struct proc *p = curproc;
- struct proc *t; /* target thread */
- struct process *tr; /* target process */
- struct str_process *newstrp;
- if ((t = pfind(pid)) == NULL || (t->p_flag & P_THREAD)) {
- error = ESRCH;
- goto out;
- }
- tr = t->p_p;
- if (ISSET(tr->ps_flags, PS_INEXEC)) {
- error = EAGAIN;
- goto out;
- }
- /*
- * You can't attach to a process if:
- * (1) it's the process that's doing the attaching,
- */
- if (tr == p->p_p) {
- error = EINVAL;
- goto out;
- }
- /*
- * (2) it's a system process
- */
- if (ISSET(t->p_flag, P_SYSTEM)) {
- error = EPERM;
- goto out;
- }
- /*
- * (3) it's being traced already
- */
- if (ISSET(t->p_flag, P_SYSTRACE)) {
- error = EBUSY;
- goto out;
- }
- /*
- * (4) it's not owned by you, or the last exec
- * gave us setuid/setgid privs (unless
- * you're root), or...
- *
- * [Note: once PS_SUGID or PS_SUGIDEXEC gets set in execve(),
- * it stays set until the process does another execve(). Hence
- * this prevents a setuid process which revokes its
- * special privileges using setuid() from being
- * traced. This is good security.]
- */
- if ((tr->ps_ucred->cr_ruid != p->p_ucred->cr_ruid ||
- ISSET(tr->ps_flags, PS_SUGID | PS_SUGIDEXEC)) &&
- (error = suser(p, 0)) != 0)
- goto out;
- /*
- * (5) ...it's init, which controls the security level
- * of the entire system, and the system was not
- * compiled with permanently insecure mode turned
- * on.
- */
- if ((tr->ps_pid == 1) && (securelevel > -1)) {
- error = EPERM;
- goto out;
- }
- newstrp = systrace_getproc();
- systrace_insert_process(fst, t, newstrp);
- out:
- return (error);
- }
- void
- systrace_execve0(struct proc *p)
- {
- struct str_process *strp;
- systrace_lock();
- strp = p->p_systrace;
- strp->isscript = 0;
- systrace_unlock();
- }
- void
- systrace_execve1(char *path, struct proc *p)
- {
- struct str_process *strp;
- struct fsystrace *fst;
- struct str_msg_execve *msg_execve;
- do {
- systrace_lock();
- strp = p->p_systrace;
- if (strp == NULL) {
- systrace_unlock();
- return;
- }
- msg_execve = &strp->msg.msg_data.msg_execve;
- fst = strp->parent;
- rw_enter_write(&fst->lock);
- systrace_unlock();
- /*
- * susers will get the execve call anyway. Also, if
- * we're not allowed to control the process, escape.
- */
- if (fst->issuser ||
- fst->p_ruid != p->p_ucred->cr_ruid ||
- fst->p_rgid != p->p_ucred->cr_rgid) {
- rw_exit_write(&fst->lock);
- return;
- }
- strlcpy(msg_execve->path, path, MAXPATHLEN);
- } while (systrace_make_msg(strp, SYSTR_MSG_EXECVE) != 0);
- }
- /* Prepare to replace arguments */
- int
- systrace_preprepl(struct str_process *strp, struct systrace_replace *repl)
- {
- size_t len;
- int i, ret = 0;
- ret = systrace_processready(strp);
- if (ret)
- return (ret);
- if (strp->replace != NULL) {
- free(strp->replace, M_XDATA, 0);
- strp->replace = NULL;
- }
- if (repl->strr_nrepl < 0 || repl->strr_nrepl > SYSTR_MAXARGS)
- return (EINVAL);
- for (i = 0, len = 0; i < repl->strr_nrepl; i++) {
- if (repl->strr_argind[i] < 0 ||
- repl->strr_argind[i] >= SYSTR_MAXARGS)
- return (EINVAL);
- if (repl->strr_offlen[i] == 0)
- continue;
- len += repl->strr_offlen[i];
- if (repl->strr_offlen[i] > SYSTR_MAXREPLEN ||
- repl->strr_off[i] > SYSTR_MAXREPLEN ||
- len > SYSTR_MAXREPLEN)
- return (EINVAL);
- if (repl->strr_offlen[i] + repl->strr_off[i] > len)
- return (EINVAL);
- }
- /* Make sure that the length adds up */
- if (repl->strr_len != len)
- return (EINVAL);
- /* Check against a maximum length */
- if (repl->strr_len > SYSTR_MAXREPLEN)
- return (EINVAL);
- strp->replace = (struct systrace_replace *)
- malloc(sizeof(struct systrace_replace) + len, M_XDATA, M_WAITOK);
- memcpy(strp->replace, repl, sizeof(struct systrace_replace));
- ret = copyin(repl->strr_base, strp->replace + 1, len);
- if (ret) {
- free(strp->replace, M_XDATA, 0);
- strp->replace = NULL;
- return (ret);
- }
- /* Adjust the offset */
- repl = strp->replace;
- repl->strr_base = (caddr_t)(repl + 1);
- return (0);
- }
- /*
- * Replace the arguments with arguments from the monitoring process.
- */
- int
- systrace_replace(struct str_process *strp, size_t argsize, register_t args[])
- {
- struct systrace_replace *repl = strp->replace;
- caddr_t kdata, kbase;
- caddr_t udata, ubase;
- int i, maxarg, ind, ret = 0;
- maxarg = argsize/sizeof(register_t);
- ubase = stackgap_alloc(&strp->sg, repl->strr_len);
- if (ubase == NULL) {
- ret = EINVAL;
- goto out;
- }
- kbase = repl->strr_base;
- for (i = 0; i < maxarg && i < repl->strr_nrepl; i++) {
- ind = repl->strr_argind[i];
- if (ind < 0 || ind >= maxarg) {
- ret = EINVAL;
- goto out;
- }
- if (repl->strr_offlen[i] == 0) {
- args[ind] = repl->strr_off[i];
- continue;
- }
- kdata = kbase + repl->strr_off[i];
- if (repl->strr_flags[i] & SYSTR_NOLINKS) {
- ret = systrace_fname(strp, kdata, repl->strr_offlen[i]);
- if (ret != 0)
- goto out;
- }
- udata = ubase + repl->strr_off[i];
- if (copyout(kdata, udata, repl->strr_offlen[i])) {
- ret = EINVAL;
- goto out;
- }
- /* Replace the argument with the new address */
- args[ind] = (register_t)udata;
- }
- out:
- return (ret);
- }
- int
- systrace_fname(struct str_process *strp, caddr_t kdata, size_t len)
- {
- if (strp->nfname >= SYSTR_MAXFNAME || len < 1)
- return EINVAL;
- strp->fname[strp->nfname] = kdata;
- strp->fname[strp->nfname][len - 1] = '\0';
- strp->nfname++;
- return 0;
- }
- void
- systrace_replacefree(struct str_process *strp)
- {
- if (strp->replace != NULL) {
- free(strp->replace, M_XDATA, 0);
- strp->replace = NULL;
- }
- while (strp->nfname > 0) {
- strp->nfname--;
- strp->fname[strp->nfname] = NULL;
- }
- }
- int
- systrace_scriptname(struct proc *p, char *dst)
- {
- struct str_process *strp;
- struct fsystrace *fst;
- int error = 0;
- systrace_lock();
- strp = p->p_systrace;
- fst = strp->parent;
- rw_enter_write(&fst->lock);
- systrace_unlock();
- if (!fst->issuser &&
- (ISSET(p->p_p->ps_flags, PS_SUGID | PS_SUGIDEXEC) ||
- fst->p_ruid != p->p_ucred->cr_ruid ||
- fst->p_rgid != p->p_ucred->cr_rgid)) {
- error = EPERM;
- goto out;
- }
- if (strp != NULL) {
- if (strp->scriptname[0] == '\0') {
- error = ENOENT;
- goto out;
- }
- strlcpy(dst, strp->scriptname, MAXPATHLEN);
- strp->isscript = 1;
- }
- out:
- strp->scriptname[0] = '\0';
- rw_exit_write(&fst->lock);
- return (error);
- }
- void
- systrace_namei(struct nameidata *ndp)
- {
- struct str_process *strp;
- struct fsystrace *fst;
- struct componentname *cnp = &ndp->ni_cnd;
- size_t i;
- int hamper = 0;
- systrace_lock();
- strp = cnp->cn_proc->p_systrace;
- if (strp != NULL) {
- fst = strp->parent;
- rw_enter_write(&fst->lock);
- systrace_unlock();
- for (i = 0; i < strp->nfname; i++)
- if (strcmp(cnp->cn_pnbuf, strp->fname[i]) == 0) {
- hamper = 1;
- break;
- }
- if (!hamper && strp->isscript &&
- strcmp(cnp->cn_pnbuf, strp->scriptname) == 0)
- hamper = 1;
- rw_exit_write(&fst->lock);
- } else
- systrace_unlock();
- if (hamper) {
- /* ELOOP if namei() tries to readlink */
- ndp->ni_loopcnt = SYMLOOP_MAX;
- cnp->cn_flags &= ~FOLLOW;
- cnp->cn_flags |= NOFOLLOW;
- }
- }
- struct str_process *
- systrace_findpid(struct fsystrace *fst, pid_t pid)
- {
- struct str_process *strp;
- struct proc *proc = NULL;
- TAILQ_FOREACH(strp, &fst->processes, next)
- if (strp->pid == pid)
- break;
- if (strp == NULL)
- return (NULL);
- proc = systrace_find(strp);
- return (proc ? strp : NULL);
- }
- int
- systrace_detach(struct str_process *strp)
- {
- struct proc *proc;
- struct fsystrace *fst = NULL;
- int error = 0;
- DPRINTF(("%s: Trying to detach from %d\n", __func__, strp->pid));
- if ((proc = systrace_find(strp)) != NULL) {
- atomic_clearbits_int(&proc->p_flag, P_SYSTRACE);
- proc->p_systrace = NULL;
- } else
- error = ESRCH;
- if (ISSET(strp->flags, STR_PROC_WAITANSWER)) {
- CLR(strp->flags, STR_PROC_WAITANSWER);
- wakeup(strp);
- }
- fst = strp->parent;
- systrace_wakeup(fst);
- if (ISSET(strp->flags, STR_PROC_ONQUEUE))
- TAILQ_REMOVE(&fst->messages, strp, msg_next);
- TAILQ_REMOVE(&fst->processes, strp, next);
- fst->nprocesses--;
- if (strp->policy)
- systrace_closepolicy(fst, strp->policy);
- systrace_replacefree(strp);
- systrace_freeproc(strp);
- return (error);
- }
- void
- systrace_closepolicy(struct fsystrace *fst, struct str_policy *policy)
- {
- if (--policy->refcount)
- return;
- fst->npolicies--;
- if (policy->nsysent)
- free(policy->sysent, M_XDATA, 0);
- TAILQ_REMOVE(&fst->policies, policy, next);
- pool_put(&systr_policy_pl, policy);
- }
- void
- systrace_insert_process(struct fsystrace *fst, struct proc *proc,
- struct str_process *strp)
- {
- strp->pid = proc->p_pid;
- strp->proc = proc;
- strp->parent = fst;
- TAILQ_INSERT_TAIL(&fst->processes, strp, next);
- fst->nprocesses++;
- proc->p_systrace = strp;
- atomic_setbits_int(&proc->p_flag, P_SYSTRACE);
- }
- struct str_policy *
- systrace_newpolicy(struct fsystrace *fst, int maxents)
- {
- struct str_policy *pol;
- int i;
- if (fst->npolicies > SYSTR_MAX_POLICIES && !fst->issuser) {
- struct str_policy *tmp;
- /* Try to find a policy for freeing */
- TAILQ_FOREACH(tmp, &fst->policies, next) {
- if (tmp->refcount == 1)
- break;
- }
- if (tmp == NULL)
- return (NULL);
- /* Notify userland about freed policy */
- systrace_msg_policyfree(fst, tmp);
- /* Free this policy */
- systrace_closepolicy(fst, tmp);
- }
- pol = pool_get(&systr_policy_pl, PR_NOWAIT|PR_ZERO);
- if (pol == NULL)
- return (NULL);
- DPRINTF(("%s: allocating %d -> %lu\n", __func__,
- maxents, (u_long)maxents * sizeof(int)));
- pol->sysent = malloc(maxents, M_XDATA, M_WAITOK);
- pol->nsysent = maxents;
- for (i = 0; i < maxents; i++)
- pol->sysent[i] = SYSTR_POLICY_ASK;
- fst->npolicies++;
- pol->nr = fst->npolicynr++;
- pol->refcount = 1;
- TAILQ_INSERT_TAIL(&fst->policies, pol, next);
- return (pol);
- }
- int
- systrace_msg_ask(struct fsystrace *fst, struct str_process *strp,
- int code, size_t argsize, register_t args[])
- {
- struct str_msg_ask *msg_ask = &strp->msg.msg_data.msg_ask;
- int i;
- msg_ask->code = code;
- msg_ask->argsize = argsize;
- for (i = 0; i < (argsize/sizeof(register_t)) && i < SYSTR_MAXARGS; i++)
- msg_ask->args[i] = args[i];
- return (systrace_make_msg(strp, SYSTR_MSG_ASK));
- }
- int
- systrace_msg_result(struct fsystrace *fst, struct str_process *strp,
- int error, int code, size_t argsize, register_t args[], register_t rval[])
- {
- struct str_msg_ask *msg_ask = &strp->msg.msg_data.msg_ask;
- int i;
- msg_ask->code = code;
- msg_ask->argsize = argsize;
- msg_ask->result = error;
- for (i = 0; i < (argsize/sizeof(register_t)) && i < SYSTR_MAXARGS; i++)
- msg_ask->args[i] = args[i];
- msg_ask->rval[0] = rval[0];
- msg_ask->rval[1] = rval[1];
- return (systrace_make_msg(strp, SYSTR_MSG_RES));
- }
- int
- systrace_msg_emul(struct fsystrace *fst, struct str_process *strp)
- {
- struct str_msg_emul *msg_emul = &strp->msg.msg_data.msg_emul;
- struct proc *p = strp->proc;
- memcpy(msg_emul->emul, p->p_p->ps_emul->e_name, SYSTR_EMULEN);
- return (systrace_make_msg(strp, SYSTR_MSG_EMUL));
- }
- int
- systrace_msg_ugid(struct fsystrace *fst, struct str_process *strp)
- {
- struct str_msg_ugid *msg_ugid = &strp->msg.msg_data.msg_ugid;
- struct ucred *uc = strp->proc->p_p->ps_ucred;
- msg_ugid->uid = uc->cr_ruid;
- msg_ugid->gid = uc->cr_rgid;
- return (systrace_make_msg(strp, SYSTR_MSG_UGID));
- }
- int
- systrace_make_msg(struct str_process *strp, int type)
- {
- struct str_message *msg = &strp->msg;
- struct fsystrace *fst = strp->parent;
- int st, pri;
- pri = PWAIT|PCATCH;
- if (type == SYSTR_MSG_EXECVE)
- pri &= ~PCATCH;
- msg->msg_seqnr = ++strp->seqnr;
- msg->msg_type = type;
- msg->msg_pid = strp->pid;
- if (strp->policy)
- msg->msg_policy = strp->policy->nr;
- else
- msg->msg_policy = -1;
- SET(strp->flags, STR_PROC_WAITANSWER);
- if (ISSET(strp->flags, STR_PROC_ONQUEUE))
- goto out;
- TAILQ_INSERT_TAIL(&fst->messages, strp, msg_next);
- SET(strp->flags, STR_PROC_ONQUEUE);
- out:
- systrace_wakeup(fst);
- /* Release the lock - XXX */
- rw_exit_write(&fst->lock);
- while (1) {
- st = tsleep(strp, pri, "systrmsg", 0);
- if (st != 0)
- return (ERESTART);
- /* If we detach, then everything is permitted */
- if ((strp = curproc->p_systrace) == NULL)
- return (0);
- if (!ISSET(strp->flags, STR_PROC_WAITANSWER))
- break;
- }
- return (0);
- }
- int
- systrace_msg_child(struct fsystrace *fst, struct str_process *strp, pid_t npid)
- {
- struct str_process *nstrp;
- struct str_message *msg;
- struct str_msg_child *msg_child;
- if (strp->firstmsg) {
- nstrp = strp->firstmsg;
- strp->firstmsg = NULL;
- } else
- nstrp = pool_get(&systr_proc_pl, PR_WAITOK|PR_ZERO);
- DPRINTF(("%s: %p: pid %d -> pid %d\n", __func__,
- nstrp, strp->pid, npid));
- msg = &nstrp->msg;
- msg_child = &msg->msg_data.msg_child;
- msg->msg_type = SYSTR_MSG_CHILD;
- msg->msg_pid = strp->pid;
- if (strp->policy)
- msg->msg_policy = strp->policy->nr;
- else
- msg->msg_policy = -1;
- msg_child->new_pid = npid;
- TAILQ_INSERT_TAIL(&fst->messages, nstrp, msg_next);
- systrace_wakeup(fst);
- return (0);
- }
- int
- systrace_msg_policyfree(struct fsystrace *fst, struct str_policy *strpol)
- {
- struct str_process *nstrp;
- struct str_message *msg;
- nstrp = pool_get(&systr_proc_pl, PR_WAITOK|PR_ZERO);
- DPRINTF(("%s: free %d\n", __func__, strpol->nr));
- msg = &nstrp->msg;
- msg->msg_type = SYSTR_MSG_POLICYFREE;
- msg->msg_policy = strpol->nr;
- TAILQ_INSERT_TAIL(&fst->messages, nstrp, msg_next);
- systrace_wakeup(fst);
- return (0);
- }
|