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 #include <sys/uio.h> 36 37 #include <arpa/inet.h> 38 #include <netinet/in.h> 39 40 #include <ctype.h> 41 #include <err.h> 42 #include <errno.h> 43 #include <grp.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 #define SJPARAM "security.jail.param" 54 #define ERRMSG_SIZE 256 55 56 struct param { 57 struct iovec name; 58 struct iovec value; 59 }; 60 61 static struct param *params; 62 static char **param_values; 63 static int nparams; 64 65 static char *ip4_addr; 66 #ifdef INET6 67 static char *ip6_addr; 68 #endif 69 70 static void add_ip_addr(char **addrp, char *newaddr); 71 #ifdef INET6 72 static void add_ip_addr46(char *newaddr); 73 #endif 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); 78 79 extern char **environ; 80 81 #define GET_USER_INFO do { \ 82 pwd = getpwnam(username); \ 83 if (pwd == NULL) { \ 84 if (errno) \ 85 err(1, "getpwnam: %s", username); \ 86 else \ 87 errx(1, "%s: no such user", username); \ 88 } \ 89 lcap = login_getpwclass(pwd); \ 90 if (lcap == NULL) \ 91 err(1, "getpwclass: %s", username); \ 92 ngroups = NGROUPS; \ 93 if (getgrouplist(username, pwd->pw_gid, groups, &ngroups) != 0) \ 94 err(1, "getgrouplist: %s", username); \ 95 } while (0) 96 97 int 98 main(int argc, char **argv) 99 { 100 login_cap_t *lcap = NULL; 101 struct iovec rparams[2]; 102 struct passwd *pwd = NULL; 103 gid_t groups[NGROUPS]; 104 int ch, cmdarg, i, jail_set_flags, jid, ngroups; 105 int hflag, iflag, Jflag, lflag, rflag, uflag, Uflag; 106 char *ep, *jailname, *securelevel, *username, *JidFile; 107 char errmsg[ERRMSG_SIZE]; 108 static char *cleanenv; 109 const char *shell, *p = NULL; 110 FILE *fp; 111 112 hflag = iflag = Jflag = lflag = rflag = uflag = Uflag = 113 jail_set_flags = 0; 114 cmdarg = jid = -1; 115 jailname = securelevel = username = JidFile = cleanenv = NULL; 116 fp = NULL; 117 118 while ((ch = getopt(argc, argv, "cdhilmn:r:s:u:U:J:")) != -1) { 119 switch (ch) { 120 case 'd': 121 jail_set_flags |= JAIL_DYING; 122 break; 123 case 'h': 124 hflag = 1; 125 break; 126 case 'i': 127 iflag = 1; 128 break; 129 case 'J': 130 JidFile = optarg; 131 Jflag = 1; 132 break; 133 case 'n': 134 jailname = optarg; 135 break; 136 case 's': 137 securelevel = optarg; 138 break; 139 case 'u': 140 username = optarg; 141 uflag = 1; 142 break; 143 case 'U': 144 username = optarg; 145 Uflag = 1; 146 break; 147 case 'l': 148 lflag = 1; 149 break; 150 case 'c': 151 jail_set_flags |= JAIL_CREATE; 152 break; 153 case 'm': 154 jail_set_flags |= JAIL_UPDATE; 155 break; 156 case 'r': 157 jid = strtoul(optarg, &ep, 10); 158 if (!*optarg || *ep) { 159 *(const void **)&rparams[0].iov_base = "name"; 160 rparams[0].iov_len = sizeof("name"); 161 rparams[1].iov_base = optarg; 162 rparams[1].iov_len = strlen(optarg) + 1; 163 jid = jail_get(rparams, 2, 0); 164 if (jid < 0) 165 errx(1, "unknown jail: %s", optarg); 166 } 167 rflag = 1; 168 break; 169 default: 170 usage(); 171 } 172 } 173 argc -= optind; 174 argv += optind; 175 if (rflag) { 176 if (argc > 0 || iflag || Jflag || lflag || uflag || Uflag) 177 usage(); 178 if (jail_remove(jid) < 0) 179 err(1, "jail_remove"); 180 exit (0); 181 } 182 if (argc == 0) 183 usage(); 184 if (uflag && Uflag) 185 usage(); 186 if (lflag && username == NULL) 187 usage(); 188 if (uflag) 189 GET_USER_INFO; 190 191 /* 192 * If the first argument (path) starts with a slash, and the third 193 * argument (IP address) starts with a digit, it is likely to be 194 * an old-style fixed-parameter command line. 195 */ 196 if (jailname) 197 set_param("name", jailname); 198 if (securelevel) 199 set_param("securelevel", securelevel); 200 if (jail_set_flags) { 201 for (i = 0; i < argc; i++) { 202 if (!strncmp(argv[i], "command=", 8)) { 203 cmdarg = i; 204 argv[cmdarg] += 8; 205 jail_set_flags |= JAIL_ATTACH; 206 break; 207 } 208 if (hflag) { 209 if (!strncmp(argv[i], "ip4.addr=", 9)) { 210 add_ip_addr(&ip4_addr, argv[i] + 9); 211 break; 212 } 213 #ifdef INET6 214 if (!strncmp(argv[i], "ip6.addr=", 9)) { 215 add_ip_addr(&ip6_addr, argv[i] + 9); 216 break; 217 } 218 #endif 219 if (!strncmp(argv[i], "host.hostname=", 14)) 220 add_ip_addrinfo(0, argv[i] + 14); 221 } 222 set_param(NULL, argv[i]); 223 } 224 } else { 225 if (argc < 4 || argv[0][0] != '/') 226 errx(1, "%s\n%s", 227 "no -c or -m, so this must be an old-style command.", 228 "But it doesn't look like one."); 229 set_param("path", argv[0]); 230 set_param("host.hostname", argv[1]); 231 if (hflag) 232 add_ip_addrinfo(0, argv[1]); 233 #ifdef INET6 234 add_ip_addr46(argv[2]); 235 #else 236 add_ip_addr(&ip4_addr, argv[2]); 237 #endif 238 cmdarg = 3; 239 } 240 if (ip4_addr != NULL) 241 set_param("ip4.addr", ip4_addr); 242 #ifdef INET6 243 if (ip6_addr != NULL) 244 set_param("ip6.addr", ip6_addr); 245 #endif 246 errmsg[0] = 0; 247 set_param("errmsg", errmsg); 248 249 if (Jflag) { 250 fp = fopen(JidFile, "w"); 251 if (fp == NULL) 252 errx(1, "Could not create JidFile: %s", JidFile); 253 } 254 jid = jail_set(¶ms->name, 2 * nparams, 255 jail_set_flags ? jail_set_flags : JAIL_CREATE | JAIL_ATTACH); 256 if (jid < 0) { 257 if (errmsg[0] != '\0') 258 errx(1, "%s", errmsg); 259 err(1, "jail_set"); 260 } 261 if (iflag) { 262 printf("%d\n", jid); 263 fflush(stdout); 264 } 265 if (Jflag) { 266 if (jail_set_flags) { 267 fprintf(fp, "jid=%d", jid); 268 for (i = 0; i < nparams; i++) 269 if (strcmp(params[i].name.iov_base, "jid") && 270 strcmp(params[i].name.iov_base, "errmsg")) { 271 fprintf(fp, " %s", 272 (char *)params[i].name.iov_base); 273 if (param_values[i]) { 274 putc('=', fp); 275 quoted_print(fp, 276 param_values[i]); 277 } 278 } 279 fprintf(fp, "\n"); 280 } else { 281 for (i = 0; i < nparams; i++) 282 if (!strcmp(params[i].name.iov_base, "path")) 283 break; 284 #ifdef INET6 285 fprintf(fp, "%d\t%s\t%s\t%s%s%s\t%s\n", 286 jid, i < nparams 287 ? (char *)params[i].value.iov_base : argv[0], 288 argv[1], ip4_addr ? ip4_addr : "", 289 ip4_addr && ip4_addr[0] && ip6_addr && ip6_addr[0] 290 ? "," : "", ip6_addr ? ip6_addr : "", argv[3]); 291 #else 292 fprintf(fp, "%d\t%s\t%s\t%s\t%s\n", 293 jid, i < nparams 294 ? (char *)params[i].value.iov_base : argv[0], 295 argv[1], ip4_addr ? ip4_addr : "", argv[3]); 296 #endif 297 } 298 (void)fclose(fp); 299 } 300 if (cmdarg < 0) 301 exit(0); 302 if (username != NULL) { 303 if (Uflag) 304 GET_USER_INFO; 305 if (lflag) { 306 p = getenv("TERM"); 307 environ = &cleanenv; 308 } 309 if (setgroups(ngroups, groups) != 0) 310 err(1, "setgroups"); 311 if (setgid(pwd->pw_gid) != 0) 312 err(1, "setgid"); 313 if (setusercontext(lcap, pwd, pwd->pw_uid, 314 LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0) 315 err(1, "setusercontext"); 316 login_close(lcap); 317 } 318 if (lflag) { 319 if (*pwd->pw_shell) 320 shell = pwd->pw_shell; 321 else 322 shell = _PATH_BSHELL; 323 if (chdir(pwd->pw_dir) < 0) 324 errx(1, "no home directory"); 325 setenv("HOME", pwd->pw_dir, 1); 326 setenv("SHELL", shell, 1); 327 setenv("USER", pwd->pw_name, 1); 328 if (p) 329 setenv("TERM", p, 1); 330 } 331 execvp(argv[cmdarg], argv + cmdarg); 332 err(1, "execvp: %s", argv[cmdarg]); 333 } 334 335 static void 336 add_ip_addr(char **addrp, char *value) 337 { 338 int addrlen; 339 char *addr; 340 341 if (!*addrp) { 342 *addrp = strdup(value); 343 if (!*addrp) 344 err(1, "malloc"); 345 } else if (value[0]) { 346 addrlen = strlen(*addrp) + strlen(value) + 2; 347 addr = malloc(addrlen); 348 if (!addr) 349 err(1, "malloc"); 350 snprintf(addr, addrlen, "%s,%s", *addrp, value); 351 free(*addrp); 352 *addrp = addr; 353 } 354 } 355 356 #ifdef INET6 357 static void 358 add_ip_addr46(char *value) 359 { 360 char *p, *np; 361 362 if (!value[0]) { 363 add_ip_addr(&ip4_addr, value); 364 add_ip_addr(&ip6_addr, value); 365 return; 366 } 367 for (p = value;; p = np + 1) 368 { 369 np = strchr(p, ','); 370 if (np) 371 *np = '\0'; 372 add_ip_addrinfo(AI_NUMERICHOST, p); 373 if (!np) 374 break; 375 } 376 } 377 #endif 378 379 static void 380 add_ip_addrinfo(int ai_flags, char *value) 381 { 382 struct addrinfo hints, *ai0, *ai; 383 struct in_addr addr4; 384 int error; 385 char avalue4[INET_ADDRSTRLEN]; 386 #ifdef INET6 387 struct in6_addr addr6; 388 char avalue6[INET6_ADDRSTRLEN]; 389 #endif 390 391 /* Look up the hostname (or get the address) */ 392 memset(&hints, 0, sizeof(hints)); 393 hints.ai_socktype = SOCK_STREAM; 394 #ifdef INET6 395 hints.ai_family = PF_UNSPEC; 396 #else 397 hints.ai_family = PF_INET; 398 #endif 399 hints.ai_flags = ai_flags; 400 error = getaddrinfo(value, NULL, &hints, &ai0); 401 if (error != 0) 402 errx(1, "hostname %s: %s", value, gai_strerror(error)); 403 404 /* Convert the addresses to ASCII so set_param can convert them back. */ 405 for (ai = ai0; ai; ai = ai->ai_next) 406 switch (ai->ai_family) { 407 case AF_INET: 408 memcpy(&addr4, &((struct sockaddr_in *) 409 (void *)ai->ai_addr)->sin_addr, sizeof(addr4)); 410 if (inet_ntop(AF_INET, &addr4, avalue4, 411 INET_ADDRSTRLEN) == NULL) 412 err(1, "inet_ntop"); 413 add_ip_addr(&ip4_addr, avalue4); 414 break; 415 #ifdef INET6 416 case AF_INET6: 417 memcpy(&addr6, &((struct sockaddr_in6 *) 418 (void *)ai->ai_addr)->sin6_addr, sizeof(addr6)); 419 if (inet_ntop(AF_INET6, &addr6, avalue6, 420 INET6_ADDRSTRLEN) == NULL) 421 err(1, "inet_ntop"); 422 add_ip_addr(&ip6_addr, avalue6); 423 break; 424 #endif 425 } 426 freeaddrinfo(ai0); 427 } 428 429 static void 430 quoted_print(FILE *fp, char *str) 431 { 432 int c, qc; 433 char *p = str; 434 435 /* An empty string needs quoting. */ 436 if (!*p) { 437 fputs("\"\"", fp); 438 return; 439 } 440 441 /* 442 * The value will be surrounded by quotes if it contains spaces 443 * or quotes. 444 */ 445 qc = strchr(p, '\'') ? '"' 446 : strchr(p, '"') ? '\'' 447 : strchr(p, ' ') || strchr(p, '\t') ? '"' 448 : 0; 449 if (qc) 450 putc(qc, fp); 451 while ((c = *p++)) { 452 if (c == '\\' || c == qc) 453 putc('\\', fp); 454 putc(c, fp); 455 } 456 if (qc) 457 putc(qc, fp); 458 } 459 460 static void 461 set_param(const char *name, char *value) 462 { 463 struct param *param; 464 char *ep, *p; 465 size_t buflen, mlen; 466 int i, nval, mib[CTL_MAXNAME]; 467 struct { 468 int i; 469 char s[MAXPATHLEN]; 470 } buf; 471 472 static int paramlistsize; 473 474 /* Separate the name from the value, if not done already. */ 475 if (name == NULL) { 476 name = value; 477 if ((value = strchr(value, '='))) 478 *value++ = '\0'; 479 } 480 481 /* Check for repeat parameters */ 482 for (i = 0; i < nparams; i++) 483 if (!strcmp(name, params[i].name.iov_base)) { 484 memcpy(params + i, params + i + 1, 485 (--nparams - i) * sizeof(struct param)); 486 break; 487 } 488 489 /* Make sure there is room for the new param record. */ 490 if (!nparams) { 491 paramlistsize = 32; 492 params = malloc(paramlistsize * sizeof(*params)); 493 param_values = malloc(paramlistsize * sizeof(*param_values)); 494 if (params == NULL || param_values == NULL) 495 err(1, "malloc"); 496 } else if (nparams >= paramlistsize) { 497 paramlistsize *= 2; 498 params = realloc(params, paramlistsize * sizeof(*params)); 499 param_values = realloc(param_values, 500 paramlistsize * sizeof(*param_values)); 501 if (params == NULL) 502 err(1, "realloc"); 503 } 504 505 /* Look up the paramter. */ 506 param_values[nparams] = value; 507 param = params + nparams++; 508 *(const void **)¶m->name.iov_base = name; 509 param->name.iov_len = strlen(name) + 1; 510 /* Trivial values - no value or errmsg. */ 511 if (value == NULL) { 512 param->value.iov_base = NULL; 513 param->value.iov_len = 0; 514 return; 515 } 516 if (!strcmp(name, "errmsg")) { 517 param->value.iov_base = value; 518 param->value.iov_len = ERRMSG_SIZE; 519 return; 520 } 521 mib[0] = 0; 522 mib[1] = 3; 523 snprintf(buf.s, sizeof(buf.s), SJPARAM ".%s", name); 524 mlen = sizeof(mib) - 2 * sizeof(int); 525 if (sysctl(mib, 2, mib + 2, &mlen, buf.s, strlen(buf.s)) < 0) 526 errx(1, "unknown parameter: %s", name); 527 mib[1] = 4; 528 buflen = sizeof(buf); 529 if (sysctl(mib, (mlen / sizeof(int)) + 2, &buf, &buflen, NULL, 0) < 0) 530 err(1, "sysctl(0.4.%s)", name); 531 /* 532 * See if this is an array type. 533 * Treat non-arrays as an array of one. 534 */ 535 p = strchr(buf.s, '\0'); 536 nval = 1; 537 if (p - 2 >= buf.s && !strcmp(p - 2, ",a")) { 538 if (value[0] == '\0' || 539 (value[0] == '-' && value[1] == '\0')) { 540 param->value.iov_base = value; 541 param->value.iov_len = 0; 542 return; 543 } 544 p[-2] = 0; 545 for (p = strchr(value, ','); p; p = strchr(p + 1, ',')) { 546 *p = '\0'; 547 nval++; 548 } 549 } 550 551 /* Set the values according to the parameter type. */ 552 switch (buf.i & CTLTYPE) { 553 case CTLTYPE_INT: 554 case CTLTYPE_UINT: 555 param->value.iov_len = nval * sizeof(int); 556 break; 557 case CTLTYPE_LONG: 558 case CTLTYPE_ULONG: 559 param->value.iov_len = nval * sizeof(long); 560 break; 561 case CTLTYPE_STRUCT: 562 if (!strcmp(buf.s, "S,in_addr")) 563 param->value.iov_len = nval * sizeof(struct in_addr); 564 #ifdef INET6 565 else if (!strcmp(buf.s, "S,in6_addr")) 566 param->value.iov_len = nval * sizeof(struct in6_addr); 567 #endif 568 else 569 errx(1, "%s: unknown parameter structure (%s)", 570 name, buf.s); 571 break; 572 case CTLTYPE_STRING: 573 if (!strcmp(name, "path")) { 574 param->value.iov_base = malloc(MAXPATHLEN); 575 if (param->value.iov_base == NULL) 576 err(1, "malloc"); 577 if (realpath(value, param->value.iov_base) == NULL) 578 err(1, "%s: realpath(%s)", name, value); 579 if (chdir(param->value.iov_base) != 0) 580 err(1, "chdir: %s", 581 (char *)param->value.iov_base); 582 } else 583 param->value.iov_base = value; 584 param->value.iov_len = strlen(param->value.iov_base) + 1; 585 return; 586 default: 587 errx(1, "%s: unknown parameter type %d (%s)", 588 name, buf.i, buf.s); 589 } 590 param->value.iov_base = malloc(param->value.iov_len); 591 for (i = 0; i < nval; i++) { 592 switch (buf.i & CTLTYPE) { 593 case CTLTYPE_INT: 594 ((int *)param->value.iov_base)[i] = 595 strtol(value, &ep, 10); 596 if (ep[0] != '\0') 597 errx(1, "%s: non-integer value \"%s\"", 598 name, value); 599 break; 600 case CTLTYPE_UINT: 601 ((unsigned *)param->value.iov_base)[i] = 602 strtoul(value, &ep, 10); 603 if (ep[0] != '\0') 604 errx(1, "%s: non-integer value \"%s\"", 605 name, value); 606 break; 607 case CTLTYPE_LONG: 608 ((long *)param->value.iov_base)[i] = 609 strtol(value, &ep, 10); 610 if (ep[0] != '\0') 611 errx(1, "%s: non-integer value \"%s\"", 612 name, value); 613 break; 614 case CTLTYPE_ULONG: 615 ((unsigned long *)param->value.iov_base)[i] = 616 strtoul(value, &ep, 10); 617 if (ep[0] != '\0') 618 errx(1, "%s: non-integer value \"%s\"", 619 name, value); 620 break; 621 case CTLTYPE_STRUCT: 622 if (!strcmp(buf.s, "S,in_addr")) { 623 if (inet_pton(AF_INET, value, 624 &((struct in_addr *) 625 param->value.iov_base)[i]) != 1) 626 errx(1, "%s: not an IPv4 address: %s", 627 name, value); 628 } 629 #ifdef INET6 630 else if (!strcmp(buf.s, "S,in6_addr")) { 631 if (inet_pton(AF_INET6, value, 632 &((struct in6_addr *) 633 param->value.iov_base)[i]) != 1) 634 errx(1, "%s: not an IPv6 address: %s", 635 name, value); 636 } 637 #endif 638 } 639 if (i > 0) 640 value[-1] = ','; 641 value = strchr(value, '\0') + 1; 642 } 643 } 644 645 static void 646 usage(void) 647 { 648 649 (void)fprintf(stderr, 650 "usage: jail [-d] [-h] [-i] [-J jid_file] " 651 "[-l -u username | -U username]\n" 652 " [-c | -m] param=value ... [command=command ...]\n" 653 " jail [-r jail]\n"); 654 exit(1); 655 } 656