jail.c revision 222465
1/*- 2 * Copyright (c) 1999 Poul-Henning Kamp. 3 * Copyright (c) 2009 James Gritton 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 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. 14 * 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 25 * SUCH DAMAGE. 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: head/usr.sbin/jail/jail.c 222465 2011-05-29 21:03:40Z bz $"); 30 31#include <sys/param.h> 32#include <sys/jail.h> 33#include <sys/socket.h> 34#include <sys/sysctl.h> 35 36#include <arpa/inet.h> 37#include <netinet/in.h> 38 39#include <ctype.h> 40#include <err.h> 41#include <errno.h> 42#include <grp.h> 43#include <jail.h> 44#include <login_cap.h> 45#include <netdb.h> 46#include <paths.h> 47#include <pwd.h> 48#include <stdio.h> 49#include <stdlib.h> 50#include <string.h> 51#include <unistd.h> 52 53static struct jailparam *params; 54static char **param_values; 55static int nparams; 56 57#ifdef INET6 58static int ip6_ok; 59static char *ip6_addr; 60#endif 61#ifdef INET 62static int ip4_ok; 63static char *ip4_addr; 64#endif 65 66#if defined(INET6) || defined(INET) 67static void add_ip_addr(char **addrp, char *newaddr); 68#endif 69#ifdef INET6 70static void add_ip_addr46(char *newaddr); 71#endif 72static void add_ip_addrinfo(int ai_flags, char *value); 73static void quoted_print(FILE *fp, char *str); 74static void set_param(const char *name, char *value); 75static void usage(void); 76 77static 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" }, 90}; 91 92extern char **environ; 93 94#define GET_USER_INFO do { \ 95 pwd = getpwnam(username); \ 96 if (pwd == NULL) { \ 97 if (errno) \ 98 err(1, "getpwnam: %s", username); \ 99 else \ 100 errx(1, "%s: no such user", username); \ 101 } \ 102 lcap = login_getpwclass(pwd); \ 103 if (lcap == NULL) \ 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); \ 108} while (0) 109 110int 111main(int argc, char **argv) 112{ 113 login_cap_t *lcap = NULL; 114 struct passwd *pwd = NULL; 115 gid_t *groups; 116 size_t sysvallen; 117 int ch, cmdarg, i, jail_set_flags, jid, ngroups, sysval; 118 int hflag, iflag, Jflag, lflag, rflag, uflag, Uflag; 119 long ngroups_max; 120 unsigned pi; 121 char *jailname, *securelevel, *username, *JidFile; 122 char enforce_statfs[4]; 123 static char *cleanenv; 124 const char *shell, *p = NULL; 125 FILE *fp; 126 127 hflag = iflag = Jflag = lflag = rflag = uflag = Uflag = 128 jail_set_flags = 0; 129 cmdarg = jid = -1; 130 jailname = securelevel = username = JidFile = cleanenv = NULL; 131 fp = NULL; 132 133 ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1; 134 if ((groups = malloc(sizeof(gid_t) * ngroups_max)) == NULL) 135 err(1, "malloc"); 136 137 while ((ch = getopt(argc, argv, "cdhilmn:r:s:u:U:J:")) != -1) { 138 switch (ch) { 139 case 'd': 140 jail_set_flags |= JAIL_DYING; 141 break; 142 case 'h': 143 hflag = 1; 144 break; 145 case 'i': 146 iflag = 1; 147 break; 148 case 'J': 149 JidFile = optarg; 150 Jflag = 1; 151 break; 152 case 'n': 153 jailname = optarg; 154 break; 155 case 's': 156 securelevel = optarg; 157 break; 158 case 'u': 159 username = optarg; 160 uflag = 1; 161 break; 162 case 'U': 163 username = optarg; 164 Uflag = 1; 165 break; 166 case 'l': 167 lflag = 1; 168 break; 169 case 'c': 170 jail_set_flags |= JAIL_CREATE; 171 break; 172 case 'm': 173 jail_set_flags |= JAIL_UPDATE; 174 break; 175 case 'r': 176 jid = jail_getid(optarg); 177 if (jid < 0) 178 errx(1, "%s", jail_errmsg); 179 rflag = 1; 180 break; 181 default: 182 usage(); 183 } 184 } 185 argc -= optind; 186 argv += optind; 187 if (rflag) { 188 if (argc > 0 || iflag || Jflag || lflag || uflag || Uflag) 189 usage(); 190 if (jail_remove(jid) < 0) 191 err(1, "jail_remove"); 192 exit (0); 193 } 194 if (argc == 0) 195 usage(); 196 if (uflag && Uflag) 197 usage(); 198 if (lflag && username == NULL) 199 usage(); 200 if (uflag) 201 GET_USER_INFO; 202 203#ifdef INET6 204 ip6_ok = feature_present("inet6"); 205#endif 206#ifdef INET 207 ip4_ok = feature_present("inet"); 208#endif 209 210 if (jailname) 211 set_param("name", jailname); 212 if (securelevel) 213 set_param("securelevel", securelevel); 214 if (jail_set_flags) { 215 for (i = 0; i < argc; i++) { 216 if (!strncmp(argv[i], "command=", 8)) { 217 cmdarg = i; 218 argv[cmdarg] += 8; 219 jail_set_flags |= JAIL_ATTACH; 220 break; 221 } 222 if (hflag) { 223#ifdef INET 224 if (!strncmp(argv[i], "ip4.addr=", 9)) { 225 add_ip_addr(&ip4_addr, argv[i] + 9); 226 break; 227 } 228#endif 229#ifdef INET6 230 if (!strncmp(argv[i], "ip6.addr=", 9)) { 231 add_ip_addr(&ip6_addr, argv[i] + 9); 232 break; 233 } 234#endif 235 if (!strncmp(argv[i], "host.hostname=", 14)) 236 add_ip_addrinfo(0, argv[i] + 14); 237 } 238 set_param(NULL, argv[i]); 239 } 240 } else { 241 if (argc < 4 || argv[0][0] != '/') 242 errx(1, "%s\n%s", 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]); 247 if (hflag) 248 add_ip_addrinfo(0, argv[1]); 249#if defined(INET6) || defined(INET) 250 if (argv[2][0] != '\0') 251#ifdef INET6 252 add_ip_addr46(argv[2]); 253#else 254 add_ip_addr(&ip4_addr, argv[2]); 255#endif 256#endif 257 cmdarg = 3; 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); 269 } 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); 276 } 277 } 278 } 279#ifdef INET 280 if (ip4_addr != NULL) 281 set_param("ip4.addr", ip4_addr); 282#endif 283#ifdef INET6 284 if (ip6_addr != NULL) 285 set_param("ip6.addr", ip6_addr); 286#endif 287 288 if (Jflag) { 289 fp = fopen(JidFile, "w"); 290 if (fp == NULL) 291 errx(1, "Could not create JidFile: %s", JidFile); 292 } 293 jid = jailparam_set(params, nparams, 294 jail_set_flags ? jail_set_flags : JAIL_CREATE | JAIL_ATTACH); 295 if (jid < 0) 296 errx(1, "%s", jail_errmsg); 297 if (iflag) { 298 printf("%d\n", jid); 299 fflush(stdout); 300 } 301 if (Jflag) { 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")) { 306 fprintf(fp, " %s", 307 (char *)params[i].jp_name); 308 if (param_values[i]) { 309 putc('=', fp); 310 quoted_print(fp, 311 param_values[i]); 312 } 313 } 314 fprintf(fp, "\n"); 315 } else { 316 for (i = 0; i < nparams; i++) 317 if (!strcmp(params[i].jp_name, "path")) 318 break; 319#if defined(INET6) && defined(INET) 320 fprintf(fp, "%d\t%s\t%s\t%s%s%s\t%s\n", 321 jid, i < nparams 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]); 326#elif defined(INET6) 327 fprintf(fp, "%d\t%s\t%s\t%s\t%s\n", 328 jid, i < nparams 329 ? (char *)params[i].jp_value : argv[0], 330 argv[1], ip6_addr ? ip6_addr : "", argv[3]); 331#elif defined(INET) 332 fprintf(fp, "%d\t%s\t%s\t%s\t%s\n", 333 jid, i < nparams 334 ? (char *)params[i].jp_value : argv[0], 335 argv[1], ip4_addr ? ip4_addr : "", argv[3]); 336#endif 337 } 338 (void)fclose(fp); 339 } 340 if (cmdarg < 0) 341 exit(0); 342 if (username != NULL) { 343 if (Uflag) 344 GET_USER_INFO; 345 if (lflag) { 346 p = getenv("TERM"); 347 environ = &cleanenv; 348 } 349 if (setgroups(ngroups, groups) != 0) 350 err(1, "setgroups"); 351 if (setgid(pwd->pw_gid) != 0) 352 err(1, "setgid"); 353 if (setusercontext(lcap, pwd, pwd->pw_uid, 354 LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0) 355 err(1, "setusercontext"); 356 login_close(lcap); 357 } 358 if (lflag) { 359 if (*pwd->pw_shell) 360 shell = pwd->pw_shell; 361 else 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); 368 if (p) 369 setenv("TERM", p, 1); 370 } 371 execvp(argv[cmdarg], argv + cmdarg); 372 err(1, "execvp: %s", argv[cmdarg]); 373} 374 375#if defined(INET6) || defined(INET) 376static void 377add_ip_addr(char **addrp, char *value) 378{ 379 int addrlen; 380 char *addr; 381 382 if (!*addrp) { 383 *addrp = strdup(value); 384 if (!*addrp) 385 err(1, "malloc"); 386 } else if (value[0]) { 387 addrlen = strlen(*addrp) + strlen(value) + 2; 388 addr = malloc(addrlen); 389 if (!addr) 390 err(1, "malloc"); 391 snprintf(addr, addrlen, "%s,%s", *addrp, value); 392 free(*addrp); 393 *addrp = addr; 394 } 395} 396#endif 397 398#ifdef INET6 399static void 400add_ip_addr46(char *value) 401{ 402 char *p, *np; 403 404 for (p = value;; p = np + 1) 405 { 406 np = strchr(p, ','); 407 if (np) 408 *np = '\0'; 409 add_ip_addrinfo(AI_NUMERICHOST, p); 410 if (!np) 411 break; 412 } 413} 414#endif 415 416static void 417add_ip_addrinfo(int ai_flags, char *value) 418{ 419 struct addrinfo hints, *ai0, *ai; 420 int error; 421#ifdef INET 422 char avalue4[INET_ADDRSTRLEN]; 423 struct in_addr addr4; 424#endif 425#ifdef INET6 426 char avalue6[INET6_ADDRSTRLEN]; 427 struct in6_addr addr6; 428#endif 429 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; 435#elif defined(INET6) 436 hints.ai_family = PF_INET6; 437#elif defined(INET) 438 hints.ai_family = PF_INET; 439#endif 440 hints.ai_flags = ai_flags; 441 error = getaddrinfo(value, NULL, &hints, &ai0); 442 if (error != 0) 443 errx(1, "hostname %s: %s", value, gai_strerror(error)); 444 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) { 448#ifdef INET 449 case AF_INET: 450 if (!ip4_ok && (ai_flags & AI_NUMERICHOST) == 0) 451 break; 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) 456 err(1, "inet_ntop"); 457 add_ip_addr(&ip4_addr, avalue4); 458 break; 459#endif 460#ifdef INET6 461 case AF_INET6: 462 if (!ip6_ok && (ai_flags & AI_NUMERICHOST) == 0) 463 break; 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) 468 err(1, "inet_ntop"); 469 add_ip_addr(&ip6_addr, avalue6); 470 break; 471#endif 472 } 473 freeaddrinfo(ai0); 474} 475 476static void 477quoted_print(FILE *fp, char *str) 478{ 479 int c, qc; 480 char *p = str; 481 482 /* An empty string needs quoting. */ 483 if (!*p) { 484 fputs("\"\"", fp); 485 return; 486 } 487 488 /* 489 * The value will be surrounded by quotes if it contains spaces 490 * or quotes. 491 */ 492 qc = strchr(p, '\'') ? '"' 493 : strchr(p, '"') ? '\'' 494 : strchr(p, ' ') || strchr(p, '\t') ? '"' 495 : 0; 496 if (qc) 497 putc(qc, fp); 498 while ((c = *p++)) { 499 if (c == '\\' || c == qc) 500 putc('\\', fp); 501 putc(c, fp); 502 } 503 if (qc) 504 putc(qc, fp); 505} 506 507static void 508set_param(const char *name, char *value) 509{ 510 struct jailparam *param; 511 int i; 512 513 static int paramlistsize; 514 515 /* Separate the name from the value, if not done already. */ 516 if (name == NULL) { 517 name = value; 518 if ((value = strchr(value, '='))) 519 *value++ = '\0'; 520 } 521 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); 525 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)); 532 break; 533 } 534 535 /* Make sure there is room for the new param record. */ 536 if (!nparams) { 537 paramlistsize = 32; 538 params = malloc(paramlistsize * sizeof(*params)); 539 param_values = malloc(paramlistsize * sizeof(*param_values)); 540 if (params == NULL || param_values == NULL) 541 err(1, "malloc"); 542 } else if (nparams >= paramlistsize) { 543 paramlistsize *= 2; 544 params = realloc(params, paramlistsize * sizeof(*params)); 545 param_values = realloc(param_values, 546 paramlistsize * sizeof(*param_values)); 547 if (params == NULL) 548 err(1, "realloc"); 549 } 550 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); 557} 558 559static void 560usage(void) 561{ 562 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"); 568 exit(1); 569} 570