Annotation of sys/dev/systrace.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: systrace.c,v 1.44 2007/03/15 10:22:30 art Exp $ */
! 2: /*
! 3: * Copyright 2002 Niels Provos <provos@citi.umich.edu>
! 4: * All rights reserved.
! 5: *
! 6: * Redistribution and use in source and binary forms, with or without
! 7: * modification, are permitted provided that the following conditions
! 8: * are met:
! 9: * 1. Redistributions of source code must retain the above copyright
! 10: * notice, this list of conditions and the following disclaimer.
! 11: * 2. Redistributions in binary form must reproduce the above copyright
! 12: * notice, this list of conditions and the following disclaimer in the
! 13: * documentation and/or other materials provided with the distribution.
! 14: * 3. All advertising materials mentioning features or use of this software
! 15: * must display the following acknowledgement:
! 16: * This product includes software developed by Niels Provos.
! 17: * 4. The name of the author may not be used to endorse or promote products
! 18: * derived from this software without specific prior written permission.
! 19: *
! 20: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 21: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 22: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 23: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
! 24: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
! 25: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
! 26: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
! 27: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 28: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
! 29: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 30: */
! 31: #include <sys/param.h>
! 32: #include <sys/systm.h>
! 33: #include <sys/tree.h>
! 34: #include <sys/malloc.h>
! 35: #include <sys/syscall.h>
! 36: #include <sys/vnode.h>
! 37: #include <sys/errno.h>
! 38: #include <sys/conf.h>
! 39: #include <sys/device.h>
! 40: #include <sys/proc.h>
! 41: #include <sys/file.h>
! 42: #include <sys/filedesc.h>
! 43: #include <sys/filio.h>
! 44: #include <sys/signalvar.h>
! 45: #include <sys/rwlock.h>
! 46: #include <sys/pool.h>
! 47: #include <sys/mount.h>
! 48: #include <sys/namei.h>
! 49: #include <sys/poll.h>
! 50: #include <sys/ptrace.h>
! 51:
! 52: #include <compat/common/compat_util.h>
! 53:
! 54: #include <dev/systrace.h>
! 55:
! 56: void systraceattach(int);
! 57:
! 58: int systraceopen(dev_t, int, int, struct proc *);
! 59: int systraceclose(dev_t, int, int, struct proc *);
! 60: int systraceread(dev_t, struct uio *, int);
! 61: int systracewrite(dev_t, struct uio *, int);
! 62: int systraceioctl(dev_t, u_long, caddr_t, int, struct proc *);
! 63: int systracepoll(dev_t, int, struct proc *);
! 64:
! 65: uid_t systrace_seteuid(struct proc *, uid_t);
! 66: gid_t systrace_setegid(struct proc *, gid_t);
! 67: int systracef_read(struct file *, off_t *, struct uio *, struct ucred *);
! 68: int systracef_write(struct file *, off_t *, struct uio *, struct ucred *);
! 69: int systracef_ioctl(struct file *, u_long, caddr_t, struct proc *p);
! 70: int systracef_poll(struct file *, int, struct proc *);
! 71: int systracef_kqfilter(struct file *, struct knote *);
! 72: int systracef_stat(struct file *, struct stat *, struct proc *);
! 73: int systracef_close(struct file *, struct proc *);
! 74:
! 75: struct str_policy {
! 76: TAILQ_ENTRY(str_policy) next;
! 77:
! 78: int nr;
! 79:
! 80: struct emul *emul; /* Is only valid for this emulation */
! 81:
! 82: int refcount;
! 83:
! 84: int nsysent;
! 85: u_char *sysent;
! 86: };
! 87:
! 88: struct str_inject {
! 89: caddr_t kaddr;
! 90: caddr_t uaddr;
! 91: size_t len;
! 92: };
! 93:
! 94: #define STR_PROC_ONQUEUE 0x01
! 95: #define STR_PROC_WAITANSWER 0x02
! 96: #define STR_PROC_SYSCALLRES 0x04
! 97: #define STR_PROC_REPORT 0x08 /* Report emulation */
! 98: #define STR_PROC_NEEDSEQNR 0x10 /* Answer must quote seqnr */
! 99: #define STR_PROC_SETEUID 0x20 /* Elevate privileges */
! 100: #define STR_PROC_SETEGID 0x40
! 101:
! 102: struct str_process {
! 103: TAILQ_ENTRY(str_process) next;
! 104: TAILQ_ENTRY(str_process) msg_next;
! 105:
! 106: struct proc *proc;
! 107: pid_t pid;
! 108:
! 109: struct fsystrace *parent;
! 110: struct str_policy *policy;
! 111:
! 112: struct systrace_replace *replace;
! 113: char *fname[SYSTR_MAXFNAME];
! 114: size_t nfname;
! 115:
! 116: int flags;
! 117: short answer;
! 118: short error;
! 119: u_int16_t seqnr; /* expected reply sequence number */
! 120:
! 121: uid_t seteuid;
! 122: uid_t saveuid;
! 123: gid_t setegid;
! 124: gid_t savegid;
! 125:
! 126: int isscript;
! 127: char scriptname[MAXPATHLEN];
! 128:
! 129: struct str_message msg;
! 130:
! 131: caddr_t sg;
! 132: struct str_inject injects[SYSTR_MAXINJECTS];
! 133: int injectind;
! 134: };
! 135:
! 136: struct rwlock systrace_lck;
! 137:
! 138: static __inline void
! 139: systrace_lock(void)
! 140: {
! 141: rw_enter_write(&systrace_lck);
! 142: }
! 143:
! 144: static __inline void
! 145: systrace_unlock(void)
! 146: {
! 147: rw_exit_write(&systrace_lck);
! 148: }
! 149:
! 150: /* Needs to be called with fst locked */
! 151:
! 152: int systrace_attach(struct fsystrace *, pid_t);
! 153: int systrace_detach(struct str_process *);
! 154: int systrace_answer(struct str_process *, struct systrace_answer *);
! 155: int systrace_setscriptname(struct str_process *,
! 156: struct systrace_scriptname *);
! 157: int systrace_prepinject(struct str_process *, struct systrace_inject *);
! 158: int systrace_inject(struct str_process *, int);
! 159: int systrace_io(struct str_process *, struct systrace_io *);
! 160: int systrace_policy(struct fsystrace *, struct systrace_policy *);
! 161: int systrace_preprepl(struct str_process *, struct systrace_replace *);
! 162: int systrace_replace(struct str_process *, size_t, register_t []);
! 163: int systrace_getcwd(struct fsystrace *, struct str_process *);
! 164: int systrace_fname(struct str_process *, caddr_t, size_t);
! 165: void systrace_replacefree(struct str_process *);
! 166:
! 167: int systrace_processready(struct str_process *);
! 168: struct proc *systrace_find(struct str_process *);
! 169: struct str_process *systrace_findpid(struct fsystrace *fst, pid_t pid);
! 170: void systrace_wakeup(struct fsystrace *);
! 171: void systrace_closepolicy(struct fsystrace *, struct str_policy *);
! 172: int systrace_insert_process(struct fsystrace *, struct proc *);
! 173: struct str_policy *systrace_newpolicy(struct fsystrace *, int);
! 174: int systrace_msg_child(struct fsystrace *, struct str_process *, pid_t);
! 175: int systrace_msg_policyfree(struct fsystrace *, struct str_policy *);
! 176: int systrace_msg_ask(struct fsystrace *, struct str_process *,
! 177: int, size_t, register_t []);
! 178: int systrace_msg_result(struct fsystrace *, struct str_process *,
! 179: int, int, size_t, register_t [], register_t []);
! 180: int systrace_msg_emul(struct fsystrace *, struct str_process *);
! 181: int systrace_msg_ugid(struct fsystrace *, struct str_process *);
! 182: int systrace_make_msg(struct str_process *, int);
! 183:
! 184: static struct fileops systracefops = {
! 185: systracef_read,
! 186: systracef_write,
! 187: systracef_ioctl,
! 188: systracef_poll,
! 189: systracef_kqfilter,
! 190: systracef_stat,
! 191: systracef_close
! 192: };
! 193:
! 194: struct pool systr_proc_pl;
! 195: struct pool systr_policy_pl;
! 196:
! 197: int systrace_debug = 0;
! 198:
! 199: #define DPRINTF(y) if (systrace_debug) printf y;
! 200:
! 201: /* ARGSUSED */
! 202: int
! 203: systracef_read(fp, poff, uio, cred)
! 204: struct file *fp;
! 205: off_t *poff;
! 206: struct uio *uio;
! 207: struct ucred *cred;
! 208: {
! 209: struct fsystrace *fst = (struct fsystrace *)fp->f_data;
! 210: struct str_process *process;
! 211: int error = 0;
! 212:
! 213: if (uio->uio_resid != sizeof(struct str_message))
! 214: return (EINVAL);
! 215:
! 216: again:
! 217: systrace_lock();
! 218: rw_enter_write(&fst->lock);
! 219: systrace_unlock();
! 220: if ((process = TAILQ_FIRST(&fst->messages)) != NULL) {
! 221: error = uiomove((caddr_t)&process->msg,
! 222: sizeof(struct str_message), uio);
! 223: if (!error) {
! 224: TAILQ_REMOVE(&fst->messages, process, msg_next);
! 225: CLR(process->flags, STR_PROC_ONQUEUE);
! 226:
! 227: if (SYSTR_MSG_NOPROCESS(process))
! 228: pool_put(&systr_proc_pl, process);
! 229:
! 230: }
! 231: } else if (TAILQ_FIRST(&fst->processes) == NULL) {
! 232: /* EOF situation */
! 233: ;
! 234: } else {
! 235: if (fp->f_flag & FNONBLOCK)
! 236: error = EAGAIN;
! 237: else {
! 238: rw_exit_write(&fst->lock);
! 239: error = tsleep(fst, PWAIT|PCATCH, "systrrd", 0);
! 240: if (error)
! 241: goto out;
! 242: goto again;
! 243: }
! 244:
! 245: }
! 246:
! 247: rw_exit_write(&fst->lock);
! 248: out:
! 249: return (error);
! 250: }
! 251:
! 252: /* ARGSUSED */
! 253: int
! 254: systracef_write(fp, poff, uio, cred)
! 255: struct file *fp;
! 256: off_t *poff;
! 257: struct uio *uio;
! 258: struct ucred *cred;
! 259: {
! 260: return (EIO);
! 261: }
! 262:
! 263: #define POLICY_VALID(x) ((x) == SYSTR_POLICY_PERMIT || \
! 264: (x) == SYSTR_POLICY_ASK || \
! 265: (x) == SYSTR_POLICY_NEVER)
! 266:
! 267: /* ARGSUSED */
! 268: int
! 269: systracef_ioctl(fp, cmd, data, p)
! 270: struct file *fp;
! 271: u_long cmd;
! 272: caddr_t data;
! 273: struct proc *p;
! 274: {
! 275: int ret = 0;
! 276: struct fsystrace *fst = (struct fsystrace *)fp->f_data;
! 277: struct filedesc *fdp;
! 278: struct str_process *strp;
! 279: pid_t pid = 0;
! 280:
! 281: switch (cmd) {
! 282: case FIONBIO:
! 283: case FIOASYNC:
! 284: return (0);
! 285:
! 286: case STRIOCDETACH:
! 287: case STRIOCREPORT:
! 288: pid = *(pid_t *)data;
! 289: if (!pid)
! 290: ret = EINVAL;
! 291: break;
! 292: case STRIOCANSWER:
! 293: pid = ((struct systrace_answer *)data)->stra_pid;
! 294: if (!pid)
! 295: ret = EINVAL;
! 296: break;
! 297: case STRIOCIO:
! 298: pid = ((struct systrace_io *)data)->strio_pid;
! 299: if (!pid)
! 300: ret = EINVAL;
! 301: break;
! 302: case STRIOCSCRIPTNAME:
! 303: pid = ((struct systrace_scriptname *)data)->sn_pid;
! 304: if (!pid)
! 305: ret = EINVAL;
! 306: break;
! 307: case STRIOCINJECT:
! 308: pid = ((struct systrace_inject *)data)->stri_pid;
! 309: if (!pid)
! 310: ret = EINVAL;
! 311: break;
! 312: case STRIOCGETCWD:
! 313: pid = *(pid_t *)data;
! 314: if (!pid)
! 315: ret = EINVAL;
! 316: break;
! 317: case STRIOCATTACH:
! 318: case STRIOCRESCWD:
! 319: case STRIOCPOLICY:
! 320: break;
! 321: case STRIOCREPLACE:
! 322: pid = ((struct systrace_replace *)data)->strr_pid;
! 323: if (!pid)
! 324: ret = EINVAL;
! 325: break;
! 326: default:
! 327: ret = EINVAL;
! 328: break;
! 329: }
! 330:
! 331: if (ret)
! 332: return (ret);
! 333:
! 334: systrace_lock();
! 335: rw_enter_write(&fst->lock);
! 336: systrace_unlock();
! 337: if (pid) {
! 338: strp = systrace_findpid(fst, pid);
! 339: if (strp == NULL) {
! 340: ret = ESRCH;
! 341: goto unlock;
! 342: }
! 343: }
! 344:
! 345: switch (cmd) {
! 346: case STRIOCATTACH:
! 347: pid = *(pid_t *)data;
! 348: if (!pid)
! 349: ret = EINVAL;
! 350: else
! 351: ret = systrace_attach(fst, pid);
! 352: DPRINTF(("%s: attach to %u: %d\n", __func__, pid, ret));
! 353: break;
! 354: case STRIOCDETACH:
! 355: ret = systrace_detach(strp);
! 356: break;
! 357: case STRIOCREPORT:
! 358: SET(strp->flags, STR_PROC_REPORT);
! 359: break;
! 360: case STRIOCANSWER:
! 361: ret = systrace_answer(strp, (struct systrace_answer *)data);
! 362: break;
! 363: case STRIOCIO:
! 364: ret = systrace_io(strp, (struct systrace_io *)data);
! 365: break;
! 366: case STRIOCSCRIPTNAME:
! 367: ret = systrace_setscriptname(strp,
! 368: (struct systrace_scriptname *)data);
! 369: break;
! 370: case STRIOCINJECT:
! 371: ret = systrace_prepinject(strp, (struct systrace_inject *)data);
! 372: break;
! 373: case STRIOCPOLICY:
! 374: ret = systrace_policy(fst, (struct systrace_policy *)data);
! 375: break;
! 376: case STRIOCREPLACE:
! 377: ret = systrace_preprepl(strp, (struct systrace_replace *)data);
! 378: break;
! 379: case STRIOCRESCWD:
! 380: if (!fst->fd_pid) {
! 381: ret = EINVAL;
! 382: break;
! 383: }
! 384: fdp = p->p_fd;
! 385:
! 386: /* Release cwd from other process */
! 387: if (fdp->fd_cdir)
! 388: vrele(fdp->fd_cdir);
! 389: if (fdp->fd_rdir)
! 390: vrele(fdp->fd_rdir);
! 391: /* This restores the cwd we had before */
! 392: fdp->fd_cdir = fst->fd_cdir;
! 393: fdp->fd_rdir = fst->fd_rdir;
! 394: /* Note that we are normal again */
! 395: fst->fd_pid = 0;
! 396: fst->fd_cdir = fst->fd_rdir = NULL;
! 397: break;
! 398: case STRIOCGETCWD:
! 399: ret = systrace_getcwd(fst, strp);
! 400: break;
! 401: default:
! 402: ret = EINVAL;
! 403: break;
! 404: }
! 405:
! 406: unlock:
! 407: rw_exit_write(&fst->lock);
! 408: return (ret);
! 409: }
! 410:
! 411: /* ARGSUSED */
! 412: int
! 413: systracef_poll(fp, events, p)
! 414: struct file *fp;
! 415: int events;
! 416: struct proc *p;
! 417: {
! 418: struct fsystrace *fst = (struct fsystrace *)fp->f_data;
! 419: int revents = 0;
! 420:
! 421: if ((events & (POLLIN | POLLRDNORM)) == 0)
! 422: return (0);
! 423:
! 424: systrace_lock();
! 425: rw_enter_write(&fst->lock);
! 426: systrace_unlock();
! 427: if (!TAILQ_EMPTY(&fst->messages))
! 428: revents = events & (POLLIN | POLLRDNORM);
! 429: else
! 430: selrecord(p, &fst->si);
! 431: rw_exit_write(&fst->lock);
! 432:
! 433: return (revents);
! 434: }
! 435:
! 436: /* ARGSUSED */
! 437: int
! 438: systracef_kqfilter(fp, kn)
! 439: struct file *fp;
! 440: struct knote *kn;
! 441: {
! 442: return (1);
! 443: }
! 444:
! 445: /* ARGSUSED */
! 446: int
! 447: systracef_stat(fp, sb, p)
! 448: struct file *fp;
! 449: struct stat *sb;
! 450: struct proc *p;
! 451: {
! 452: return (EOPNOTSUPP);
! 453: }
! 454:
! 455: /* ARGSUSED */
! 456: int
! 457: systracef_close(fp, p)
! 458: struct file *fp;
! 459: struct proc *p;
! 460: {
! 461: struct fsystrace *fst = (struct fsystrace *)fp->f_data;
! 462: struct str_process *strp;
! 463: struct str_policy *strpol;
! 464:
! 465: systrace_lock();
! 466: rw_enter_write(&fst->lock);
! 467: systrace_unlock();
! 468:
! 469: /* Untrace all processes */
! 470: for (strp = TAILQ_FIRST(&fst->processes); strp;
! 471: strp = TAILQ_FIRST(&fst->processes)) {
! 472: struct proc *q = strp->proc;
! 473:
! 474: systrace_detach(strp);
! 475: psignal(q, SIGKILL);
! 476: }
! 477:
! 478: /* Clean up fork and exit messages */
! 479: for (strp = TAILQ_FIRST(&fst->messages); strp;
! 480: strp = TAILQ_FIRST(&fst->messages)) {
! 481: TAILQ_REMOVE(&fst->messages, strp, msg_next);
! 482: pool_put(&systr_proc_pl, strp);
! 483: }
! 484:
! 485: /* Clean up all policies */
! 486: for (strpol = TAILQ_FIRST(&fst->policies); strpol;
! 487: strpol = TAILQ_FIRST(&fst->policies))
! 488: systrace_closepolicy(fst, strpol);
! 489:
! 490: /* Release vnodes */
! 491: if (fst->fd_cdir)
! 492: vrele(fst->fd_cdir);
! 493: if (fst->fd_rdir)
! 494: vrele(fst->fd_rdir);
! 495: rw_exit_write(&fst->lock);
! 496:
! 497: FREE(fp->f_data, M_XDATA);
! 498: fp->f_data = NULL;
! 499:
! 500: return (0);
! 501: }
! 502:
! 503: void
! 504: systraceattach(int n)
! 505: {
! 506: pool_init(&systr_proc_pl, sizeof(struct str_process), 0, 0, 0,
! 507: "strprocpl", NULL);
! 508: pool_init(&systr_policy_pl, sizeof(struct str_policy), 0, 0, 0,
! 509: "strpolpl", NULL);
! 510: rw_init(&systrace_lck, "systrace");
! 511: }
! 512:
! 513: int
! 514: systraceopen(dev, flag, mode, p)
! 515: dev_t dev;
! 516: int flag;
! 517: int mode;
! 518: struct proc *p;
! 519: {
! 520: return (0);
! 521: }
! 522:
! 523: int
! 524: systraceclose(dev, flag, mode, p)
! 525: dev_t dev;
! 526: int flag;
! 527: int mode;
! 528: struct proc *p;
! 529: {
! 530: return (0);
! 531: }
! 532:
! 533: int
! 534: systraceread(dev, uio, ioflag)
! 535: dev_t dev;
! 536: struct uio *uio;
! 537: int ioflag;
! 538: {
! 539: return (EIO);
! 540: }
! 541:
! 542: int
! 543: systracewrite(dev, uio, ioflag)
! 544: dev_t dev;
! 545: struct uio *uio;
! 546: int ioflag;
! 547: {
! 548: return (EIO);
! 549: }
! 550:
! 551: int
! 552: systraceioctl(dev, cmd, data, flag, p)
! 553: dev_t dev;
! 554: u_long cmd;
! 555: caddr_t data;
! 556: int flag;
! 557: struct proc *p;
! 558: {
! 559: struct file *f;
! 560: struct fsystrace *fst = NULL;
! 561: int fd, error;
! 562:
! 563: switch (cmd) {
! 564: case STRIOCCLONE:
! 565: MALLOC(fst, struct fsystrace *, sizeof(struct fsystrace),
! 566: M_XDATA, M_WAITOK);
! 567:
! 568: memset(fst, 0, sizeof(struct fsystrace));
! 569: rw_init(&fst->lock, "systrace");
! 570: TAILQ_INIT(&fst->processes);
! 571: TAILQ_INIT(&fst->messages);
! 572: TAILQ_INIT(&fst->policies);
! 573:
! 574: if (suser(p, 0) == 0)
! 575: fst->issuser = 1;
! 576: fst->p_ruid = p->p_cred->p_ruid;
! 577: fst->p_rgid = p->p_cred->p_rgid;
! 578:
! 579: error = falloc(p, &f, &fd);
! 580: if (error) {
! 581: FREE(fst, M_XDATA);
! 582: return (error);
! 583: }
! 584: f->f_flag = FREAD | FWRITE;
! 585: f->f_type = DTYPE_SYSTRACE;
! 586: f->f_ops = &systracefops;
! 587: f->f_data = (caddr_t) fst;
! 588: *(int *)data = fd;
! 589: FILE_SET_MATURE(f);
! 590: break;
! 591: default:
! 592: error = EINVAL;
! 593: break;
! 594: }
! 595: return (error);
! 596: }
! 597:
! 598: int
! 599: systracepoll(dev, events, p)
! 600: dev_t dev;
! 601: int events;
! 602: struct proc *p;
! 603: {
! 604: return (seltrue(dev, events, p));
! 605: }
! 606:
! 607: void
! 608: systrace_wakeup(struct fsystrace *fst)
! 609: {
! 610: wakeup((caddr_t)fst);
! 611: selwakeup(&fst->si);
! 612: }
! 613:
! 614: struct proc *
! 615: systrace_find(struct str_process *strp)
! 616: {
! 617: struct proc *proc;
! 618:
! 619: if ((proc = pfind(strp->pid)) == NULL)
! 620: return (NULL);
! 621:
! 622: if (proc != strp->proc)
! 623: return (NULL);
! 624:
! 625: if (!ISSET(proc->p_flag, P_SYSTRACE))
! 626: return (NULL);
! 627:
! 628: return (proc);
! 629: }
! 630:
! 631: void
! 632: systrace_exit(struct proc *proc)
! 633: {
! 634: struct str_process *strp;
! 635: struct fsystrace *fst;
! 636:
! 637: systrace_lock();
! 638: strp = proc->p_systrace;
! 639: if (strp != NULL) {
! 640: fst = strp->parent;
! 641: rw_enter_write(&fst->lock);
! 642: systrace_unlock();
! 643:
! 644: /* Insert Exit message */
! 645: systrace_msg_child(fst, strp, -1);
! 646:
! 647: systrace_detach(strp);
! 648: rw_exit_write(&fst->lock);
! 649: } else
! 650: systrace_unlock();
! 651: atomic_clearbits_int(&proc->p_flag, P_SYSTRACE);
! 652: }
! 653:
! 654: void
! 655: systrace_fork(struct proc *oldproc, struct proc *p)
! 656: {
! 657: struct str_process *oldstrp, *strp;
! 658: struct fsystrace *fst;
! 659:
! 660: systrace_lock();
! 661: oldstrp = oldproc->p_systrace;
! 662: if (oldstrp == NULL) {
! 663: systrace_unlock();
! 664: return;
! 665: }
! 666:
! 667: fst = oldstrp->parent;
! 668: rw_enter_write(&fst->lock);
! 669: systrace_unlock();
! 670:
! 671: if (systrace_insert_process(fst, p))
! 672: goto out;
! 673: if ((strp = systrace_findpid(fst, p->p_pid)) == NULL)
! 674: panic("systrace_fork");
! 675:
! 676: /* Reference policy */
! 677: if ((strp->policy = oldstrp->policy) != NULL)
! 678: strp->policy->refcount++;
! 679:
! 680: /* Insert fork message */
! 681: systrace_msg_child(fst, oldstrp, p->p_pid);
! 682: out:
! 683: rw_exit_write(&fst->lock);
! 684: }
! 685:
! 686: #define REACQUIRE_LOCK do { \
! 687: systrace_lock(); \
! 688: strp = p->p_systrace; \
! 689: if (strp == NULL) { \
! 690: systrace_unlock(); \
! 691: return (error); \
! 692: } \
! 693: fst = strp->parent; \
! 694: rw_enter_write(&fst->lock); \
! 695: systrace_unlock(); \
! 696: } while (0)
! 697:
! 698: int
! 699: systrace_redirect(int code, struct proc *p, void *v, register_t *retval)
! 700: {
! 701: struct sysent *callp;
! 702: struct str_process *strp;
! 703: struct str_policy *strpolicy;
! 704: struct fsystrace *fst = NULL;
! 705: struct emul *oldemul;
! 706: struct pcred *pc;
! 707: uid_t olduid;
! 708: gid_t oldgid;
! 709: int policy, error = 0, report = 0, maycontrol = 0, issuser = 0;
! 710:
! 711: systrace_lock();
! 712: strp = p->p_systrace;
! 713: if (strp == NULL) {
! 714: systrace_unlock();
! 715: return (EINVAL);
! 716: }
! 717:
! 718: if (code < 0 || code >= p->p_emul->e_nsysent) {
! 719: systrace_unlock();
! 720: return (EINVAL);
! 721: }
! 722:
! 723: KASSERT(strp->proc == p);
! 724:
! 725: fst = strp->parent;
! 726:
! 727: rw_enter_write(&fst->lock);
! 728: systrace_unlock();
! 729:
! 730: /*
! 731: * We can not monitor a SUID process unless we are root,
! 732: * but we wait until it executes something unprivileged.
! 733: * A non-root user may only monitor if the real uid and
! 734: * real gid match the monitored process. Changing the
! 735: * uid or gid causes P_SUGID to be set.
! 736: */
! 737: if (fst->issuser) {
! 738: maycontrol = 1;
! 739: issuser = 1;
! 740: } else if (!ISSET(p->p_flag, P_SUGID) &&
! 741: !ISSET(p->p_flag, P_SUGIDEXEC)) {
! 742: maycontrol = fst->p_ruid == p->p_cred->p_ruid &&
! 743: fst->p_rgid == p->p_cred->p_rgid;
! 744: }
! 745:
! 746: if (!maycontrol) {
! 747: policy = SYSTR_POLICY_PERMIT;
! 748: } else {
! 749: /* Find out current policy */
! 750: if ((strpolicy = strp->policy) == NULL)
! 751: policy = SYSTR_POLICY_ASK;
! 752: else {
! 753: if (code >= strpolicy->nsysent)
! 754: policy = SYSTR_POLICY_NEVER;
! 755: else
! 756: policy = strpolicy->sysent[code];
! 757: }
! 758: }
! 759:
! 760: callp = p->p_emul->e_sysent + code;
! 761:
! 762: /* Fast-path */
! 763: if (policy != SYSTR_POLICY_ASK) {
! 764: if (policy != SYSTR_POLICY_PERMIT) {
! 765: if (policy > 0)
! 766: error = policy;
! 767: else
! 768: error = EPERM;
! 769: }
! 770: systrace_replacefree(strp);
! 771: rw_exit_write(&fst->lock);
! 772: if (policy == SYSTR_POLICY_PERMIT)
! 773: error = (*callp->sy_call)(p, v, retval);
! 774: return (error);
! 775: }
! 776:
! 777: /*
! 778: * Reset our stackgap allocation. Note that when resetting
! 779: * the stackgap allocation, we expect to get the same address
! 780: * base; i.e. that stackgap_init() is idempotent.
! 781: */
! 782: systrace_inject(strp, 0 /* Just reset internal state */);
! 783: strp->sg = stackgap_init(p->p_emul);
! 784:
! 785: /* Puts the current process to sleep, return unlocked */
! 786: error = systrace_msg_ask(fst, strp, code, callp->sy_argsize, v);
! 787: /* lock has been released in systrace_msg_ask() */
! 788:
! 789: if (error)
! 790: return (error);
! 791:
! 792: /* We might have detached by now for some reason */
! 793: systrace_lock();
! 794: if ((strp = p->p_systrace) == NULL) {
! 795: systrace_unlock();
! 796: return (error);
! 797: }
! 798:
! 799: fst = strp->parent;
! 800: rw_enter_write(&fst->lock);
! 801: systrace_unlock();
! 802:
! 803: if (strp->answer == SYSTR_POLICY_NEVER) {
! 804: error = strp->error;
! 805: systrace_replacefree(strp);
! 806: goto out_unlock;
! 807: }
! 808:
! 809: if (ISSET(strp->flags, STR_PROC_SYSCALLRES)) {
! 810: CLR(strp->flags, STR_PROC_SYSCALLRES);
! 811: report = 1;
! 812: }
! 813:
! 814: error = systrace_inject(strp, 1/* Perform copies */);
! 815: /* Replace the arguments if necessary */
! 816: if (!error && strp->replace != NULL)
! 817: error = systrace_replace(strp, callp->sy_argsize, v);
! 818: if (error)
! 819: goto out_unlock;
! 820:
! 821: oldemul = p->p_emul;
! 822: pc = p->p_cred;
! 823: olduid = pc->p_ruid;
! 824: oldgid = pc->p_rgid;
! 825:
! 826: /* Elevate privileges as desired */
! 827: if (issuser) {
! 828: if (ISSET(strp->flags, STR_PROC_SETEUID))
! 829: strp->saveuid = systrace_seteuid(p, strp->seteuid);
! 830: if (ISSET(strp->flags, STR_PROC_SETEGID))
! 831: strp->savegid = systrace_setegid(p, strp->setegid);
! 832: } else
! 833: CLR(strp->flags, STR_PROC_SETEUID|STR_PROC_SETEGID);
! 834:
! 835: rw_exit_write(&fst->lock);
! 836:
! 837: error = (*callp->sy_call)(p, v, retval);
! 838:
! 839: /* Return to old privileges */
! 840: systrace_lock();
! 841: if ((strp = p->p_systrace) == NULL) {
! 842: systrace_unlock();
! 843: return (error);
! 844: }
! 845:
! 846: if (issuser) {
! 847: if (ISSET(strp->flags, STR_PROC_SETEUID)) {
! 848: if (pc->pc_ucred->cr_uid == strp->seteuid)
! 849: systrace_seteuid(p, strp->saveuid);
! 850: CLR(strp->flags, STR_PROC_SETEUID);
! 851: }
! 852: if (ISSET(strp->flags, STR_PROC_SETEGID)) {
! 853: if (pc->pc_ucred->cr_gid == strp->setegid)
! 854: systrace_setegid(p, strp->savegid);
! 855: CLR(strp->flags, STR_PROC_SETEGID);
! 856: }
! 857: }
! 858:
! 859: systrace_replacefree(strp);
! 860:
! 861: if (ISSET(p->p_flag, P_SUGID) || ISSET(p->p_flag, P_SUGIDEXEC)) {
! 862: if ((fst = strp->parent) == NULL || !fst->issuser) {
! 863: systrace_unlock();
! 864: return (error);
! 865: }
! 866: }
! 867:
! 868: /* Report change in emulation */
! 869:
! 870: /* See if we should force a report */
! 871: if (ISSET(strp->flags, STR_PROC_REPORT)) {
! 872: CLR(strp->flags, STR_PROC_REPORT);
! 873: oldemul = NULL;
! 874: }
! 875:
! 876: /* Acquire lock */
! 877: fst = strp->parent;
! 878: rw_enter_write(&fst->lock);
! 879: systrace_unlock();
! 880:
! 881: if (p->p_emul != oldemul) {
! 882: /* Old policy is without meaning now */
! 883: if (strp->policy) {
! 884: systrace_closepolicy(fst, strp->policy);
! 885: strp->policy = NULL;
! 886: }
! 887: systrace_msg_emul(fst, strp);
! 888:
! 889: REACQUIRE_LOCK;
! 890: }
! 891:
! 892: /* Report if effective uid or gid changed */
! 893: if (olduid != p->p_cred->p_ruid ||
! 894: oldgid != p->p_cred->p_rgid) {
! 895: systrace_msg_ugid(fst, strp);
! 896:
! 897: REACQUIRE_LOCK;
! 898: }
! 899:
! 900: /* Report result from system call */
! 901: if (report) {
! 902: systrace_msg_result(fst, strp, error, code,
! 903: callp->sy_argsize, v, retval);
! 904:
! 905: /* not locked */
! 906: goto out;
! 907: }
! 908:
! 909: out_unlock:
! 910: rw_exit_write(&fst->lock);
! 911: out:
! 912: return (error);
! 913: }
! 914:
! 915: uid_t
! 916: systrace_seteuid(struct proc *p, uid_t euid)
! 917: {
! 918: struct pcred *pc = p->p_cred;
! 919: uid_t oeuid = pc->pc_ucred->cr_uid;
! 920:
! 921: if (pc->pc_ucred->cr_uid == euid)
! 922: return (oeuid);
! 923:
! 924: /*
! 925: * Copy credentials so other references do not see our changes.
! 926: */
! 927: pc->pc_ucred = crcopy(pc->pc_ucred);
! 928: pc->pc_ucred->cr_uid = euid;
! 929: atomic_setbits_int(&p->p_flag, P_SUGID);
! 930:
! 931: return (oeuid);
! 932: }
! 933:
! 934: gid_t
! 935: systrace_setegid(struct proc *p, gid_t egid)
! 936: {
! 937: struct pcred *pc = p->p_cred;
! 938: gid_t oegid = pc->pc_ucred->cr_gid;
! 939:
! 940: if (pc->pc_ucred->cr_gid == egid)
! 941: return (oegid);
! 942:
! 943: /*
! 944: * Copy credentials so other references do not see our changes.
! 945: */
! 946: pc->pc_ucred = crcopy(pc->pc_ucred);
! 947: pc->pc_ucred->cr_gid = egid;
! 948: atomic_setbits_int(&p->p_flag, P_SUGID);
! 949:
! 950: return (oegid);
! 951: }
! 952:
! 953: /* Called with fst locked */
! 954:
! 955: int
! 956: systrace_answer(struct str_process *strp, struct systrace_answer *ans)
! 957: {
! 958: int error = 0;
! 959:
! 960: DPRINTF(("%s: %u: policy %d\n", __func__,
! 961: ans->stra_pid, ans->stra_policy));
! 962:
! 963: if (!POLICY_VALID(ans->stra_policy)) {
! 964: error = EINVAL;
! 965: goto out;
! 966: }
! 967:
! 968: /* Check if answer is in sync with us */
! 969: if (ans->stra_seqnr != strp->seqnr) {
! 970: error = ESRCH;
! 971: goto out;
! 972: }
! 973:
! 974: if ((error = systrace_processready(strp)) != 0)
! 975: goto out;
! 976:
! 977: strp->answer = ans->stra_policy;
! 978: strp->error = ans->stra_error;
! 979: if (!strp->error)
! 980: strp->error = EPERM;
! 981: if (ISSET(ans->stra_flags, SYSTR_FLAGS_RESULT))
! 982: SET(strp->flags, STR_PROC_SYSCALLRES);
! 983:
! 984: /* See if we should elevate privileges for this system call */
! 985: if (ISSET(ans->stra_flags, SYSTR_FLAGS_SETEUID)) {
! 986: SET(strp->flags, STR_PROC_SETEUID);
! 987: strp->seteuid = ans->stra_seteuid;
! 988: }
! 989: if (ISSET(ans->stra_flags, SYSTR_FLAGS_SETEGID)) {
! 990: SET(strp->flags, STR_PROC_SETEGID);
! 991: strp->setegid = ans->stra_setegid;
! 992: }
! 993:
! 994: /* Clearing the flag indicates to the process that it woke up */
! 995: CLR(strp->flags, STR_PROC_WAITANSWER);
! 996: wakeup(strp);
! 997: out:
! 998:
! 999: return (error);
! 1000: }
! 1001:
! 1002: int
! 1003: systrace_setscriptname(struct str_process *strp, struct systrace_scriptname *ans)
! 1004: {
! 1005: strlcpy(strp->scriptname,
! 1006: ans->sn_scriptname, sizeof(strp->scriptname));
! 1007:
! 1008: return (0);
! 1009: }
! 1010:
! 1011: int
! 1012: systrace_inject(struct str_process *strp, int docopy)
! 1013: {
! 1014: int ind, ret = 0;
! 1015:
! 1016: for (ind = 0; ind < strp->injectind; ind++) {
! 1017: struct str_inject *inject = &strp->injects[ind];
! 1018: if (!ret && docopy &&
! 1019: copyout(inject->kaddr, inject->uaddr, inject->len))
! 1020: ret = EINVAL;
! 1021: free(inject->kaddr, M_XDATA);
! 1022: }
! 1023:
! 1024: strp->injectind = 0;
! 1025: return (ret);
! 1026: }
! 1027:
! 1028: int
! 1029: systrace_prepinject(struct str_process *strp, struct systrace_inject *inj)
! 1030: {
! 1031: caddr_t udata, kaddr = NULL;
! 1032: int ret = 0;
! 1033: struct str_inject *inject;
! 1034:
! 1035: if (strp->injectind >= SYSTR_MAXINJECTS)
! 1036: return (ENOBUFS);
! 1037:
! 1038: udata = stackgap_alloc(&strp->sg, inj->stri_len);
! 1039: if (udata == NULL)
! 1040: return (ENOMEM);
! 1041:
! 1042: /*
! 1043: * We have infact forced a maximum length on stri_len because
! 1044: * of the stackgap.
! 1045: */
! 1046:
! 1047: kaddr = malloc(inj->stri_len, M_XDATA, M_WAITOK);
! 1048: ret = copyin(inj->stri_addr, kaddr, inj->stri_len);
! 1049: if (ret) {
! 1050: free(kaddr, M_XDATA);
! 1051: return (ret);
! 1052: }
! 1053:
! 1054: inject = &strp->injects[strp->injectind++];
! 1055: inject->kaddr = kaddr;
! 1056: inject->uaddr = inj->stri_addr = udata;
! 1057: inject->len = inj->stri_len;
! 1058:
! 1059: return (0);
! 1060: }
! 1061:
! 1062: int
! 1063: systrace_policy(struct fsystrace *fst, struct systrace_policy *pol)
! 1064: {
! 1065: struct str_policy *strpol;
! 1066: struct str_process *strp;
! 1067:
! 1068: switch(pol->strp_op) {
! 1069: case SYSTR_POLICY_NEW:
! 1070: DPRINTF(("%s: new, ents %d\n", __func__,
! 1071: pol->strp_maxents));
! 1072: if (pol->strp_maxents <= 0 || pol->strp_maxents > 1024)
! 1073: return (EINVAL);
! 1074: strpol = systrace_newpolicy(fst, pol->strp_maxents);
! 1075: if (strpol == NULL)
! 1076: return (ENOBUFS);
! 1077: pol->strp_num = strpol->nr;
! 1078: break;
! 1079: case SYSTR_POLICY_ASSIGN:
! 1080: DPRINTF(("%s: %d -> pid %d\n", __func__,
! 1081: pol->strp_num, pol->strp_pid));
! 1082:
! 1083: /* Find right policy by number */
! 1084: TAILQ_FOREACH(strpol, &fst->policies, next)
! 1085: if (strpol->nr == pol->strp_num)
! 1086: break;
! 1087: if (strpol == NULL)
! 1088: return (EINVAL);
! 1089:
! 1090: strp = systrace_findpid(fst, pol->strp_pid);
! 1091: if (strp == NULL)
! 1092: return (EINVAL);
! 1093:
! 1094: /* Check that emulation matches */
! 1095: if (strpol->emul && strpol->emul != strp->proc->p_emul)
! 1096: return (EINVAL);
! 1097:
! 1098: if (strp->policy)
! 1099: systrace_closepolicy(fst, strp->policy);
! 1100: strp->policy = strpol;
! 1101:
! 1102: /* LRU for policy use */
! 1103: TAILQ_REMOVE(&fst->policies, strpol, next);
! 1104: TAILQ_INSERT_TAIL(&fst->policies, strpol, next);
! 1105: strpol->refcount++;
! 1106:
! 1107: /* Record emulation for this policy */
! 1108: if (strpol->emul == NULL)
! 1109: strpol->emul = strp->proc->p_emul;
! 1110:
! 1111: break;
! 1112: case SYSTR_POLICY_MODIFY:
! 1113: DPRINTF(("%s: %d: code %d -> policy %d\n", __func__,
! 1114: pol->strp_num, pol->strp_code, pol->strp_policy));
! 1115: if (!POLICY_VALID(pol->strp_policy))
! 1116: return (EINVAL);
! 1117: TAILQ_FOREACH(strpol, &fst->policies, next)
! 1118: if (strpol->nr == pol->strp_num)
! 1119: break;
! 1120: if (strpol == NULL)
! 1121: return (EINVAL);
! 1122: if (pol->strp_code < 0 || pol->strp_code >= strpol->nsysent)
! 1123: return (EINVAL);
! 1124: strpol->sysent[pol->strp_code] = pol->strp_policy;
! 1125: break;
! 1126: default:
! 1127: return (EINVAL);
! 1128: }
! 1129:
! 1130: return (0);
! 1131: }
! 1132:
! 1133: int
! 1134: systrace_processready(struct str_process *strp)
! 1135: {
! 1136: if (ISSET(strp->flags, STR_PROC_ONQUEUE))
! 1137: return (EBUSY);
! 1138:
! 1139: if (!ISSET(strp->flags, STR_PROC_WAITANSWER))
! 1140: return (EBUSY);
! 1141:
! 1142: if (strp->proc->p_stat != SSLEEP)
! 1143: return (EBUSY);
! 1144:
! 1145: return (0);
! 1146: }
! 1147:
! 1148: int
! 1149: systrace_getcwd(struct fsystrace *fst, struct str_process *strp)
! 1150: {
! 1151: struct filedesc *myfdp, *fdp;
! 1152: int error;
! 1153:
! 1154: DPRINTF(("%s: %d\n", __func__, strp->pid));
! 1155:
! 1156: error = systrace_processready(strp);
! 1157: if (error)
! 1158: return (error);
! 1159:
! 1160: myfdp = curproc->p_fd;
! 1161: fdp = strp->proc->p_fd;
! 1162: if (myfdp == NULL || fdp == NULL)
! 1163: return (EINVAL);
! 1164:
! 1165: /* Store our current values */
! 1166: fst->fd_pid = strp->pid;
! 1167: fst->fd_cdir = myfdp->fd_cdir;
! 1168: fst->fd_rdir = myfdp->fd_rdir;
! 1169:
! 1170: if ((myfdp->fd_cdir = fdp->fd_cdir) != NULL)
! 1171: VREF(myfdp->fd_cdir);
! 1172: if ((myfdp->fd_rdir = fdp->fd_rdir) != NULL)
! 1173: VREF(myfdp->fd_rdir);
! 1174:
! 1175: return (0);
! 1176: }
! 1177:
! 1178: int
! 1179: systrace_io(struct str_process *strp, struct systrace_io *io)
! 1180: {
! 1181: struct proc *p = curproc, *t = strp->proc;
! 1182: struct uio uio;
! 1183: struct iovec iov;
! 1184: int error = 0;
! 1185:
! 1186: DPRINTF(("%s: %u: %p(%lu)\n", __func__,
! 1187: io->strio_pid, io->strio_offs, (u_long)io->strio_len));
! 1188:
! 1189: switch (io->strio_op) {
! 1190: case SYSTR_READ:
! 1191: uio.uio_rw = UIO_READ;
! 1192: break;
! 1193: case SYSTR_WRITE:
! 1194: uio.uio_rw = UIO_WRITE;
! 1195: break;
! 1196: default:
! 1197: return (EINVAL);
! 1198: }
! 1199:
! 1200: error = systrace_processready(strp);
! 1201: if (error)
! 1202: goto out;
! 1203:
! 1204: iov.iov_base = io->strio_addr;
! 1205: iov.iov_len = io->strio_len;
! 1206: uio.uio_iov = &iov;
! 1207: uio.uio_iovcnt = 1;
! 1208: uio.uio_offset = (off_t)(u_long)io->strio_offs;
! 1209: uio.uio_resid = io->strio_len;
! 1210: uio.uio_segflg = UIO_USERSPACE;
! 1211: uio.uio_procp = p;
! 1212:
! 1213: error = process_domem(p, t, &uio, PT_WRITE_I);
! 1214: io->strio_len -= uio.uio_resid;
! 1215: out:
! 1216:
! 1217: return (error);
! 1218: }
! 1219:
! 1220: int
! 1221: systrace_attach(struct fsystrace *fst, pid_t pid)
! 1222: {
! 1223: int error = 0;
! 1224: struct proc *proc, *p = curproc;
! 1225:
! 1226: if ((proc = pfind(pid)) == NULL) {
! 1227: error = ESRCH;
! 1228: goto out;
! 1229: }
! 1230:
! 1231: if (ISSET(proc->p_flag, P_INEXEC)) {
! 1232: error = EAGAIN;
! 1233: goto out;
! 1234: }
! 1235:
! 1236: /*
! 1237: * You can't attach to a process if:
! 1238: * (1) it's the process that's doing the attaching,
! 1239: */
! 1240: if (proc->p_pid == p->p_pid) {
! 1241: error = EINVAL;
! 1242: goto out;
! 1243: }
! 1244:
! 1245: /*
! 1246: * (2) it's a system process
! 1247: */
! 1248: if (ISSET(proc->p_flag, P_SYSTEM)) {
! 1249: error = EPERM;
! 1250: goto out;
! 1251: }
! 1252:
! 1253: /*
! 1254: * (3) it's being traced already
! 1255: */
! 1256: if (ISSET(proc->p_flag, P_SYSTRACE)) {
! 1257: error = EBUSY;
! 1258: goto out;
! 1259: }
! 1260:
! 1261: /*
! 1262: * (4) it's not owned by you, or the last exec
! 1263: * gave us setuid/setgid privs (unless
! 1264: * you're root), or...
! 1265: *
! 1266: * [Note: once P_SUGID or P_SUGIDEXEC gets set in execve(),
! 1267: * it stays set until the process does another execve(). Hence
! 1268: * this prevents a setuid process which revokes its
! 1269: * special privileges using setuid() from being
! 1270: * traced. This is good security.]
! 1271: */
! 1272: if ((proc->p_cred->p_ruid != p->p_cred->p_ruid ||
! 1273: ISSET(proc->p_flag, P_SUGID) ||
! 1274: ISSET(proc->p_flag, P_SUGIDEXEC)) &&
! 1275: (error = suser(p, 0)) != 0)
! 1276: goto out;
! 1277:
! 1278: /*
! 1279: * (5) ...it's init, which controls the security level
! 1280: * of the entire system, and the system was not
! 1281: * compiled with permanently insecure mode turned
! 1282: * on.
! 1283: */
! 1284: if ((proc->p_pid == 1) && (securelevel > -1)) {
! 1285: error = EPERM;
! 1286: goto out;
! 1287: }
! 1288:
! 1289: error = systrace_insert_process(fst, proc);
! 1290:
! 1291: out:
! 1292: return (error);
! 1293: }
! 1294:
! 1295: void
! 1296: systrace_execve0(struct proc *p)
! 1297: {
! 1298: struct str_process *strp;
! 1299:
! 1300: systrace_lock();
! 1301: strp = p->p_systrace;
! 1302: strp->isscript = 0;
! 1303: systrace_unlock();
! 1304: }
! 1305:
! 1306: void
! 1307: systrace_execve1(char *path, struct proc *p)
! 1308: {
! 1309: struct str_process *strp;
! 1310: struct fsystrace *fst;
! 1311: struct str_msg_execve *msg_execve;
! 1312:
! 1313: do {
! 1314: systrace_lock();
! 1315: strp = p->p_systrace;
! 1316: if (strp == NULL) {
! 1317: systrace_unlock();
! 1318: return;
! 1319: }
! 1320:
! 1321: msg_execve = &strp->msg.msg_data.msg_execve;
! 1322: fst = strp->parent;
! 1323: rw_enter_write(&fst->lock);
! 1324: systrace_unlock();
! 1325:
! 1326: /*
! 1327: * susers will get the execve call anyway. Also, if
! 1328: * we're not allowed to control the process, escape.
! 1329: */
! 1330:
! 1331: if (fst->issuser ||
! 1332: fst->p_ruid != p->p_cred->p_ruid ||
! 1333: fst->p_rgid != p->p_cred->p_rgid) {
! 1334: rw_exit_write(&fst->lock);
! 1335: return;
! 1336: }
! 1337: strlcpy(msg_execve->path, path, MAXPATHLEN);
! 1338: } while (systrace_make_msg(strp, SYSTR_MSG_EXECVE) != 0);
! 1339: }
! 1340:
! 1341: /* Prepare to replace arguments */
! 1342:
! 1343: int
! 1344: systrace_preprepl(struct str_process *strp, struct systrace_replace *repl)
! 1345: {
! 1346: size_t len;
! 1347: int i, ret = 0;
! 1348:
! 1349: ret = systrace_processready(strp);
! 1350: if (ret)
! 1351: return (ret);
! 1352:
! 1353: if (strp->replace != NULL) {
! 1354: free(strp->replace, M_XDATA);
! 1355: strp->replace = NULL;
! 1356: }
! 1357:
! 1358: if (repl->strr_nrepl < 0 || repl->strr_nrepl > SYSTR_MAXARGS)
! 1359: return (EINVAL);
! 1360:
! 1361: for (i = 0, len = 0; i < repl->strr_nrepl; i++) {
! 1362: if (repl->strr_argind[i] < 0 ||
! 1363: repl->strr_argind[i] >= SYSTR_MAXARGS)
! 1364: return (EINVAL);
! 1365: if (repl->strr_offlen[i] == 0)
! 1366: continue;
! 1367: len += repl->strr_offlen[i];
! 1368: if (repl->strr_offlen[i] > SYSTR_MAXREPLEN ||
! 1369: repl->strr_off[i] > SYSTR_MAXREPLEN ||
! 1370: len > SYSTR_MAXREPLEN)
! 1371: return (EINVAL);
! 1372: if (repl->strr_offlen[i] + repl->strr_off[i] > len)
! 1373: return (EINVAL);
! 1374: }
! 1375:
! 1376: /* Make sure that the length adds up */
! 1377: if (repl->strr_len != len)
! 1378: return (EINVAL);
! 1379:
! 1380: /* Check against a maximum length */
! 1381: if (repl->strr_len > SYSTR_MAXREPLEN)
! 1382: return (EINVAL);
! 1383:
! 1384: strp->replace = (struct systrace_replace *)
! 1385: malloc(sizeof(struct systrace_replace) + len, M_XDATA, M_WAITOK);
! 1386:
! 1387: memcpy(strp->replace, repl, sizeof(struct systrace_replace));
! 1388: ret = copyin(repl->strr_base, strp->replace + 1, len);
! 1389: if (ret) {
! 1390: free(strp->replace, M_XDATA);
! 1391: strp->replace = NULL;
! 1392: return (ret);
! 1393: }
! 1394:
! 1395: /* Adjust the offset */
! 1396: repl = strp->replace;
! 1397: repl->strr_base = (caddr_t)(repl + 1);
! 1398:
! 1399: return (0);
! 1400: }
! 1401:
! 1402: /*
! 1403: * Replace the arguments with arguments from the monitoring process.
! 1404: */
! 1405:
! 1406: int
! 1407: systrace_replace(struct str_process *strp, size_t argsize, register_t args[])
! 1408: {
! 1409: struct systrace_replace *repl = strp->replace;
! 1410: caddr_t kdata, kbase;
! 1411: caddr_t udata, ubase;
! 1412: int i, maxarg, ind, ret = 0;
! 1413:
! 1414: maxarg = argsize/sizeof(register_t);
! 1415: ubase = stackgap_alloc(&strp->sg, repl->strr_len);
! 1416: if (ubase == NULL) {
! 1417: ret = EINVAL;
! 1418: goto out;
! 1419: }
! 1420:
! 1421: kbase = repl->strr_base;
! 1422: for (i = 0; i < maxarg && i < repl->strr_nrepl; i++) {
! 1423: ind = repl->strr_argind[i];
! 1424: if (ind < 0 || ind >= maxarg) {
! 1425: ret = EINVAL;
! 1426: goto out;
! 1427: }
! 1428: if (repl->strr_offlen[i] == 0) {
! 1429: args[ind] = repl->strr_off[i];
! 1430: continue;
! 1431: }
! 1432: kdata = kbase + repl->strr_off[i];
! 1433: if (repl->strr_flags[i] & SYSTR_NOLINKS) {
! 1434: ret = systrace_fname(strp, kdata, repl->strr_offlen[i]);
! 1435: if (ret != 0)
! 1436: goto out;
! 1437: }
! 1438: udata = ubase + repl->strr_off[i];
! 1439: if (copyout(kdata, udata, repl->strr_offlen[i])) {
! 1440: ret = EINVAL;
! 1441: goto out;
! 1442: }
! 1443:
! 1444: /* Replace the argument with the new address */
! 1445: args[ind] = (register_t)udata;
! 1446: }
! 1447:
! 1448: out:
! 1449: return (ret);
! 1450: }
! 1451:
! 1452: int
! 1453: systrace_fname(struct str_process *strp, caddr_t kdata, size_t len)
! 1454: {
! 1455: if (strp->nfname >= SYSTR_MAXFNAME || len < 1)
! 1456: return EINVAL;
! 1457:
! 1458: strp->fname[strp->nfname] = kdata;
! 1459: strp->fname[strp->nfname][len - 1] = '\0';
! 1460: strp->nfname++;
! 1461:
! 1462: return 0;
! 1463: }
! 1464:
! 1465: void
! 1466: systrace_replacefree(struct str_process *strp)
! 1467: {
! 1468: if (strp->replace != NULL) {
! 1469: free(strp->replace, M_XDATA);
! 1470: strp->replace = NULL;
! 1471: }
! 1472: while (strp->nfname > 0) {
! 1473: strp->nfname--;
! 1474: strp->fname[strp->nfname] = NULL;
! 1475: }
! 1476: }
! 1477: int
! 1478: systrace_scriptname(struct proc *p, char *dst)
! 1479: {
! 1480: struct str_process *strp;
! 1481: struct fsystrace *fst;
! 1482: int error = 0;
! 1483:
! 1484: systrace_lock();
! 1485: strp = p->p_systrace;
! 1486: fst = strp->parent;
! 1487:
! 1488: rw_enter_write(&fst->lock);
! 1489: systrace_unlock();
! 1490:
! 1491: if (!fst->issuser && (ISSET(p->p_flag, P_SUGID) ||
! 1492: ISSET(p->p_flag, P_SUGIDEXEC) ||
! 1493: fst->p_ruid != p->p_cred->p_ruid ||
! 1494: fst->p_rgid != p->p_cred->p_rgid)) {
! 1495: error = EPERM;
! 1496: goto out;
! 1497: }
! 1498:
! 1499: if (strp != NULL) {
! 1500: if (strp->scriptname[0] == '\0') {
! 1501: error = ENOENT;
! 1502: goto out;
! 1503: }
! 1504:
! 1505: strlcpy(dst, strp->scriptname, MAXPATHLEN);
! 1506: strp->isscript = 1;
! 1507: }
! 1508:
! 1509: out:
! 1510: strp->scriptname[0] = '\0';
! 1511: rw_exit_write(&fst->lock);
! 1512:
! 1513: return (error);
! 1514: }
! 1515:
! 1516: void
! 1517: systrace_namei(struct nameidata *ndp)
! 1518: {
! 1519: struct str_process *strp;
! 1520: struct fsystrace *fst;
! 1521: struct componentname *cnp = &ndp->ni_cnd;
! 1522: size_t i;
! 1523: int hamper = 0;
! 1524:
! 1525: systrace_lock();
! 1526: strp = cnp->cn_proc->p_systrace;
! 1527: if (strp != NULL) {
! 1528: fst = strp->parent;
! 1529: rw_enter_write(&fst->lock);
! 1530: systrace_unlock();
! 1531:
! 1532: for (i = 0; i < strp->nfname; i++)
! 1533: if (strcmp(cnp->cn_pnbuf, strp->fname[i]) == 0) {
! 1534: hamper = 1;
! 1535: break;
! 1536: }
! 1537:
! 1538: if (!hamper && strp->isscript &&
! 1539: strcmp(cnp->cn_pnbuf, strp->scriptname) == 0)
! 1540: hamper = 1;
! 1541:
! 1542: rw_exit_write(&fst->lock);
! 1543: } else
! 1544: systrace_unlock();
! 1545:
! 1546: if (hamper) {
! 1547: /* ELOOP if namei() tries to readlink */
! 1548: ndp->ni_loopcnt = MAXSYMLINKS;
! 1549: cnp->cn_flags &= ~FOLLOW;
! 1550: cnp->cn_flags |= NOFOLLOW;
! 1551: }
! 1552: }
! 1553:
! 1554: struct str_process *
! 1555: systrace_findpid(struct fsystrace *fst, pid_t pid)
! 1556: {
! 1557: struct str_process *strp;
! 1558: struct proc *proc = NULL;
! 1559:
! 1560: TAILQ_FOREACH(strp, &fst->processes, next)
! 1561: if (strp->pid == pid)
! 1562: break;
! 1563:
! 1564: if (strp == NULL)
! 1565: return (NULL);
! 1566:
! 1567: proc = systrace_find(strp);
! 1568:
! 1569: return (proc ? strp : NULL);
! 1570: }
! 1571:
! 1572: int
! 1573: systrace_detach(struct str_process *strp)
! 1574: {
! 1575: struct proc *proc;
! 1576: struct fsystrace *fst = NULL;
! 1577: int error = 0;
! 1578:
! 1579: DPRINTF(("%s: Trying to detach from %d\n", __func__, strp->pid));
! 1580:
! 1581: if ((proc = systrace_find(strp)) != NULL) {
! 1582: atomic_clearbits_int(&proc->p_flag, P_SYSTRACE);
! 1583: proc->p_systrace = NULL;
! 1584: } else
! 1585: error = ESRCH;
! 1586:
! 1587: if (ISSET(strp->flags, STR_PROC_WAITANSWER)) {
! 1588: CLR(strp->flags, STR_PROC_WAITANSWER);
! 1589: wakeup(strp);
! 1590: }
! 1591:
! 1592: fst = strp->parent;
! 1593: systrace_wakeup(fst);
! 1594:
! 1595: if (ISSET(strp->flags, STR_PROC_ONQUEUE))
! 1596: TAILQ_REMOVE(&fst->messages, strp, msg_next);
! 1597:
! 1598: TAILQ_REMOVE(&fst->processes, strp, next);
! 1599: fst->nprocesses--;
! 1600:
! 1601: if (strp->policy)
! 1602: systrace_closepolicy(fst, strp->policy);
! 1603: systrace_replacefree(strp);
! 1604: pool_put(&systr_proc_pl, strp);
! 1605:
! 1606: return (error);
! 1607: }
! 1608:
! 1609: void
! 1610: systrace_closepolicy(struct fsystrace *fst, struct str_policy *policy)
! 1611: {
! 1612: if (--policy->refcount)
! 1613: return;
! 1614:
! 1615: fst->npolicies--;
! 1616:
! 1617: if (policy->nsysent)
! 1618: free(policy->sysent, M_XDATA);
! 1619:
! 1620: TAILQ_REMOVE(&fst->policies, policy, next);
! 1621:
! 1622: pool_put(&systr_policy_pl, policy);
! 1623: }
! 1624:
! 1625:
! 1626: int
! 1627: systrace_insert_process(struct fsystrace *fst, struct proc *proc)
! 1628: {
! 1629: struct str_process *strp;
! 1630:
! 1631: strp = pool_get(&systr_proc_pl, PR_NOWAIT);
! 1632: if (strp == NULL)
! 1633: return (ENOBUFS);
! 1634:
! 1635: memset((caddr_t)strp, 0, sizeof(struct str_process));
! 1636: strp->pid = proc->p_pid;
! 1637: strp->proc = proc;
! 1638: strp->parent = fst;
! 1639:
! 1640: TAILQ_INSERT_TAIL(&fst->processes, strp, next);
! 1641: fst->nprocesses++;
! 1642:
! 1643: proc->p_systrace = strp;
! 1644: atomic_setbits_int(&proc->p_flag, P_SYSTRACE);
! 1645:
! 1646: return (0);
! 1647: }
! 1648:
! 1649: struct str_policy *
! 1650: systrace_newpolicy(struct fsystrace *fst, int maxents)
! 1651: {
! 1652: struct str_policy *pol;
! 1653: int i;
! 1654:
! 1655: if (fst->npolicies > SYSTR_MAX_POLICIES && !fst->issuser) {
! 1656: struct str_policy *tmp;
! 1657:
! 1658: /* Try to find a policy for freeing */
! 1659: TAILQ_FOREACH(tmp, &fst->policies, next) {
! 1660: if (tmp->refcount == 1)
! 1661: break;
! 1662: }
! 1663:
! 1664: if (tmp == NULL)
! 1665: return (NULL);
! 1666:
! 1667: /* Notify userland about freed policy */
! 1668: systrace_msg_policyfree(fst, tmp);
! 1669: /* Free this policy */
! 1670: systrace_closepolicy(fst, tmp);
! 1671: }
! 1672:
! 1673: pol = pool_get(&systr_policy_pl, PR_NOWAIT);
! 1674: if (pol == NULL)
! 1675: return (NULL);
! 1676:
! 1677: DPRINTF(("%s: allocating %d -> %lu\n", __func__,
! 1678: maxents, (u_long)maxents * sizeof(int)));
! 1679:
! 1680: memset((caddr_t)pol, 0, sizeof(struct str_policy));
! 1681:
! 1682: pol->sysent = (u_char *)malloc(maxents * sizeof(u_char),
! 1683: M_XDATA, M_WAITOK);
! 1684: pol->nsysent = maxents;
! 1685: for (i = 0; i < maxents; i++)
! 1686: pol->sysent[i] = SYSTR_POLICY_ASK;
! 1687:
! 1688: fst->npolicies++;
! 1689: pol->nr = fst->npolicynr++;
! 1690: pol->refcount = 1;
! 1691:
! 1692: TAILQ_INSERT_TAIL(&fst->policies, pol, next);
! 1693:
! 1694: return (pol);
! 1695: }
! 1696:
! 1697: int
! 1698: systrace_msg_ask(struct fsystrace *fst, struct str_process *strp,
! 1699: int code, size_t argsize, register_t args[])
! 1700: {
! 1701: struct str_msg_ask *msg_ask = &strp->msg.msg_data.msg_ask;
! 1702: int i;
! 1703:
! 1704: msg_ask->code = code;
! 1705: msg_ask->argsize = argsize;
! 1706: for (i = 0; i < (argsize/sizeof(register_t)) && i < SYSTR_MAXARGS; i++)
! 1707: msg_ask->args[i] = args[i];
! 1708:
! 1709: return (systrace_make_msg(strp, SYSTR_MSG_ASK));
! 1710: }
! 1711:
! 1712: int
! 1713: systrace_msg_result(struct fsystrace *fst, struct str_process *strp,
! 1714: int error, int code, size_t argsize, register_t args[], register_t rval[])
! 1715: {
! 1716: struct str_msg_ask *msg_ask = &strp->msg.msg_data.msg_ask;
! 1717: int i;
! 1718:
! 1719: msg_ask->code = code;
! 1720: msg_ask->argsize = argsize;
! 1721: msg_ask->result = error;
! 1722: for (i = 0; i < (argsize/sizeof(register_t)) && i < SYSTR_MAXARGS; i++)
! 1723: msg_ask->args[i] = args[i];
! 1724:
! 1725: msg_ask->rval[0] = rval[0];
! 1726: msg_ask->rval[1] = rval[1];
! 1727:
! 1728: return (systrace_make_msg(strp, SYSTR_MSG_RES));
! 1729: }
! 1730:
! 1731: int
! 1732: systrace_msg_emul(struct fsystrace *fst, struct str_process *strp)
! 1733: {
! 1734: struct str_msg_emul *msg_emul = &strp->msg.msg_data.msg_emul;
! 1735: struct proc *p = strp->proc;
! 1736:
! 1737: memcpy(msg_emul->emul, p->p_emul->e_name, SYSTR_EMULEN);
! 1738:
! 1739: return (systrace_make_msg(strp, SYSTR_MSG_EMUL));
! 1740: }
! 1741:
! 1742: int
! 1743: systrace_msg_ugid(struct fsystrace *fst, struct str_process *strp)
! 1744: {
! 1745: struct str_msg_ugid *msg_ugid = &strp->msg.msg_data.msg_ugid;
! 1746: struct proc *p = strp->proc;
! 1747:
! 1748: msg_ugid->uid = p->p_cred->p_ruid;
! 1749: msg_ugid->gid = p->p_cred->p_rgid;
! 1750:
! 1751: return (systrace_make_msg(strp, SYSTR_MSG_UGID));
! 1752: }
! 1753:
! 1754: int
! 1755: systrace_make_msg(struct str_process *strp, int type)
! 1756: {
! 1757: struct str_message *msg = &strp->msg;
! 1758: struct fsystrace *fst = strp->parent;
! 1759: int st, pri;
! 1760:
! 1761: pri = PWAIT|PCATCH;
! 1762: if (type == SYSTR_MSG_EXECVE)
! 1763: pri &= ~PCATCH;
! 1764:
! 1765: msg->msg_seqnr = ++strp->seqnr;
! 1766: msg->msg_type = type;
! 1767: msg->msg_pid = strp->pid;
! 1768: if (strp->policy)
! 1769: msg->msg_policy = strp->policy->nr;
! 1770: else
! 1771: msg->msg_policy = -1;
! 1772:
! 1773: SET(strp->flags, STR_PROC_WAITANSWER);
! 1774: if (ISSET(strp->flags, STR_PROC_ONQUEUE))
! 1775: goto out;
! 1776:
! 1777: TAILQ_INSERT_TAIL(&fst->messages, strp, msg_next);
! 1778: SET(strp->flags, STR_PROC_ONQUEUE);
! 1779:
! 1780: out:
! 1781: systrace_wakeup(fst);
! 1782:
! 1783: /* Release the lock - XXX */
! 1784: rw_exit_write(&fst->lock);
! 1785:
! 1786: while (1) {
! 1787: st = tsleep(strp, pri, "systrmsg", 0);
! 1788: if (st != 0)
! 1789: return (ERESTART);
! 1790: /* If we detach, then everything is permitted */
! 1791: if ((strp = curproc->p_systrace) == NULL)
! 1792: return (0);
! 1793: if (!ISSET(strp->flags, STR_PROC_WAITANSWER))
! 1794: break;
! 1795: }
! 1796:
! 1797: return (0);
! 1798: }
! 1799:
! 1800: int
! 1801: systrace_msg_child(struct fsystrace *fst, struct str_process *strp, pid_t npid)
! 1802: {
! 1803: struct str_process *nstrp;
! 1804: struct str_message *msg;
! 1805: struct str_msg_child *msg_child;
! 1806:
! 1807: nstrp = pool_get(&systr_proc_pl, PR_WAITOK);
! 1808: memset(nstrp, 0, sizeof(struct str_process));
! 1809:
! 1810: DPRINTF(("%s: %p: pid %d -> pid %d\n", __func__,
! 1811: nstrp, strp->pid, npid));
! 1812:
! 1813: msg = &nstrp->msg;
! 1814: msg_child = &msg->msg_data.msg_child;
! 1815:
! 1816: msg->msg_type = SYSTR_MSG_CHILD;
! 1817: msg->msg_pid = strp->pid;
! 1818: if (strp->policy)
! 1819: msg->msg_policy = strp->policy->nr;
! 1820: else
! 1821: msg->msg_policy = -1;
! 1822: msg_child->new_pid = npid;
! 1823:
! 1824: TAILQ_INSERT_TAIL(&fst->messages, nstrp, msg_next);
! 1825:
! 1826: systrace_wakeup(fst);
! 1827:
! 1828: return (0);
! 1829: }
! 1830:
! 1831: int
! 1832: systrace_msg_policyfree(struct fsystrace *fst, struct str_policy *strpol)
! 1833: {
! 1834: struct str_process *nstrp;
! 1835: struct str_message *msg;
! 1836:
! 1837: nstrp = pool_get(&systr_proc_pl, PR_WAITOK);
! 1838: memset(nstrp, 0, sizeof(struct str_process));
! 1839:
! 1840: DPRINTF(("%s: free %d\n", __func__, strpol->nr));
! 1841:
! 1842: msg = &nstrp->msg;
! 1843:
! 1844: msg->msg_type = SYSTR_MSG_POLICYFREE;
! 1845: msg->msg_policy = strpol->nr;
! 1846:
! 1847: TAILQ_INSERT_TAIL(&fst->messages, nstrp, msg_next);
! 1848:
! 1849: systrace_wakeup(fst);
! 1850:
! 1851: return (0);
! 1852: }
CVSweb