Deleted Added
sdiff udiff text old ( 223188 ) new ( 223189 )
full compact
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

--- 11 unchanged lines hidden (view full) ---

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 223188 2011-06-17 16:06:13Z jamie $");
29
30#include <sys/types.h>
31#include <sys/event.h>
32#include <sys/mount.h>
33#include <sys/stat.h>
34#include <sys/sysctl.h>
35#include <sys/user.h>
36#include <sys/wait.h>

--- 8 unchanged lines hidden (view full) ---

45#include <signal.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <unistd.h>
50
51#include "jailp.h"
52
53#define COMSTRING_DUMMY ((struct cfstring *)1)
54#define DEFAULT_STOP_TIMEOUT 10
55#define PHASH_SIZE 256
56
57LIST_HEAD(phhead, phash);
58
59struct phash {
60 LIST_ENTRY(phash) le;
61 struct cfjail *j;
62 pid_t pid;
63};
64
65int paralimit = -1;
66
67extern char **environ;
68
69static int get_user_info(struct cfjail *j, const char *username,
70 const struct passwd **pwdp, login_cap_t **lcapp);
71static void add_proc(struct cfjail *j, pid_t pid);
72static void clear_procs(struct cfjail *j);
73static struct cfjail *find_proc(pid_t pid);
74static int term_procs(struct cfjail *j);
75static int check_path(struct cfjail *j, const char *pname, const char *path,
76 int isfile, const char *umount_type);
77
78static struct cfjails sleeping = TAILQ_HEAD_INITIALIZER(sleeping);
79static struct cfjails runnable = TAILQ_HEAD_INITIALIZER(runnable);
80static struct phhead phash[PHASH_SIZE];
81static int kq;
82
83/*
84 * Run a command associated with a jail, possibly inside the jail.
85 */
86int
87run_command(struct cfjail *j, enum intparam comparam)
88{
89 const struct passwd *pwd;
90 struct cfstring *comstring, *s;
91 login_cap_t *lcap;
92 char **argv;
93 char *cs, *addr, *comcs, *devpath;
94 const char *jidstr, *conslog, *path, *ruleset, *term, *username;
95 size_t comlen;
96 pid_t pid;
97 int argc, bg, clean, consfd, down, fib, i, injail, sjuser, timeout;
98
99 static char *cleanenv;
100
101 if (comparam) {
102 switch (comparam) {
103 case IP_MOUNT_DEVFS:
104 if (!bool_param(j->intparams[IP_MOUNT_DEVFS]))
105 return 0;
106 /* FALLTHROUGH */
107 case IP_STOP_TIMEOUT:
108 j->comstring = COMSTRING_DUMMY;
109 break;
110 default:
111 if (j->intparams[comparam] == NULL)
112 return 0;
113 j->comstring =
114 TAILQ_FIRST(&j->intparams[comparam]->val);
115 }
116 j->comparam = comparam;
117 } else
118 comparam = j->comparam;
119 next_comstring:
120 comstring = j->comstring;
121 if (comstring == NULL)
122 return 0;
123 if (paralimit == 0) {
124 requeue(j, &runnable);
125 return 1;
126 }
127 j->comstring =
128 comstring == COMSTRING_DUMMY ? NULL : TAILQ_NEXT(comstring, tq);
129 if (comstring != COMSTRING_DUMMY && comstring->len == 0)
130 goto next_comstring;
131 /*
132 * Collect exec arguments. Internal commands for network and
133 * mounting build their own argument lists.
134 */
135 bg = j->flags & JF_FAILED;
136 down = j->flags & (JF_STOP | JF_FAILED);
137 switch (comparam) {
138 case IP_STOP_TIMEOUT:
139 /* This isn't really a command */
140 return term_procs(j);
141
142 case IP__IP4_IFADDR:
143 argv = alloca(8 * sizeof(char *));

--- 20 unchanged lines hidden (view full) ---

164 *(const char **)&argv[5] = cs + 1;
165 argc = 6;
166 } else {
167 argv[3] = addr;
168 argc = 4;
169 }
170 *(const char **)&argv[argc] = down ? "-alias" : "alias";
171 argv[argc + 1] = NULL;
172 j->flags |= JF_IFUP;
173 break;
174
175#ifdef INET6
176 case IP__IP6_IFADDR:
177 argv = alloca(8 * sizeof(char *));
178 *(const char **)&argv[0] = _PATH_IFCONFIG;
179 if ((cs = strchr(comstring->s, '|'))) {
180 argv[1] = alloca(cs - comstring->s + 1);

--- 9 unchanged lines hidden (view full) ---

190 if (!(cs = strchr(addr, '/'))) {
191 *(const char **)&argv[4] = "prefixlen";
192 *(const char **)&argv[5] = "128";
193 argc = 6;
194 } else
195 argc = 4;
196 *(const char **)&argv[argc] = down ? "-alias" : "alias";
197 argv[argc + 1] = NULL;
198 j->flags |= JF_IFUP;
199 break;
200#endif
201
202 case IP_VNET_INTERFACE:
203 argv = alloca(5 * sizeof(char *));
204 *(const char **)&argv[0] = _PATH_IFCONFIG;
205 argv[1] = comstring->s;
206 *(const char **)&argv[2] = down ? "-vnet" : "vnet";
207 jidstr = string_param(j->intparams[KP_JID]);
208 *(const char **)&argv[3] =
209 jidstr ? jidstr : string_param(j->intparams[KP_NAME]);
210 argv[4] = NULL;
211 j->flags |= JF_IFUP;
212 break;
213
214 case IP_MOUNT:
215 case IP__MOUNT_FROM_FSTAB:
216 argv = alloca(8 * sizeof(char *));
217 comcs = alloca(comstring->len + 1);
218 strcpy(comcs, comstring->s);
219 argc = 0;
220 for (cs = strtok(comcs, " \t\f\v\r\n"); cs && argc < 4;
221 cs = strtok(NULL, " \t\f\v\r\n"))
222 argv[argc++] = cs;
223 if (argc == 0)
224 goto next_comstring;
225 if (argc < 3) {
226 jail_warnx(j, "%s: %s: missing information",
227 j->intparams[comparam]->name, comstring->s);
228 failed(j);
229 return -1;
230 }
231 if (check_path(j, j->intparams[comparam]->name, argv[1], 0,
232 down ? argv[2] : NULL) < 0) {

--- 14 unchanged lines hidden (view full) ---

247 } else {
248 argv[5] = NULL;
249 argv[4] = argv[1];
250 argv[3] = argv[0];
251 }
252 *(const char **)&argv[0] = _PATH_MOUNT;
253 }
254 *(const char **)&argv[1] = "-t";
255 j->flags |= JF_MOUNTED;
256 break;
257
258 case IP_MOUNT_DEVFS:
259 path = string_param(j->intparams[KP_PATH]);
260 if (path == NULL) {
261 jail_warnx(j, "mount.devfs: no path");
262 failed(j);
263 return -1;

--- 18 unchanged lines hidden (view full) ---

282 [IP_MOUNT_DEVFS_RULESET]);
283 argv[2] = alloca(strlen(path) +
284 (ruleset ? strlen(ruleset) + 1 : 0) + 56);
285 sprintf(argv[2], ". /etc/rc.subr; load_rc_config .; "
286 "devfs_mount_jail %s/dev%s%s", path,
287 ruleset ? " " : "", ruleset ? ruleset : "");
288 argv[3] = NULL;
289 }
290 j->flags |= JF_MOUNTED;
291 break;
292
293 case IP_COMMAND:
294 if (j->name != NULL)
295 goto default_command;
296 argc = 0;
297 TAILQ_FOREACH(s, &j->intparams[IP_COMMAND]->val, tq)
298 argc++;

