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$"); 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 53 static struct jailparam *params; 54 static char **param_values; 55 static int nparams; 56 57 #ifdef INET6 58 static int ip6_ok; 59 static char *ip6_addr; 60 #endif 61 #ifdef INET 62 static int ip4_ok; 63 static char *ip4_addr; 64 #endif 65 66 #if defined(INET6) || defined(INET) 67 static void add_ip_addr(char **addrp, char *newaddr); 68 #endif 69 #ifdef INET6 70 static void add_ip_addr46(char *newaddr); 71 #endif 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); 76 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" }, 90 }; 91 92 extern 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 110 int 111 main(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) 376 static void 377 add_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 399 static void 400 add_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 416 static void 417 add_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 476 static void 477 quoted_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 507 static void 508 set_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 559 static void 560 usage(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