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>
36 #include <arpa/inet.h>
37 #include <netinet/in.h>
44 #include <login_cap.h>
53 static struct jailparam *params;
54 static char **param_values;
59 static char *ip6_addr;
63 static char *ip4_addr;
66 #if defined(INET6) || defined(INET)
67 static void add_ip_addr(char **addrp, char *newaddr);
70 static void add_ip_addr46(char *newaddr);
72 static void add_ip_addrinfo(int ai_flags, char *value);
73 static void quoted_print(FILE *fp, char *str);
74 static void set_param(const char *name, char *value);
75 static void usage(void);
77 static const char *perm_sysctl[][3] = {
78 { "security.jail.set_hostname_allowed",
79 "allow.noset_hostname", "allow.set_hostname" },
80 { "security.jail.sysvipc_allowed",
81 "allow.nosysvipc", "allow.sysvipc" },
82 { "security.jail.allow_raw_sockets",
83 "allow.noraw_sockets", "allow.raw_sockets" },
84 { "security.jail.chflags_allowed",
85 "allow.nochflags", "allow.chflags" },
86 { "security.jail.mount_allowed",
87 "allow.nomount", "allow.mount" },
88 { "security.jail.socket_unixiproute_only",
89 "allow.socket_af", "allow.nosocket_af" },
92 extern char **environ;
94 #define GET_USER_INFO do { \
95 pwd = getpwnam(username); \
98 err(1, "getpwnam: %s", username); \
100 errx(1, "%s: no such user", username); \
102 lcap = login_getpwclass(pwd); \
104 err(1, "getpwclass: %s", username); \
105 ngroups = ngroups_max; \
106 if (getgrouplist(username, pwd->pw_gid, groups, &ngroups) != 0) \
107 err(1, "getgrouplist: %s", username); \
111 main(int argc, char **argv)
113 login_cap_t *lcap = NULL;
114 struct passwd *pwd = NULL;
117 int ch, cmdarg, i, jail_set_flags, jid, ngroups, sysval;
118 int hflag, iflag, Jflag, lflag, rflag, uflag, Uflag;
121 char *jailname, *securelevel, *username, *JidFile;
122 char enforce_statfs[4];
123 static char *cleanenv;
124 const char *shell, *p = NULL;
127 hflag = iflag = Jflag = lflag = rflag = uflag = Uflag =
130 jailname = securelevel = username = JidFile = cleanenv = NULL;
133 ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1;
134 if ((groups = malloc(sizeof(gid_t) * ngroups_max)) == NULL)
137 while ((ch = getopt(argc, argv, "cdhilmn:r:s:u:U:J:")) != -1) {
140 jail_set_flags |= JAIL_DYING;
156 securelevel = optarg;
170 jail_set_flags |= JAIL_CREATE;
173 jail_set_flags |= JAIL_UPDATE;
176 jid = jail_getid(optarg);
178 errx(1, "%s", jail_errmsg);
188 if (argc > 0 || iflag || Jflag || lflag || uflag || Uflag)
190 if (jail_remove(jid) < 0)
191 err(1, "jail_remove");
198 if (lflag && username == NULL)
204 ip6_ok = feature_present("inet6");
207 ip4_ok = feature_present("inet");
211 set_param("name", jailname);
213 set_param("securelevel", securelevel);
214 if (jail_set_flags) {
215 for (i = 0; i < argc; i++) {
216 if (!strncmp(argv[i], "command=", 8)) {
219 jail_set_flags |= JAIL_ATTACH;
224 if (!strncmp(argv[i], "ip4.addr=", 9)) {
225 add_ip_addr(&ip4_addr, argv[i] + 9);
230 if (!strncmp(argv[i], "ip6.addr=", 9)) {
231 add_ip_addr(&ip6_addr, argv[i] + 9);
235 if (!strncmp(argv[i], "host.hostname=", 14))
236 add_ip_addrinfo(0, argv[i] + 14);
238 set_param(NULL, argv[i]);
241 if (argc < 4 || argv[0][0] != '/')
243 "no -c or -m, so this must be an old-style command.",
244 "But it doesn't look like one.");
245 set_param("path", argv[0]);
246 set_param("host.hostname", argv[1]);
248 add_ip_addrinfo(0, argv[1]);
249 #if defined(INET6) || defined(INET)
250 if (argv[2][0] != '\0')
252 add_ip_addr46(argv[2]);
254 add_ip_addr(&ip4_addr, argv[2]);
258 /* Emulate the defaults from security.jail.* sysctls */
259 sysvallen = sizeof(sysval);
260 if (sysctlbyname("security.jail.jailed", &sysval, &sysvallen,
261 NULL, 0) == 0 && sysval == 0) {
262 for (pi = 0; pi < sizeof(perm_sysctl) /
263 sizeof(perm_sysctl[0]); pi++) {
264 sysvallen = sizeof(sysval);
265 if (sysctlbyname(perm_sysctl[pi][0],
266 &sysval, &sysvallen, NULL, 0) == 0)
267 set_param(perm_sysctl[pi]
268 [sysval ? 2 : 1], NULL);
270 sysvallen = sizeof(sysval);
271 if (sysctlbyname("security.jail.enforce_statfs",
272 &sysval, &sysvallen, NULL, 0) == 0) {
273 snprintf(enforce_statfs,
274 sizeof(enforce_statfs), "%d", sysval);
275 set_param("enforce_statfs", enforce_statfs);
280 if (ip4_addr != NULL)
281 set_param("ip4.addr", ip4_addr);
284 if (ip6_addr != NULL)
285 set_param("ip6.addr", ip6_addr);
289 fp = fopen(JidFile, "w");
291 errx(1, "Could not create JidFile: %s", JidFile);
293 jid = jailparam_set(params, nparams,
294 jail_set_flags ? jail_set_flags : JAIL_CREATE | JAIL_ATTACH);
296 errx(1, "%s", jail_errmsg);
302 if (jail_set_flags) {
303 fprintf(fp, "jid=%d", jid);
304 for (i = 0; i < nparams; i++)
305 if (strcmp(params[i].jp_name, "jid")) {
307 (char *)params[i].jp_name);
308 if (param_values[i]) {
316 for (i = 0; i < nparams; i++)
317 if (!strcmp(params[i].jp_name, "path"))
319 #if defined(INET6) && defined(INET)
320 fprintf(fp, "%d\t%s\t%s\t%s%s%s\t%s\n",
322 ? (char *)params[i].jp_value : argv[0],
323 argv[1], ip4_addr ? ip4_addr : "",
324 ip4_addr && ip4_addr[0] && ip6_addr && ip6_addr[0]
325 ? "," : "", ip6_addr ? ip6_addr : "", argv[3]);
327 fprintf(fp, "%d\t%s\t%s\t%s\t%s\n",
329 ? (char *)params[i].jp_value : argv[0],
330 argv[1], ip6_addr ? ip6_addr : "", argv[3]);
332 fprintf(fp, "%d\t%s\t%s\t%s\t%s\n",
334 ? (char *)params[i].jp_value : argv[0],
335 argv[1], ip4_addr ? ip4_addr : "", argv[3]);
342 if (username != NULL) {
349 if (setgroups(ngroups, groups) != 0)
351 if (setgid(pwd->pw_gid) != 0)
353 if (setusercontext(lcap, pwd, pwd->pw_uid,
354 LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0)
355 err(1, "setusercontext");
360 shell = pwd->pw_shell;
362 shell = _PATH_BSHELL;
363 if (chdir(pwd->pw_dir) < 0)
364 errx(1, "no home directory");
365 setenv("HOME", pwd->pw_dir, 1);
366 setenv("SHELL", shell, 1);
367 setenv("USER", pwd->pw_name, 1);
369 setenv("TERM", p, 1);
371 execvp(argv[cmdarg], argv + cmdarg);
372 err(1, "execvp: %s", argv[cmdarg]);
375 #if defined(INET6) || defined(INET)
377 add_ip_addr(char **addrp, char *value)
383 *addrp = strdup(value);
386 } else if (value[0]) {
387 addrlen = strlen(*addrp) + strlen(value) + 2;
388 addr = malloc(addrlen);
391 snprintf(addr, addrlen, "%s,%s", *addrp, value);
400 add_ip_addr46(char *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;
422 char avalue4[INET_ADDRSTRLEN];
423 struct in_addr addr4;
426 char avalue6[INET6_ADDRSTRLEN];
427 struct in6_addr addr6;
430 /* Look up the hostname (or get the address) */
431 memset(&hints, 0, sizeof(hints));
432 hints.ai_socktype = SOCK_STREAM;
433 #if defined(INET6) && defined(INET)
434 hints.ai_family = PF_UNSPEC;
436 hints.ai_family = PF_INET6;
438 hints.ai_family = PF_INET;
440 hints.ai_flags = ai_flags;
441 error = getaddrinfo(value, NULL, &hints, &ai0);
443 errx(1, "hostname %s: %s", value, gai_strerror(error));
445 /* Convert the addresses to ASCII so set_param can convert them back. */
446 for (ai = ai0; ai; ai = ai->ai_next)
447 switch (ai->ai_family) {
450 if (!ip4_ok && (ai_flags & AI_NUMERICHOST) == 0)
452 memcpy(&addr4, &((struct sockaddr_in *)
453 (void *)ai->ai_addr)->sin_addr, sizeof(addr4));
454 if (inet_ntop(AF_INET, &addr4, avalue4,
455 INET_ADDRSTRLEN) == NULL)
457 add_ip_addr(&ip4_addr, avalue4);
462 if (!ip6_ok && (ai_flags & AI_NUMERICHOST) == 0)
464 memcpy(&addr6, &((struct sockaddr_in6 *)
465 (void *)ai->ai_addr)->sin6_addr, sizeof(addr6));
466 if (inet_ntop(AF_INET6, &addr6, avalue6,
467 INET6_ADDRSTRLEN) == NULL)
469 add_ip_addr(&ip6_addr, avalue6);
477 quoted_print(FILE *fp, char *str)
482 /* An empty string needs quoting. */
489 * The value will be surrounded by quotes if it contains spaces
492 qc = strchr(p, '\'') ? '"'
493 : strchr(p, '"') ? '\''
494 : strchr(p, ' ') || strchr(p, '\t') ? '"'
499 if (c == '\\' || c == qc)
508 set_param(const char *name, char *value)
510 struct jailparam *param;
513 static int paramlistsize;
515 /* Separate the name from the value, if not done already. */
518 if ((value = strchr(value, '=')))
522 /* jail_set won't chdir along with its chroot, so do it here. */
523 if (!strcmp(name, "path") && chdir(value) < 0)
524 err(1, "chdir: %s", value);
526 /* Check for repeat parameters */
527 for (i = 0; i < nparams; i++)
528 if (!strcmp(name, params[i].jp_name)) {
529 jailparam_free(params + i, 1);
530 memcpy(params + i, params + i + 1,
531 (--nparams - i) * sizeof(struct jailparam));
535 /* Make sure there is room for the new param record. */
538 params = malloc(paramlistsize * sizeof(*params));
539 param_values = malloc(paramlistsize * sizeof(*param_values));
540 if (params == NULL || param_values == NULL)
542 } else if (nparams >= paramlistsize) {
544 params = realloc(params, paramlistsize * sizeof(*params));
545 param_values = realloc(param_values,
546 paramlistsize * sizeof(*param_values));
551 /* Look up the paramter. */
552 param_values[nparams] = value;
553 param = params + nparams++;
554 if (jailparam_init(param, name) < 0 ||
555 jailparam_import(param, value) < 0)
556 errx(1, "%s", jail_errmsg);
563 (void)fprintf(stderr,
564 "usage: jail [-d] [-h] [-i] [-J jid_file] "
565 "[-l -u username | -U username]\n"
566 " [-c | -m] param=value ... [command=command ...]\n"
567 " jail [-r jail]\n");