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 static char *ip4_addr; 58 #ifdef INET6 59 static char *ip6_addr; 60 #endif 61 62 static void add_ip_addr(char **addrp, char *newaddr); 63 #ifdef INET6 64 static void add_ip_addr46(char *newaddr); 65 #endif 66 static void add_ip_addrinfo(int ai_flags, char *value); 67 static void quoted_print(FILE *fp, char *str); 68 static void set_param(const char *name, char *value); 69 static void usage(void); 70 71 static const char *perm_sysctl[][3] = { 72 { "security.jail.set_hostname_allowed", 73 "allow.noset_hostname", "allow.set_hostname" }, 74 { "security.jail.sysvipc_allowed", 75 "allow.nosysvipc", "allow.sysvipc" }, 76 { "security.jail.allow_raw_sockets", 77 "allow.noraw_sockets", "allow.raw_sockets" }, 78 { "security.jail.chflags_allowed", 79 "allow.nochflags", "allow.chflags" }, 80 { "security.jail.mount_allowed", 81 "allow.nomount", "allow.mount" }, 82 { "security.jail.socket_unixiproute_only", 83 "allow.socket_af", "allow.nosocket_af" }, 84 }; 85 86 extern char **environ; 87 88 #define GET_USER_INFO do { \ 89 pwd = getpwnam(username); \ 90 if (pwd == NULL) { \ 91 if (errno) \ 92 err(1, "getpwnam: %s", username); \ 93 else \ 94 errx(1, "%s: no such user", username); \ 95 } \ 96 lcap = login_getpwclass(pwd); \ 97 if (lcap == NULL) \ 98 err(1, "getpwclass: %s", username); \ 99 ngroups = ngroups_max; \ 100 if (getgrouplist(username, pwd->pw_gid, groups, &ngroups) != 0) \ 101 err(1, "getgrouplist: %s", username); \ 102 } while (0) 103 104 int 105 main(int argc, char **argv) 106 { 107 login_cap_t *lcap = NULL; 108 struct passwd *pwd = NULL; 109 gid_t *groups; 110 size_t sysvallen; 111 int ch, cmdarg, i, jail_set_flags, jid, ngroups, sysval; 112 int hflag, iflag, Jflag, lflag, rflag, uflag, Uflag; 113 long ngroups_max; 114 unsigned pi; 115 char *jailname, *securelevel, *username, *JidFile; 116 char enforce_statfs[4]; 117 static char *cleanenv; 118 const char *shell, *p = NULL; 119 FILE *fp; 120 121 hflag = iflag = Jflag = lflag = rflag = uflag = Uflag = 122 jail_set_flags = 0; 123 cmdarg = jid = -1; 124 jailname = securelevel = username = JidFile = cleanenv = NULL; 125 fp = NULL; 126 127 ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1; 128 if ((groups = malloc(sizeof(gid_t) * ngroups_max)) == NULL) 129 err(1, "malloc"); 130 131 while ((ch = getopt(argc, argv, "cdhilmn:r:s:u:U:J:")) != -1) { 132 switch (ch) { 133 case 'd': 134 jail_set_flags |= JAIL_DYING; 135 break; 136 case 'h': 137 hflag = 1; 138 break; 139 case 'i': 140 iflag = 1; 141 break; 142 case 'J': 143 JidFile = optarg; 144 Jflag = 1; 145 break; 146 case 'n': 147 jailname = optarg; 148 break; 149 case 's': 150 securelevel = optarg; 151 break; 152 case 'u': 153 username = optarg; 154 uflag = 1; 155 break; 156 case 'U': 157 username = optarg; 158 Uflag = 1; 159 break; 160 case 'l': 161 lflag = 1; 162 break; 163 case 'c': 164 jail_set_flags |= JAIL_CREATE; 165 break; 166 case 'm': 167 jail_set_flags |= JAIL_UPDATE; 168 break; 169 case 'r': 170 jid = jail_getid(optarg); 171 if (jid < 0) 172 errx(1, "%s", jail_errmsg); 173 rflag = 1; 174 break; 175 default: 176 usage(); 177 } 178 } 179 argc -= optind; 180 argv += optind; 181 if (rflag) { 182 if (argc > 0 || iflag || Jflag || lflag || uflag || Uflag) 183 usage(); 184 if (jail_remove(jid) < 0) 185 err(1, "jail_remove"); 186 exit (0); 187 } 188 if (argc == 0) 189 usage(); 190 if (uflag && Uflag) 191 usage(); 192 if (lflag && username == NULL) 193 usage(); 194 if (uflag) 195 GET_USER_INFO; 196 197 if (jailname) 198 set_param("name", jailname); 199 if (securelevel) 200 set_param("securelevel", securelevel); 201 if (jail_set_flags) { 202 for (i = 0; i < argc; i++) { 203 if (!strncmp(argv[i], "command=", 8)) { 204 cmdarg = i; 205 argv[cmdarg] += 8; 206 jail_set_flags |= JAIL_ATTACH; 207 break; 208 } 209 if (hflag) { 210 if (!strncmp(argv[i], "ip4.addr=", 9)) { 211 add_ip_addr(&ip4_addr, argv[i] + 9); 212 break; 213 } 214 #ifdef INET6 215 if (!strncmp(argv[i], "ip6.addr=", 9)) { 216 add_ip_addr(&ip6_addr, argv[i] + 9); 217 break; 218 } 219 #endif 220 if (!strncmp(argv[i], "host.hostname=", 14)) 221 add_ip_addrinfo(0, argv[i] + 14); 222 } 223 set_param(NULL, argv[i]); 224 } 225 } else { 226 if (argc < 4 || argv[0][0] != '/') 227 errx(1, "%s\n%s", 228 "no -c or -m, so this must be an old-style command.", 229 "But it doesn't look like one."); 230 set_param("path", argv[0]); 231 set_param("host.hostname", argv[1]); 232 if (hflag) 233 add_ip_addrinfo(0, argv[1]); 234 if (argv[2][0] != '\0') 235 #ifdef INET6 236 add_ip_addr46(argv[2]); 237 #else 238 add_ip_addr(&ip4_addr, argv[2]); 239 #endif 240 cmdarg = 3; 241 /* Emulate the defaults from security.jail.* sysctls */ 242 sysvallen = sizeof(sysval); 243 if (sysctlbyname("security.jail.jailed", &sysval, &sysvallen, 244 NULL, 0) == 0 && sysval == 0) { 245 for (pi = 0; pi < sizeof(perm_sysctl) / 246 sizeof(perm_sysctl[0]); pi++) { 247 sysvallen = sizeof(sysval); 248 if (sysctlbyname(perm_sysctl[pi][0], 249 &sysval, &sysvallen, NULL, 0) == 0) 250 set_param(perm_sysctl[pi] 251 [sysval ? 2 : 1], NULL); 252 } 253 sysvallen = sizeof(sysval); 254 if (sysctlbyname("security.jail.enforce_statfs", 255 &sysval, &sysvallen, NULL, 0) == 0) { 256 snprintf(enforce_statfs, 257 sizeof(enforce_statfs), "%d", sysval); 258 set_param("enforce_statfs", enforce_statfs); 259 } 260 } 261 } 262 if (ip4_addr != NULL) 263 set_param("ip4.addr", ip4_addr); 264 #ifdef INET6 265 if (ip6_addr != NULL) 266 set_param("ip6.addr", ip6_addr); 267 #endif 268 269 if (Jflag) { 270 fp = fopen(JidFile, "w"); 271 if (fp == NULL) 272 errx(1, "Could not create JidFile: %s", JidFile); 273 } 274 jid = jailparam_set(params, nparams, 275 jail_set_flags ? jail_set_flags : JAIL_CREATE | JAIL_ATTACH); 276 if (jid < 0) 277 errx(1, "%s", jail_errmsg); 278 if (iflag) { 279 printf("%d\n", jid); 280 fflush(stdout); 281 } 282 if (Jflag) { 283 if (jail_set_flags) { 284 fprintf(fp, "jid=%d", jid); 285 for (i = 0; i < nparams; i++) 286 if (strcmp(params[i].jp_name, "jid")) { 287 fprintf(fp, " %s", 288 (char *)params[i].jp_name); 289 if (param_values[i]) { 290 putc('=', fp); 291 quoted_print(fp, 292 param_values[i]); 293 } 294 } 295 fprintf(fp, "\n"); 296 } else { 297 for (i = 0; i < nparams; i++) 298 if (!strcmp(params[i].jp_name, "path")) 299 break; 300 #ifdef INET6 301 fprintf(fp, "%d\t%s\t%s\t%s%s%s\t%s\n", 302 jid, i < nparams 303 ? (char *)params[i].jp_value : argv[0], 304 argv[1], ip4_addr ? ip4_addr : "", 305 ip4_addr && ip4_addr[0] && ip6_addr && ip6_addr[0] 306 ? "," : "", ip6_addr ? ip6_addr : "", argv[3]); 307 #else 308 fprintf(fp, "%d\t%s\t%s\t%s\t%s\n", 309 jid, i < nparams 310 ? (char *)params[i].jp_value : argv[0], 311 argv[1], ip4_addr ? ip4_addr : "", argv[3]); 312 #endif 313 } 314 (void)fclose(fp); 315 } 316 if (cmdarg < 0) 317 exit(0); 318 if (username != NULL) { 319 if (Uflag) 320 GET_USER_INFO; 321 if (lflag) { 322 p = getenv("TERM"); 323 environ = &cleanenv; 324 } 325 if (setgroups(ngroups, groups) != 0) 326 err(1, "setgroups"); 327 if (setgid(pwd->pw_gid) != 0) 328 err(1, "setgid"); 329 if (setusercontext(lcap, pwd, pwd->pw_uid, 330 LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0) 331 err(1, "setusercontext"); 332 login_close(lcap); 333 } 334 if (lflag) { 335 if (*pwd->pw_shell) 336 shell = pwd->pw_shell; 337 else 338 shell = _PATH_BSHELL; 339 if (chdir(pwd->pw_dir) < 0) 340 errx(1, "no home directory"); 341 setenv("HOME", pwd->pw_dir, 1); 342 setenv("SHELL", shell, 1); 343 setenv("USER", pwd->pw_name, 1); 344 if (p) 345 setenv("TERM", p, 1); 346 } 347 execvp(argv[cmdarg], argv + cmdarg); 348 err(1, "execvp: %s", argv[cmdarg]); 349 } 350 351 static void 352 add_ip_addr(char **addrp, char *value) 353 { 354 int addrlen; 355 char *addr; 356 357 if (!*addrp) { 358 *addrp = strdup(value); 359 if (!*addrp) 360 err(1, "malloc"); 361 } else if (value[0]) { 362 addrlen = strlen(*addrp) + strlen(value) + 2; 363 addr = malloc(addrlen); 364 if (!addr) 365 err(1, "malloc"); 366 snprintf(addr, addrlen, "%s,%s", *addrp, value); 367 free(*addrp); 368 *addrp = addr; 369 } 370 } 371 372 #ifdef INET6 373 static void 374 add_ip_addr46(char *value) 375 { 376 char *p, *np; 377 378 for (p = value;; p = np + 1) 379 { 380 np = strchr(p, ','); 381 if (np) 382 *np = '\0'; 383 add_ip_addrinfo(AI_NUMERICHOST, p); 384 if (!np) 385 break; 386 } 387 } 388 #endif 389 390 static void 391 add_ip_addrinfo(int ai_flags, char *value) 392 { 393 struct addrinfo hints, *ai0, *ai; 394 struct in_addr addr4; 395 size_t size; 396 int error, ip4ok; 397 int mib[4]; 398 char avalue4[INET_ADDRSTRLEN]; 399 #ifdef INET6 400 struct in6_addr addr6; 401 int ip6ok; 402 char avalue6[INET6_ADDRSTRLEN]; 403 #endif 404 405 /* Look up the hostname (or get the address) */ 406 memset(&hints, 0, sizeof(hints)); 407 hints.ai_socktype = SOCK_STREAM; 408 #ifdef INET6 409 hints.ai_family = PF_UNSPEC; 410 #else 411 hints.ai_family = PF_INET; 412 #endif 413 hints.ai_flags = ai_flags; 414 error = getaddrinfo(value, NULL, &hints, &ai0); 415 if (error != 0) 416 errx(1, "hostname %s: %s", value, gai_strerror(error)); 417 418 /* 419 * Silently ignore unsupported address families from DNS lookups. 420 * But if this is a numeric address, let the kernel give the error. 421 */ 422 if (ai_flags & AI_NUMERICHOST) 423 ip4ok = 424 #ifdef INET6 425 ip6ok = 426 #endif 427 1; 428 else { 429 size = 4; 430 ip4ok = (sysctlnametomib("security.jail.param.ip4", mib, 431 &size) == 0); 432 #ifdef INET6 433 size = 4; 434 ip6ok = (sysctlnametomib("security.jail.param.ip6", mib, 435 &size) == 0); 436 #endif 437 } 438 439 /* Convert the addresses to ASCII so set_param can convert them back. */ 440 for (ai = ai0; ai; ai = ai->ai_next) 441 switch (ai->ai_family) { 442 case AF_INET: 443 if (!ip4ok) 444 break; 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) 449 err(1, "inet_ntop"); 450 add_ip_addr(&ip4_addr, avalue4); 451 break; 452 #ifdef INET6 453 case AF_INET6: 454 if (!ip6ok) 455 break; 456 memcpy(&addr6, &((struct sockaddr_in6 *) 457 (void *)ai->ai_addr)->sin6_addr, sizeof(addr6)); 458 if (inet_ntop(AF_INET6, &addr6, avalue6, 459 INET6_ADDRSTRLEN) == NULL) 460 err(1, "inet_ntop"); 461 add_ip_addr(&ip6_addr, avalue6); 462 break; 463 #endif 464 } 465 freeaddrinfo(ai0); 466 } 467 468 static void 469 quoted_print(FILE *fp, char *str) 470 { 471 int c, qc; 472 char *p = str; 473 474 /* An empty string needs quoting. */ 475 if (!*p) { 476 fputs("\"\"", fp); 477 return; 478 } 479 480 /* 481 * The value will be surrounded by quotes if it contains spaces 482 * or quotes. 483 */ 484 qc = strchr(p, '\'') ? '"' 485 : strchr(p, '"') ? '\'' 486 : strchr(p, ' ') || strchr(p, '\t') ? '"' 487 : 0; 488 if (qc) 489 putc(qc, fp); 490 while ((c = *p++)) { 491 if (c == '\\' || c == qc) 492 putc('\\', fp); 493 putc(c, fp); 494 } 495 if (qc) 496 putc(qc, fp); 497 } 498 499 static void 500 set_param(const char *name, char *value) 501 { 502 struct jailparam *param; 503 int i; 504 505 static int paramlistsize; 506 507 /* Separate the name from the value, if not done already. */ 508 if (name == NULL) { 509 name = value; 510 if ((value = strchr(value, '='))) 511 *value++ = '\0'; 512 } 513 514 /* Check for repeat parameters */ 515 for (i = 0; i < nparams; i++) 516 if (!strcmp(name, params[i].jp_name)) { 517 jailparam_free(params + i, 1); 518 memcpy(params + i, params + i + 1, 519 (--nparams - i) * sizeof(struct jailparam)); 520 break; 521 } 522 523 /* Make sure there is room for the new param record. */ 524 if (!nparams) { 525 paramlistsize = 32; 526 params = malloc(paramlistsize * sizeof(*params)); 527 param_values = malloc(paramlistsize * sizeof(*param_values)); 528 if (params == NULL || param_values == NULL) 529 err(1, "malloc"); 530 } else if (nparams >= paramlistsize) { 531 paramlistsize *= 2; 532 params = realloc(params, paramlistsize * sizeof(*params)); 533 param_values = realloc(param_values, 534 paramlistsize * sizeof(*param_values)); 535 if (params == NULL) 536 err(1, "realloc"); 537 } 538 539 /* Look up the paramter. */ 540 param_values[nparams] = value; 541 param = params + nparams++; 542 if (jailparam_init(param, name) < 0 || 543 jailparam_import(param, value) < 0) 544 errx(1, "%s", jail_errmsg); 545 } 546 547 static void 548 usage(void) 549 { 550 551 (void)fprintf(stderr, 552 "usage: jail [-d] [-h] [-i] [-J jid_file] " 553 "[-l -u username | -U username]\n" 554 " [-c | -m] param=value ... [command=command ...]\n" 555 " jail [-r jail]\n"); 556 exit(1); 557 } 558