--- 29 unchanged lines hidden (view full) ---

328 strcpy(comcs, comstring->s);
329 argc = 0;
330 for (cs = strtok(comcs, " \t\f\v\r\n"); cs;
331 cs = strtok(NULL, " \t\f\v\r\n"))
332 argv[argc++] = cs;
333 argv[argc] = NULL;
334 }
335 }
336
337 if (argv[0] == NULL)
338 goto next_comstring;
339 if (int_param(j->intparams[IP_EXEC_TIMEOUT], &timeout) &&
340 timeout != 0) {
341 clock_gettime(CLOCK_REALTIME, &j->timeout);
342 j->timeout.tv_sec += timeout;
343 } else
344 j->timeout.tv_sec = 0;
345
346 injail = comparam == IP_EXEC_START || comparam == IP_COMMAND ||

--- 47 unchanged lines hidden (view full) ---

394 paralimit--;
395 add_proc(j, pid);
396 }
397 return 1;
398 }
399 if (bg)
400 setsid();
401
402 pwd = NULL;
403 lcap = NULL;
404 if ((clean || username) && injail && sjuser &&
405 get_user_info(j, username, &pwd, &lcap) < 0)
406 exit(1);
407 if (injail) {
408 /* jail_attach won't chdir along with its chroot. */
409 path = string_param(j->intparams[KP_PATH]);

--- 47 unchanged lines hidden (view full) ---

457 }
458 closefrom(3);
459 execvp(argv[0], argv);
460 jail_warnx(j, "exec %s: %s", argv[0], strerror(errno));
461 exit(1);
462}
463
464/*
465 * Check command exit status
466 */
467int
468finish_command(struct cfjail *j)
469{
470 int error;
471
472 if (!(j->flags & JF_SLEEPQ))
473 return 0;
474 j->flags &= ~JF_SLEEPQ;
475 if (j->comparam != IP_STOP_TIMEOUT) {
476 paralimit++;
477 if (!TAILQ_EMPTY(&runnable))
478 requeue(TAILQ_FIRST(&runnable), &ready);
479 }
480 error = 0;
481 if (j->flags & JF_TIMEOUT) {
482 j->flags &= ~JF_TIMEOUT;
483 if (j->comparam != IP_STOP_TIMEOUT) {
484 jail_warnx(j, "%s: timed out", j->comline);
485 failed(j);
486 error = -1;
487 } else if (verbose > 0)
488 jail_note(j, "timed out\n");
489 } else if (j->pstatus != 0) {
490 if (WIFSIGNALED(j->pstatus))
491 jail_warnx(j, "%s: exited on signal %d",
492 j->comline, WTERMSIG(j->pstatus));
493 else
494 jail_warnx(j, "%s: failed", j->comline);
495 j->pstatus = 0;
496 failed(j);
497 error = -1;
498 }
499 free(j->comline);
500 j->comline = NULL;
501 return error;
502}
503
504/*
505 * Check for finished processed or timeouts.
506 */
507struct cfjail *
508next_proc(int nonblock)
509{
510 struct kevent ke;
511 struct timespec ts;
512 struct timespec *tsp;
513 struct cfjail *j;
514
515 if (!TAILQ_EMPTY(&sleeping)) {
516 again:
517 tsp = NULL;
518 if ((j = TAILQ_FIRST(&sleeping)) && j->timeout.tv_sec) {
519 clock_gettime(CLOCK_REALTIME, &ts);
520 ts.tv_sec = j->timeout.tv_sec - ts.tv_sec;
521 ts.tv_nsec = j->timeout.tv_nsec - ts.tv_nsec;
522 if (ts.tv_nsec < 0) {
523 ts.tv_sec--;
524 ts.tv_nsec += 1000000000;
525 }
526 if (ts.tv_sec < 0 ||
527 (ts.tv_sec == 0 && ts.tv_nsec == 0)) {
528 j->flags |= JF_TIMEOUT;
529 clear_procs(j);
530 return j;
531 }
532 tsp = &ts;
533 }
534 if (nonblock) {
535 ts.tv_sec = 0;
536 ts.tv_nsec = 0;
537 tsp = &ts;
538 }
539 switch (kevent(kq, NULL, 0, &ke, 1, tsp)) {
540 case -1:
541 if (errno != EINTR)
542 err(1, "kevent");
543 goto again;
544 case 0:
545 if (!nonblock) {
546 j = TAILQ_FIRST(&sleeping);
547 j->flags |= JF_TIMEOUT;
548 clear_procs(j);
549 return j;
550 }
551 break;
552 case 1:
553 (void)waitpid(ke.ident, NULL, WNOHANG);
554 if ((j = find_proc(ke.ident))) {
555 j->pstatus = ke.data;
556 return j;
557 }
558 goto again;
559 }
560 }
561 return NULL;
562}
563
564/*
565 * Add a process to the hash, tied to a jail.
566 */
567static void
568add_proc(struct cfjail *j, pid_t pid)
569{
570 struct kevent ke;
571 struct cfjail *tj;
572 struct phash *ph;

--- 230 unchanged lines hidden ---