Deleted Added
full compact
command.c (214117) command.c (214783)
1/*-
2 * Copyright (c) 2010 James Gritton
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
1/*-
2 * Copyright (c) 2010 James Gritton
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: projects/jailconf/usr.sbin/jail/command.c 214117 2010-10-20 20:42:33Z jamie $");
28__FBSDID("$FreeBSD: projects/jailconf/usr.sbin/jail/command.c 214783 2010-11-04 17:01:21Z jamie $");
29
30#include <sys/types.h>
31#include <sys/event.h>
32#include <sys/stat.h>
33#include <sys/sysctl.h>
34#include <sys/user.h>
35#include <sys/wait.h>
36
37#include <err.h>
38#include <errno.h>
39#include <fcntl.h>
40#include <kvm.h>
41#include <login_cap.h>
42#include <paths.h>
43#include <pwd.h>
44#include <signal.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48#include <unistd.h>
49
50#include "jailp.h"
51
52#define COMSTRING_DUMMY ((struct cfstring *)1)
53#define DEFAULT_STOP_TIMEOUT 10
54#define PHASH_SIZE 256
55
56LIST_HEAD(phhead, phash);
57
58struct phash {
59 LIST_ENTRY(phash) le;
60 struct cfjail *j;
61 pid_t pid;
62};
63
64extern char **environ;
65
66static int get_user_info(struct cfjail *j, const char *username,
67 const struct passwd **pwdp, login_cap_t **lcapp);
68static void add_proc(struct cfjail *j, pid_t pid);
69static void clear_procs(struct cfjail *j);
70static struct cfjail *find_proc(pid_t pid);
71
72static struct cfjails sleeping = TAILQ_HEAD_INITIALIZER(sleeping);
73static struct cfjails runnable = TAILQ_HEAD_INITIALIZER(runnable);
74static struct phhead phash[PHASH_SIZE];
75static int kq;
76
77/*
78 * Run a command associated with a jail, possibly inside the jail.
79 */
80int
81run_command(struct cfjail *j, int *plimit, enum intparam comparam)
82{
83 const struct passwd *pwd;
84 struct cfstring *comstring, *s;
85 login_cap_t *lcap;
86 char **argv;
87 char *cs, *addr, *comcs;
88 const char *jidstr, *conslog, *path, *ruleset, *term, *username;
89 size_t comlen;
90 pid_t pid;
91 int argc, bg, clean, consfd, down, fib, i, injail, sjuser, timeout;
92
93 static char *cleanenv;
94
95 if (comparam) {
96 if (comparam == IP_MOUNT_DEVFS
97 ? !bool_param(j->intparams[IP_MOUNT_DEVFS])
98 : j->intparams[comparam] == NULL)
99 return 0;
100 j->comparam = comparam;
101 j->comstring = comparam == IP_MOUNT_DEVFS ? COMSTRING_DUMMY
102 : STAILQ_FIRST(&j->intparams[comparam]->val);
103 } else {
104 comparam = j->comparam;
105 if (!(j->flags & JF_RUNQ))
106 j->comstring = j->comstring == COMSTRING_DUMMY
107 ? NULL : STAILQ_NEXT(j->comstring, tq);
108 }
109 comstring = j->comstring;
110 if (comstring == NULL ||
111 (comstring != COMSTRING_DUMMY && comstring->len == 0))
112 return 0;
113 if (plimit && *plimit == 0) {
114 j->flags |= JF_RUNQ;
115 requeue(j, &runnable);
116 return 1;
117 }
118 j->flags &= ~(JF_RUNQ | JF_BACKGROUND);
119 /*
120 * Collect exec arguments. Internal commands for network and
121 * mounting build their own argument lists (XXX they should be
122 * truly internal).
123 */
124 bg = j->flags & JF_FAILED;
125 down = j->flags & (JF_STOP | JF_FAILED);
126 if (comparam == IP__IP4_IFADDR) {
127 argv = alloca(8 * sizeof(char *));
128 *(const char **)&argv[0] = _PATH_IFCONFIG;
129 if ((cs = strchr(comstring->s, '|'))) {
130 argv[1] = alloca(cs - comstring->s + 1);
131 strlcpy(argv[1], comstring->s, cs - comstring->s + 1);
132 addr = cs + 1;
133 } else {
134 *(const char **)&argv[1] =
135 string_param(j->intparams[IP_INTERFACE]);
136 addr = comstring->s;
137 }
138 *(const char **)&argv[2] = "inet";
139 if (!(cs = strchr(addr, '/'))) {
140 argv[3] = addr;
141 *(const char **)&argv[4] = "netmask";
142 *(const char **)&argv[5] = "255.255.255.255";
143 argc = 6;
144 } else if (strchr(cs + 1, '.')) {
145 argv[3] = alloca(cs - addr + 1);
146 strlcpy(argv[3], addr, cs - addr + 1);
147 *(const char **)&argv[4] = "netmask";
148 *(const char **)&argv[5] = cs + 1;
149 argc = 6;
150 } else {
151 argv[3] = addr;
152 argc = 4;
153 }
154 *(const char **)&argv[argc] = down ? "-alias" : "alias";
155 argv[argc + 1] = NULL;
156 j->flags |= JF_IFUP;
157#ifdef INET6
158 } else if (comparam == IP__IP6_IFADDR) {
159 argv = alloca(8 * sizeof(char *));
160 *(const char **)&argv[0] = _PATH_IFCONFIG;
161 if ((cs = strchr(comstring->s, '|'))) {
162 argv[1] = alloca(cs - comstring->s + 1);
163 strlcpy(argv[1], comstring->s, cs - comstring->s + 1);
164 addr = cs + 1;
165 } else {
166 *(const char **)&argv[1] =
167 string_param(j->intparams[IP_INTERFACE]);
168 addr = comstring->s;
169 }
170 *(const char **)&argv[2] = "inet6";
171 argv[3] = addr;
172 if (!(cs = strchr(addr, '/'))) {
173 *(const char **)&argv[4] = "prefixlen";
174 *(const char **)&argv[5] = "128";
175 argc = 6;
176 } else
177 argc = 4;
178 *(const char **)&argv[argc] = down ? "-alias" : "alias";
179 argv[argc + 1] = NULL;
180 j->flags |= JF_IFUP;
181#endif
182 } else if (comparam == IP_VNET_INTERFACE) {
183 argv = alloca(5 * sizeof(char *));
184 *(const char **)&argv[0] = _PATH_IFCONFIG;
185 argv[1] = comstring->s;
186 *(const char **)&argv[2] = down ? "-vnet" : "vnet";
187 jidstr = string_param(j->intparams[KP_JID]);
188 *(const char **)&argv[3] =
189 jidstr ? jidstr : string_param(j->intparams[KP_NAME]);
190 argv[4] = NULL;
191 j->flags |= JF_IFUP;
29
30#include <sys/types.h>
31#include <sys/event.h>
32#include <sys/stat.h>
33#include <sys/sysctl.h>
34#include <sys/user.h>
35#include <sys/wait.h>
36
37#include <err.h>
38#include <errno.h>
39#include <fcntl.h>
40#include <kvm.h>
41#include <login_cap.h>
42#include <paths.h>
43#include <pwd.h>
44#include <signal.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48#include <unistd.h>
49
50#include "jailp.h"
51
52#define COMSTRING_DUMMY ((struct cfstring *)1)
53#define DEFAULT_STOP_TIMEOUT 10
54#define PHASH_SIZE 256
55
56LIST_HEAD(phhead, phash);
57
58struct phash {
59 LIST_ENTRY(phash) le;
60 struct cfjail *j;
61 pid_t pid;
62};
63
64extern char **environ;
65
66static int get_user_info(struct cfjail *j, const char *username,
67 const struct passwd **pwdp, login_cap_t **lcapp);
68static void add_proc(struct cfjail *j, pid_t pid);
69static void clear_procs(struct cfjail *j);
70static struct cfjail *find_proc(pid_t pid);
71
72static struct cfjails sleeping = TAILQ_HEAD_INITIALIZER(sleeping);
73static struct cfjails runnable = TAILQ_HEAD_INITIALIZER(runnable);
74static struct phhead phash[PHASH_SIZE];
75static int kq;
76
77/*
78 * Run a command associated with a jail, possibly inside the jail.
79 */
80int
81run_command(struct cfjail *j, int *plimit, enum intparam comparam)
82{
83 const struct passwd *pwd;
84 struct cfstring *comstring, *s;
85 login_cap_t *lcap;
86 char **argv;
87 char *cs, *addr, *comcs;
88 const char *jidstr, *conslog, *path, *ruleset, *term, *username;
89 size_t comlen;
90 pid_t pid;
91 int argc, bg, clean, consfd, down, fib, i, injail, sjuser, timeout;
92
93 static char *cleanenv;
94
95 if (comparam) {
96 if (comparam == IP_MOUNT_DEVFS
97 ? !bool_param(j->intparams[IP_MOUNT_DEVFS])
98 : j->intparams[comparam] == NULL)
99 return 0;
100 j->comparam = comparam;
101 j->comstring = comparam == IP_MOUNT_DEVFS ? COMSTRING_DUMMY
102 : STAILQ_FIRST(&j->intparams[comparam]->val);
103 } else {
104 comparam = j->comparam;
105 if (!(j->flags & JF_RUNQ))
106 j->comstring = j->comstring == COMSTRING_DUMMY
107 ? NULL : STAILQ_NEXT(j->comstring, tq);
108 }
109 comstring = j->comstring;
110 if (comstring == NULL ||
111 (comstring != COMSTRING_DUMMY && comstring->len == 0))
112 return 0;
113 if (plimit && *plimit == 0) {
114 j->flags |= JF_RUNQ;
115 requeue(j, &runnable);
116 return 1;
117 }
118 j->flags &= ~(JF_RUNQ | JF_BACKGROUND);
119 /*
120 * Collect exec arguments. Internal commands for network and
121 * mounting build their own argument lists (XXX they should be
122 * truly internal).
123 */
124 bg = j->flags & JF_FAILED;
125 down = j->flags & (JF_STOP | JF_FAILED);
126 if (comparam == IP__IP4_IFADDR) {
127 argv = alloca(8 * sizeof(char *));
128 *(const char **)&argv[0] = _PATH_IFCONFIG;
129 if ((cs = strchr(comstring->s, '|'))) {
130 argv[1] = alloca(cs - comstring->s + 1);
131 strlcpy(argv[1], comstring->s, cs - comstring->s + 1);
132 addr = cs + 1;
133 } else {
134 *(const char **)&argv[1] =
135 string_param(j->intparams[IP_INTERFACE]);
136 addr = comstring->s;
137 }
138 *(const char **)&argv[2] = "inet";
139 if (!(cs = strchr(addr, '/'))) {
140 argv[3] = addr;
141 *(const char **)&argv[4] = "netmask";
142 *(const char **)&argv[5] = "255.255.255.255";
143 argc = 6;
144 } else if (strchr(cs + 1, '.')) {
145 argv[3] = alloca(cs - addr + 1);
146 strlcpy(argv[3], addr, cs - addr + 1);
147 *(const char **)&argv[4] = "netmask";
148 *(const char **)&argv[5] = cs + 1;
149 argc = 6;
150 } else {
151 argv[3] = addr;
152 argc = 4;
153 }
154 *(const char **)&argv[argc] = down ? "-alias" : "alias";
155 argv[argc + 1] = NULL;
156 j->flags |= JF_IFUP;
157#ifdef INET6
158 } else if (comparam == IP__IP6_IFADDR) {
159 argv = alloca(8 * sizeof(char *));
160 *(const char **)&argv[0] = _PATH_IFCONFIG;
161 if ((cs = strchr(comstring->s, '|'))) {
162 argv[1] = alloca(cs - comstring->s + 1);
163 strlcpy(argv[1], comstring->s, cs - comstring->s + 1);
164 addr = cs + 1;
165 } else {
166 *(const char **)&argv[1] =
167 string_param(j->intparams[IP_INTERFACE]);
168 addr = comstring->s;
169 }
170 *(const char **)&argv[2] = "inet6";
171 argv[3] = addr;
172 if (!(cs = strchr(addr, '/'))) {
173 *(const char **)&argv[4] = "prefixlen";
174 *(const char **)&argv[5] = "128";
175 argc = 6;
176 } else
177 argc = 4;
178 *(const char **)&argv[argc] = down ? "-alias" : "alias";
179 argv[argc + 1] = NULL;
180 j->flags |= JF_IFUP;
181#endif
182 } else if (comparam == IP_VNET_INTERFACE) {
183 argv = alloca(5 * sizeof(char *));
184 *(const char **)&argv[0] = _PATH_IFCONFIG;
185 argv[1] = comstring->s;
186 *(const char **)&argv[2] = down ? "-vnet" : "vnet";
187 jidstr = string_param(j->intparams[KP_JID]);
188 *(const char **)&argv[3] =
189 jidstr ? jidstr : string_param(j->intparams[KP_NAME]);
190 argv[4] = NULL;
191 j->flags |= JF_IFUP;
192 } else if (comparam == IP_MOUNT) {
192 } else if (comparam == IP_MOUNT || comparam == IP__MOUNT_FROM_FSTAB) {
193 argv = alloca(8 * sizeof(char *));
194 comcs = alloca(comstring->len + 1);
195 strcpy(comcs, comstring->s);
196 argc = 0;
197 for (cs = strtok(comcs, " \t\f\v\r\n"); cs && argc < 4;
198 cs = strtok(NULL, " \t\f\v\r\n"))
199 argv[argc++] = cs;
200 if (argc < 3) {
193 argv = alloca(8 * sizeof(char *));
194 comcs = alloca(comstring->len + 1);
195 strcpy(comcs, comstring->s);
196 argc = 0;
197 for (cs = strtok(comcs, " \t\f\v\r\n"); cs && argc < 4;
198 cs = strtok(NULL, " \t\f\v\r\n"))
199 argv[argc++] = cs;
200 if (argc < 3) {
201 jail_warnx(j, "mount: %s: missing information",
202 comstring->s);
201 jail_warnx(j, "%s: %s: missing information",
202 j->intparams[comparam]->name, comstring->s);
203 failed(j);
204 return -1;
205 }
206 if (down) {
207 argv[4] = NULL;
208 argv[3] = argv[1];
209 *(const char **)&argv[0] = "/sbin/umount";
210 } else {
211 if (argc == 4) {
212 argv[7] = NULL;
213 argv[6] = argv[1];
214 argv[5] = argv[0];
215 argv[4] = argv[3];
216 *(const char **)&argv[3] = "-o";
217 } else {
218 argv[5] = NULL;
219 argv[4] = argv[1];
220 argv[3] = argv[0];
221 }
222 *(const char **)&argv[0] = _PATH_MOUNT;
223 }
224 *(const char **)&argv[1] = "-t";
225 j->flags |= JF_MOUNTED;
203 failed(j);
204 return -1;
205 }
206 if (down) {
207 argv[4] = NULL;
208 argv[3] = argv[1];
209 *(const char **)&argv[0] = "/sbin/umount";
210 } else {
211 if (argc == 4) {
212 argv[7] = NULL;
213 argv[6] = argv[1];
214 argv[5] = argv[0];
215 argv[4] = argv[3];
216 *(const char **)&argv[3] = "-o";
217 } else {
218 argv[5] = NULL;
219 argv[4] = argv[1];
220 argv[3] = argv[0];
221 }
222 *(const char **)&argv[0] = _PATH_MOUNT;
223 }
224 *(const char **)&argv[1] = "-t";
225 j->flags |= JF_MOUNTED;
226 } else if (comparam == IP_MOUNT_FSTAB) {
227 argv = alloca(4 * sizeof(char *));
228 *(const char **)&argv[0] = down ? "/sbin/umount" : _PATH_MOUNT;
229 *(const char **)&argv[1] = "-aF";
230 argv[2] = comstring->s;
231 argv[3] = NULL;
232 j->flags |= JF_MOUNTED;
233 } else if (comparam == IP_MOUNT_DEVFS) {
234 path = string_param(j->intparams[KP_PATH]);
235 if (path == NULL) {
236 jail_warnx(j, "mount.devfs: no path");
237 failed(j);
238 return -1;
239 }
240 if (down) {
241 argv = alloca(3 * sizeof(char *));
242 *(const char **)&argv[0] = "/sbin/umount";
243 argv[1] = alloca(strlen(path) + 5);
244 sprintf(argv[1], "%s/dev", path);
245 argv[2] = NULL;
246 } else {
247 argv = alloca(4 * sizeof(char *));
248 *(const char **)&argv[0] = _PATH_BSHELL;
249 *(const char **)&argv[1] = "-c";
250 ruleset = string_param(j->intparams
251 [IP_MOUNT_DEVFS_RULESET]);
252 argv[2] = alloca(strlen(path) +
253 (ruleset ? strlen(ruleset) + 1 : 0) + 56);
254 sprintf(argv[2], ". /etc/rc.subr; load_rc_config .; "
255 "devfs_mount_jail %s/dev%s%s", path,
256 ruleset ? " " : "", ruleset ? ruleset : "");
257 argv[3] = NULL;
258 }
259 j->flags |= JF_MOUNTED;
260 } else if (comparam == IP_COMMAND && j->name == NULL) {
261 argc = 0;
262 STAILQ_FOREACH(s, &j->intparams[IP_COMMAND]->val, tq)
263 argc++;
264 argv = alloca((argc + 1) * sizeof(char *));
265 argc = 0;
266 STAILQ_FOREACH(s, &j->intparams[IP_COMMAND]->val, tq)
267 argv[argc++] = s->s;
268 argv[argc] = NULL;
269 j->comstring = NULL;
270 } else if ((cs = strpbrk(comstring->s, "!\"$&'()*;<>?[\\]`{|}~")) &&
271 !(cs[0] == '&' && cs[1] == '\0')) {
272 argv = alloca(4 * sizeof(char *));
273 *(const char **)&argv[0] = _PATH_BSHELL;
274 *(const char **)&argv[1] = "-c";
275 argv[2] = comstring->s;
276 argv[3] = NULL;
277 } else {
278 if (cs) {
279 *cs = 0;
280 bg = 1;
281 }
282 comcs = alloca(comstring->len + 1);
283 strcpy(comcs, comstring->s);
284 argc = 0;
285 for (cs = strtok(comcs, " \t\f\v\r\n"); cs;
286 cs = strtok(NULL, " \t\f\v\r\n"))
287 argc++;
288 argv = alloca((argc + 1) * sizeof(char *));
289 strcpy(comcs, comstring->s);
290 argc = 0;
291 for (cs = strtok(comcs, " \t\f\v\r\n"); cs;
292 cs = strtok(NULL, " \t\f\v\r\n"))
293 argv[argc++] = cs;
294 argv[argc] = NULL;
295 }
296 if (argv[0] == NULL)
297 return 0;
298
299 j->pstatus = 0;
300 if (int_param(j->intparams[IP_EXEC_TIMEOUT], &timeout) &&
301 timeout != 0) {
302 clock_gettime(CLOCK_REALTIME, &j->timeout);
303 j->timeout.tv_sec += timeout;
304 } else
305 j->timeout.tv_sec = 0;
306
307 injail = comparam == IP_EXEC_START || comparam == IP_COMMAND ||
308 comparam == IP_EXEC_STOP;
309 clean = bool_param(j->intparams[IP_EXEC_CLEAN]);
310 username = string_param(j->intparams[injail
311 ? IP_EXEC_JAIL_USER : IP_EXEC_SYSTEM_USER]);
312 sjuser = bool_param(j->intparams[IP_EXEC_SYSTEM_JAIL_USER]);
313
314 consfd = 0;
315 if (injail &&
316 (conslog = string_param(j->intparams[IP_EXEC_CONSOLELOG]))) {
317 consfd =
318 open(conslog, O_WRONLY | O_CREAT | O_APPEND, DEFFILEMODE);
319 if (consfd < 0) {
320 jail_warnx(j, "open %s: %s", conslog, strerror(errno));
321 failed(j);
322 return -1;
323 }
324 }
325
326 comlen = 0;
327 for (i = 0; argv[i]; i++)
328 comlen += strlen(argv[i]) + 1;
329 j->comline = cs = emalloc(comlen);
330 for (i = 0; argv[i]; i++) {
331 strcpy(cs, argv[i]);
332 if (argv[i + 1]) {
333 cs += strlen(argv[i]) + 1;
334 cs[-1] = ' ';
335 }
336 }
337 if (verbose > 0)
338 jail_note(j, "run command%s%s%s: %s\n",
339 injail ? " in jail" : "", username ? " as " : "",
340 username ? username : "", j->comline);
341
342 pid = fork();
343 if (pid < 0)
344 err(1, "fork");
345 if (pid > 0) {
346 if (bg) {
347 j->flags |= JF_BACKGROUND;
348 requeue(j, &ready);
349 } else {
350 --*plimit;
351 add_proc(j, pid);
352 }
353 return 1;
354 }
355 if (bg)
356 setsid();
357
358 pwd = NULL;
359 lcap = NULL;
360 if ((clean || username) && injail && sjuser &&
361 get_user_info(j, username, &pwd, &lcap) < 0)
362 exit(1);
363 if (injail) {
364 /* jail_attach won't chdir along with its chroot. */
365 path = string_param(j->intparams[KP_PATH]);
366 if (path && chdir(path) < 0) {
367 jail_warnx(j, "chdir %s: %s", path, strerror(errno));
368 exit(1);
369 }
370 if (int_param(j->intparams[IP_EXEC_FIB], &fib) &&
371 setfib(fib) < 0) {
372 jail_warnx(j, "setfib: %s", strerror(errno));
373 exit(1);
374 }
375 if (jail_attach(j->jid) < 0) {
376 jail_warnx(j, "jail_attach: %s", strerror(errno));
377 exit(1);
378 }
379 }
380 if (clean || username) {
381 if (!(injail && sjuser) &&
382 get_user_info(j, username, &pwd, &lcap) < 0)
383 exit(1);
384 if (clean) {
385 term = getenv("TERM");
386 environ = &cleanenv;
387 setenv("PATH", "/bin:/usr/bin", 0);
388 setenv("TERM", term, 1);
389 }
390 if (setusercontext(lcap, pwd, pwd->pw_uid, username
391 ? LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN
392 : LOGIN_SETPATH | LOGIN_SETENV) < 0) {
393 jail_warnx(j, "setusercontext %s: %s", pwd->pw_name,
394 strerror(errno));
395 exit(1);
396 }
397 login_close(lcap);
398 setenv("USER", pwd->pw_name, 1);
399 setenv("HOME", pwd->pw_dir, 1);
400 setenv("SHELL",
401 *pwd->pw_shell ? pwd->pw_shell : _PATH_BSHELL, 1);
402 if (clean && chdir(pwd->pw_dir) < 0) {
403 jail_warnx(j, "chdir %s: %s",
404 pwd->pw_dir, strerror(errno));
405 exit(1);
406 }
407 endpwent();
408 }
409
410 if (consfd != 0 && (dup2(consfd, 1) < 0 || dup2(consfd, 2) < 0)) {
411 jail_warnx(j, "exec.consolelog: %s", strerror(errno));
412 exit(1);
413 }
414 closefrom(3);
415 execvp(argv[0], argv);
416 jail_warnx(j, "exec %s: %s", argv[0], strerror(errno));
417 exit(1);
418}
419
420/*
421 * Check command exit status
422 */
423int
424finish_command(struct cfjail *j, int *plimit)
425{
426 int error;
427
428 if (j->flags & (JF_RUNQ | JF_BACKGROUND))
429 return 0;
430 ++*plimit;
431 if (!TAILQ_EMPTY(&runnable))
432 requeue(TAILQ_FIRST(&runnable), &ready);
433 error = 0;
434 if (j->flags & JF_TIMEOUT) {
435 j->flags &= ~JF_TIMEOUT;
436 if (j->comparam != IP_STOP_TIMEOUT) {
437 jail_warnx(j, "%s: timed out", j->comline);
438 failed(j);
439 error = -1;
440 } else if (verbose > 0)
441 jail_note(j, "timed out\n");
442 } else if (j->pstatus != 0) {
443 if (WIFSIGNALED(j->pstatus))
444 jail_warnx(j, "%s: exited on signal %d",
445 j->comline, WTERMSIG(j->pstatus));
446 else
447 jail_warnx(j, "%s: failed", j->comline);
448 failed(j);
449 error = -1;
450 }
451 free(j->comline);
452 return error;
453}
454
455/*
456 * Check for finished processed or timeouts.
457 */
458struct cfjail *
459next_proc(int nonblock)
460{
461 struct kevent ke;
462 struct timespec ts;
463 struct timespec *tsp;
464 struct cfjail *j;
465
466 if (!TAILQ_EMPTY(&sleeping)) {
467 again:
468 tsp = NULL;
469 if ((j = TAILQ_FIRST(&sleeping)) && j->timeout.tv_sec) {
470 clock_gettime(CLOCK_REALTIME, &ts);
471 ts.tv_sec = j->timeout.tv_sec - ts.tv_sec;
472 ts.tv_nsec = j->timeout.tv_nsec - ts.tv_nsec;
473 if (ts.tv_nsec < 0) {
474 ts.tv_sec--;
475 ts.tv_nsec += 1000000000;
476 }
477 if (ts.tv_sec < 0 ||
478 (ts.tv_sec == 0 && ts.tv_nsec == 0)) {
479 j->flags |= JF_TIMEOUT;
480 clear_procs(j);
481 return j;
482 }
483 tsp = &ts;
484 }
485 if (nonblock) {
486 ts.tv_sec = 0;
487 ts.tv_nsec = 0;
488 tsp = &ts;
489 }
490 switch (kevent(kq, NULL, 0, &ke, 1, tsp)) {
491 case -1:
492 if (errno != EINTR)
493 err(1, "kevent");
494 goto again;
495 case 0:
496 if (!nonblock) {
497 j = TAILQ_FIRST(&sleeping);
498 j->flags |= JF_TIMEOUT;
499 clear_procs(j);
500 return j;
501 }
502 break;
503 case 1:
504 (void)waitpid(ke.ident, NULL, WNOHANG);
505 if ((j = find_proc(ke.ident))) {
506 j->pstatus = ke.data;
507 return j;
508 }
509 goto again;
510 }
511 }
512 return NULL;
513}
514
515/*
516 * Send SIGTERM to all processes in a jail and wait for them to die.
517 */
518int
519term_procs(struct cfjail *j)
520{
521 struct kinfo_proc *ki;
522 int i, noted, pcnt, timeout;
523
524 static kvm_t *kd;
525
526 if (!int_param(j->intparams[IP_STOP_TIMEOUT], &timeout))
527 timeout = DEFAULT_STOP_TIMEOUT;
528 else if (timeout == 0)
529 return 0;
530
531 if (kd == NULL) {
532 kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "jail");
533 if (kd == NULL)
534 exit(1);
535 }
536
537 ki = kvm_getprocs(kd, KERN_PROC_PROC, 0, &pcnt);
538 if (ki == NULL)
539 exit(1);
540 noted = 0;
541 for (i = 0; i < pcnt; i++)
542 if (ki[i].ki_jid == j->jid &&
543 kill(ki[i].ki_pid, SIGTERM) == 0) {
544 add_proc(j, ki[i].ki_pid);
545 if (verbose > 0) {
546 if (!noted) {
547 noted = 1;
548 jail_note(j, "sent SIGTERM to:");
549 }
550 printf(" %d", ki[i].ki_pid);
551 }
552 }
553 if (noted)
554 printf("\n");
555 if (j->nprocs > 0) {
556 clock_gettime(CLOCK_REALTIME, &j->timeout);
557 j->timeout.tv_sec += timeout;
558 return 1;
559 }
560 return 0;
561}
562
563/*
564 * Add a process to the hash, tied to a jail.
565 */
566static void
567add_proc(struct cfjail *j, pid_t pid)
568{
569 struct kevent ke;
570 struct cfjail *tj;
571 struct phash *ph;
572
573 if (!kq && (kq = kqueue()) < 0)
574 err(1, "kqueue");
575 EV_SET(&ke, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
576 if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0)
577 err(1, "kevent");
578 ph = emalloc(sizeof(struct phash));
579 ph->j = j;
580 ph->pid = pid;
581 LIST_INSERT_HEAD(&phash[pid % PHASH_SIZE], ph, le);
582 j->nprocs++;
583 if (j->timeout.tv_sec) {
584 TAILQ_REMOVE(j->queue, j, tq);
585 TAILQ_FOREACH(tj, &sleeping, tq) {
586 if (!tj->timeout.tv_sec ||
587 j->timeout.tv_sec < tj->timeout.tv_sec ||
588 (j->timeout.tv_sec == tj->timeout.tv_sec &&
589 j->timeout.tv_nsec <= tj->timeout.tv_nsec)) {
590 TAILQ_INSERT_BEFORE(tj, j, tq);
591 break;
592 }
593 }
594 if (tj == NULL)
595 TAILQ_INSERT_TAIL(&sleeping, j, tq);
596 j->queue = &sleeping;
597 } else
598 requeue(j, &sleeping);
599}
600
601/*
602 * Remove any processes from the hash that correspond to a jail.
603 */
604static void
605clear_procs(struct cfjail *j)
606{
607 struct kevent ke;
608 struct phash *ph, *tph;
609 int i;
610
611 j->nprocs = 0;
612 for (i = 0; i < PHASH_SIZE; i++)
613 LIST_FOREACH_SAFE(ph, &phash[i], le, tph)
614 if (ph->j == j) {
615 EV_SET(&ke, ph->pid, EVFILT_PROC, EV_DELETE,
616 NOTE_EXIT, 0, NULL);
617 (void)kevent(kq, &ke, 1, NULL, 0, NULL);
618 LIST_REMOVE(ph, le);
619 free(ph);
620 }
621}
622
623/*
624 * Find the jail that corresponds to an exited process.
625 */
626static struct cfjail *
627find_proc(pid_t pid)
628{
629 struct cfjail *j;
630 struct phash *ph;
631
632 LIST_FOREACH(ph, &phash[pid % PHASH_SIZE], le)
633 if (ph->pid == pid) {
634 j = ph->j;
635 LIST_REMOVE(ph, le);
636 free(ph);
637 return --j->nprocs ? NULL : j;
638 }
639 return NULL;
640}
641
642/*
643 * Look up a user in the passwd and login.conf files.
644 */
645static int
646get_user_info(struct cfjail *j, const char *username,
647 const struct passwd **pwdp, login_cap_t **lcapp)
648{
649 const struct passwd *pwd;
650
651 *pwdp = pwd = username ? getpwnam(username) : getpwuid(getuid());
652 if (pwd == NULL) {
653 if (errno)
654 jail_warnx(j, "getpwnam%s%s: %s", username ? " " : "",
655 username ? username : "", strerror(errno));
656 else if (username)
657 jail_warnx(j, "%s: no such user", username);
658 else
659 jail_warnx(j, "unknown uid %d", getuid());
660 return -1;
661 }
662 *lcapp = login_getpwclass(pwd);
663 if (*lcapp == NULL) {
664 jail_warnx(j, "getpwclass %s: %s", pwd->pw_name,
665 strerror(errno));
666 return -1;
667 }
668 /* Set the groups while the group file is still available */
669 if (initgroups(pwd->pw_name, pwd->pw_gid) < 0) {
670 jail_warnx(j, "initgroups %s: %s", pwd->pw_name,
671 strerror(errno));
672 return -1;
673 }
674 return 0;
675}
226 } else if (comparam == IP_MOUNT_DEVFS) {
227 path = string_param(j->intparams[KP_PATH]);
228 if (path == NULL) {
229 jail_warnx(j, "mount.devfs: no path");
230 failed(j);
231 return -1;
232 }
233 if (down) {
234 argv = alloca(3 * sizeof(char *));
235 *(const char **)&argv[0] = "/sbin/umount";
236 argv[1] = alloca(strlen(path) + 5);
237 sprintf(argv[1], "%s/dev", path);
238 argv[2] = NULL;
239 } else {
240 argv = alloca(4 * sizeof(char *));
241 *(const char **)&argv[0] = _PATH_BSHELL;
242 *(const char **)&argv[1] = "-c";
243 ruleset = string_param(j->intparams
244 [IP_MOUNT_DEVFS_RULESET]);
245 argv[2] = alloca(strlen(path) +
246 (ruleset ? strlen(ruleset) + 1 : 0) + 56);
247 sprintf(argv[2], ". /etc/rc.subr; load_rc_config .; "
248 "devfs_mount_jail %s/dev%s%s", path,
249 ruleset ? " " : "", ruleset ? ruleset : "");
250 argv[3] = NULL;
251 }
252 j->flags |= JF_MOUNTED;
253 } else if (comparam == IP_COMMAND && j->name == NULL) {
254 argc = 0;
255 STAILQ_FOREACH(s, &j->intparams[IP_COMMAND]->val, tq)
256 argc++;
257 argv = alloca((argc + 1) * sizeof(char *));
258 argc = 0;
259 STAILQ_FOREACH(s, &j->intparams[IP_COMMAND]->val, tq)
260 argv[argc++] = s->s;
261 argv[argc] = NULL;
262 j->comstring = NULL;
263 } else if ((cs = strpbrk(comstring->s, "!\"$&'()*;<>?[\\]`{|}~")) &&
264 !(cs[0] == '&' && cs[1] == '\0')) {
265 argv = alloca(4 * sizeof(char *));
266 *(const char **)&argv[0] = _PATH_BSHELL;
267 *(const char **)&argv[1] = "-c";
268 argv[2] = comstring->s;
269 argv[3] = NULL;
270 } else {
271 if (cs) {
272 *cs = 0;
273 bg = 1;
274 }
275 comcs = alloca(comstring->len + 1);
276 strcpy(comcs, comstring->s);
277 argc = 0;
278 for (cs = strtok(comcs, " \t\f\v\r\n"); cs;
279 cs = strtok(NULL, " \t\f\v\r\n"))
280 argc++;
281 argv = alloca((argc + 1) * sizeof(char *));
282 strcpy(comcs, comstring->s);
283 argc = 0;
284 for (cs = strtok(comcs, " \t\f\v\r\n"); cs;
285 cs = strtok(NULL, " \t\f\v\r\n"))
286 argv[argc++] = cs;
287 argv[argc] = NULL;
288 }
289 if (argv[0] == NULL)
290 return 0;
291
292 j->pstatus = 0;
293 if (int_param(j->intparams[IP_EXEC_TIMEOUT], &timeout) &&
294 timeout != 0) {
295 clock_gettime(CLOCK_REALTIME, &j->timeout);
296 j->timeout.tv_sec += timeout;
297 } else
298 j->timeout.tv_sec = 0;
299
300 injail = comparam == IP_EXEC_START || comparam == IP_COMMAND ||
301 comparam == IP_EXEC_STOP;
302 clean = bool_param(j->intparams[IP_EXEC_CLEAN]);
303 username = string_param(j->intparams[injail
304 ? IP_EXEC_JAIL_USER : IP_EXEC_SYSTEM_USER]);
305 sjuser = bool_param(j->intparams[IP_EXEC_SYSTEM_JAIL_USER]);
306
307 consfd = 0;
308 if (injail &&
309 (conslog = string_param(j->intparams[IP_EXEC_CONSOLELOG]))) {
310 consfd =
311 open(conslog, O_WRONLY | O_CREAT | O_APPEND, DEFFILEMODE);
312 if (consfd < 0) {
313 jail_warnx(j, "open %s: %s", conslog, strerror(errno));
314 failed(j);
315 return -1;
316 }
317 }
318
319 comlen = 0;
320 for (i = 0; argv[i]; i++)
321 comlen += strlen(argv[i]) + 1;
322 j->comline = cs = emalloc(comlen);
323 for (i = 0; argv[i]; i++) {
324 strcpy(cs, argv[i]);
325 if (argv[i + 1]) {
326 cs += strlen(argv[i]) + 1;
327 cs[-1] = ' ';
328 }
329 }
330 if (verbose > 0)
331 jail_note(j, "run command%s%s%s: %s\n",
332 injail ? " in jail" : "", username ? " as " : "",
333 username ? username : "", j->comline);
334
335 pid = fork();
336 if (pid < 0)
337 err(1, "fork");
338 if (pid > 0) {
339 if (bg) {
340 j->flags |= JF_BACKGROUND;
341 requeue(j, &ready);
342 } else {
343 --*plimit;
344 add_proc(j, pid);
345 }
346 return 1;
347 }
348 if (bg)
349 setsid();
350
351 pwd = NULL;
352 lcap = NULL;
353 if ((clean || username) && injail && sjuser &&
354 get_user_info(j, username, &pwd, &lcap) < 0)
355 exit(1);
356 if (injail) {
357 /* jail_attach won't chdir along with its chroot. */
358 path = string_param(j->intparams[KP_PATH]);
359 if (path && chdir(path) < 0) {
360 jail_warnx(j, "chdir %s: %s", path, strerror(errno));
361 exit(1);
362 }
363 if (int_param(j->intparams[IP_EXEC_FIB], &fib) &&
364 setfib(fib) < 0) {
365 jail_warnx(j, "setfib: %s", strerror(errno));
366 exit(1);
367 }
368 if (jail_attach(j->jid) < 0) {
369 jail_warnx(j, "jail_attach: %s", strerror(errno));
370 exit(1);
371 }
372 }
373 if (clean || username) {
374 if (!(injail && sjuser) &&
375 get_user_info(j, username, &pwd, &lcap) < 0)
376 exit(1);
377 if (clean) {
378 term = getenv("TERM");
379 environ = &cleanenv;
380 setenv("PATH", "/bin:/usr/bin", 0);
381 setenv("TERM", term, 1);
382 }
383 if (setusercontext(lcap, pwd, pwd->pw_uid, username
384 ? LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN
385 : LOGIN_SETPATH | LOGIN_SETENV) < 0) {
386 jail_warnx(j, "setusercontext %s: %s", pwd->pw_name,
387 strerror(errno));
388 exit(1);
389 }
390 login_close(lcap);
391 setenv("USER", pwd->pw_name, 1);
392 setenv("HOME", pwd->pw_dir, 1);
393 setenv("SHELL",
394 *pwd->pw_shell ? pwd->pw_shell : _PATH_BSHELL, 1);
395 if (clean && chdir(pwd->pw_dir) < 0) {
396 jail_warnx(j, "chdir %s: %s",
397 pwd->pw_dir, strerror(errno));
398 exit(1);
399 }
400 endpwent();
401 }
402
403 if (consfd != 0 && (dup2(consfd, 1) < 0 || dup2(consfd, 2) < 0)) {
404 jail_warnx(j, "exec.consolelog: %s", strerror(errno));
405 exit(1);
406 }
407 closefrom(3);
408 execvp(argv[0], argv);
409 jail_warnx(j, "exec %s: %s", argv[0], strerror(errno));
410 exit(1);
411}
412
413/*
414 * Check command exit status
415 */
416int
417finish_command(struct cfjail *j, int *plimit)
418{
419 int error;
420
421 if (j->flags & (JF_RUNQ | JF_BACKGROUND))
422 return 0;
423 ++*plimit;
424 if (!TAILQ_EMPTY(&runnable))
425 requeue(TAILQ_FIRST(&runnable), &ready);
426 error = 0;
427 if (j->flags & JF_TIMEOUT) {
428 j->flags &= ~JF_TIMEOUT;
429 if (j->comparam != IP_STOP_TIMEOUT) {
430 jail_warnx(j, "%s: timed out", j->comline);
431 failed(j);
432 error = -1;
433 } else if (verbose > 0)
434 jail_note(j, "timed out\n");
435 } else if (j->pstatus != 0) {
436 if (WIFSIGNALED(j->pstatus))
437 jail_warnx(j, "%s: exited on signal %d",
438 j->comline, WTERMSIG(j->pstatus));
439 else
440 jail_warnx(j, "%s: failed", j->comline);
441 failed(j);
442 error = -1;
443 }
444 free(j->comline);
445 return error;
446}
447
448/*
449 * Check for finished processed or timeouts.
450 */
451struct cfjail *
452next_proc(int nonblock)
453{
454 struct kevent ke;
455 struct timespec ts;
456 struct timespec *tsp;
457 struct cfjail *j;
458
459 if (!TAILQ_EMPTY(&sleeping)) {
460 again:
461 tsp = NULL;
462 if ((j = TAILQ_FIRST(&sleeping)) && j->timeout.tv_sec) {
463 clock_gettime(CLOCK_REALTIME, &ts);
464 ts.tv_sec = j->timeout.tv_sec - ts.tv_sec;
465 ts.tv_nsec = j->timeout.tv_nsec - ts.tv_nsec;
466 if (ts.tv_nsec < 0) {
467 ts.tv_sec--;
468 ts.tv_nsec += 1000000000;
469 }
470 if (ts.tv_sec < 0 ||
471 (ts.tv_sec == 0 && ts.tv_nsec == 0)) {
472 j->flags |= JF_TIMEOUT;
473 clear_procs(j);
474 return j;
475 }
476 tsp = &ts;
477 }
478 if (nonblock) {
479 ts.tv_sec = 0;
480 ts.tv_nsec = 0;
481 tsp = &ts;
482 }
483 switch (kevent(kq, NULL, 0, &ke, 1, tsp)) {
484 case -1:
485 if (errno != EINTR)
486 err(1, "kevent");
487 goto again;
488 case 0:
489 if (!nonblock) {
490 j = TAILQ_FIRST(&sleeping);
491 j->flags |= JF_TIMEOUT;
492 clear_procs(j);
493 return j;
494 }
495 break;
496 case 1:
497 (void)waitpid(ke.ident, NULL, WNOHANG);
498 if ((j = find_proc(ke.ident))) {
499 j->pstatus = ke.data;
500 return j;
501 }
502 goto again;
503 }
504 }
505 return NULL;
506}
507
508/*
509 * Send SIGTERM to all processes in a jail and wait for them to die.
510 */
511int
512term_procs(struct cfjail *j)
513{
514 struct kinfo_proc *ki;
515 int i, noted, pcnt, timeout;
516
517 static kvm_t *kd;
518
519 if (!int_param(j->intparams[IP_STOP_TIMEOUT], &timeout))
520 timeout = DEFAULT_STOP_TIMEOUT;
521 else if (timeout == 0)
522 return 0;
523
524 if (kd == NULL) {
525 kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "jail");
526 if (kd == NULL)
527 exit(1);
528 }
529
530 ki = kvm_getprocs(kd, KERN_PROC_PROC, 0, &pcnt);
531 if (ki == NULL)
532 exit(1);
533 noted = 0;
534 for (i = 0; i < pcnt; i++)
535 if (ki[i].ki_jid == j->jid &&
536 kill(ki[i].ki_pid, SIGTERM) == 0) {
537 add_proc(j, ki[i].ki_pid);
538 if (verbose > 0) {
539 if (!noted) {
540 noted = 1;
541 jail_note(j, "sent SIGTERM to:");
542 }
543 printf(" %d", ki[i].ki_pid);
544 }
545 }
546 if (noted)
547 printf("\n");
548 if (j->nprocs > 0) {
549 clock_gettime(CLOCK_REALTIME, &j->timeout);
550 j->timeout.tv_sec += timeout;
551 return 1;
552 }
553 return 0;
554}
555
556/*
557 * Add a process to the hash, tied to a jail.
558 */
559static void
560add_proc(struct cfjail *j, pid_t pid)
561{
562 struct kevent ke;
563 struct cfjail *tj;
564 struct phash *ph;
565
566 if (!kq && (kq = kqueue()) < 0)
567 err(1, "kqueue");
568 EV_SET(&ke, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
569 if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0)
570 err(1, "kevent");
571 ph = emalloc(sizeof(struct phash));
572 ph->j = j;
573 ph->pid = pid;
574 LIST_INSERT_HEAD(&phash[pid % PHASH_SIZE], ph, le);
575 j->nprocs++;
576 if (j->timeout.tv_sec) {
577 TAILQ_REMOVE(j->queue, j, tq);
578 TAILQ_FOREACH(tj, &sleeping, tq) {
579 if (!tj->timeout.tv_sec ||
580 j->timeout.tv_sec < tj->timeout.tv_sec ||
581 (j->timeout.tv_sec == tj->timeout.tv_sec &&
582 j->timeout.tv_nsec <= tj->timeout.tv_nsec)) {
583 TAILQ_INSERT_BEFORE(tj, j, tq);
584 break;
585 }
586 }
587 if (tj == NULL)
588 TAILQ_INSERT_TAIL(&sleeping, j, tq);
589 j->queue = &sleeping;
590 } else
591 requeue(j, &sleeping);
592}
593
594/*
595 * Remove any processes from the hash that correspond to a jail.
596 */
597static void
598clear_procs(struct cfjail *j)
599{
600 struct kevent ke;
601 struct phash *ph, *tph;
602 int i;
603
604 j->nprocs = 0;
605 for (i = 0; i < PHASH_SIZE; i++)
606 LIST_FOREACH_SAFE(ph, &phash[i], le, tph)
607 if (ph->j == j) {
608 EV_SET(&ke, ph->pid, EVFILT_PROC, EV_DELETE,
609 NOTE_EXIT, 0, NULL);
610 (void)kevent(kq, &ke, 1, NULL, 0, NULL);
611 LIST_REMOVE(ph, le);
612 free(ph);
613 }
614}
615
616/*
617 * Find the jail that corresponds to an exited process.
618 */
619static struct cfjail *
620find_proc(pid_t pid)
621{
622 struct cfjail *j;
623 struct phash *ph;
624
625 LIST_FOREACH(ph, &phash[pid % PHASH_SIZE], le)
626 if (ph->pid == pid) {
627 j = ph->j;
628 LIST_REMOVE(ph, le);
629 free(ph);
630 return --j->nprocs ? NULL : j;
631 }
632 return NULL;
633}
634
635/*
636 * Look up a user in the passwd and login.conf files.
637 */
638static int
639get_user_info(struct cfjail *j, const char *username,
640 const struct passwd **pwdp, login_cap_t **lcapp)
641{
642 const struct passwd *pwd;
643
644 *pwdp = pwd = username ? getpwnam(username) : getpwuid(getuid());
645 if (pwd == NULL) {
646 if (errno)
647 jail_warnx(j, "getpwnam%s%s: %s", username ? " " : "",
648 username ? username : "", strerror(errno));
649 else if (username)
650 jail_warnx(j, "%s: no such user", username);
651 else
652 jail_warnx(j, "unknown uid %d", getuid());
653 return -1;
654 }
655 *lcapp = login_getpwclass(pwd);
656 if (*lcapp == NULL) {
657 jail_warnx(j, "getpwclass %s: %s", pwd->pw_name,
658 strerror(errno));
659 return -1;
660 }
661 /* Set the groups while the group file is still available */
662 if (initgroups(pwd->pw_name, pwd->pw_gid) < 0) {
663 jail_warnx(j, "initgroups %s: %s", pwd->pw_name,
664 strerror(errno));
665 return -1;
666 }
667 return 0;
668}