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 #ifdef INET6 235 add_ip_addr46(argv[2]); 236 #else 237 add_ip_addr(&ip4_addr, argv[2]); 238 #endif 239 cmdarg = 3; 240 /* Emulate the defaults from security.jail.* sysctls */ 241 sysvallen = sizeof(sysval); 242 if (sysctlbyname("security.jail.jailed", &sysval, &sysvallen, 243 NULL, 0) == 0 && sysval == 0) { 244 for (pi = 0; pi < sizeof(perm_sysctl) / 245 sizeof(perm_sysctl[0]); pi++) { 246 sysvallen = sizeof(sysval); 247 if (sysctlbyname(perm_sysctl[pi][0], 248 &sysval, &sysvallen, NULL, 0) == 0) 249 set_param(perm_sysctl[pi] 250 [sysval ? 2 : 1], NULL); 251 } 252 sysvallen = sizeof(sysval); 253 if (sysctlbyname("security.jail.enforce_statfs", 254 &sysval, &sysvallen, NULL, 0) == 0) { 255 snprintf(enforce_statfs, 256 sizeof(enforce_statfs), "%d", sysval); 257 set_param("enforce_statfs", enforce_statfs); 258 } 259 } 260 } 261 if (ip4_addr != NULL) 262 set_param("ip4.addr", ip4_addr); 263 #ifdef INET6 264 if (ip6_addr != NULL) 265 set_param("ip6.addr", ip6_addr); 266 #endif 267 268 if (Jflag) { 269 fp = fopen(JidFile, "w"); 270 if (fp == NULL) 271 errx(1, "Could not create JidFile: %s", JidFile); 272 } 273 jid = jailparam_set(params, nparams, 274 jail_set_flags ? jail_set_flags : JAIL_CREATE | JAIL_ATTACH); 275 if (jid < 0) 276 errx(1, "%s", jail_errmsg); 277 if (iflag) { 278 printf("%d\n", jid); 279 fflush(stdout); 280 } 281 if (Jflag) { 282 if (jail_set_flags) { 283 fprintf(fp, "jid=%d", jid); 284 for (i = 0; i < nparams; i++) 285 if (strcmp(params[i].jp_name, "jid")) { 286 fprintf(fp, " %s", 287 (char *)params[i].jp_name); 288 if (param_values[i]) { 289 putc('=', fp); 290 quoted_print(fp, 291 param_values[i]); 292 } 293 } 294 fprintf(fp, "\n"); 295 } else { 296 for (i = 0; i < nparams; i++) 297 if (!strcmp(params[i].jp_name, "path")) 298 break; 299 #ifdef INET6 300 fprintf(fp, "%d\t%s\t%s\t%s%s%s\t%s\n", 301 jid, i < nparams 302 ? (char *)params[i].jp_value : argv[0], 303 argv[1], ip4_addr ? ip4_addr : "", 304 ip4_addr && ip4_addr[0] && ip6_addr && ip6_addr[0] 305 ? "," : "", ip6_addr ? ip6_addr : "", argv[3]); 306 #else 307 fprintf(fp, "%d\t%s\t%s\t%s\t%s\n", 308 jid, i < nparams 309 ? (char *)params[i].jp_value : argv[0], 310 argv[1], ip4_addr ? ip4_addr : "", argv[3]); 311 #endif 312 } 313 (void)fclose(fp); 314 } 315 if (cmdarg < 0) 316 exit(0); 317 if (username != NULL) { 318 if (Uflag) 319 GET_USER_INFO; 320 if (lflag) { 321 p = getenv("TERM"); 322 environ = &cleanenv; 323 } 324 if (setgroups(ngroups, groups) != 0) 325 err(1, "setgroups"); 326 if (setgid(pwd->pw_gid) != 0) 327 err(1, "setgid"); 328 if (setusercontext(lcap, pwd, pwd->pw_uid, 329 LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0) 330 err(1, "setusercontext"); 331 login_close(lcap); 332 } 333 if (lflag) { 334 if (*pwd->pw_shell) 335 shell = pwd->pw_shell; 336 else 337 shell = _PATH_BSHELL; 338 if (chdir(pwd->pw_dir) < 0) 339 errx(1, "no home directory"); 340 setenv("HOME", pwd->pw_dir, 1); 341 setenv("SHELL", shell, 1); 342 setenv("USER", pwd->pw_name, 1); 343 if (p) 344 setenv("TERM", p, 1); 345 } 346 execvp(argv[cmdarg], argv + cmdarg); 347 err(1, "execvp: %s", argv[cmdarg]); 348 } 349 350 static void 351 add_ip_addr(char **addrp, char *value) 352 { 353 int addrlen; 354 char *addr; 355 356 if (!*addrp) { 357 *addrp = strdup(value); 358 if (!*addrp) 359 err(1, "malloc"); 360 } else if (value[0]) { 361 addrlen = strlen(*addrp) + strlen(value) + 2; 362 addr = malloc(addrlen); 363 if (!addr) 364 err(1, "malloc"); 365 snprintf(addr, addrlen, "%s,%s", *addrp, value); 366 free(*addrp); 367 *addrp = addr; 368 } 369 } 370 371 #ifdef INET6 372 static void 373 add_ip_addr46(char *value) 374 { 375 char *p, *np; 376 377 if (!value[0]) { 378 add_ip_addr(&ip4_addr, value); 379 add_ip_addr(&ip6_addr, value); 380 return; 381 } 382 for (p = value;; p = np + 1) 383 { 384 np = strchr(p, ','); 385 if (np) 386 *np = '\0'; 387 add_ip_addrinfo(AI_NUMERICHOST, p); 388 if (!np) 389 break; 390 } 391 } 392 #endif 393 394 static void 395 add_ip_addrinfo(int ai_flags, char *value) 396 { 397 struct addrinfo hints, *ai0, *ai; 398 struct in_addr addr4; 399 int error; 400 char avalue4[INET_ADDRSTRLEN]; 401 #ifdef INET6 402 struct in6_addr addr6; 403 char avalue6[INET6_ADDRSTRLEN]; 404 #endif 405 406 /* Look up the hostname (or get the address) */ 407 memset(&hints, 0, sizeof(hints)); 408 hints.ai_socktype = SOCK_STREAM; 409 #ifdef INET6 410 hints.ai_family = PF_UNSPEC; 411 #else 412 hints.ai_family = PF_INET; 413 #endif 414 hints.ai_flags = ai_flags; 415 error = getaddrinfo(value, NULL, &hints, &ai0); 416 if (error != 0) 417 errx(1, "hostname %s: %s", value, gai_strerror(error)); 418 419 /* Convert the addresses to ASCII so set_param can convert them back. */ 420 for (ai = ai0; ai; ai = ai->ai_next) 421 switch (ai->ai_family) { 422 case AF_INET: 423 memcpy(&addr4, &((struct sockaddr_in *) 424 (void *)ai->ai_addr)->sin_addr, sizeof(addr4)); 425 if (inet_ntop(AF_INET, &addr4, avalue4, 426 INET_ADDRSTRLEN) == NULL) 427 err(1, "inet_ntop"); 428 add_ip_addr(&ip4_addr, avalue4); 429 break; 430 #ifdef INET6 431 case AF_INET6: 432 memcpy(&addr6, &((struct sockaddr_in6 *) 433 (void *)ai->ai_addr)->sin6_addr, sizeof(addr6)); 434 if (inet_ntop(AF_INET6, &addr6, avalue6, 435 INET6_ADDRSTRLEN) == NULL) 436 err(1, "inet_ntop"); 437 add_ip_addr(&ip6_addr, avalue6); 438 break; 439 #endif 440 } 441 freeaddrinfo(ai0); 442 } 443 444 static void 445 quoted_print(FILE *fp, char *str) 446 { 447 int c, qc; 448 char *p = str; 449 450 /* An empty string needs quoting. */ 451 if (!*p) { 452 fputs("\"\"", fp); 453 return; 454 } 455 456 /* 457 * The value will be surrounded by quotes if it contains spaces 458 * or quotes. 459 */ 460 qc = strchr(p, '\'') ? '"' 461 : strchr(p, '"') ? '\'' 462 : strchr(p, ' ') || strchr(p, '\t') ? '"' 463 : 0; 464 if (qc) 465 putc(qc, fp); 466 while ((c = *p++)) { 467 if (c == '\\' || c == qc) 468 putc('\\', fp); 469 putc(c, fp); 470 } 471 if (qc) 472 putc(qc, fp); 473 } 474 475 static void 476 set_param(const char *name, char *value) 477 { 478 struct jailparam *param; 479 int i; 480 481 static int paramlistsize; 482 483 /* Separate the name from the value, if not done already. */ 484 if (name == NULL) { 485 name = value; 486 if ((value = strchr(value, '='))) 487 *value++ = '\0'; 488 } 489 490 /* Check for repeat parameters */ 491 for (i = 0; i < nparams; i++) 492 if (!strcmp(name, params[i].jp_name)) { 493 jailparam_free(params + i, 1); 494 memcpy(params + i, params + i + 1, 495 (--nparams - i) * sizeof(struct jailparam)); 496 break; 497 } 498 499 /* Make sure there is room for the new param record. */ 500 if (!nparams) { 501 paramlistsize = 32; 502 params = malloc(paramlistsize * sizeof(*params)); 503 param_values = malloc(paramlistsize * sizeof(*param_values)); 504 if (params == NULL || param_values == NULL) 505 err(1, "malloc"); 506 } else if (nparams >= paramlistsize) { 507 paramlistsize *= 2; 508 params = realloc(params, paramlistsize * sizeof(*params)); 509 param_values = realloc(param_values, 510 paramlistsize * sizeof(*param_values)); 511 if (params == NULL) 512 err(1, "realloc"); 513 } 514 515 /* Look up the paramter. */ 516 param_values[nparams] = value; 517 param = params + nparams++; 518 if (jailparam_init(param, name) < 0 || 519 jailparam_import(param, value) < 0) 520 errx(1, "%s", jail_errmsg); 521 } 522 523 static void 524 usage(void) 525 { 526 527 (void)fprintf(stderr, 528 "usage: jail [-d] [-h] [-i] [-J jid_file] " 529 "[-l -u username | -U username]\n" 530 " [-c | -m] param=value ... [command=command ...]\n" 531 " jail [-r jail]\n"); 532 exit(1); 533 } 534