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 223189 2011-06-17 16:18:44Z 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 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
64int paralimit = -1;
65
66extern char **environ;
67
68static int run_command(struct cfjail *j);
69static void add_proc(struct cfjail *j, pid_t pid);
70static void clear_procs(struct cfjail *j);
71static struct cfjail *find_proc(pid_t pid);
72static int term_procs(struct cfjail *j);
73static int get_user_info(struct cfjail *j, const char *username,
74 const struct passwd **pwdp, login_cap_t **lcapp);
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 the next command associated with a jail.
85 */
86int
87next_command(struct cfjail *j)
88{
89 const struct cfstring *comstring;
90 enum intparam comparam;
91 int rval, create_failed;
92
93 static struct cfstring dummystring = { .len = 1 };
94
95 rval = 0;
96 create_failed = (j->flags & (JF_STOP | JF_FAILED)) == JF_FAILED;
97 for (; (comparam = *j->comparam) && comparam != IP__OP;
98 j->comparam += create_failed ? -1 : 1) {
99 if (j->comstring == NULL) {
100 switch (comparam) {
101 case IP_MOUNT_DEVFS:
102 if (!bool_param(j->intparams[IP_MOUNT_DEVFS]))
103 continue;
104 /* FALLTHROUGH */
105 case IP_STOP_TIMEOUT:
106 j->comstring = &dummystring;
107 break;
108 default:
109 if (j->intparams[comparam] == NULL)
110 continue;
111 j->comstring = create_failed
112 ? TAILQ_LAST(&j->intparams[comparam]->val,
113 cfstrings)
114 : TAILQ_FIRST(&j->intparams[comparam]->val);
115 }
116 }
117 for (; j->comstring != NULL;
118 j->comstring = create_failed
119 ? TAILQ_PREV(j->comstring, cfstrings, tq)
120 : TAILQ_NEXT(j->comstring, tq)) {
121 if (rval != 0)
122 return rval;
123 if (j->comstring->len == 0 || (create_failed &&
124 (comparam == IP_EXEC_PRESTART || comparam ==
125 IP_EXEC_START || comparam == IP_COMMAND ||
126 comparam == IP_EXEC_POSTSTART)))
127 continue;
128 if (paralimit == 0) {
129 requeue(j, &runnable);
130 return 1;
131 }
132 rval = run_command(j);
133 create_failed =
134 (j->flags & (JF_STOP | JF_FAILED)) == JF_FAILED;
135 }
136 }
137 return rval;
138}
139
140/*
141 * Check command exit status
142 */
143int
144finish_command(struct cfjail *j)
145{
146 int error;
147
148 if (!(j->flags & JF_SLEEPQ))
149 return 0;
150 j->flags &= ~JF_SLEEPQ;
151 if (*j->comparam != IP_STOP_TIMEOUT) {
152 paralimit++;
153 if (!TAILQ_EMPTY(&runnable))
154 requeue(TAILQ_FIRST(&runnable), &ready);
155 }
156 error = 0;
157 if (j->flags & JF_TIMEOUT) {
158 j->flags &= ~JF_TIMEOUT;
159 if (*j->comparam != IP_STOP_TIMEOUT) {
160 jail_warnx(j, "%s: timed out", j->comline);
161 failed(j);
162 error = -1;
163 } else if (verbose > 0)
164 jail_note(j, "timed out\n");
165 } else if (j->pstatus != 0) {
166 if (WIFSIGNALED(j->pstatus))
167 jail_warnx(j, "%s: exited on signal %d",
168 j->comline, WTERMSIG(j->pstatus));
169 else
170 jail_warnx(j, "%s: failed", j->comline);
171 j->pstatus = 0;
172 failed(j);
173 error = -1;
174 }
175 free(j->comline);
176 j->comline = NULL;
177 return error;
178}
179
180/*
181 * Check for finished processed or timeouts.
182 */
183struct cfjail *
184next_proc(int nonblock)
185{
186 struct kevent ke;
187 struct timespec ts;
188 struct timespec *tsp;
189 struct cfjail *j;
190
191 if (!TAILQ_EMPTY(&sleeping)) {
192 again:
193 tsp = NULL;
194 if ((j = TAILQ_FIRST(&sleeping)) && j->timeout.tv_sec) {
195 clock_gettime(CLOCK_REALTIME, &ts);
196 ts.tv_sec = j->timeout.tv_sec - ts.tv_sec;
197 ts.tv_nsec = j->timeout.tv_nsec - ts.tv_nsec;
198 if (ts.tv_nsec < 0) {
199 ts.tv_sec--;
200 ts.tv_nsec += 1000000000;
201 }
202 if (ts.tv_sec < 0 ||
203 (ts.tv_sec == 0 && ts.tv_nsec == 0)) {
204 j->flags |= JF_TIMEOUT;
205 clear_procs(j);
206 return j;
207 }
208 tsp = &ts;
209 }
210 if (nonblock) {
211 ts.tv_sec = 0;
212 ts.tv_nsec = 0;
213 tsp = &ts;
214 }
215 switch (kevent(kq, NULL, 0, &ke, 1, tsp)) {
216 case -1:
217 if (errno != EINTR)
218 err(1, "kevent");
219 goto again;
220 case 0:
221 if (!nonblock) {
222 j = TAILQ_FIRST(&sleeping);
223 j->flags |= JF_TIMEOUT;
224 clear_procs(j);
225 return j;
226 }
227 break;
228 case 1:
229 (void)waitpid(ke.ident, NULL, WNOHANG);
230 if ((j = find_proc(ke.ident))) {
231 j->pstatus = ke.data;
232 return j;
233 }
234 goto again;
235 }
236 }
237 return NULL;
238}
239
240/*
241 * Run a single command for a jail, possible inside the jail.
242 */
243int
244run_command(struct cfjail *j)
245{
246 const struct passwd *pwd;
247 const struct cfstring *comstring, *s;
248 login_cap_t *lcap;
249 char **argv;
250 char *cs, *addr, *comcs, *devpath;
251 const char *jidstr, *conslog, *path, *ruleset, *term, *username;
252 enum intparam comparam;
253 size_t comlen;
254 pid_t pid;
255 int argc, bg, clean, consfd, down, fib, i, injail, sjuser, timeout;
256
257 static char *cleanenv;
258
259 /*
260 * Collect exec arguments. Internal commands for network and
261 * mounting build their own argument lists.
262 */
263 comparam = *j->comparam;
264 comstring = j->comstring;
265 bg = 0;
266 down = j->flags & (JF_STOP | JF_FAILED);
267 switch (comparam) {
268 case IP_STOP_TIMEOUT:
269 /* This isn't really a command */
270 return term_procs(j);
271
272 case IP__IP4_IFADDR:
273 argv = alloca(8 * sizeof(char *));

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

294 *(const char **)&argv[5] = cs + 1;
295 argc = 6;
296 } else {
297 argv[3] = addr;
298 argc = 4;
299 }
300 *(const char **)&argv[argc] = down ? "-alias" : "alias";
301 argv[argc + 1] = NULL;
302 break;
303
304#ifdef INET6
305 case IP__IP6_IFADDR:
306 argv = alloca(8 * sizeof(char *));
307 *(const char **)&argv[0] = _PATH_IFCONFIG;
308 if ((cs = strchr(comstring->s, '|'))) {
309 argv[1] = alloca(cs - comstring->s + 1);

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

319 if (!(cs = strchr(addr, '/'))) {
320 *(const char **)&argv[4] = "prefixlen";
321 *(const char **)&argv[5] = "128";
322 argc = 6;
323 } else
324 argc = 4;
325 *(const char **)&argv[argc] = down ? "-alias" : "alias";
326 argv[argc + 1] = NULL;
327 break;
328#endif
329
330 case IP_VNET_INTERFACE:
331 argv = alloca(5 * sizeof(char *));
332 *(const char **)&argv[0] = _PATH_IFCONFIG;
333 argv[1] = comstring->s;
334 *(const char **)&argv[2] = down ? "-vnet" : "vnet";
335 jidstr = string_param(j->intparams[KP_JID]);
336 *(const char **)&argv[3] =
337 jidstr ? jidstr : string_param(j->intparams[KP_NAME]);
338 argv[4] = NULL;
339 break;
340
341 case IP_MOUNT:
342 case IP__MOUNT_FROM_FSTAB:
343 argv = alloca(8 * sizeof(char *));
344 comcs = alloca(comstring->len + 1);
345 strcpy(comcs, comstring->s);
346 argc = 0;
347 for (cs = strtok(comcs, " \t\f\v\r\n"); cs && argc < 4;
348 cs = strtok(NULL, " \t\f\v\r\n"))
349 argv[argc++] = cs;
350 if (argc == 0)
351 return 0;
352 if (argc < 3) {
353 jail_warnx(j, "%s: %s: missing information",
354 j->intparams[comparam]->name, comstring->s);
355 failed(j);
356 return -1;
357 }
358 if (check_path(j, j->intparams[comparam]->name, argv[1], 0,
359 down ? argv[2] : NULL) < 0) {

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

374 } else {
375 argv[5] = NULL;
376 argv[4] = argv[1];
377 argv[3] = argv[0];
378 }
379 *(const char **)&argv[0] = _PATH_MOUNT;
380 }
381 *(const char **)&argv[1] = "-t";
382 break;
383
384 case IP_MOUNT_DEVFS:
385 path = string_param(j->intparams[KP_PATH]);
386 if (path == NULL) {
387 jail_warnx(j, "mount.devfs: no path");
388 failed(j);
389 return -1;

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

408 [IP_MOUNT_DEVFS_RULESET]);
409 argv[2] = alloca(strlen(path) +
410 (ruleset ? strlen(ruleset) + 1 : 0) + 56);
411 sprintf(argv[2], ". /etc/rc.subr; load_rc_config .; "
412 "devfs_mount_jail %s/dev%s%s", path,
413 ruleset ? " " : "", ruleset ? ruleset : "");
414 argv[3] = NULL;
415 }
416 break;
417
418 case IP_COMMAND:
419 if (j->name != NULL)
420 goto default_command;
421 argc = 0;
422 TAILQ_FOREACH(s, &j->intparams[IP_COMMAND]->val, tq)
423 argc++;

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

