1*1ed3cb15Sdoug /* $OpenBSD: kern_pledge.c,v 1.31 2015/10/16 06:40:53 doug Exp $ */ 2350b464cSderaadt 3350b464cSderaadt /* 4350b464cSderaadt * Copyright (c) 2015 Nicholas Marriott <nicm@openbsd.org> 5350b464cSderaadt * Copyright (c) 2015 Theo de Raadt <deraadt@openbsd.org> 6350b464cSderaadt * 7350b464cSderaadt * Permission to use, copy, modify, and distribute this software for any 8350b464cSderaadt * purpose with or without fee is hereby granted, provided that the above 9350b464cSderaadt * copyright notice and this permission notice appear in all copies. 10350b464cSderaadt * 11350b464cSderaadt * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12350b464cSderaadt * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13350b464cSderaadt * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14350b464cSderaadt * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15350b464cSderaadt * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16350b464cSderaadt * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17350b464cSderaadt * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18350b464cSderaadt */ 19350b464cSderaadt 20350b464cSderaadt #include <sys/param.h> 21350b464cSderaadt #include <sys/types.h> 22350b464cSderaadt 23350b464cSderaadt #include <sys/mount.h> 24350b464cSderaadt #include <sys/proc.h> 25350b464cSderaadt #include <sys/fcntl.h> 26350b464cSderaadt #include <sys/file.h> 27350b464cSderaadt #include <sys/filedesc.h> 28350b464cSderaadt #include <sys/vnode.h> 29350b464cSderaadt #include <sys/mbuf.h> 30350b464cSderaadt #include <sys/sysctl.h> 31350b464cSderaadt #include <sys/ktrace.h> 32350b464cSderaadt 33350b464cSderaadt #include <sys/ioctl.h> 34350b464cSderaadt #include <sys/termios.h> 35350b464cSderaadt #include <sys/mtio.h> 36350b464cSderaadt #include <net/bpf.h> 37350b464cSderaadt #include <net/route.h> 38350b464cSderaadt #include <net/if.h> 3970325c08Sderaadt #include <net/if_var.h> 40350b464cSderaadt #include <netinet/in.h> 4170325c08Sderaadt #include <netinet6/in6_var.h> 4270325c08Sderaadt #include <netinet6/nd6.h> 43350b464cSderaadt #include <netinet/tcp.h> 44350b464cSderaadt 45350b464cSderaadt #include <sys/signal.h> 46350b464cSderaadt #include <sys/signalvar.h> 47350b464cSderaadt #include <sys/syscall.h> 48350b464cSderaadt #include <sys/syscallargs.h> 49350b464cSderaadt #include <sys/systm.h> 50350b464cSderaadt #include <sys/pledge.h> 51350b464cSderaadt 52350b464cSderaadt int canonpath(const char *input, char *buf, size_t bufsize); 53350b464cSderaadt 54350b464cSderaadt const u_int pledge_syscalls[SYS_MAXSYSCALL] = { 55350b464cSderaadt [SYS_exit] = 0xffffffff, 56350b464cSderaadt [SYS_kbind] = 0xffffffff, 57edca6e16Sguenther [SYS___get_tcb] = 0xffffffff, 58350b464cSderaadt 59350b464cSderaadt [SYS_getuid] = PLEDGE_SELF, 60350b464cSderaadt [SYS_geteuid] = PLEDGE_SELF, 61350b464cSderaadt [SYS_getresuid] = PLEDGE_SELF, 62350b464cSderaadt [SYS_getgid] = PLEDGE_SELF, 63350b464cSderaadt [SYS_getegid] = PLEDGE_SELF, 64350b464cSderaadt [SYS_getresgid] = PLEDGE_SELF, 65350b464cSderaadt [SYS_getgroups] = PLEDGE_SELF, 66350b464cSderaadt [SYS_getlogin] = PLEDGE_SELF, 67350b464cSderaadt [SYS_getpgrp] = PLEDGE_SELF, 68350b464cSderaadt [SYS_getpgid] = PLEDGE_SELF, 69350b464cSderaadt [SYS_getppid] = PLEDGE_SELF, 70350b464cSderaadt [SYS_getsid] = PLEDGE_SELF, 71350b464cSderaadt [SYS_getthrid] = PLEDGE_SELF, 72350b464cSderaadt [SYS_getrlimit] = PLEDGE_SELF, 73350b464cSderaadt [SYS_gettimeofday] = PLEDGE_SELF, 74350b464cSderaadt [SYS_getdtablecount] = PLEDGE_SELF, 75350b464cSderaadt [SYS_getrusage] = PLEDGE_SELF, 76350b464cSderaadt [SYS_issetugid] = PLEDGE_SELF, 77350b464cSderaadt [SYS_clock_getres] = PLEDGE_SELF, 78350b464cSderaadt [SYS_clock_gettime] = PLEDGE_SELF, 79350b464cSderaadt [SYS_getpid] = PLEDGE_SELF, 80350b464cSderaadt [SYS_umask] = PLEDGE_SELF, 81350b464cSderaadt [SYS_sysctl] = PLEDGE_SELF, /* read-only; narrow subset */ 82350b464cSderaadt [SYS_adjtime] = PLEDGE_SELF, /* read-only */ 83350b464cSderaadt 84350b464cSderaadt [SYS_fchdir] = PLEDGE_SELF, /* careful of directory fd inside jails */ 85350b464cSderaadt 86350b464cSderaadt /* needed by threaded programs */ 87350b464cSderaadt [SYS_sched_yield] = PLEDGE_SELF, 88350b464cSderaadt [SYS___thrsleep] = PLEDGE_SELF, 89350b464cSderaadt [SYS___thrwakeup] = PLEDGE_SELF, 90350b464cSderaadt [SYS___threxit] = PLEDGE_SELF, 91350b464cSderaadt [SYS___thrsigdivert] = PLEDGE_SELF, 92350b464cSderaadt 93350b464cSderaadt [SYS_sendsyslog] = PLEDGE_SELF, 94350b464cSderaadt [SYS_nanosleep] = PLEDGE_SELF, 95b5b5adfeSderaadt [SYS_sigaltstack] = PLEDGE_SELF, 96350b464cSderaadt [SYS_sigprocmask] = PLEDGE_SELF, 9744e5764eSderaadt [SYS_sigsuspend] = PLEDGE_SELF, 98350b464cSderaadt [SYS_sigaction] = PLEDGE_SELF, 99350b464cSderaadt [SYS_sigreturn] = PLEDGE_SELF, 100350b464cSderaadt [SYS_sigpending] = PLEDGE_SELF, 101350b464cSderaadt [SYS_getitimer] = PLEDGE_SELF, 102350b464cSderaadt [SYS_setitimer] = PLEDGE_SELF, 103350b464cSderaadt 104350b464cSderaadt [SYS_pledge] = PLEDGE_SELF, 105350b464cSderaadt 106350b464cSderaadt [SYS_wait4] = PLEDGE_SELF, 107350b464cSderaadt 108350b464cSderaadt [SYS_poll] = PLEDGE_RW, 109350b464cSderaadt [SYS_kevent] = PLEDGE_RW, 110350b464cSderaadt [SYS_kqueue] = PLEDGE_RW, 111350b464cSderaadt [SYS_select] = PLEDGE_RW, 112350b464cSderaadt 113350b464cSderaadt [SYS_close] = PLEDGE_RW, 114350b464cSderaadt [SYS_dup] = PLEDGE_RW, 115350b464cSderaadt [SYS_dup2] = PLEDGE_RW, 116350b464cSderaadt [SYS_dup3] = PLEDGE_RW, 117350b464cSderaadt [SYS_closefrom] = PLEDGE_RW, 118350b464cSderaadt [SYS_shutdown] = PLEDGE_RW, 119350b464cSderaadt [SYS_read] = PLEDGE_RW, 120350b464cSderaadt [SYS_readv] = PLEDGE_RW, 121350b464cSderaadt [SYS_pread] = PLEDGE_RW, 122350b464cSderaadt [SYS_preadv] = PLEDGE_RW, 123350b464cSderaadt [SYS_write] = PLEDGE_RW, 124350b464cSderaadt [SYS_writev] = PLEDGE_RW, 125350b464cSderaadt [SYS_pwrite] = PLEDGE_RW, 126350b464cSderaadt [SYS_pwritev] = PLEDGE_RW, 127350b464cSderaadt [SYS_ftruncate] = PLEDGE_RW, 128350b464cSderaadt [SYS_lseek] = PLEDGE_RW, 129350b464cSderaadt [SYS_fstat] = PLEDGE_RW, 130350b464cSderaadt 131350b464cSderaadt [SYS_fcntl] = PLEDGE_RW, 132350b464cSderaadt [SYS_fsync] = PLEDGE_RW, 133350b464cSderaadt [SYS_pipe] = PLEDGE_RW, 134350b464cSderaadt [SYS_pipe2] = PLEDGE_RW, 135350b464cSderaadt [SYS_socketpair] = PLEDGE_RW, 136350b464cSderaadt [SYS_getdents] = PLEDGE_RW, 137350b464cSderaadt 138350b464cSderaadt [SYS_sendto] = PLEDGE_RW | PLEDGE_DNS_ACTIVE | PLEDGE_YP_ACTIVE, 139350b464cSderaadt [SYS_sendmsg] = PLEDGE_RW, 140350b464cSderaadt [SYS_recvmsg] = PLEDGE_RW, 141350b464cSderaadt [SYS_recvfrom] = PLEDGE_RW | PLEDGE_DNS_ACTIVE | PLEDGE_YP_ACTIVE, 142350b464cSderaadt 143350b464cSderaadt [SYS_fork] = PLEDGE_PROC, 144350b464cSderaadt [SYS_vfork] = PLEDGE_PROC, 145ef1ca8b6Sderaadt [SYS_kill] = PLEDGE_SELF | PLEDGE_PROC, 146350b464cSderaadt [SYS_setpgid] = PLEDGE_PROC, 14743f44a87Sderaadt [SYS_setsid] = PLEDGE_PROC, 148350b464cSderaadt [SYS_setrlimit] = PLEDGE_PROC, 149350b464cSderaadt 150350b464cSderaadt [SYS_execve] = PLEDGE_EXEC, 151350b464cSderaadt 152350b464cSderaadt [SYS_setgroups] = PLEDGE_PROC, 153350b464cSderaadt [SYS_setresgid] = PLEDGE_PROC, 154350b464cSderaadt [SYS_setresuid] = PLEDGE_PROC, 155350b464cSderaadt 156350b464cSderaadt /* FIONREAD/FIONBIO, plus further checks in pledge_ioctl_check() */ 157350b464cSderaadt [SYS_ioctl] = PLEDGE_RW | PLEDGE_IOCTL | PLEDGE_TTY, 158350b464cSderaadt 159350b464cSderaadt [SYS_getentropy] = PLEDGE_MALLOC, 160350b464cSderaadt [SYS_madvise] = PLEDGE_MALLOC, 161350b464cSderaadt [SYS_minherit] = PLEDGE_MALLOC, 162350b464cSderaadt [SYS_mmap] = PLEDGE_MALLOC, 163350b464cSderaadt [SYS_mprotect] = PLEDGE_MALLOC, 164350b464cSderaadt [SYS_mquery] = PLEDGE_MALLOC, 165350b464cSderaadt [SYS_munmap] = PLEDGE_MALLOC, 166350b464cSderaadt 167350b464cSderaadt [SYS_open] = PLEDGE_SELF, /* further checks in namei */ 168350b464cSderaadt [SYS_stat] = PLEDGE_SELF, /* further checks in namei */ 169350b464cSderaadt [SYS_access] = PLEDGE_SELF, /* further checks in namei */ 170350b464cSderaadt [SYS_readlink] = PLEDGE_SELF, /* further checks in namei */ 171350b464cSderaadt 172350b464cSderaadt [SYS_chdir] = PLEDGE_RPATH, 173350b464cSderaadt [SYS_openat] = PLEDGE_RPATH | PLEDGE_WPATH, 174350b464cSderaadt [SYS_fstatat] = PLEDGE_RPATH | PLEDGE_WPATH, 175350b464cSderaadt [SYS_faccessat] = PLEDGE_RPATH | PLEDGE_WPATH, 176350b464cSderaadt [SYS_readlinkat] = PLEDGE_RPATH | PLEDGE_WPATH, 177350b464cSderaadt [SYS_lstat] = PLEDGE_RPATH | PLEDGE_WPATH | PLEDGE_TMPPATH, 178350b464cSderaadt [SYS_rename] = PLEDGE_CPATH, 179350b464cSderaadt [SYS_rmdir] = PLEDGE_CPATH, 180350b464cSderaadt [SYS_renameat] = PLEDGE_CPATH, 181350b464cSderaadt [SYS_link] = PLEDGE_CPATH, 182350b464cSderaadt [SYS_linkat] = PLEDGE_CPATH, 183350b464cSderaadt [SYS_symlink] = PLEDGE_CPATH, 184350b464cSderaadt [SYS_unlink] = PLEDGE_CPATH | PLEDGE_TMPPATH, 185350b464cSderaadt [SYS_unlinkat] = PLEDGE_CPATH, 186350b464cSderaadt [SYS_mkdir] = PLEDGE_CPATH, 187350b464cSderaadt [SYS_mkdirat] = PLEDGE_CPATH, 188350b464cSderaadt 189350b464cSderaadt /* 190350b464cSderaadt * Classify as RPATH|WPATH, because of path information leakage. 191350b464cSderaadt * WPATH due to unknown use of mk*temp(3) on non-/tmp paths.. 192350b464cSderaadt */ 193350b464cSderaadt [SYS___getcwd] = PLEDGE_RPATH | PLEDGE_WPATH, 194350b464cSderaadt 195350b464cSderaadt /* Classify as RPATH, because these leak path information */ 196350b464cSderaadt [SYS_getfsstat] = PLEDGE_RPATH, 197350b464cSderaadt [SYS_statfs] = PLEDGE_RPATH, 198350b464cSderaadt [SYS_fstatfs] = PLEDGE_RPATH, 199350b464cSderaadt 200350b464cSderaadt [SYS_utimes] = PLEDGE_FATTR, 201350b464cSderaadt [SYS_futimes] = PLEDGE_FATTR, 202350b464cSderaadt [SYS_utimensat] = PLEDGE_FATTR, 203350b464cSderaadt [SYS_futimens] = PLEDGE_FATTR, 204350b464cSderaadt [SYS_chmod] = PLEDGE_FATTR, 205350b464cSderaadt [SYS_fchmod] = PLEDGE_FATTR, 206350b464cSderaadt [SYS_fchmodat] = PLEDGE_FATTR, 207350b464cSderaadt [SYS_chflags] = PLEDGE_FATTR, 208350b464cSderaadt [SYS_chflagsat] = PLEDGE_FATTR, 2090b92220eSdoug [SYS_fchflags] = PLEDGE_FATTR, 210350b464cSderaadt [SYS_chown] = PLEDGE_FATTR, 211350b464cSderaadt [SYS_fchownat] = PLEDGE_FATTR, 212350b464cSderaadt [SYS_lchown] = PLEDGE_FATTR, 213350b464cSderaadt [SYS_fchown] = PLEDGE_FATTR, 214350b464cSderaadt 215350b464cSderaadt [SYS_socket] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS_ACTIVE | PLEDGE_YP_ACTIVE, 216350b464cSderaadt [SYS_connect] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS_ACTIVE | PLEDGE_YP_ACTIVE, 217350b464cSderaadt 218350b464cSderaadt [SYS_listen] = PLEDGE_INET | PLEDGE_UNIX, 219350b464cSderaadt [SYS_bind] = PLEDGE_INET | PLEDGE_UNIX, 220350b464cSderaadt [SYS_accept4] = PLEDGE_INET | PLEDGE_UNIX, 221350b464cSderaadt [SYS_accept] = PLEDGE_INET | PLEDGE_UNIX, 222350b464cSderaadt [SYS_getpeername] = PLEDGE_INET | PLEDGE_UNIX, 223350b464cSderaadt [SYS_getsockname] = PLEDGE_INET | PLEDGE_UNIX, 224350b464cSderaadt [SYS_setsockopt] = PLEDGE_INET | PLEDGE_UNIX, 225350b464cSderaadt [SYS_getsockopt] = PLEDGE_INET | PLEDGE_UNIX, 226350b464cSderaadt 2275929c5d0Smillert [SYS_flock] = PLEDGE_RW | PLEDGE_CPATH, 228350b464cSderaadt }; 229350b464cSderaadt 230350b464cSderaadt static const struct { 231350b464cSderaadt char *name; 232350b464cSderaadt int flags; 233350b464cSderaadt } pledgereq[] = { 234350b464cSderaadt { "malloc", PLEDGE_SELF | PLEDGE_MALLOC }, 235350b464cSderaadt { "rw", PLEDGE_SELF | PLEDGE_RW }, 236350b464cSderaadt { "stdio", PLEDGE_SELF | PLEDGE_MALLOC | PLEDGE_RW }, 237350b464cSderaadt { "rpath", PLEDGE_SELF | PLEDGE_RW | PLEDGE_RPATH }, 238350b464cSderaadt { "wpath", PLEDGE_SELF | PLEDGE_RW | PLEDGE_WPATH }, 239350b464cSderaadt { "tmppath", PLEDGE_SELF | PLEDGE_RW | PLEDGE_TMPPATH }, 240350b464cSderaadt { "inet", PLEDGE_SELF | PLEDGE_RW | PLEDGE_INET }, 241350b464cSderaadt { "unix", PLEDGE_SELF | PLEDGE_RW | PLEDGE_UNIX }, 242350b464cSderaadt { "dns", PLEDGE_SELF | PLEDGE_MALLOC | PLEDGE_DNSPATH }, 243350b464cSderaadt { "getpw", PLEDGE_SELF | PLEDGE_MALLOC | PLEDGE_RW | PLEDGE_GETPW }, 244350b464cSderaadt { "sendfd", PLEDGE_RW | PLEDGE_SENDFD }, 245350b464cSderaadt { "recvfd", PLEDGE_RW | PLEDGE_RECVFD }, 246350b464cSderaadt { "ioctl", PLEDGE_IOCTL }, 247350b464cSderaadt { "route", PLEDGE_ROUTE }, 248350b464cSderaadt { "mcast", PLEDGE_MCAST }, 249350b464cSderaadt { "tty", PLEDGE_TTY }, 250350b464cSderaadt { "proc", PLEDGE_PROC }, 251350b464cSderaadt { "exec", PLEDGE_EXEC }, 252350b464cSderaadt { "cpath", PLEDGE_CPATH }, 253350b464cSderaadt { "abort", PLEDGE_ABORT }, 254350b464cSderaadt { "fattr", PLEDGE_FATTR }, 255350b464cSderaadt { "prot_exec", PLEDGE_PROTEXEC }, 2565929c5d0Smillert { "flock", PLEDGE_RW | PLEDGE_CPATH }, 257350b464cSderaadt }; 258350b464cSderaadt 259350b464cSderaadt int 260350b464cSderaadt sys_pledge(struct proc *p, void *v, register_t *retval) 261350b464cSderaadt { 262350b464cSderaadt struct sys_pledge_args /* { 263350b464cSderaadt syscallarg(const char *)request; 264350b464cSderaadt syscallarg(const char **)paths; 265350b464cSderaadt } */ *uap = v; 266350b464cSderaadt int flags = 0; 267350b464cSderaadt int error; 268350b464cSderaadt 269350b464cSderaadt if (SCARG(uap, request)) { 270350b464cSderaadt size_t rbuflen; 271350b464cSderaadt char *rbuf, *rp, *pn; 272350b464cSderaadt int f, i; 273350b464cSderaadt 274350b464cSderaadt rbuf = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 275350b464cSderaadt error = copyinstr(SCARG(uap, request), rbuf, MAXPATHLEN, 276350b464cSderaadt &rbuflen); 277350b464cSderaadt if (error) { 278350b464cSderaadt free(rbuf, M_TEMP, MAXPATHLEN); 279350b464cSderaadt return (error); 280350b464cSderaadt } 281350b464cSderaadt #ifdef KTRACE 282350b464cSderaadt if (KTRPOINT(p, KTR_STRUCT)) 283350b464cSderaadt ktrstruct(p, "pledgereq", rbuf, rbuflen-1); 284350b464cSderaadt #endif 285350b464cSderaadt 286350b464cSderaadt for (rp = rbuf; rp && *rp && error == 0; rp = pn) { 287350b464cSderaadt pn = strchr(rp, ' '); /* find terminator */ 288350b464cSderaadt if (pn) { 289350b464cSderaadt while (*pn == ' ') 290350b464cSderaadt *pn++ = '\0'; 291350b464cSderaadt } 292350b464cSderaadt 293350b464cSderaadt for (f = i = 0; i < nitems(pledgereq); i++) { 294350b464cSderaadt if (strcmp(rp, pledgereq[i].name) == 0) { 295350b464cSderaadt f = pledgereq[i].flags; 296350b464cSderaadt break; 297350b464cSderaadt } 298350b464cSderaadt } 299350b464cSderaadt if (f == 0) { 300350b464cSderaadt free(rbuf, M_TEMP, MAXPATHLEN); 301350b464cSderaadt return (EINVAL); 302350b464cSderaadt } 303350b464cSderaadt flags |= f; 304350b464cSderaadt } 305350b464cSderaadt free(rbuf, M_TEMP, MAXPATHLEN); 306350b464cSderaadt } 307350b464cSderaadt 308350b464cSderaadt if (flags & ~PLEDGE_USERSET) 309350b464cSderaadt return (EINVAL); 310350b464cSderaadt 311350b464cSderaadt if ((p->p_p->ps_flags & PS_PLEDGE)) { 312350b464cSderaadt /* Already pledged, only allow reductions */ 313350b464cSderaadt if (((flags | p->p_p->ps_pledge) & PLEDGE_USERSET) != 314350b464cSderaadt (p->p_p->ps_pledge & PLEDGE_USERSET)) { 315350b464cSderaadt return (EPERM); 316350b464cSderaadt } 317350b464cSderaadt 318350b464cSderaadt flags &= p->p_p->ps_pledge; 319350b464cSderaadt flags &= PLEDGE_USERSET; /* Relearn _ACTIVE */ 320350b464cSderaadt } 321350b464cSderaadt 322350b464cSderaadt if (SCARG(uap, paths)) { 323350b464cSderaadt const char **u = SCARG(uap, paths), *sp; 324350b464cSderaadt struct whitepaths *wl; 325350b464cSderaadt char *cwdpath = NULL, *path; 326350b464cSderaadt size_t cwdpathlen = MAXPATHLEN * 4, cwdlen, len, maxargs = 0; 327350b464cSderaadt int i, error; 328350b464cSderaadt 329350b464cSderaadt if (p->p_p->ps_pledgepaths) 330350b464cSderaadt return (EPERM); 331350b464cSderaadt 332350b464cSderaadt /* Count paths */ 333350b464cSderaadt for (i = 0; i < PLEDGE_MAXPATHS; i++) { 334350b464cSderaadt if ((error = copyin(u + i, &sp, sizeof(sp))) != 0) 335350b464cSderaadt return (error); 336350b464cSderaadt if (sp == NULL) 337350b464cSderaadt break; 338350b464cSderaadt } 339350b464cSderaadt if (i == PLEDGE_MAXPATHS) 340350b464cSderaadt return (E2BIG); 341350b464cSderaadt 342350b464cSderaadt wl = malloc(sizeof *wl + sizeof(struct whitepath) * (i+1), 343350b464cSderaadt M_TEMP, M_WAITOK | M_ZERO); 344350b464cSderaadt wl->wl_size = sizeof *wl + sizeof(struct whitepath) * (i+1); 345350b464cSderaadt wl->wl_count = i; 346350b464cSderaadt wl->wl_ref = 1; 347350b464cSderaadt 348350b464cSderaadt path = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 349350b464cSderaadt 350350b464cSderaadt /* Copy in */ 351350b464cSderaadt for (i = 0; i < wl->wl_count; i++) { 352350b464cSderaadt char *fullpath = NULL, *builtpath = NULL, *canopath = NULL, *cwd; 353350b464cSderaadt size_t builtlen = 0; 354350b464cSderaadt 355350b464cSderaadt if ((error = copyin(u + i, &sp, sizeof(sp))) != 0) 356350b464cSderaadt break; 357350b464cSderaadt if (sp == NULL) 358350b464cSderaadt break; 359350b464cSderaadt if ((error = copyinstr(sp, path, MAXPATHLEN, &len)) != 0) 360350b464cSderaadt break; 361350b464cSderaadt #ifdef KTRACE 362350b464cSderaadt if (KTRPOINT(p, KTR_STRUCT)) 363350b464cSderaadt ktrstruct(p, "pledgepath", path, len-1); 364350b464cSderaadt #endif 365350b464cSderaadt 366350b464cSderaadt /* If path is relative, prepend cwd */ 367350b464cSderaadt if (path[0] != '/') { 368350b464cSderaadt if (cwdpath == NULL) { 369350b464cSderaadt char *bp, *bpend; 370350b464cSderaadt 371350b464cSderaadt cwdpath = malloc(cwdpathlen, M_TEMP, M_WAITOK); 372350b464cSderaadt bp = &cwdpath[cwdpathlen]; 373350b464cSderaadt bpend = bp; 374350b464cSderaadt *(--bp) = '\0'; 375350b464cSderaadt 376350b464cSderaadt error = vfs_getcwd_common(p->p_fd->fd_cdir, 377350b464cSderaadt NULL, &bp, cwdpath, cwdpathlen/2, 378350b464cSderaadt GETCWD_CHECK_ACCESS, p); 379350b464cSderaadt if (error) 380350b464cSderaadt break; 381350b464cSderaadt cwd = bp; 382350b464cSderaadt cwdlen = (bpend - bp); 383350b464cSderaadt } 384350b464cSderaadt 385350b464cSderaadt /* NUL included in cwd component */ 386350b464cSderaadt builtlen = cwdlen + 1 + strlen(path); 387350b464cSderaadt if (builtlen > PATH_MAX) { 388350b464cSderaadt error = ENAMETOOLONG; 389350b464cSderaadt break; 390350b464cSderaadt } 391350b464cSderaadt builtpath = malloc(builtlen, M_TEMP, M_WAITOK); 392350b464cSderaadt snprintf(builtpath, builtlen, "%s/%s", cwd, path); 393350b464cSderaadt // printf("pledge: builtpath = %s\n", builtpath); 394350b464cSderaadt fullpath = builtpath; 395350b464cSderaadt } else 396350b464cSderaadt fullpath = path; 397350b464cSderaadt 398350b464cSderaadt canopath = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 399350b464cSderaadt error = canonpath(fullpath, canopath, MAXPATHLEN); 400350b464cSderaadt 401350b464cSderaadt free(builtpath, M_TEMP, builtlen); 402350b464cSderaadt if (error != 0) { 403350b464cSderaadt free(canopath, M_TEMP, MAXPATHLEN); 404350b464cSderaadt break; 405350b464cSderaadt } 406350b464cSderaadt 407350b464cSderaadt len = strlen(canopath) + 1; 408350b464cSderaadt 409350b464cSderaadt //printf("pledge: canopath = %s %lld strlen %lld\n", canopath, 410350b464cSderaadt // (long long)len, (long long)strlen(canopath)); 411350b464cSderaadt 412350b464cSderaadt if (maxargs += len > ARG_MAX) { 413350b464cSderaadt error = E2BIG; 414350b464cSderaadt break; 415350b464cSderaadt } 416350b464cSderaadt wl->wl_paths[i].name = malloc(len, M_TEMP, M_WAITOK); 417350b464cSderaadt memcpy(wl->wl_paths[i].name, canopath, len); 418350b464cSderaadt wl->wl_paths[i].len = len; 419350b464cSderaadt free(canopath, M_TEMP, MAXPATHLEN); 420350b464cSderaadt } 421350b464cSderaadt free(path, M_TEMP, MAXPATHLEN); 422350b464cSderaadt free(cwdpath, M_TEMP, cwdpathlen); 423350b464cSderaadt 424350b464cSderaadt if (error) { 425350b464cSderaadt for (i = 0; i < wl->wl_count; i++) 426350b464cSderaadt free(wl->wl_paths[i].name, 427350b464cSderaadt M_TEMP, wl->wl_paths[i].len); 428350b464cSderaadt free(wl, M_TEMP, wl->wl_size); 429350b464cSderaadt return (error); 430350b464cSderaadt } 431350b464cSderaadt p->p_p->ps_pledgepaths = wl; 432350b464cSderaadt #if 0 433350b464cSderaadt printf("pledge: %s(%d): paths loaded:\n", p->p_comm, p->p_pid); 434350b464cSderaadt for (i = 0; i < wl->wl_count; i++) 435350b464cSderaadt if (wl->wl_paths[i].name) 436350b464cSderaadt printf("pledge: %d=%s %lld\n", i, wl->wl_paths[i].name, 437350b464cSderaadt (long long)wl->wl_paths[i].len); 438350b464cSderaadt #endif 439350b464cSderaadt } 440350b464cSderaadt 441350b464cSderaadt p->p_p->ps_pledge = flags; 442350b464cSderaadt p->p_p->ps_flags |= PS_PLEDGE; 443350b464cSderaadt return (0); 444350b464cSderaadt } 445350b464cSderaadt 446350b464cSderaadt int 447350b464cSderaadt pledge_check(struct proc *p, int code) 448350b464cSderaadt { 449350b464cSderaadt p->p_pledgenote = p->p_pledgeafter = 0; /* XX optimise? */ 450350b464cSderaadt p->p_pledge_syscall = code; 451350b464cSderaadt 452350b464cSderaadt if (code < 0 || code > SYS_MAXSYSCALL - 1) 453350b464cSderaadt return (0); 454350b464cSderaadt 455350b464cSderaadt if (p->p_p->ps_pledge == 0) 456350b464cSderaadt return (code == SYS_exit || code == SYS_kbind); 457350b464cSderaadt return (p->p_p->ps_pledge & pledge_syscalls[code]); 458350b464cSderaadt } 459350b464cSderaadt 460350b464cSderaadt int 461350b464cSderaadt pledge_fail(struct proc *p, int error, int code) 462350b464cSderaadt { 463350b464cSderaadt printf("%s(%d): syscall %d\n", p->p_comm, p->p_pid, p->p_pledge_syscall); 464350b464cSderaadt if (p->p_p->ps_pledge & PLEDGE_ABORT) { /* Core dump requested */ 465350b464cSderaadt struct sigaction sa; 466350b464cSderaadt 467350b464cSderaadt memset(&sa, 0, sizeof sa); 468350b464cSderaadt sa.sa_handler = SIG_DFL; 469350b464cSderaadt setsigvec(p, SIGABRT, &sa); 470350b464cSderaadt psignal(p, SIGABRT); 471350b464cSderaadt } else 472350b464cSderaadt psignal(p, SIGKILL); 473350b464cSderaadt 474350b464cSderaadt p->p_p->ps_pledge = 0; /* Disable all PLEDGE_ flags */ 475350b464cSderaadt return (error); 476350b464cSderaadt } 477350b464cSderaadt 478350b464cSderaadt /* 479350b464cSderaadt * Need to make it more obvious that one cannot get through here 480350b464cSderaadt * without the right flags set 481350b464cSderaadt */ 482350b464cSderaadt int 483350b464cSderaadt pledge_namei(struct proc *p, char *origpath) 484350b464cSderaadt { 485350b464cSderaadt char path[PATH_MAX]; 486350b464cSderaadt 487350b464cSderaadt if (p->p_pledgenote == TMN_COREDUMP) 488350b464cSderaadt return (0); /* Allow a coredump */ 489350b464cSderaadt 490350b464cSderaadt if (canonpath(origpath, path, sizeof(path)) != 0) 491350b464cSderaadt return (pledge_fail(p, EPERM, PLEDGE_RPATH)); 492350b464cSderaadt 493350b464cSderaadt if ((p->p_pledgenote & TMN_FATTR) && 494350b464cSderaadt (p->p_p->ps_pledge & PLEDGE_FATTR) == 0) { 495350b464cSderaadt printf("%s(%d): inode syscall%d, not allowed\n", 496350b464cSderaadt p->p_comm, p->p_pid, p->p_pledge_syscall); 497350b464cSderaadt return (pledge_fail(p, EPERM, PLEDGE_FATTR)); 498350b464cSderaadt } 499350b464cSderaadt 500350b464cSderaadt /* Detect what looks like a mkstemp(3) family operation */ 501350b464cSderaadt if ((p->p_p->ps_pledge & PLEDGE_TMPPATH) && 502350b464cSderaadt (p->p_pledge_syscall == SYS_open) && 503350b464cSderaadt (p->p_pledgenote & TMN_CPATH) && 504350b464cSderaadt strncmp(path, "/tmp/", sizeof("/tmp/") - 1) == 0) { 505350b464cSderaadt return (0); 506350b464cSderaadt } 507350b464cSderaadt 508350b464cSderaadt /* Allow unlinking of a mkstemp(3) file... 509350b464cSderaadt * Good opportunity for strict checks here. 510350b464cSderaadt */ 511350b464cSderaadt if ((p->p_p->ps_pledge & PLEDGE_TMPPATH) && 512350b464cSderaadt (p->p_pledge_syscall == SYS_unlink) && 513350b464cSderaadt strncmp(path, "/tmp/", sizeof("/tmp/") - 1) == 0) { 514350b464cSderaadt return (0); 515350b464cSderaadt } 516350b464cSderaadt 517350b464cSderaadt /* open, mkdir, or other path creation operation */ 518350b464cSderaadt if ((p->p_pledgenote & TMN_CPATH) && 519350b464cSderaadt ((p->p_p->ps_pledge & PLEDGE_CPATH) == 0)) 520350b464cSderaadt return (pledge_fail(p, EPERM, PLEDGE_CPATH)); 521350b464cSderaadt 52259146ba0Sderaadt /* Doing a permitted execve() */ 52359146ba0Sderaadt if ((p->p_pledgenote & TMN_XPATH) && 52459146ba0Sderaadt (p->p_p->ps_pledge & PLEDGE_EXEC)) 52559146ba0Sderaadt return (0); 52659146ba0Sderaadt 527350b464cSderaadt if ((p->p_pledgenote & TMN_WPATH) && 528350b464cSderaadt (p->p_p->ps_pledge & PLEDGE_WPATH) == 0) 529350b464cSderaadt return (pledge_fail(p, EPERM, PLEDGE_WPATH)); 530350b464cSderaadt 531350b464cSderaadt /* Read-only paths used occasionally by libc */ 532350b464cSderaadt switch (p->p_pledge_syscall) { 533350b464cSderaadt case SYS_access: 534350b464cSderaadt /* tzset() needs this. */ 535350b464cSderaadt if ((p->p_pledgenote == TMN_RPATH) && 536350b464cSderaadt strcmp(path, "/etc/localtime") == 0) 537350b464cSderaadt return (0); 538350b464cSderaadt break; 539350b464cSderaadt case SYS_open: 540350b464cSderaadt /* getpw* and friends need a few files */ 541350b464cSderaadt if ((p->p_pledgenote == TMN_RPATH) && 542350b464cSderaadt (p->p_p->ps_pledge & PLEDGE_GETPW)) { 543350b464cSderaadt if (strcmp(path, "/etc/spwd.db") == 0) 544350b464cSderaadt return (EPERM); 545350b464cSderaadt if (strcmp(path, "/etc/pwd.db") == 0) 546350b464cSderaadt return (0); 547350b464cSderaadt if (strcmp(path, "/etc/group") == 0) 548350b464cSderaadt return (0); 549350b464cSderaadt } 550350b464cSderaadt 551350b464cSderaadt /* DNS needs /etc/{resolv.conf,hosts,services}. */ 552350b464cSderaadt if ((p->p_pledgenote == TMN_RPATH) && 553350b464cSderaadt (p->p_p->ps_pledge & PLEDGE_DNSPATH)) { 554350b464cSderaadt if (strcmp(path, "/etc/resolv.conf") == 0) { 555350b464cSderaadt p->p_pledgeafter |= TMA_DNSRESOLV; 556350b464cSderaadt return (0); 557350b464cSderaadt } 558350b464cSderaadt if (strcmp(path, "/etc/hosts") == 0) 559350b464cSderaadt return (0); 560350b464cSderaadt if (strcmp(path, "/etc/services") == 0) 561350b464cSderaadt return (0); 562350b464cSderaadt } 563350b464cSderaadt if ((p->p_pledgenote == TMN_RPATH) && 564350b464cSderaadt (p->p_p->ps_pledge & PLEDGE_GETPW)) { 565350b464cSderaadt if (strcmp(path, "/var/run/ypbind.lock") == 0) { 566350b464cSderaadt p->p_pledgeafter |= TMA_YPLOCK; 567350b464cSderaadt return (0); 568350b464cSderaadt } 569350b464cSderaadt if (strncmp(path, "/var/yp/binding/", 570350b464cSderaadt sizeof("/var/yp/binding/") - 1) == 0) 571350b464cSderaadt return (0); 572350b464cSderaadt } 573350b464cSderaadt /* tzset() needs these. */ 574350b464cSderaadt if ((p->p_pledgenote == TMN_RPATH) && 575350b464cSderaadt strncmp(path, "/usr/share/zoneinfo/", 576350b464cSderaadt sizeof("/usr/share/zoneinfo/") - 1) == 0) 577350b464cSderaadt return (0); 578350b464cSderaadt if ((p->p_pledgenote == TMN_RPATH) && 579350b464cSderaadt strcmp(path, "/etc/localtime") == 0) 580350b464cSderaadt return (0); 581350b464cSderaadt 582350b464cSderaadt /* /usr/share/nls/../libc.cat has to succeed for strerror(3). */ 583350b464cSderaadt if ((p->p_pledgenote == TMN_RPATH) && 584350b464cSderaadt strncmp(path, "/usr/share/nls/", 585350b464cSderaadt sizeof("/usr/share/nls/") - 1) == 0 && 586350b464cSderaadt strcmp(path + strlen(path) - 9, "/libc.cat") == 0) 587350b464cSderaadt return (0); 588350b464cSderaadt break; 589350b464cSderaadt case SYS_readlink: 590350b464cSderaadt /* Allow /etc/malloc.conf for malloc(3). */ 591350b464cSderaadt if ((p->p_pledgenote == TMN_RPATH) && 592350b464cSderaadt strcmp(path, "/etc/malloc.conf") == 0) 593350b464cSderaadt return (0); 594350b464cSderaadt break; 595350b464cSderaadt case SYS_stat: 596350b464cSderaadt /* DNS needs /etc/resolv.conf. */ 597350b464cSderaadt if ((p->p_pledgenote == TMN_RPATH) && 598350b464cSderaadt (p->p_p->ps_pledge & PLEDGE_DNSPATH)) { 599350b464cSderaadt if (strcmp(path, "/etc/resolv.conf") == 0) { 600350b464cSderaadt p->p_pledgeafter |= TMA_DNSRESOLV; 601350b464cSderaadt return (0); 602350b464cSderaadt } 603350b464cSderaadt } 604350b464cSderaadt break; 605350b464cSderaadt } 606350b464cSderaadt 60751f93831Ssemarie /* ensure PLEDGE_RPATH request for doing read */ 60851f93831Ssemarie if ((p->p_pledgenote & TMN_RPATH) && 60951f93831Ssemarie (p->p_p->ps_pledge & PLEDGE_RPATH) == 0) 61051f93831Ssemarie return (pledge_fail(p, EPERM, PLEDGE_RPATH)); 61151f93831Ssemarie 612350b464cSderaadt /* 613350b464cSderaadt * If a whitelist is set, compare canonical paths. Anything 614350b464cSderaadt * not on the whitelist gets ENOENT. 615350b464cSderaadt */ 616350b464cSderaadt if (p->p_p->ps_pledgepaths) { 617350b464cSderaadt struct whitepaths *wl = p->p_p->ps_pledgepaths; 618350b464cSderaadt char *fullpath, *builtpath = NULL, *canopath = NULL; 619350b464cSderaadt size_t builtlen = 0; 620350b464cSderaadt int i, error; 621350b464cSderaadt 622350b464cSderaadt if (origpath[0] != '/') { 623350b464cSderaadt char *cwdpath, *cwd, *bp, *bpend; 624350b464cSderaadt size_t cwdpathlen = MAXPATHLEN * 4, cwdlen; 625350b464cSderaadt 626350b464cSderaadt cwdpath = malloc(cwdpathlen, M_TEMP, M_WAITOK); 627350b464cSderaadt bp = &cwdpath[cwdpathlen]; 628350b464cSderaadt bpend = bp; 629350b464cSderaadt *(--bp) = '\0'; 630350b464cSderaadt 631350b464cSderaadt error = vfs_getcwd_common(p->p_fd->fd_cdir, 632350b464cSderaadt NULL, &bp, cwdpath, cwdpathlen/2, 633350b464cSderaadt GETCWD_CHECK_ACCESS, p); 634350b464cSderaadt if (error) { 635350b464cSderaadt free(cwdpath, M_TEMP, cwdpathlen); 636350b464cSderaadt return (error); 637350b464cSderaadt } 638350b464cSderaadt cwd = bp; 639350b464cSderaadt cwdlen = (bpend - bp); 640350b464cSderaadt 641350b464cSderaadt /* NUL included in cwd component */ 642350b464cSderaadt builtlen = cwdlen + 1 + strlen(origpath); 643350b464cSderaadt builtpath = malloc(builtlen, M_TEMP, M_WAITOK); 644350b464cSderaadt snprintf(builtpath, builtlen, "%s/%s", cwd, origpath); 645350b464cSderaadt fullpath = builtpath; 646350b464cSderaadt free(cwdpath, M_TEMP, cwdpathlen); 647350b464cSderaadt 648350b464cSderaadt //printf("namei: builtpath = %s %lld strlen %lld\n", builtpath, 649350b464cSderaadt // (long long)builtlen, (long long)strlen(builtpath)); 650350b464cSderaadt } else 651350b464cSderaadt fullpath = path; 652350b464cSderaadt 653350b464cSderaadt canopath = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 654350b464cSderaadt error = canonpath(fullpath, canopath, MAXPATHLEN); 655350b464cSderaadt 656350b464cSderaadt free(builtpath, M_TEMP, builtlen); 657350b464cSderaadt if (error != 0) { 658350b464cSderaadt free(canopath, M_TEMP, MAXPATHLEN); 659350b464cSderaadt return (pledge_fail(p, EPERM, PLEDGE_RPATH)); 660350b464cSderaadt } 661350b464cSderaadt 662350b464cSderaadt //printf("namei: canopath = %s strlen %lld\n", canopath, 663350b464cSderaadt // (long long)strlen(canopath)); 664350b464cSderaadt 665350b464cSderaadt error = ENOENT; 666350b464cSderaadt for (i = 0; i < wl->wl_count && wl->wl_paths[i].name && error; i++) { 667350b464cSderaadt if (strncmp(canopath, wl->wl_paths[i].name, 668350b464cSderaadt wl->wl_paths[i].len - 1) == 0) { 669350b464cSderaadt u_char term = canopath[wl->wl_paths[i].len - 1]; 670350b464cSderaadt 671350b464cSderaadt if (term == '\0' || term == '/' || 672350b464cSderaadt wl->wl_paths[i].name[1] == '\0') 673350b464cSderaadt error = 0; 674350b464cSderaadt } 675350b464cSderaadt } 676350b464cSderaadt free(canopath, M_TEMP, MAXPATHLEN); 677350b464cSderaadt return (error); /* Don't hint why it failed */ 678350b464cSderaadt } 679350b464cSderaadt 680350b464cSderaadt if (p->p_p->ps_pledge & PLEDGE_RPATH) 681350b464cSderaadt return (0); 682350b464cSderaadt if (p->p_p->ps_pledge & PLEDGE_WPATH) 683350b464cSderaadt return (0); 684350b464cSderaadt if (p->p_p->ps_pledge & PLEDGE_CPATH) 685350b464cSderaadt return (0); 686350b464cSderaadt 687350b464cSderaadt return (pledge_fail(p, EPERM, PLEDGE_RPATH)); 688350b464cSderaadt } 689350b464cSderaadt 690350b464cSderaadt void 691350b464cSderaadt pledge_aftersyscall(struct proc *p, int code, int error) 692350b464cSderaadt { 693350b464cSderaadt if ((p->p_pledgeafter & TMA_YPLOCK) && error == 0) 694350b464cSderaadt atomic_setbits_int(&p->p_p->ps_pledge, PLEDGE_YP_ACTIVE | PLEDGE_INET); 695350b464cSderaadt if ((p->p_pledgeafter & TMA_DNSRESOLV) && error == 0) 696350b464cSderaadt atomic_setbits_int(&p->p_p->ps_pledge, PLEDGE_DNS_ACTIVE); 697350b464cSderaadt } 698350b464cSderaadt 699350b464cSderaadt /* 700350b464cSderaadt * By default, only the advisory cmsg's can be received from the kernel, 701350b464cSderaadt * such as TIMESTAMP ntpd. 702350b464cSderaadt * 703350b464cSderaadt * If PLEDGE_RECVFD is set SCM_RIGHTS is also allowed in for a carefully 704350b464cSderaadt * selected set of descriptors (specifically to exclude directories). 705350b464cSderaadt * 706350b464cSderaadt * This results in a kill upon recv, if some other process on the system 707350b464cSderaadt * send a SCM_RIGHTS to an open socket of some sort. That will discourage 708350b464cSderaadt * leaving such sockets lying around... 709350b464cSderaadt */ 710350b464cSderaadt int 711350b464cSderaadt pledge_cmsg_recv(struct proc *p, struct mbuf *control) 712350b464cSderaadt { 713350b464cSderaadt struct msghdr tmp; 714350b464cSderaadt struct cmsghdr *cmsg; 715350b464cSderaadt int *fdp, fd; 716350b464cSderaadt struct file *fp; 717350b464cSderaadt int nfds, i; 718350b464cSderaadt 719350b464cSderaadt if ((p->p_p->ps_flags & PS_PLEDGE) == 0) 720350b464cSderaadt return (0); 721350b464cSderaadt 722350b464cSderaadt /* Scan the cmsg */ 723350b464cSderaadt memset(&tmp, 0, sizeof(tmp)); 724350b464cSderaadt tmp.msg_control = mtod(control, struct cmsghdr *); 725350b464cSderaadt tmp.msg_controllen = control->m_len; 726350b464cSderaadt cmsg = CMSG_FIRSTHDR(&tmp); 727350b464cSderaadt 728350b464cSderaadt while (cmsg != NULL) { 729350b464cSderaadt if (cmsg->cmsg_level == SOL_SOCKET && 730350b464cSderaadt cmsg->cmsg_type == SCM_RIGHTS) 731350b464cSderaadt break; 732350b464cSderaadt cmsg = CMSG_NXTHDR(&tmp, cmsg); 733350b464cSderaadt } 734350b464cSderaadt 735350b464cSderaadt /* No SCM_RIGHTS found -> OK */ 736350b464cSderaadt if (cmsg == NULL) 737350b464cSderaadt return (0); 738350b464cSderaadt 739350b464cSderaadt if ((p->p_p->ps_pledge & PLEDGE_RECVFD) == 0) 740350b464cSderaadt return pledge_fail(p, EPERM, PLEDGE_RECVFD); 741350b464cSderaadt 742350b464cSderaadt /* In OpenBSD, a CMSG only contains one SCM_RIGHTS. Check it. */ 743350b464cSderaadt fdp = (int *)CMSG_DATA(cmsg); 744350b464cSderaadt nfds = (cmsg->cmsg_len - CMSG_ALIGN(sizeof(*cmsg))) / 745350b464cSderaadt sizeof(struct file *); 746350b464cSderaadt for (i = 0; i < nfds; i++) { 747350b464cSderaadt struct vnode *vp; 748350b464cSderaadt 749350b464cSderaadt fd = *fdp++; 750350b464cSderaadt fp = fd_getfile(p->p_fd, fd); 751350b464cSderaadt if (fp == NULL) 752350b464cSderaadt return pledge_fail(p, EBADF, PLEDGE_RECVFD); 753350b464cSderaadt 754350b464cSderaadt /* Only allow passing of sockets, pipes, and pure files */ 755350b464cSderaadt switch (fp->f_type) { 756350b464cSderaadt case DTYPE_SOCKET: 757350b464cSderaadt case DTYPE_PIPE: 758350b464cSderaadt continue; 759350b464cSderaadt case DTYPE_VNODE: 760350b464cSderaadt vp = (struct vnode *)fp->f_data; 761350b464cSderaadt if (vp->v_type == VREG) 762350b464cSderaadt continue; 763350b464cSderaadt break; 764350b464cSderaadt default: 765350b464cSderaadt break; 766350b464cSderaadt } 767350b464cSderaadt return pledge_fail(p, EPERM, PLEDGE_RECVFD); 768350b464cSderaadt } 769350b464cSderaadt return (0); 770350b464cSderaadt } 771350b464cSderaadt 772350b464cSderaadt /* 773350b464cSderaadt * When pledged, default prevents sending of a cmsg. 774350b464cSderaadt * 775350b464cSderaadt * Unlike pledge_cmsg_recv pledge_cmsg_send is called with individual 776350b464cSderaadt * cmsgs one per mbuf. So no need to loop or scan. 777350b464cSderaadt */ 778350b464cSderaadt int 779350b464cSderaadt pledge_cmsg_send(struct proc *p, struct mbuf *control) 780350b464cSderaadt { 781350b464cSderaadt struct cmsghdr *cmsg; 782350b464cSderaadt int *fdp, fd; 783350b464cSderaadt struct file *fp; 784350b464cSderaadt int nfds, i; 785350b464cSderaadt 786350b464cSderaadt if ((p->p_p->ps_flags & PS_PLEDGE) == 0) 787350b464cSderaadt return (0); 788350b464cSderaadt 789350b464cSderaadt /* Scan the cmsg */ 790350b464cSderaadt cmsg = mtod(control, struct cmsghdr *); 791350b464cSderaadt 792350b464cSderaadt /* Contains no SCM_RIGHTS, so OK */ 793350b464cSderaadt if (!(cmsg->cmsg_level == SOL_SOCKET && 794350b464cSderaadt cmsg->cmsg_type == SCM_RIGHTS)) 795350b464cSderaadt return (0); 796350b464cSderaadt 7973793cba7Sderaadt if ((p->p_p->ps_pledge & PLEDGE_SENDFD) == 0) 7983793cba7Sderaadt return pledge_fail(p, EPERM, PLEDGE_SENDFD); 7993793cba7Sderaadt 800350b464cSderaadt /* In OpenBSD, a CMSG only contains one SCM_RIGHTS. Check it. */ 801350b464cSderaadt fdp = (int *)CMSG_DATA(cmsg); 802350b464cSderaadt nfds = (cmsg->cmsg_len - CMSG_ALIGN(sizeof(*cmsg))) / 803350b464cSderaadt sizeof(struct file *); 804350b464cSderaadt for (i = 0; i < nfds; i++) { 805350b464cSderaadt struct vnode *vp; 806350b464cSderaadt 807350b464cSderaadt fd = *fdp++; 808350b464cSderaadt fp = fd_getfile(p->p_fd, fd); 809350b464cSderaadt if (fp == NULL) 810350b464cSderaadt return pledge_fail(p, EBADF, PLEDGE_SENDFD); 811350b464cSderaadt 812350b464cSderaadt /* Only allow passing of sockets, pipes, and pure files */ 813350b464cSderaadt switch (fp->f_type) { 814350b464cSderaadt case DTYPE_SOCKET: 815350b464cSderaadt case DTYPE_PIPE: 816350b464cSderaadt continue; 817350b464cSderaadt case DTYPE_VNODE: 818350b464cSderaadt vp = (struct vnode *)fp->f_data; 819350b464cSderaadt if (vp->v_type == VREG) 820350b464cSderaadt continue; 821350b464cSderaadt break; 822350b464cSderaadt default: 823350b464cSderaadt break; 824350b464cSderaadt } 825350b464cSderaadt /* Not allowed to send a bad fd type */ 826350b464cSderaadt return pledge_fail(p, EPERM, PLEDGE_SENDFD); 827350b464cSderaadt } 828350b464cSderaadt return (0); 829350b464cSderaadt } 830350b464cSderaadt 831350b464cSderaadt int 832350b464cSderaadt pledge_sysctl_check(struct proc *p, int miblen, int *mib, void *new) 833350b464cSderaadt { 834350b464cSderaadt if ((p->p_p->ps_flags & PS_PLEDGE) == 0) 835350b464cSderaadt return (0); 836350b464cSderaadt 837350b464cSderaadt if (new) 838350b464cSderaadt return (EFAULT); 839350b464cSderaadt 840350b464cSderaadt /* routing table observation */ 841350b464cSderaadt if ((p->p_p->ps_pledge & PLEDGE_ROUTE)) { 842350b464cSderaadt if (miblen == 7 && 843350b464cSderaadt mib[0] == CTL_NET && mib[1] == PF_ROUTE && 844350b464cSderaadt mib[2] == 0 && 845350b464cSderaadt (mib[3] == 0 || mib[3] == AF_INET6 || mib[3] == AF_INET) && 846350b464cSderaadt mib[4] == NET_RT_DUMP) 847350b464cSderaadt return (0); 848350b464cSderaadt 849350b464cSderaadt if (miblen == 6 && 850350b464cSderaadt mib[0] == CTL_NET && mib[1] == PF_ROUTE && 851350b464cSderaadt mib[2] == 0 && 852350b464cSderaadt (mib[3] == 0 || mib[3] == AF_INET6 || mib[3] == AF_INET) && 853350b464cSderaadt mib[4] == NET_RT_TABLE) 854350b464cSderaadt return (0); 855350b464cSderaadt 856350b464cSderaadt if (miblen == 7 && /* exposes MACs */ 857350b464cSderaadt mib[0] == CTL_NET && mib[1] == PF_ROUTE && 858ee88d605Sderaadt mib[2] == 0 && 859ee88d605Sderaadt (mib[3] == 0 || mib[3] == AF_INET6 || mib[3] == AF_INET) && 860350b464cSderaadt mib[4] == NET_RT_FLAGS && mib[5] == RTF_LLINFO) 861350b464cSderaadt return (0); 862350b464cSderaadt } 863350b464cSderaadt 864350b464cSderaadt if ((p->p_p->ps_pledge & (PLEDGE_ROUTE | PLEDGE_INET))) { 865350b464cSderaadt if (miblen == 6 && /* getifaddrs() */ 866350b464cSderaadt mib[0] == CTL_NET && mib[1] == PF_ROUTE && 867350b464cSderaadt mib[2] == 0 && 868350b464cSderaadt (mib[3] == 0 || mib[3] == AF_INET6 || mib[3] == AF_INET) && 869350b464cSderaadt mib[4] == NET_RT_IFLIST) 870350b464cSderaadt return (0); 871350b464cSderaadt } 872350b464cSderaadt 873350b464cSderaadt /* used by ntpd(8) to read sensors. */ 874350b464cSderaadt if (miblen >= 3 && 875350b464cSderaadt mib[0] == CTL_HW && mib[1] == HW_SENSORS) 876350b464cSderaadt return (0); 877350b464cSderaadt 878350b464cSderaadt if (miblen == 2 && /* getdomainname() */ 879350b464cSderaadt mib[0] == CTL_KERN && mib[1] == KERN_DOMAINNAME) 880350b464cSderaadt return (0); 881350b464cSderaadt if (miblen == 2 && /* gethostname() */ 882350b464cSderaadt mib[0] == CTL_KERN && mib[1] == KERN_HOSTNAME) 883350b464cSderaadt return (0); 884350b464cSderaadt if (miblen == 2 && /* uname() */ 885350b464cSderaadt mib[0] == CTL_KERN && mib[1] == KERN_OSTYPE) 886350b464cSderaadt return (0); 887350b464cSderaadt if (miblen == 2 && /* uname() */ 888350b464cSderaadt mib[0] == CTL_KERN && mib[1] == KERN_OSRELEASE) 889350b464cSderaadt return (0); 890350b464cSderaadt if (miblen == 2 && /* uname() */ 891350b464cSderaadt mib[0] == CTL_KERN && mib[1] == KERN_OSVERSION) 892350b464cSderaadt return (0); 893350b464cSderaadt if (miblen == 2 && /* uname() */ 894350b464cSderaadt mib[0] == CTL_KERN && mib[1] == KERN_VERSION) 895350b464cSderaadt return (0); 8964de5c569Sderaadt if (miblen == 2 && /* kern.clockrate */ 8974de5c569Sderaadt mib[0] == CTL_KERN && mib[1] == KERN_CLOCKRATE) 8984de5c569Sderaadt return (0); 899350b464cSderaadt if (miblen == 2 && /* uname() */ 900350b464cSderaadt mib[0] == CTL_HW && mib[1] == HW_MACHINE) 901350b464cSderaadt return (0); 902350b464cSderaadt if (miblen == 2 && /* getpagesize() */ 903350b464cSderaadt mib[0] == CTL_HW && mib[1] == HW_PAGESIZE) 904350b464cSderaadt return (0); 905350b464cSderaadt if (miblen == 2 && /* setproctitle() */ 906350b464cSderaadt mib[0] == CTL_VM && mib[1] == VM_PSSTRINGS) 907350b464cSderaadt return (0); 908350b464cSderaadt 909350b464cSderaadt printf("%s(%d): sysctl %d: %d %d %d %d %d %d\n", 910350b464cSderaadt p->p_comm, p->p_pid, miblen, mib[0], mib[1], 911350b464cSderaadt mib[2], mib[3], mib[4], mib[5]); 912350b464cSderaadt return (EFAULT); 913350b464cSderaadt } 914350b464cSderaadt 915350b464cSderaadt int 916702d2838Sderaadt pledge_chown_check(struct proc *p, uid_t uid, gid_t gid) 917702d2838Sderaadt { 918702d2838Sderaadt if ((p->p_p->ps_flags & PS_PLEDGE) == 0) 919702d2838Sderaadt return (0); 920702d2838Sderaadt if (uid != -1 && uid != p->p_ucred->cr_uid) 921702d2838Sderaadt return (EPERM); 922702d2838Sderaadt if (gid != -1 && !groupmember(gid, p->p_ucred)) 923702d2838Sderaadt return (EPERM); 924702d2838Sderaadt return (0); 925702d2838Sderaadt } 926702d2838Sderaadt 927702d2838Sderaadt int 928350b464cSderaadt pledge_adjtime_check(struct proc *p, const void *v) 929350b464cSderaadt { 930350b464cSderaadt const struct timeval *delta = v; 931350b464cSderaadt 932350b464cSderaadt if ((p->p_p->ps_flags & PS_PLEDGE) == 0) 933350b464cSderaadt return (0); 934350b464cSderaadt 935350b464cSderaadt if (delta) 936350b464cSderaadt return (EFAULT); 937350b464cSderaadt return (0); 938350b464cSderaadt } 939350b464cSderaadt 940350b464cSderaadt int 941350b464cSderaadt pledge_connect_check(struct proc *p) 942350b464cSderaadt { 943350b464cSderaadt if ((p->p_p->ps_flags & PS_PLEDGE) == 0) 944350b464cSderaadt return (0); 945350b464cSderaadt 946350b464cSderaadt if ((p->p_p->ps_pledge & PLEDGE_DNS_ACTIVE)) 947350b464cSderaadt return (0); /* A port check happens inside sys_connect() */ 948350b464cSderaadt 949350b464cSderaadt if ((p->p_p->ps_pledge & (PLEDGE_INET | PLEDGE_UNIX))) 950350b464cSderaadt return (0); 951350b464cSderaadt return (EPERM); 952350b464cSderaadt } 953350b464cSderaadt 954350b464cSderaadt int 955350b464cSderaadt pledge_recvfrom_check(struct proc *p, void *v) 956350b464cSderaadt { 957350b464cSderaadt struct sockaddr *from = v; 958350b464cSderaadt 959350b464cSderaadt if ((p->p_p->ps_flags & PS_PLEDGE) == 0) 960350b464cSderaadt return (0); 961350b464cSderaadt 962350b464cSderaadt if ((p->p_p->ps_pledge & PLEDGE_DNS_ACTIVE) && from == NULL) 963350b464cSderaadt return (0); 964350b464cSderaadt if (p->p_p->ps_pledge & PLEDGE_INET) 965350b464cSderaadt return (0); 966350b464cSderaadt if (p->p_p->ps_pledge & PLEDGE_UNIX) 967350b464cSderaadt return (0); 968350b464cSderaadt if (from == NULL) 969350b464cSderaadt return (0); /* behaves just like write */ 970350b464cSderaadt return (EPERM); 971350b464cSderaadt } 972350b464cSderaadt 973350b464cSderaadt int 974350b464cSderaadt pledge_sendto_check(struct proc *p, const void *v) 975350b464cSderaadt { 976350b464cSderaadt const struct sockaddr *to = v; 977350b464cSderaadt 978350b464cSderaadt if ((p->p_p->ps_flags & PS_PLEDGE) == 0) 979350b464cSderaadt return (0); 980350b464cSderaadt 981350b464cSderaadt if ((p->p_p->ps_pledge & PLEDGE_DNS_ACTIVE) && to == NULL) 982350b464cSderaadt return (0); 983350b464cSderaadt 984350b464cSderaadt if ((p->p_p->ps_pledge & PLEDGE_INET)) 985350b464cSderaadt return (0); 986350b464cSderaadt if ((p->p_p->ps_pledge & PLEDGE_UNIX)) 987350b464cSderaadt return (0); 988350b464cSderaadt if (to == NULL) 989350b464cSderaadt return (0); /* behaves just like write */ 990350b464cSderaadt return (EPERM); 991350b464cSderaadt } 992350b464cSderaadt 993350b464cSderaadt int 994350b464cSderaadt pledge_socket_check(struct proc *p, int domain) 995350b464cSderaadt { 996350b464cSderaadt if ((p->p_p->ps_flags & PS_PLEDGE) == 0) 997350b464cSderaadt return (0); 998350b464cSderaadt if ((p->p_p->ps_pledge & (PLEDGE_INET | PLEDGE_UNIX))) 999350b464cSderaadt return (0); 1000350b464cSderaadt if ((p->p_p->ps_pledge & PLEDGE_DNS_ACTIVE) && 1001350b464cSderaadt (domain == AF_INET || domain == AF_INET6)) 1002350b464cSderaadt return (0); 1003350b464cSderaadt return (EPERM); 1004350b464cSderaadt } 1005350b464cSderaadt 1006350b464cSderaadt int 1007350b464cSderaadt pledge_bind_check(struct proc *p, const void *v) 1008350b464cSderaadt { 1009350b464cSderaadt 1010350b464cSderaadt if ((p->p_p->ps_flags & PS_PLEDGE) == 0) 1011350b464cSderaadt return (0); 1012350b464cSderaadt if ((p->p_p->ps_pledge & PLEDGE_INET)) 1013350b464cSderaadt return (0); 1014350b464cSderaadt return (EPERM); 1015350b464cSderaadt } 1016350b464cSderaadt 1017350b464cSderaadt int 1018350b464cSderaadt pledge_ioctl_check(struct proc *p, long com, void *v) 1019350b464cSderaadt { 1020350b464cSderaadt struct file *fp = v; 1021350b464cSderaadt struct vnode *vp = NULL; 1022350b464cSderaadt 1023350b464cSderaadt if ((p->p_p->ps_flags & PS_PLEDGE) == 0) 1024350b464cSderaadt return (0); 1025350b464cSderaadt 1026350b464cSderaadt /* 1027350b464cSderaadt * The ioctl's which are always allowed. 1028350b464cSderaadt */ 1029350b464cSderaadt switch (com) { 1030350b464cSderaadt case FIONREAD: 1031350b464cSderaadt case FIONBIO: 103239e8cee0Sderaadt case FIOCLEX: 103339e8cee0Sderaadt case FIONCLEX: 1034350b464cSderaadt return (0); 1035350b464cSderaadt } 1036350b464cSderaadt 1037350b464cSderaadt if (fp == NULL) 1038350b464cSderaadt return (EBADF); 1039350b464cSderaadt vp = (struct vnode *)fp->f_data; 1040350b464cSderaadt 1041350b464cSderaadt /* 1042350b464cSderaadt * Further sets of ioctl become available, but are checked a 1043350b464cSderaadt * bit more carefully against the vnode. 1044350b464cSderaadt */ 1045350b464cSderaadt if ((p->p_p->ps_pledge & PLEDGE_IOCTL)) { 1046350b464cSderaadt switch (com) { 1047350b464cSderaadt case FIOSETOWN: 1048350b464cSderaadt case FIOGETOWN: 1049350b464cSderaadt return (0); 1050350b464cSderaadt case TIOCGETA: 105151d1e1a4Sderaadt if (fp->f_type == DTYPE_VNODE && (vp->v_flag & VISTTY)) 105251d1e1a4Sderaadt return (0); 105351d1e1a4Sderaadt return (ENOTTY); 1054350b464cSderaadt case TIOCGPGRP: 1055350b464cSderaadt case TIOCGWINSZ: /* various programs */ 1056350b464cSderaadt if (fp->f_type == DTYPE_VNODE && (vp->v_flag & VISTTY)) 1057350b464cSderaadt return (0); 1058350b464cSderaadt break; 1059350b464cSderaadt case BIOCGSTATS: /* bpf: tcpdump privsep on ^C */ 1060350b464cSderaadt if (fp->f_type == DTYPE_VNODE && 1061350b464cSderaadt fp->f_ops->fo_ioctl == vn_ioctl) 1062350b464cSderaadt return (0); 1063350b464cSderaadt break; 1064350b464cSderaadt case MTIOCGET: 1065350b464cSderaadt case MTIOCTOP: 1066350b464cSderaadt /* for pax(1) and such, checking tapes... */ 1067350b464cSderaadt if (fp->f_type == DTYPE_VNODE && 1068350b464cSderaadt (vp->v_type == VCHR || vp->v_type == VBLK)) 1069350b464cSderaadt return (0); 1070350b464cSderaadt break; 1071350b464cSderaadt case SIOCGIFGROUP: 1072350b464cSderaadt if ((p->p_p->ps_pledge & PLEDGE_INET) && 1073350b464cSderaadt fp->f_type == DTYPE_SOCKET) 1074350b464cSderaadt return (0); 1075350b464cSderaadt break; 1076350b464cSderaadt } 1077350b464cSderaadt } 1078350b464cSderaadt 1079350b464cSderaadt if ((p->p_p->ps_pledge & PLEDGE_TTY)) { 1080350b464cSderaadt switch (com) { 1081350b464cSderaadt case TIOCSPGRP: 1082350b464cSderaadt if ((p->p_p->ps_pledge & PLEDGE_PROC) == 0) 1083350b464cSderaadt break; 108406152653Ssthen /* FALLTHROUGH */ 1085350b464cSderaadt case TIOCGETA: 108651d1e1a4Sderaadt if (fp->f_type == DTYPE_VNODE && (vp->v_flag & VISTTY)) 108751d1e1a4Sderaadt return (0); 108851d1e1a4Sderaadt return (ENOTTY); 1089350b464cSderaadt #if notyet 1090350b464cSderaadt case TIOCSTI: /* ksh? csh? */ 10911cff096fSderaadt if (fp->f_type == DTYPE_VNODE && (vp->v_flag & VISTTY)) 10921cff096fSderaadt return (0); 10931cff096fSderaadt break; 1094350b464cSderaadt #endif 10951cff096fSderaadt case TIOCGPGRP: 10961cff096fSderaadt case TIOCGWINSZ: /* various programs */ 1097f50373aeSderaadt case TIOCSWINSZ: 1098*1ed3cb15Sdoug case TIOCCBRK: /* cu */ 1099350b464cSderaadt case TIOCSBRK: /* cu */ 1100350b464cSderaadt case TIOCCDTR: /* cu */ 1101*1ed3cb15Sdoug case TIOCSDTR: /* cu */ 11020fb7496eSderaadt case TIOCEXCL: /* cu */ 1103350b464cSderaadt case TIOCSETA: /* cu, ... */ 1104350b464cSderaadt case TIOCSETAW: /* cu, ... */ 1105350b464cSderaadt case TIOCSETAF: /* tcsetattr TCSAFLUSH, script */ 1106bdd44680Sderaadt case TIOCFLUSH: /* getty */ 1107350b464cSderaadt if (fp->f_type == DTYPE_VNODE && (vp->v_flag & VISTTY)) 1108350b464cSderaadt return (0); 1109350b464cSderaadt break; 1110350b464cSderaadt } 1111350b464cSderaadt } 1112350b464cSderaadt 1113f50373aeSderaadt if ((p->p_p->ps_pledge & PLEDGE_ROUTE)) { 1114f50373aeSderaadt switch (com) { 1115f50373aeSderaadt case SIOCGIFADDR: 1116f50373aeSderaadt case SIOCGIFFLAGS: 1117f50373aeSderaadt case SIOCGIFRDOMAIN: 1118733b77e8Sderaadt case SIOCGNBRINFO_IN6: 1119f50373aeSderaadt if (fp->f_type == DTYPE_SOCKET) 1120f50373aeSderaadt return (0); 1121f50373aeSderaadt break; 1122f50373aeSderaadt } 1123f50373aeSderaadt } 1124f50373aeSderaadt 1125350b464cSderaadt return pledge_fail(p, EPERM, PLEDGE_IOCTL); 1126350b464cSderaadt } 1127350b464cSderaadt 1128350b464cSderaadt int 1129350b464cSderaadt pledge_setsockopt_check(struct proc *p, int level, int optname) 1130350b464cSderaadt { 1131350b464cSderaadt if ((p->p_p->ps_flags & PS_PLEDGE) == 0) 1132350b464cSderaadt return (0); 1133350b464cSderaadt 1134350b464cSderaadt /* common case for PLEDGE_UNIX and PLEDGE_INET */ 1135350b464cSderaadt switch (level) { 1136350b464cSderaadt case SOL_SOCKET: 1137350b464cSderaadt switch (optname) { 1138350b464cSderaadt case SO_RTABLE: 1139350b464cSderaadt return (EPERM); 1140350b464cSderaadt } 1141350b464cSderaadt return (0); 1142350b464cSderaadt } 1143350b464cSderaadt 1144350b464cSderaadt if ((p->p_p->ps_pledge & PLEDGE_INET) == 0) 1145350b464cSderaadt return (EPERM); 1146350b464cSderaadt 1147350b464cSderaadt switch (level) { 1148350b464cSderaadt case IPPROTO_TCP: 1149350b464cSderaadt switch (optname) { 1150350b464cSderaadt case TCP_NODELAY: 1151350b464cSderaadt case TCP_MD5SIG: 1152350b464cSderaadt case TCP_SACK_ENABLE: 1153350b464cSderaadt case TCP_MAXSEG: 1154350b464cSderaadt case TCP_NOPUSH: 1155350b464cSderaadt return (0); 1156350b464cSderaadt } 1157350b464cSderaadt break; 1158350b464cSderaadt case IPPROTO_IP: 1159350b464cSderaadt switch (optname) { 1160350b464cSderaadt case IP_TOS: 1161350b464cSderaadt case IP_TTL: 1162350b464cSderaadt case IP_MINTTL: 1163350b464cSderaadt case IP_PORTRANGE: 1164350b464cSderaadt case IP_RECVDSTADDR: 1165645be954Sderaadt case IP_RECVDSTPORT: 1166350b464cSderaadt return (0); 1167350b464cSderaadt case IP_MULTICAST_IF: 1168350b464cSderaadt case IP_ADD_MEMBERSHIP: 1169350b464cSderaadt case IP_DROP_MEMBERSHIP: 1170c601592cSderaadt if (p->p_p->ps_pledge & PLEDGE_MCAST) 1171350b464cSderaadt return (0); 1172350b464cSderaadt break; 1173350b464cSderaadt } 1174350b464cSderaadt break; 1175350b464cSderaadt case IPPROTO_ICMP: 1176350b464cSderaadt break; 1177350b464cSderaadt case IPPROTO_IPV6: 1178350b464cSderaadt switch (optname) { 1179350b464cSderaadt case IPV6_UNICAST_HOPS: 1180350b464cSderaadt case IPV6_RECVHOPLIMIT: 1181350b464cSderaadt case IPV6_PORTRANGE: 1182350b464cSderaadt case IPV6_RECVPKTINFO: 1183645be954Sderaadt case IPV6_RECVDSTPORT: 1184350b464cSderaadt #ifdef notyet 1185350b464cSderaadt case IPV6_V6ONLY: 1186350b464cSderaadt #endif 1187350b464cSderaadt return (0); 1188350b464cSderaadt case IPV6_MULTICAST_IF: 1189350b464cSderaadt case IPV6_JOIN_GROUP: 1190350b464cSderaadt case IPV6_LEAVE_GROUP: 11919e1a3736Sderaadt if (p->p_p->ps_pledge & PLEDGE_MCAST) 1192350b464cSderaadt return (0); 1193350b464cSderaadt break; 1194350b464cSderaadt } 1195350b464cSderaadt break; 1196350b464cSderaadt case IPPROTO_ICMPV6: 1197350b464cSderaadt break; 1198350b464cSderaadt } 1199350b464cSderaadt return (EPERM); 1200350b464cSderaadt } 1201350b464cSderaadt 1202350b464cSderaadt int 1203350b464cSderaadt pledge_dns_check(struct proc *p, in_port_t port) 1204350b464cSderaadt { 1205350b464cSderaadt if ((p->p_p->ps_flags & PS_PLEDGE) == 0) 1206350b464cSderaadt return (0); 1207350b464cSderaadt 1208350b464cSderaadt if ((p->p_p->ps_pledge & PLEDGE_INET)) 1209350b464cSderaadt return (0); 1210350b464cSderaadt if ((p->p_p->ps_pledge & PLEDGE_DNS_ACTIVE) && port == htons(53)) 1211350b464cSderaadt return (0); /* Allow a DNS connect outbound */ 1212350b464cSderaadt return (EPERM); 1213350b464cSderaadt } 1214350b464cSderaadt 1215350b464cSderaadt void 1216350b464cSderaadt pledge_dropwpaths(struct process *pr) 1217350b464cSderaadt { 1218350b464cSderaadt if (pr->ps_pledgepaths && --pr->ps_pledgepaths->wl_ref == 0) { 1219350b464cSderaadt struct whitepaths *wl = pr->ps_pledgepaths; 1220350b464cSderaadt int i; 1221350b464cSderaadt 1222350b464cSderaadt for (i = 0; i < wl->wl_count; i++) 1223350b464cSderaadt free(wl->wl_paths[i].name, M_TEMP, wl->wl_paths[i].len); 1224350b464cSderaadt free(wl, M_TEMP, wl->wl_size); 1225350b464cSderaadt } 1226350b464cSderaadt pr->ps_pledgepaths = NULL; 1227350b464cSderaadt } 1228350b464cSderaadt 1229350b464cSderaadt int 1230350b464cSderaadt canonpath(const char *input, char *buf, size_t bufsize) 1231350b464cSderaadt { 1232350b464cSderaadt char *p, *q, *s, *end; 1233350b464cSderaadt 1234350b464cSderaadt /* can't canon relative paths, don't bother */ 1235350b464cSderaadt if (input[0] != '/') { 1236350b464cSderaadt if (strlcpy(buf, input, bufsize) >= bufsize) 1237350b464cSderaadt return (ENAMETOOLONG); 1238350b464cSderaadt return (0); 1239350b464cSderaadt } 1240350b464cSderaadt 1241350b464cSderaadt /* easiest to work with strings always ending in '/' */ 1242350b464cSderaadt if (snprintf(buf, bufsize, "%s/", input) >= bufsize) 1243350b464cSderaadt return (ENAMETOOLONG); 1244350b464cSderaadt 1245350b464cSderaadt /* after this we will only be shortening the string. */ 1246350b464cSderaadt p = buf; 1247350b464cSderaadt q = p; 1248350b464cSderaadt while (*p) { 1249350b464cSderaadt if (p[0] == '/' && p[1] == '/') { 1250350b464cSderaadt p += 1; 1251350b464cSderaadt } else if (p[0] == '/' && p[1] == '.' && 1252350b464cSderaadt p[2] == '/') { 1253350b464cSderaadt p += 2; 1254350b464cSderaadt } else { 1255350b464cSderaadt *q++ = *p++; 1256350b464cSderaadt } 1257350b464cSderaadt } 1258350b464cSderaadt *q = 0; 1259350b464cSderaadt 1260350b464cSderaadt end = buf + strlen(buf); 1261350b464cSderaadt s = buf; 1262350b464cSderaadt p = s; 1263350b464cSderaadt while (1) { 1264350b464cSderaadt /* find "/../" (where's strstr when you need it?) */ 1265350b464cSderaadt while (p < end) { 1266350b464cSderaadt if (p[0] == '/' && strncmp(p + 1, "../", 3) == 0) 1267350b464cSderaadt break; 1268350b464cSderaadt p++; 1269350b464cSderaadt } 1270350b464cSderaadt if (p == end) 1271350b464cSderaadt break; 1272350b464cSderaadt if (p == s) { 1273350b464cSderaadt memmove(s, p + 3, end - p - 3 + 1); 1274350b464cSderaadt end -= 3; 1275350b464cSderaadt } else { 1276350b464cSderaadt /* s starts with '/', so we know there's one 1277350b464cSderaadt * somewhere before p. */ 1278350b464cSderaadt q = p - 1; 1279350b464cSderaadt while (*q != '/') 1280350b464cSderaadt q--; 1281350b464cSderaadt memmove(q, p + 3, end - p - 3 + 1); 1282350b464cSderaadt end -= p + 3 - q; 1283350b464cSderaadt p = q; 1284350b464cSderaadt } 1285350b464cSderaadt } 1286350b464cSderaadt if (end > s + 1) 1287350b464cSderaadt *(end - 1) = 0; /* remove trailing '/' */ 1288350b464cSderaadt 1289350b464cSderaadt return 0; 1290350b464cSderaadt } 1291