jail.c revision 185435
146432Sphk/* 246432Sphk * ---------------------------------------------------------------------------- 346432Sphk * "THE BEER-WARE LICENSE" (Revision 42): 446432Sphk * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you 546432Sphk * can do whatever you want with this stuff. If we meet some day, and you think 646432Sphk * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 746432Sphk * ---------------------------------------------------------------------------- 846432Sphk */ 946432Sphk 10117280Scharnier#include <sys/cdefs.h> 11117280Scharnier__FBSDID("$FreeBSD: head/usr.sbin/jail/jail.c 185435 2008-11-29 14:32:14Z bz $"); 12117280Scharnier 13112705Smaxim#include <sys/param.h> 1446155Sphk#include <sys/jail.h> 15185435Sbz#include <sys/queue.h> 16185435Sbz#include <sys/socket.h> 17158428Smatteo#include <sys/sysctl.h> 18185435Sbz#include <sys/types.h> 1978723Sdd 2046155Sphk#include <netinet/in.h> 2178723Sdd#include <arpa/inet.h> 22185435Sbz#include <netdb.h> 2346155Sphk 2478723Sdd#include <err.h> 25129848Smaxim#include <errno.h> 26112705Smaxim#include <grp.h> 27112705Smaxim#include <login_cap.h> 28133743Smaxim#include <paths.h> 29112705Smaxim#include <pwd.h> 3078723Sdd#include <stdio.h> 3178723Sdd#include <stdlib.h> 32185435Sbz#include <strings.h> 3378723Sdd#include <string.h> 3478723Sdd#include <unistd.h> 3578723Sdd 36185435Sbzstatic void usage(void); 37185435Sbzstatic int add_addresses(struct addrinfo *); 38185435Sbzstatic struct in_addr *copy_addr4(void); 39185435Sbz#ifdef INET6 40185435Sbzstatic struct in6_addr *copy_addr6(void); 41185435Sbz#endif 42185435Sbz 43133743Smaximextern char **environ; 44112705Smaxim 45185435Sbzstruct addr4entry { 46185435Sbz STAILQ_ENTRY(addr4entry) addr4entries; 47185435Sbz struct in_addr ip4; 48185435Sbz int count; 49185435Sbz}; 50185435Sbzstruct addr6entry { 51185435Sbz STAILQ_ENTRY(addr6entry) addr6entries; 52185435Sbz#ifdef INET6 53185435Sbz struct in6_addr ip6; 54185435Sbz#endif 55185435Sbz int count; 56185435Sbz}; 57185435SbzSTAILQ_HEAD(addr4head, addr4entry) addr4 = STAILQ_HEAD_INITIALIZER(addr4); 58185435SbzSTAILQ_HEAD(addr6head, addr6entry) addr6 = STAILQ_HEAD_INITIALIZER(addr6); 59185435Sbz 60129848Smaxim#define GET_USER_INFO do { \ 61129848Smaxim pwd = getpwnam(username); \ 62129848Smaxim if (pwd == NULL) { \ 63129848Smaxim if (errno) \ 64129848Smaxim err(1, "getpwnam: %s", username); \ 65129848Smaxim else \ 66129848Smaxim errx(1, "%s: no such user", username); \ 67129848Smaxim } \ 68129848Smaxim lcap = login_getpwclass(pwd); \ 69129848Smaxim if (lcap == NULL) \ 70129848Smaxim err(1, "getpwclass: %s", username); \ 71129848Smaxim ngroups = NGROUPS; \ 72129848Smaxim if (getgrouplist(username, pwd->pw_gid, groups, &ngroups) != 0) \ 73129848Smaxim err(1, "getgrouplist: %s", username); \ 74129848Smaxim} while (0) 75129848Smaxim 7646155Sphkint 7746155Sphkmain(int argc, char **argv) 7846155Sphk{ 79137808Sdelphij login_cap_t *lcap = NULL; 8046155Sphk struct jail j; 81137808Sdelphij struct passwd *pwd = NULL; 82136051Sstefanf gid_t groups[NGROUPS]; 83185435Sbz int ch, error, i, ngroups, securelevel; 84185435Sbz int hflag, iflag, Jflag, lflag, uflag, Uflag; 85185435Sbz char path[PATH_MAX], *jailname, *ep, *username, *JidFile, *ip; 86133743Smaxim static char *cleanenv; 87137807Sdelphij const char *shell, *p = NULL; 88158475Smatteo long ltmp; 89153056Sphilip FILE *fp; 90185435Sbz struct addrinfo hints, *res0; 9146155Sphk 92185435Sbz hflag = iflag = Jflag = lflag = uflag = Uflag = 0; 93158475Smatteo securelevel = -1; 94185435Sbz jailname = username = JidFile = cleanenv = NULL; 95153056Sphilip fp = NULL; 96112705Smaxim 97185435Sbz while ((ch = getopt(argc, argv, "hiln:s:u:U:J:")) != -1) { 98112705Smaxim switch (ch) { 99185435Sbz case 'h': 100185435Sbz hflag = 1; 101185435Sbz break; 102113277Smike case 'i': 103113277Smike iflag = 1; 104113277Smike break; 105153056Sphilip case 'J': 106153056Sphilip JidFile = optarg; 107153056Sphilip Jflag = 1; 108153056Sphilip break; 109185435Sbz case 'n': 110185435Sbz jailname = optarg; 111185435Sbz break; 112158428Smatteo case 's': 113158475Smatteo ltmp = strtol(optarg, &ep, 0); 114158475Smatteo if (*ep || ep == optarg || ltmp > INT_MAX || !ltmp) 115158475Smatteo errx(1, "invalid securelevel: `%s'", optarg); 116158475Smatteo securelevel = ltmp; 117158428Smatteo break; 118112705Smaxim case 'u': 119112705Smaxim username = optarg; 120129848Smaxim uflag = 1; 121112705Smaxim break; 122129848Smaxim case 'U': 123129848Smaxim username = optarg; 124129848Smaxim Uflag = 1; 125129848Smaxim break; 126133743Smaxim case 'l': 127133743Smaxim lflag = 1; 128133743Smaxim break; 129112705Smaxim default: 130112705Smaxim usage(); 131112705Smaxim } 132113277Smike } 133112705Smaxim argc -= optind; 134112705Smaxim argv += optind; 135112705Smaxim if (argc < 4) 136112705Smaxim usage(); 137129848Smaxim if (uflag && Uflag) 138129848Smaxim usage(); 139133743Smaxim if (lflag && username == NULL) 140133743Smaxim usage(); 141129848Smaxim if (uflag) 142129848Smaxim GET_USER_INFO; 143131182Spjd if (realpath(argv[0], path) == NULL) 144131182Spjd err(1, "realpath: %s", argv[0]); 145131182Spjd if (chdir(path) != 0) 146131182Spjd err(1, "chdir: %s", path); 147185435Sbz /* Initialize struct jail. */ 14851399Sphk memset(&j, 0, sizeof(j)); 149185435Sbz j.version = JAIL_API_VERSION; 150131182Spjd j.path = path; 151112705Smaxim j.hostname = argv[1]; 152185435Sbz if (jailname != NULL) 153185435Sbz j.jailname = jailname; 154185435Sbz 155185435Sbz /* Handle IP addresses. If requested resolve hostname too. */ 156185435Sbz bzero(&hints, sizeof(struct addrinfo)); 157185435Sbz hints.ai_protocol = IPPROTO_TCP; 158185435Sbz hints.ai_socktype = SOCK_STREAM; 159185435Sbz if (JAIL_API_VERSION < 2) 160185435Sbz hints.ai_family = PF_INET; 161185435Sbz else 162185435Sbz hints.ai_family = PF_UNSPEC; 163185435Sbz /* Handle hostname. */ 164185435Sbz if (hflag != 0) { 165185435Sbz error = getaddrinfo(j.hostname, NULL, &hints, &res0); 166185435Sbz if (error != 0) 167185435Sbz errx(1, "failed to handle hostname: %s", 168185435Sbz gai_strerror(error)); 169185435Sbz error = add_addresses(res0); 170185435Sbz freeaddrinfo(res0); 171185435Sbz if (error != 0) 172185435Sbz errx(1, "failed to add addresses."); 173185435Sbz } 174185435Sbz /* Handle IP addresses. */ 175185435Sbz hints.ai_flags = AI_NUMERICHOST; 176185435Sbz ip = strtok(argv[2], ","); 177185435Sbz while (ip != NULL) { 178185435Sbz error = getaddrinfo(ip, NULL, &hints, &res0); 179185435Sbz if (error != 0) 180185435Sbz errx(1, "failed to handle ip: %s", gai_strerror(error)); 181185435Sbz error = add_addresses(res0); 182185435Sbz freeaddrinfo(res0); 183185435Sbz if (error != 0) 184185435Sbz errx(1, "failed to add addresses."); 185185435Sbz ip = strtok(NULL, ","); 186185435Sbz } 187185435Sbz /* Count IP addresses and add them to struct jail. */ 188185435Sbz if (!STAILQ_EMPTY(&addr4)) { 189185435Sbz j.ip4s = STAILQ_FIRST(&addr4)->count; 190185435Sbz j.ip4 = copy_addr4(); 191185435Sbz if (j.ip4s > 0 && j.ip4 == NULL) 192185435Sbz errx(1, "copy_addr4()"); 193185435Sbz } 194185435Sbz#ifdef INET6 195185435Sbz if (!STAILQ_EMPTY(&addr6)) { 196185435Sbz j.ip6s = STAILQ_FIRST(&addr6)->count; 197185435Sbz j.ip6 = copy_addr6(); 198185435Sbz if (j.ip6s > 0 && j.ip6 == NULL) 199185435Sbz errx(1, "copy_addr6()"); 200185435Sbz } 201185435Sbz#endif 202185435Sbz 203153056Sphilip if (Jflag) { 204153056Sphilip fp = fopen(JidFile, "w"); 205153056Sphilip if (fp == NULL) 206153056Sphilip errx(1, "Could not create JidFile: %s", JidFile); 207153056Sphilip } 208113277Smike i = jail(&j); 209113277Smike if (i == -1) 210185435Sbz err(1, "syscall failed with"); 211113804Smike if (iflag) { 212113277Smike printf("%d\n", i); 213113804Smike fflush(stdout); 214113804Smike } 215153056Sphilip if (Jflag) { 216153056Sphilip if (fp != NULL) { 217153056Sphilip fprintf(fp, "%d\t%s\t%s\t%s\t%s\n", 218153056Sphilip i, j.path, j.hostname, argv[2], argv[3]); 219153056Sphilip (void)fclose(fp); 220153056Sphilip } else { 221153056Sphilip errx(1, "Could not write JidFile: %s", JidFile); 222153056Sphilip } 223153056Sphilip } 224158454Smaxim if (securelevel > 0) { 225158454Smaxim if (sysctlbyname("kern.securelevel", NULL, 0, &securelevel, 226158454Smaxim sizeof(securelevel))) 227158454Smaxim err(1, "Can not set securelevel to %d", securelevel); 228158454Smaxim } 229112705Smaxim if (username != NULL) { 230129848Smaxim if (Uflag) 231129848Smaxim GET_USER_INFO; 232133743Smaxim if (lflag) { 233133743Smaxim p = getenv("TERM"); 234133743Smaxim environ = &cleanenv; 235133743Smaxim } 236112972Smaxim if (setgroups(ngroups, groups) != 0) 237112972Smaxim err(1, "setgroups"); 238112972Smaxim if (setgid(pwd->pw_gid) != 0) 239112972Smaxim err(1, "setgid"); 240112972Smaxim if (setusercontext(lcap, pwd, pwd->pw_uid, 241157790Smaxim LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0) 242112972Smaxim err(1, "setusercontext"); 243113206Smaxim login_close(lcap); 244112705Smaxim } 245133743Smaxim if (lflag) { 246133743Smaxim if (*pwd->pw_shell) 247133743Smaxim shell = pwd->pw_shell; 248133743Smaxim else 249133743Smaxim shell = _PATH_BSHELL; 250133743Smaxim if (chdir(pwd->pw_dir) < 0) 251133743Smaxim errx(1, "no home directory"); 252133743Smaxim setenv("HOME", pwd->pw_dir, 1); 253133743Smaxim setenv("SHELL", shell, 1); 254133743Smaxim setenv("USER", pwd->pw_name, 1); 255133743Smaxim if (p) 256133743Smaxim setenv("TERM", p, 1); 257133743Smaxim } 258112972Smaxim if (execv(argv[3], argv + 3) != 0) 259112972Smaxim err(1, "execv: %s", argv[3]); 260113277Smike exit(0); 26146155Sphk} 262112705Smaxim 263112705Smaximstatic void 264112705Smaximusage(void) 265112705Smaxim{ 266112705Smaxim 267158428Smatteo (void)fprintf(stderr, "%s%s%s\n", 268185435Sbz "usage: jail [-hi] [-n jailname] [-J jid_file] ", 269185435Sbz "[-s securelevel] [-l -u username | -U username] ", 270185435Sbz "path hostname [ip[,..]] command ..."); 271112972Smaxim exit(1); 272112705Smaxim} 273185435Sbz 274185435Sbzstatic int 275185435Sbzadd_addresses(struct addrinfo *res0) 276185435Sbz{ 277185435Sbz int error; 278185435Sbz struct addrinfo *res; 279185435Sbz struct addr4entry *a4p; 280185435Sbz struct sockaddr_in *sai; 281185435Sbz#ifdef INET6 282185435Sbz struct addr6entry *a6p; 283185435Sbz struct sockaddr_in6 *sai6; 284185435Sbz#endif 285185435Sbz int count; 286185435Sbz 287185435Sbz error = 0; 288185435Sbz for (res = res0; res && error == 0; res = res->ai_next) { 289185435Sbz switch (res->ai_family) { 290185435Sbz case AF_INET: 291185435Sbz sai = (struct sockaddr_in *)(void *)res->ai_addr; 292185435Sbz STAILQ_FOREACH(a4p, &addr4, addr4entries) { 293185435Sbz if (bcmp(&sai->sin_addr, &a4p->ip4, 294185435Sbz sizeof(struct in_addr)) == 0) { 295185435Sbz err(1, "Ignoring duplicate IPv4 address."); 296185435Sbz break; 297185435Sbz } 298185435Sbz } 299185435Sbz a4p = (struct addr4entry *) malloc( 300185435Sbz sizeof(struct addr4entry)); 301185435Sbz if (a4p == NULL) { 302185435Sbz error = 1; 303185435Sbz break; 304185435Sbz } 305185435Sbz bzero(a4p, sizeof(struct addr4entry)); 306185435Sbz bcopy(&sai->sin_addr, &a4p->ip4, 307185435Sbz sizeof(struct in_addr)); 308185435Sbz if (!STAILQ_EMPTY(&addr4)) 309185435Sbz count = STAILQ_FIRST(&addr4)->count; 310185435Sbz else 311185435Sbz count = 0; 312185435Sbz STAILQ_INSERT_TAIL(&addr4, a4p, addr4entries); 313185435Sbz STAILQ_FIRST(&addr4)->count = count + 1; 314185435Sbz break; 315185435Sbz#ifdef INET6 316185435Sbz case AF_INET6: 317185435Sbz sai6 = (struct sockaddr_in6 *)(void *)res->ai_addr; 318185435Sbz STAILQ_FOREACH(a6p, &addr6, addr6entries) { 319185435Sbz if (bcmp(&sai6->sin6_addr, &a6p->ip6, 320185435Sbz sizeof(struct in6_addr)) == 0) { 321185435Sbz err(1, "Ignoring duplicate IPv6 address."); 322185435Sbz break; 323185435Sbz } 324185435Sbz } 325185435Sbz a6p = (struct addr6entry *) malloc( 326185435Sbz sizeof(struct addr6entry)); 327185435Sbz if (a6p == NULL) { 328185435Sbz error = 1; 329185435Sbz break; 330185435Sbz } 331185435Sbz bzero(a6p, sizeof(struct addr6entry)); 332185435Sbz bcopy(&sai6->sin6_addr, &a6p->ip6, 333185435Sbz sizeof(struct in6_addr)); 334185435Sbz if (!STAILQ_EMPTY(&addr6)) 335185435Sbz count = STAILQ_FIRST(&addr6)->count; 336185435Sbz else 337185435Sbz count = 0; 338185435Sbz STAILQ_INSERT_TAIL(&addr6, a6p, addr6entries); 339185435Sbz STAILQ_FIRST(&addr6)->count = count + 1; 340185435Sbz break; 341185435Sbz#endif 342185435Sbz default: 343185435Sbz err(1, "Address family %d not supported. Ignoring.\n", 344185435Sbz res->ai_family); 345185435Sbz break; 346185435Sbz } 347185435Sbz } 348185435Sbz 349185435Sbz return (error); 350185435Sbz} 351185435Sbz 352185435Sbzstatic struct in_addr * 353185435Sbzcopy_addr4(void) 354185435Sbz{ 355185435Sbz size_t len; 356185435Sbz struct in_addr *ip4s, *p, ia; 357185435Sbz struct addr4entry *a4p; 358185435Sbz 359185435Sbz if (STAILQ_EMPTY(&addr4)) 360185435Sbz return NULL; 361185435Sbz 362185435Sbz len = STAILQ_FIRST(&addr4)->count * sizeof(struct in_addr); 363185435Sbz 364185435Sbz ip4s = p = (struct in_addr *)malloc(len); 365185435Sbz if (ip4s == NULL) 366185435Sbz return (NULL); 367185435Sbz 368185435Sbz bzero(p, len); 369185435Sbz 370185435Sbz while (!STAILQ_EMPTY(&addr4)) { 371185435Sbz a4p = STAILQ_FIRST(&addr4); 372185435Sbz STAILQ_REMOVE_HEAD(&addr4, addr4entries); 373185435Sbz ia.s_addr = a4p->ip4.s_addr; 374185435Sbz bcopy(&ia, p, sizeof(struct in_addr)); 375185435Sbz p++; 376185435Sbz free(a4p); 377185435Sbz } 378185435Sbz 379185435Sbz return (ip4s); 380185435Sbz} 381185435Sbz 382185435Sbz#ifdef INET6 383185435Sbzstatic struct in6_addr * 384185435Sbzcopy_addr6(void) 385185435Sbz{ 386185435Sbz size_t len; 387185435Sbz struct in6_addr *ip6s, *p; 388185435Sbz struct addr6entry *a6p; 389185435Sbz 390185435Sbz if (STAILQ_EMPTY(&addr6)) 391185435Sbz return NULL; 392185435Sbz 393185435Sbz len = STAILQ_FIRST(&addr6)->count * sizeof(struct in6_addr); 394185435Sbz 395185435Sbz ip6s = p = (struct in6_addr *)malloc(len); 396185435Sbz if (ip6s == NULL) 397185435Sbz return (NULL); 398185435Sbz 399185435Sbz bzero(p, len); 400185435Sbz 401185435Sbz while (!STAILQ_EMPTY(&addr6)) { 402185435Sbz a6p = STAILQ_FIRST(&addr6); 403185435Sbz STAILQ_REMOVE_HEAD(&addr6, addr6entries); 404185435Sbz bcopy(&a6p->ip6, p, sizeof(struct in6_addr)); 405185435Sbz p++; 406185435Sbz free(a6p); 407185435Sbz } 408185435Sbz 409185435Sbz return (ip6s); 410185435Sbz} 411185435Sbz#endif 412185435Sbz 413