453 strcpy(comcs, comstring->s);
454 argc = 0;
455 for (cs = strtok(comcs, " \t\f\v\r\n"); cs;
456 cs = strtok(NULL, " \t\f\v\r\n"))
457 argv[argc++] = cs;
458 argv[argc] = NULL;
459 }
460 }
461 if (argv[0] == NULL)
462 return 0;
463
464 if (int_param(j->intparams[IP_EXEC_TIMEOUT], &timeout) &&
465 timeout != 0) {
466 clock_gettime(CLOCK_REALTIME, &j->timeout);
467 j->timeout.tv_sec += timeout;
468 } else
469 j->timeout.tv_sec = 0;
470
471 injail = comparam == IP_EXEC_START || comparam == IP_COMMAND ||

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

519 paralimit--;
520 add_proc(j, pid);
521 }
522 return 1;
523 }
524 if (bg)
525 setsid();
526
527 /* Set up the environment and run the command */
528 pwd = NULL;
529 lcap = NULL;
530 if ((clean || username) && injail && sjuser &&
531 get_user_info(j, username, &pwd, &lcap) < 0)
532 exit(1);
533 if (injail) {
534 /* jail_attach won't chdir along with its chroot. */
535 path = string_param(j->intparams[KP_PATH]);

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

583 }
584 closefrom(3);
585 execvp(argv[0], argv);
586 jail_warnx(j, "exec %s: %s", argv[0], strerror(errno));
587 exit(1);
588}
589
590/*
591 * Add a process to the hash, tied to a jail.
592 */
593static void
594add_proc(struct cfjail *j, pid_t pid)
595{
596 struct kevent ke;
597 struct cfjail *tj;
598 struct phash *ph;

--- 230 unchanged lines hidden ---