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