2 * Copyright (c) 1999 Poul-Henning Kamp.
3 * Copyright (c) 2009 James Gritton
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
31 #include <sys/param.h>
33 #include <sys/socket.h>
34 #include <sys/sysctl.h>
37 #include <arpa/inet.h>
38 #include <netinet/in.h>
44 #include <login_cap.h>
53 #define SJPARAM "security.jail.param"
54 #define ERRMSG_SIZE 256
61 static struct param *params;
62 static char **param_values;
65 static char *ip4_addr;
67 static char *ip6_addr;
70 static void add_ip_addr(char **addrp, char *newaddr);
72 static void add_ip_addr46(char *newaddr);
74 static void add_ip_addrinfo(int ai_flags, char *value);
75 static void quoted_print(FILE *fp, char *str);
76 static void set_param(const char *name, char *value);
77 static void usage(void);
79 static const char *perm_sysctl[][3] = {
80 { "security.jail.set_hostname_allowed",
81 "allow.noset_hostname", "allow.set_hostname" },
82 { "security.jail.sysvipc_allowed",
83 "allow.nosysvipc", "allow.sysvipc" },
84 { "security.jail.allow_raw_sockets",
85 "allow.noraw_sockets", "allow.raw_sockets" },
86 { "security.jail.chflags_allowed",
87 "allow.nochflags", "allow.chflags" },
88 { "security.jail.mount_allowed",
89 "allow.nomount", "allow.mount" },
90 { "security.jail.socket_unixiproute_only",
91 "allow.socket_af", "allow.nosocket_af" },
94 extern char **environ;
96 #define GET_USER_INFO do { \
97 pwd = getpwnam(username); \
100 err(1, "getpwnam: %s", username); \
102 errx(1, "%s: no such user", username); \
104 lcap = login_getpwclass(pwd); \
106 err(1, "getpwclass: %s", username); \
108 if (getgrouplist(username, pwd->pw_gid, groups, &ngroups) != 0) \
109 err(1, "getgrouplist: %s", username); \
113 main(int argc, char **argv)
115 login_cap_t *lcap = NULL;
116 struct iovec rparams[2];
117 struct passwd *pwd = NULL;
118 gid_t groups[NGROUPS];
120 int ch, cmdarg, i, jail_set_flags, jid, ngroups, sysval;
121 int hflag, iflag, Jflag, lflag, rflag, uflag, Uflag;
123 char *ep, *jailname, *securelevel, *username, *JidFile;
124 char errmsg[ERRMSG_SIZE], enforce_statfs[4];
125 static char *cleanenv;
126 const char *shell, *p = NULL;
129 hflag = iflag = Jflag = lflag = rflag = uflag = Uflag =
132 jailname = securelevel = username = JidFile = cleanenv = NULL;
135 while ((ch = getopt(argc, argv, "cdhilmn:r:s:u:U:J:")) != -1) {
138 jail_set_flags |= JAIL_DYING;
154 securelevel = optarg;
168 jail_set_flags |= JAIL_CREATE;
171 jail_set_flags |= JAIL_UPDATE;
174 jid = strtoul(optarg, &ep, 10);
175 if (!*optarg || *ep) {
176 *(const void **)&rparams[0].iov_base = "name";
177 rparams[0].iov_len = sizeof("name");
178 rparams[1].iov_base = optarg;
179 rparams[1].iov_len = strlen(optarg) + 1;
180 jid = jail_get(rparams, 2, 0);
182 errx(1, "unknown jail: %s", optarg);
193 if (argc > 0 || iflag || Jflag || lflag || uflag || Uflag)
195 if (jail_remove(jid) < 0)
196 err(1, "jail_remove");
203 if (lflag && username == NULL)
209 * If the first argument (path) starts with a slash, and the third
210 * argument (IP address) starts with a digit, it is likely to be
211 * an old-style fixed-parameter command line.
214 set_param("name", jailname);
216 set_param("securelevel", securelevel);
217 if (jail_set_flags) {
218 for (i = 0; i < argc; i++) {
219 if (!strncmp(argv[i], "command=", 8)) {
222 jail_set_flags |= JAIL_ATTACH;
226 if (!strncmp(argv[i], "ip4.addr=", 9)) {
227 add_ip_addr(&ip4_addr, argv[i] + 9);
231 if (!strncmp(argv[i], "ip6.addr=", 9)) {
232 add_ip_addr(&ip6_addr, argv[i] + 9);
236 if (!strncmp(argv[i], "host.hostname=", 14))
237 add_ip_addrinfo(0, argv[i] + 14);
239 set_param(NULL, argv[i]);
242 if (argc < 4 || argv[0][0] != '/')
244 "no -c or -m, so this must be an old-style command.",
245 "But it doesn't look like one.");
246 set_param("path", argv[0]);
247 set_param("host.hostname", argv[1]);
249 add_ip_addrinfo(0, argv[1]);
251 add_ip_addr46(argv[2]);
253 add_ip_addr(&ip4_addr, argv[2]);
256 /* Emulate the defaults from security.jail.* sysctls */
257 sysvallen = sizeof(sysval);
258 if (sysctlbyname("security.jail.jailed", &sysval, &sysvallen,
259 NULL, 0) == 0 && sysval == 0) {
260 for (pi = 0; pi < sizeof(perm_sysctl) /
261 sizeof(perm_sysctl[0]); pi++) {
262 sysvallen = sizeof(sysval);
263 if (sysctlbyname(perm_sysctl[pi][0],
264 &sysval, &sysvallen, NULL, 0) == 0)
265 set_param(perm_sysctl[pi]
266 [sysval ? 2 : 1], NULL);
268 sysvallen = sizeof(sysval);
269 if (sysctlbyname("security.jail.enforce_statfs",
270 &sysval, &sysvallen, NULL, 0) == 0) {
271 snprintf(enforce_statfs,
272 sizeof(enforce_statfs), "%d", sysval);
273 set_param("enforce_statfs", enforce_statfs);
277 if (ip4_addr != NULL)
278 set_param("ip4.addr", ip4_addr);
280 if (ip6_addr != NULL)
281 set_param("ip6.addr", ip6_addr);
284 set_param("errmsg", errmsg);
287 fp = fopen(JidFile, "w");
289 errx(1, "Could not create JidFile: %s", JidFile);
291 jid = jail_set(¶ms->name, 2 * nparams,
292 jail_set_flags ? jail_set_flags : JAIL_CREATE | JAIL_ATTACH);
294 if (errmsg[0] != '\0')
295 errx(1, "%s", errmsg);
303 if (jail_set_flags) {
304 fprintf(fp, "jid=%d", jid);
305 for (i = 0; i < nparams; i++)
306 if (strcmp(params[i].name.iov_base, "jid") &&
307 strcmp(params[i].name.iov_base, "errmsg")) {
309 (char *)params[i].name.iov_base);
310 if (param_values[i]) {
318 for (i = 0; i < nparams; i++)
319 if (!strcmp(params[i].name.iov_base, "path"))
322 fprintf(fp, "%d\t%s\t%s\t%s%s%s\t%s\n",
324 ? (char *)params[i].value.iov_base : argv[0],
325 argv[1], ip4_addr ? ip4_addr : "",
326 ip4_addr && ip4_addr[0] && ip6_addr && ip6_addr[0]
327 ? "," : "", ip6_addr ? ip6_addr : "", argv[3]);
329 fprintf(fp, "%d\t%s\t%s\t%s\t%s\n",
331 ? (char *)params[i].value.iov_base : argv[0],
332 argv[1], ip4_addr ? ip4_addr : "", argv[3]);
339 if (username != NULL) {
346 if (setgroups(ngroups, groups) != 0)
348 if (setgid(pwd->pw_gid) != 0)
350 if (setusercontext(lcap, pwd, pwd->pw_uid,
351 LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0)
352 err(1, "setusercontext");
357 shell = pwd->pw_shell;
359 shell = _PATH_BSHELL;
360 if (chdir(pwd->pw_dir) < 0)
361 errx(1, "no home directory");
362 setenv("HOME", pwd->pw_dir, 1);
363 setenv("SHELL", shell, 1);
364 setenv("USER", pwd->pw_name, 1);
366 setenv("TERM", p, 1);
368 execvp(argv[cmdarg], argv + cmdarg);
369 err(1, "execvp: %s", argv[cmdarg]);
373 add_ip_addr(char **addrp, char *value)
379 *addrp = strdup(value);
382 } else if (value[0]) {
383 addrlen = strlen(*addrp) + strlen(value) + 2;
384 addr = malloc(addrlen);
387 snprintf(addr, addrlen, "%s,%s", *addrp, value);
395 add_ip_addr46(char *value)
400 add_ip_addr(&ip4_addr, value);
401 add_ip_addr(&ip6_addr, value);
404 for (p = value;; p = np + 1)
409 add_ip_addrinfo(AI_NUMERICHOST, p);
417 add_ip_addrinfo(int ai_flags, char *value)
419 struct addrinfo hints, *ai0, *ai;
420 struct in_addr addr4;
422 char avalue4[INET_ADDRSTRLEN];
424 struct in6_addr addr6;
425 char avalue6[INET6_ADDRSTRLEN];
428 /* Look up the hostname (or get the address) */
429 memset(&hints, 0, sizeof(hints));
430 hints.ai_socktype = SOCK_STREAM;
432 hints.ai_family = PF_UNSPEC;
434 hints.ai_family = PF_INET;
436 hints.ai_flags = ai_flags;
437 error = getaddrinfo(value, NULL, &hints, &ai0);
439 errx(1, "hostname %s: %s", value, gai_strerror(error));
441 /* Convert the addresses to ASCII so set_param can convert them back. */
442 for (ai = ai0; ai; ai = ai->ai_next)
443 switch (ai->ai_family) {
445 memcpy(&addr4, &((struct sockaddr_in *)
446 (void *)ai->ai_addr)->sin_addr, sizeof(addr4));
447 if (inet_ntop(AF_INET, &addr4, avalue4,
448 INET_ADDRSTRLEN) == NULL)
450 add_ip_addr(&ip4_addr, avalue4);
454 memcpy(&addr6, &((struct sockaddr_in6 *)
455 (void *)ai->ai_addr)->sin6_addr, sizeof(addr6));
456 if (inet_ntop(AF_INET6, &addr6, avalue6,
457 INET6_ADDRSTRLEN) == NULL)
459 add_ip_addr(&ip6_addr, avalue6);
467 quoted_print(FILE *fp, char *str)
472 /* An empty string needs quoting. */
479 * The value will be surrounded by quotes if it contains spaces
482 qc = strchr(p, '\'') ? '"'
483 : strchr(p, '"') ? '\''
484 : strchr(p, ' ') || strchr(p, '\t') ? '"'
489 if (c == '\\' || c == qc)
498 set_param(const char *name, char *value)
503 int i, nval, mib[CTL_MAXNAME];
509 static int paramlistsize;
511 /* Separate the name from the value, if not done already. */
514 if ((value = strchr(value, '=')))
518 /* Check for repeat parameters */
519 for (i = 0; i < nparams; i++)
520 if (!strcmp(name, params[i].name.iov_base)) {
521 memcpy(params + i, params + i + 1,
522 (--nparams - i) * sizeof(struct param));
526 /* Make sure there is room for the new param record. */
529 params = malloc(paramlistsize * sizeof(*params));
530 param_values = malloc(paramlistsize * sizeof(*param_values));
531 if (params == NULL || param_values == NULL)
533 } else if (nparams >= paramlistsize) {
535 params = realloc(params, paramlistsize * sizeof(*params));
536 param_values = realloc(param_values,
537 paramlistsize * sizeof(*param_values));
542 /* Look up the paramter. */
543 param_values[nparams] = value;
544 param = params + nparams++;
545 *(const void **)¶m->name.iov_base = name;
546 param->name.iov_len = strlen(name) + 1;
547 /* Trivial values - no value or errmsg. */
549 param->value.iov_base = NULL;
550 param->value.iov_len = 0;
553 if (!strcmp(name, "errmsg")) {
554 param->value.iov_base = value;
555 param->value.iov_len = ERRMSG_SIZE;
560 snprintf(buf.s, sizeof(buf.s), SJPARAM ".%s", name);
561 mlen = sizeof(mib) - 2 * sizeof(int);
562 if (sysctl(mib, 2, mib + 2, &mlen, buf.s, strlen(buf.s)) < 0)
563 errx(1, "unknown parameter: %s", name);
565 buflen = sizeof(buf);
566 if (sysctl(mib, (mlen / sizeof(int)) + 2, &buf, &buflen, NULL, 0) < 0)
567 err(1, "sysctl(0.4.%s)", name);
569 * See if this is an array type.
570 * Treat non-arrays as an array of one.
572 p = strchr(buf.s, '\0');
574 if (p - 2 >= buf.s && !strcmp(p - 2, ",a")) {
575 if (value[0] == '\0' ||
576 (value[0] == '-' && value[1] == '\0')) {
577 param->value.iov_base = value;
578 param->value.iov_len = 0;
582 for (p = strchr(value, ','); p; p = strchr(p + 1, ',')) {
588 /* Set the values according to the parameter type. */
589 switch (buf.i & CTLTYPE) {
592 param->value.iov_len = nval * sizeof(int);
596 param->value.iov_len = nval * sizeof(long);
599 if (!strcmp(buf.s, "S,in_addr"))
600 param->value.iov_len = nval * sizeof(struct in_addr);
602 else if (!strcmp(buf.s, "S,in6_addr"))
603 param->value.iov_len = nval * sizeof(struct in6_addr);
606 errx(1, "%s: unknown parameter structure (%s)",
610 if (!strcmp(name, "path")) {
611 param->value.iov_base = malloc(MAXPATHLEN);
612 if (param->value.iov_base == NULL)
614 if (realpath(value, param->value.iov_base) == NULL)
615 err(1, "%s: realpath(%s)", name, value);
616 if (chdir(param->value.iov_base) != 0)
618 (char *)param->value.iov_base);
620 param->value.iov_base = value;
621 param->value.iov_len = strlen(param->value.iov_base) + 1;
624 errx(1, "%s: unknown parameter type %d (%s)",
627 param->value.iov_base = malloc(param->value.iov_len);
628 for (i = 0; i < nval; i++) {
629 switch (buf.i & CTLTYPE) {
631 ((int *)param->value.iov_base)[i] =
632 strtol(value, &ep, 10);
634 errx(1, "%s: non-integer value \"%s\"",
638 ((unsigned *)param->value.iov_base)[i] =
639 strtoul(value, &ep, 10);
641 errx(1, "%s: non-integer value \"%s\"",
645 ((long *)param->value.iov_base)[i] =
646 strtol(value, &ep, 10);
648 errx(1, "%s: non-integer value \"%s\"",
652 ((unsigned long *)param->value.iov_base)[i] =
653 strtoul(value, &ep, 10);
655 errx(1, "%s: non-integer value \"%s\"",
659 if (!strcmp(buf.s, "S,in_addr")) {
660 if (inet_pton(AF_INET, value,
662 param->value.iov_base)[i]) != 1)
663 errx(1, "%s: not an IPv4 address: %s",
667 else if (!strcmp(buf.s, "S,in6_addr")) {
668 if (inet_pton(AF_INET6, value,
669 &((struct in6_addr *)
670 param->value.iov_base)[i]) != 1)
671 errx(1, "%s: not an IPv6 address: %s",
678 value = strchr(value, '\0') + 1;
686 (void)fprintf(stderr,
687 "usage: jail [-d] [-h] [-i] [-J jid_file] "
688 "[-l -u username | -U username]\n"
689 " [-c | -m] param=value ... [command=command ...]\n"
690 " jail [-r jail]\n");