1 /*	$Id$ */
2 /*
3  * Copyright (c) 2014 Baptiste Daroussin <bapt@freebsd.org>
4  * Copyright (c) 2015--2016 Kristaps Dzonsons <kristaps@bsd.lv>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 #include "config.h"
19 
20 #if HAVE_CAPSICUM
21 
22 #include <sys/resource.h>
23 #include <sys/capsicum.h>
24 
25 #include <assert.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <stdarg.h>
29 #include <stdlib.h>
30 
31 #include "kcgi.h"
32 #include "extern.h"
33 
34 static int
ksandbox_capsicum_init_control(int worker,int fdfiled,int fdaccept)35 ksandbox_capsicum_init_control(int worker, int fdfiled, int fdaccept)
36 {
37 	int		 rc;
38 	struct rlimit	 rl_zero;
39 	cap_rights_t	 rights;
40 
41 	cap_rights_init(&rights);
42 
43 	/*
44 	 * If we have old-style accept FastCGI sockets, then mark us as
45 	 * accepting on it.
46 	 * XXX: the CAP_READ and CAP_WRITE are necessary because they're
47 	 * required by descriptors we create from the accept().
48 	 * The new-style passing of descriptors is easier.
49 	 */
50 
51 	if (fdaccept != -1) {
52 		cap_rights_init(&rights,
53 			CAP_EVENT, CAP_FCNTL, CAP_ACCEPT,
54 			CAP_READ, CAP_WRITE);
55 		if (cap_rights_limit(fdaccept, &rights) < 0 &&
56 			 errno != ENOSYS) {
57 			kutil_warn(NULL, NULL, "cap_rights_limit");
58 			return 0;
59 		}
60 	} else {
61 		assert(fdfiled != -1);
62 		cap_rights_init(&rights, CAP_EVENT,
63 			CAP_FCNTL, CAP_READ, CAP_WRITE);
64 		if (cap_rights_limit(fdfiled, &rights) < 0 &&
65 			 errno != ENOSYS) {
66 			kutil_warn(NULL, NULL, "cap_rights_limit");
67 			return 0;
68 		}
69 	}
70 
71 	/* Always pass through write-only stderr. */
72 
73 	cap_rights_init(&rights, CAP_WRITE, CAP_FSTAT);
74 	if (cap_rights_limit(STDERR_FILENO, &rights) < 0 &&
75 		 errno != ENOSYS) {
76 		kutil_warn(NULL, NULL, "cap_rights_limit");
77 		return 0;
78 	}
79 
80 	/* Interface to worker. */
81 
82 	cap_rights_init(&rights, CAP_EVENT,
83 		CAP_FCNTL, CAP_READ, CAP_WRITE);
84 	if (cap_rights_limit(worker, &rights) < 0 &&
85 		 errno != ENOSYS) {
86 		kutil_warn(NULL, NULL, "cap_rights_limit");
87 		return 0;
88 	}
89 
90 	rl_zero.rlim_cur = rl_zero.rlim_max = 0;
91 	if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1) {
92 		kutil_warn(NULL, NULL, "setrlimit");
93 		return 0;
94 	} else if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1) {
95 		kutil_warn(NULL, NULL, "setrlimit");
96 		return 0;
97 	}
98 
99 	rc = cap_enter();
100 	if (rc && errno != ENOSYS) {
101 		kutil_warn(NULL, NULL, "cap_enter");
102 		rc = 0;
103 	} else
104 		rc = 1;
105 
106 	return rc;
107 }
108 
109 static int
ksandbox_capsicum_init_worker(int fd1,int fd2)110 ksandbox_capsicum_init_worker(int fd1, int fd2)
111 {
112 	int rc;
113 	struct rlimit	 rl_zero;
114 	cap_rights_t	 rights;
115 
116 	cap_rights_init(&rights);
117 
118 	/*
119 	 * Test for EBADF because STDIN_FILENO is usually closed by the
120 	 * caller.
121 	 */
122 
123 	cap_rights_init(&rights, CAP_EVENT, CAP_READ, CAP_FSTAT);
124 	if (cap_rights_limit(STDIN_FILENO, &rights) < 0 &&
125 		 errno != ENOSYS && errno != EBADF) {
126  		kutil_warn(NULL, NULL, "cap_rights_limit");
127 		return 0;
128 	}
129 
130 	cap_rights_init(&rights, CAP_EVENT, CAP_WRITE, CAP_FSTAT);
131 	if (cap_rights_limit(STDERR_FILENO, &rights) < 0 &&
132 		 errno != ENOSYS) {
133 		kutil_warn(NULL, NULL, "cap_rights_limit");
134 		return 0;
135 	}
136 
137 	/* Only do thesee if the descriptors are valid. */
138 
139 	cap_rights_init(&rights, CAP_EVENT, CAP_READ, CAP_WRITE, CAP_FSTAT);
140 	if (fd1 != -1 && cap_rights_limit(fd1, &rights) < 0 &&
141 		 errno != ENOSYS) {
142 		kutil_warn(NULL, NULL, "cap_rights_limit");
143 		return 0;
144 	}
145 	if (fd2 != -1 && cap_rights_limit(fd2, &rights) < 0 &&
146 		 errno != ENOSYS) {
147 		kutil_warn(NULL, NULL, "cap_rights_limit");
148 		return 0;
149 	}
150 
151 	rl_zero.rlim_cur = rl_zero.rlim_max = 0;
152 
153 #if 0
154 	/* Don't run this: if we use openlog, it will fail. */
155 
156 	if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1) {
157 		kutil_warn(NULL, NULL, "setrlimit");
158 		return 0;
159 	}
160 #endif
161 
162 	if (setrlimit(RLIMIT_NOFILE, &rl_zero) == -1) {
163 		kutil_warn(NULL, NULL, "setrlimit");
164 		return 0;
165 	} else if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1) {
166 		kutil_warn(NULL, NULL, "setrlimit");
167 		return 0;
168 	}
169 
170 	rc = cap_enter();
171 	if (rc && errno != ENOSYS) {
172 		kutil_warn(NULL, NULL, "cap_enter");
173 		rc = 0;
174 	} else
175 		rc = 1;
176 
177 	return rc;
178 }
179 
180 int
ksandbox_capsicum_init_child(enum sandtype type,int fd1,int fd2,int fdfiled,int fdaccept)181 ksandbox_capsicum_init_child(enum sandtype type,
182 	int fd1, int fd2, int fdfiled, int fdaccept)
183 {
184 	int	 rc;
185 
186 	switch (type) {
187 	case SAND_WORKER:
188 		rc = ksandbox_capsicum_init_worker(fd1, fd2);
189 		break;
190 	case SAND_CONTROL_OLD:
191 		assert(fd2 == -1);
192 		rc = ksandbox_capsicum_init_control
193 			(fd1, fdfiled, fdaccept);
194 		break;
195 	case SAND_CONTROL_NEW:
196 		assert(fd2 == -1);
197 		rc = ksandbox_capsicum_init_control
198 			(fd1, fdfiled, fdaccept);
199 		break;
200 	default:
201 		abort();
202 	}
203 
204 	if (!rc)
205 		kutil_warnx(NULL, NULL, "capsicum sandbox failure");
206 
207 	return rc;
208 }
209 
210 #else
211 int dummy;
212 #endif
213