xref: /openbsd-src/sys/kern/kern_pledge.c (revision 1ed3cb15e09052d97bf0c7a6864f8587bed0924f)
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