jail.c revision 191668
1191668Sjamie/*- 2191668Sjamie * Copyright (c) 1999 Poul-Henning Kamp. 3191668Sjamie * All rights reserved. 4191668Sjamie * 5191668Sjamie * Redistribution and use in source and binary forms, with or without 6191668Sjamie * modification, are permitted provided that the following conditions 7191668Sjamie * are met: 8191668Sjamie * 1. Redistributions of source code must retain the above copyright 9191668Sjamie * notice, this list of conditions and the following disclaimer. 10191668Sjamie * 2. Redistributions in binary form must reproduce the above copyright 11191668Sjamie * notice, this list of conditions and the following disclaimer in the 12191668Sjamie * documentation and/or other materials provided with the distribution. 13191668Sjamie * 14191668Sjamie * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15191668Sjamie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16191668Sjamie * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17191668Sjamie * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18191668Sjamie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19191668Sjamie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20191668Sjamie * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21191668Sjamie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22191668Sjamie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23191668Sjamie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24191668Sjamie * SUCH DAMAGE. 2546432Sphk */ 2646432Sphk 27117280Scharnier#include <sys/cdefs.h> 28117280Scharnier__FBSDID("$FreeBSD: head/usr.sbin/jail/jail.c 191668 2009-04-29 16:02:52Z jamie $"); 29117280Scharnier 30112705Smaxim#include <sys/param.h> 3146155Sphk#include <sys/jail.h> 32185435Sbz#include <sys/queue.h> 33185435Sbz#include <sys/socket.h> 34158428Smatteo#include <sys/sysctl.h> 35185435Sbz#include <sys/types.h> 3678723Sdd 3746155Sphk#include <netinet/in.h> 3878723Sdd#include <arpa/inet.h> 39185435Sbz#include <netdb.h> 4046155Sphk 4178723Sdd#include <err.h> 42129848Smaxim#include <errno.h> 43112705Smaxim#include <grp.h> 44112705Smaxim#include <login_cap.h> 45133743Smaxim#include <paths.h> 46112705Smaxim#include <pwd.h> 4778723Sdd#include <stdio.h> 4878723Sdd#include <stdlib.h> 49185435Sbz#include <strings.h> 5078723Sdd#include <string.h> 5178723Sdd#include <unistd.h> 5278723Sdd 53185435Sbzstatic void usage(void); 54185435Sbzstatic int add_addresses(struct addrinfo *); 55185435Sbzstatic struct in_addr *copy_addr4(void); 56185435Sbz#ifdef INET6 57185435Sbzstatic struct in6_addr *copy_addr6(void); 58185435Sbz#endif 59185435Sbz 60133743Smaximextern char **environ; 61112705Smaxim 62185435Sbzstruct addr4entry { 63185435Sbz STAILQ_ENTRY(addr4entry) addr4entries; 64185435Sbz struct in_addr ip4; 65185435Sbz int count; 66185435Sbz}; 67185435Sbzstruct addr6entry { 68185435Sbz STAILQ_ENTRY(addr6entry) addr6entries; 69185435Sbz#ifdef INET6 70185435Sbz struct in6_addr ip6; 71185435Sbz#endif 72185435Sbz int count; 73185435Sbz}; 74185435SbzSTAILQ_HEAD(addr4head, addr4entry) addr4 = STAILQ_HEAD_INITIALIZER(addr4); 75185435SbzSTAILQ_HEAD(addr6head, addr6entry) addr6 = STAILQ_HEAD_INITIALIZER(addr6); 76185435Sbz 77129848Smaxim#define GET_USER_INFO do { \ 78129848Smaxim pwd = getpwnam(username); \ 79129848Smaxim if (pwd == NULL) { \ 80129848Smaxim if (errno) \ 81129848Smaxim err(1, "getpwnam: %s", username); \ 82129848Smaxim else \ 83129848Smaxim errx(1, "%s: no such user", username); \ 84129848Smaxim } \ 85129848Smaxim lcap = login_getpwclass(pwd); \ 86129848Smaxim if (lcap == NULL) \ 87129848Smaxim err(1, "getpwclass: %s", username); \ 88129848Smaxim ngroups = NGROUPS; \ 89129848Smaxim if (getgrouplist(username, pwd->pw_gid, groups, &ngroups) != 0) \ 90129848Smaxim err(1, "getgrouplist: %s", username); \ 91129848Smaxim} while (0) 92129848Smaxim 9346155Sphkint 9446155Sphkmain(int argc, char **argv) 9546155Sphk{ 96137808Sdelphij login_cap_t *lcap = NULL; 9746155Sphk struct jail j; 98137808Sdelphij struct passwd *pwd = NULL; 99136051Sstefanf gid_t groups[NGROUPS]; 100185435Sbz int ch, error, i, ngroups, securelevel; 101185435Sbz int hflag, iflag, Jflag, lflag, uflag, Uflag; 102185435Sbz char path[PATH_MAX], *jailname, *ep, *username, *JidFile, *ip; 103133743Smaxim static char *cleanenv; 104137807Sdelphij const char *shell, *p = NULL; 105158475Smatteo long ltmp; 106153056Sphilip FILE *fp; 107185435Sbz struct addrinfo hints, *res0; 10846155Sphk 109185435Sbz hflag = iflag = Jflag = lflag = uflag = Uflag = 0; 110158475Smatteo securelevel = -1; 111185435Sbz jailname = username = JidFile = cleanenv = NULL; 112153056Sphilip fp = NULL; 113112705Smaxim 114185435Sbz while ((ch = getopt(argc, argv, "hiln:s:u:U:J:")) != -1) { 115112705Smaxim switch (ch) { 116185435Sbz case 'h': 117185435Sbz hflag = 1; 118185435Sbz break; 119113277Smike case 'i': 120113277Smike iflag = 1; 121113277Smike break; 122153056Sphilip case 'J': 123153056Sphilip JidFile = optarg; 124153056Sphilip Jflag = 1; 125153056Sphilip break; 126185435Sbz case 'n': 127185435Sbz jailname = optarg; 128185435Sbz break; 129158428Smatteo case 's': 130158475Smatteo ltmp = strtol(optarg, &ep, 0); 131158475Smatteo if (*ep || ep == optarg || ltmp > INT_MAX || !ltmp) 132158475Smatteo errx(1, "invalid securelevel: `%s'", optarg); 133158475Smatteo securelevel = ltmp; 134158428Smatteo break; 135112705Smaxim case 'u': 136112705Smaxim username = optarg; 137129848Smaxim uflag = 1; 138112705Smaxim break; 139129848Smaxim case 'U': 140129848Smaxim username = optarg; 141129848Smaxim Uflag = 1; 142129848Smaxim break; 143133743Smaxim case 'l': 144133743Smaxim lflag = 1; 145133743Smaxim break; 146112705Smaxim default: 147112705Smaxim usage(); 148112705Smaxim } 149113277Smike } 150112705Smaxim argc -= optind; 151112705Smaxim argv += optind; 152112705Smaxim if (argc < 4) 153112705Smaxim usage(); 154129848Smaxim if (uflag && Uflag) 155129848Smaxim usage(); 156133743Smaxim if (lflag && username == NULL) 157133743Smaxim usage(); 158129848Smaxim if (uflag) 159129848Smaxim GET_USER_INFO; 160131182Spjd if (realpath(argv[0], path) == NULL) 161131182Spjd err(1, "realpath: %s", argv[0]); 162131182Spjd if (chdir(path) != 0) 163131182Spjd err(1, "chdir: %s", path); 164185435Sbz /* Initialize struct jail. */ 16551399Sphk memset(&j, 0, sizeof(j)); 166185435Sbz j.version = JAIL_API_VERSION; 167131182Spjd j.path = path; 168112705Smaxim j.hostname = argv[1]; 169185435Sbz if (jailname != NULL) 170185435Sbz j.jailname = jailname; 171185435Sbz 172185435Sbz /* Handle IP addresses. If requested resolve hostname too. */ 173185435Sbz bzero(&hints, sizeof(struct addrinfo)); 174185435Sbz hints.ai_protocol = IPPROTO_TCP; 175185435Sbz hints.ai_socktype = SOCK_STREAM; 176185435Sbz if (JAIL_API_VERSION < 2) 177185435Sbz hints.ai_family = PF_INET; 178185435Sbz else 179185435Sbz hints.ai_family = PF_UNSPEC; 180185435Sbz /* Handle hostname. */ 181185435Sbz if (hflag != 0) { 182185435Sbz error = getaddrinfo(j.hostname, NULL, &hints, &res0); 183185435Sbz if (error != 0) 184185435Sbz errx(1, "failed to handle hostname: %s", 185185435Sbz gai_strerror(error)); 186185435Sbz error = add_addresses(res0); 187185435Sbz freeaddrinfo(res0); 188185435Sbz if (error != 0) 189185435Sbz errx(1, "failed to add addresses."); 190185435Sbz } 191185435Sbz /* Handle IP addresses. */ 192185435Sbz hints.ai_flags = AI_NUMERICHOST; 193185435Sbz ip = strtok(argv[2], ","); 194185435Sbz while (ip != NULL) { 195185435Sbz error = getaddrinfo(ip, NULL, &hints, &res0); 196185435Sbz if (error != 0) 197185435Sbz errx(1, "failed to handle ip: %s", gai_strerror(error)); 198185435Sbz error = add_addresses(res0); 199185435Sbz freeaddrinfo(res0); 200185435Sbz if (error != 0) 201185435Sbz errx(1, "failed to add addresses."); 202185435Sbz ip = strtok(NULL, ","); 203185435Sbz } 204185435Sbz /* Count IP addresses and add them to struct jail. */ 205185435Sbz if (!STAILQ_EMPTY(&addr4)) { 206185435Sbz j.ip4s = STAILQ_FIRST(&addr4)->count; 207185435Sbz j.ip4 = copy_addr4(); 208185435Sbz if (j.ip4s > 0 && j.ip4 == NULL) 209185435Sbz errx(1, "copy_addr4()"); 210185435Sbz } 211185435Sbz#ifdef INET6 212185435Sbz if (!STAILQ_EMPTY(&addr6)) { 213185435Sbz j.ip6s = STAILQ_FIRST(&addr6)->count; 214185435Sbz j.ip6 = copy_addr6(); 215185435Sbz if (j.ip6s > 0 && j.ip6 == NULL) 216185435Sbz errx(1, "copy_addr6()"); 217185435Sbz } 218185435Sbz#endif 219185435Sbz 220153056Sphilip if (Jflag) { 221153056Sphilip fp = fopen(JidFile, "w"); 222153056Sphilip if (fp == NULL) 223153056Sphilip errx(1, "Could not create JidFile: %s", JidFile); 224153056Sphilip } 225113277Smike i = jail(&j); 226113277Smike if (i == -1) 227185435Sbz err(1, "syscall failed with"); 228113804Smike if (iflag) { 229113277Smike printf("%d\n", i); 230113804Smike fflush(stdout); 231113804Smike } 232153056Sphilip if (Jflag) { 233153056Sphilip if (fp != NULL) { 234153056Sphilip fprintf(fp, "%d\t%s\t%s\t%s\t%s\n", 235153056Sphilip i, j.path, j.hostname, argv[2], argv[3]); 236153056Sphilip (void)fclose(fp); 237153056Sphilip } else { 238153056Sphilip errx(1, "Could not write JidFile: %s", JidFile); 239153056Sphilip } 240153056Sphilip } 241158454Smaxim if (securelevel > 0) { 242158454Smaxim if (sysctlbyname("kern.securelevel", NULL, 0, &securelevel, 243158454Smaxim sizeof(securelevel))) 244158454Smaxim err(1, "Can not set securelevel to %d", securelevel); 245158454Smaxim } 246112705Smaxim if (username != NULL) { 247129848Smaxim if (Uflag) 248129848Smaxim GET_USER_INFO; 249133743Smaxim if (lflag) { 250133743Smaxim p = getenv("TERM"); 251133743Smaxim environ = &cleanenv; 252133743Smaxim } 253112972Smaxim if (setgroups(ngroups, groups) != 0) 254112972Smaxim err(1, "setgroups"); 255112972Smaxim if (setgid(pwd->pw_gid) != 0) 256112972Smaxim err(1, "setgid"); 257112972Smaxim if (setusercontext(lcap, pwd, pwd->pw_uid, 258157790Smaxim LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0) 259112972Smaxim err(1, "setusercontext"); 260113206Smaxim login_close(lcap); 261112705Smaxim } 262133743Smaxim if (lflag) { 263133743Smaxim if (*pwd->pw_shell) 264133743Smaxim shell = pwd->pw_shell; 265133743Smaxim else 266133743Smaxim shell = _PATH_BSHELL; 267133743Smaxim if (chdir(pwd->pw_dir) < 0) 268133743Smaxim errx(1, "no home directory"); 269133743Smaxim setenv("HOME", pwd->pw_dir, 1); 270133743Smaxim setenv("SHELL", shell, 1); 271133743Smaxim setenv("USER", pwd->pw_name, 1); 272133743Smaxim if (p) 273133743Smaxim setenv("TERM", p, 1); 274133743Smaxim } 275112972Smaxim if (execv(argv[3], argv + 3) != 0) 276112972Smaxim err(1, "execv: %s", argv[3]); 277113277Smike exit(0); 27846155Sphk} 279112705Smaxim 280112705Smaximstatic void 281112705Smaximusage(void) 282112705Smaxim{ 283112705Smaxim 284158428Smatteo (void)fprintf(stderr, "%s%s%s\n", 285185435Sbz "usage: jail [-hi] [-n jailname] [-J jid_file] ", 286185435Sbz "[-s securelevel] [-l -u username | -U username] ", 287185435Sbz "path hostname [ip[,..]] command ..."); 288112972Smaxim exit(1); 289112705Smaxim} 290185435Sbz 291185435Sbzstatic int 292185435Sbzadd_addresses(struct addrinfo *res0) 293185435Sbz{ 294185435Sbz int error; 295185435Sbz struct addrinfo *res; 296185435Sbz struct addr4entry *a4p; 297185435Sbz struct sockaddr_in *sai; 298185435Sbz#ifdef INET6 299185435Sbz struct addr6entry *a6p; 300185435Sbz struct sockaddr_in6 *sai6; 301185435Sbz#endif 302185435Sbz int count; 303185435Sbz 304185435Sbz error = 0; 305185435Sbz for (res = res0; res && error == 0; res = res->ai_next) { 306185435Sbz switch (res->ai_family) { 307185435Sbz case AF_INET: 308185435Sbz sai = (struct sockaddr_in *)(void *)res->ai_addr; 309185435Sbz STAILQ_FOREACH(a4p, &addr4, addr4entries) { 310185435Sbz if (bcmp(&sai->sin_addr, &a4p->ip4, 311185435Sbz sizeof(struct in_addr)) == 0) { 312185435Sbz err(1, "Ignoring duplicate IPv4 address."); 313185435Sbz break; 314185435Sbz } 315185435Sbz } 316185435Sbz a4p = (struct addr4entry *) malloc( 317185435Sbz sizeof(struct addr4entry)); 318185435Sbz if (a4p == NULL) { 319185435Sbz error = 1; 320185435Sbz break; 321185435Sbz } 322185435Sbz bzero(a4p, sizeof(struct addr4entry)); 323185435Sbz bcopy(&sai->sin_addr, &a4p->ip4, 324185435Sbz sizeof(struct in_addr)); 325185435Sbz if (!STAILQ_EMPTY(&addr4)) 326185435Sbz count = STAILQ_FIRST(&addr4)->count; 327185435Sbz else 328185435Sbz count = 0; 329185435Sbz STAILQ_INSERT_TAIL(&addr4, a4p, addr4entries); 330185435Sbz STAILQ_FIRST(&addr4)->count = count + 1; 331185435Sbz break; 332185435Sbz#ifdef INET6 333185435Sbz case AF_INET6: 334185435Sbz sai6 = (struct sockaddr_in6 *)(void *)res->ai_addr; 335185435Sbz STAILQ_FOREACH(a6p, &addr6, addr6entries) { 336185435Sbz if (bcmp(&sai6->sin6_addr, &a6p->ip6, 337185435Sbz sizeof(struct in6_addr)) == 0) { 338185435Sbz err(1, "Ignoring duplicate IPv6 address."); 339185435Sbz break; 340185435Sbz } 341185435Sbz } 342185435Sbz a6p = (struct addr6entry *) malloc( 343185435Sbz sizeof(struct addr6entry)); 344185435Sbz if (a6p == NULL) { 345185435Sbz error = 1; 346185435Sbz break; 347185435Sbz } 348185435Sbz bzero(a6p, sizeof(struct addr6entry)); 349185435Sbz bcopy(&sai6->sin6_addr, &a6p->ip6, 350185435Sbz sizeof(struct in6_addr)); 351185435Sbz if (!STAILQ_EMPTY(&addr6)) 352185435Sbz count = STAILQ_FIRST(&addr6)->count; 353185435Sbz else 354185435Sbz count = 0; 355185435Sbz STAILQ_INSERT_TAIL(&addr6, a6p, addr6entries); 356185435Sbz STAILQ_FIRST(&addr6)->count = count + 1; 357185435Sbz break; 358185435Sbz#endif 359185435Sbz default: 360185435Sbz err(1, "Address family %d not supported. Ignoring.\n", 361185435Sbz res->ai_family); 362185435Sbz break; 363185435Sbz } 364185435Sbz } 365185435Sbz 366185435Sbz return (error); 367185435Sbz} 368185435Sbz 369185435Sbzstatic struct in_addr * 370185435Sbzcopy_addr4(void) 371185435Sbz{ 372185435Sbz size_t len; 373185435Sbz struct in_addr *ip4s, *p, ia; 374185435Sbz struct addr4entry *a4p; 375185435Sbz 376185435Sbz if (STAILQ_EMPTY(&addr4)) 377185435Sbz return NULL; 378185435Sbz 379185435Sbz len = STAILQ_FIRST(&addr4)->count * sizeof(struct in_addr); 380185435Sbz 381185435Sbz ip4s = p = (struct in_addr *)malloc(len); 382185435Sbz if (ip4s == NULL) 383185435Sbz return (NULL); 384185435Sbz 385185435Sbz bzero(p, len); 386185435Sbz 387185435Sbz while (!STAILQ_EMPTY(&addr4)) { 388185435Sbz a4p = STAILQ_FIRST(&addr4); 389185435Sbz STAILQ_REMOVE_HEAD(&addr4, addr4entries); 390185435Sbz ia.s_addr = a4p->ip4.s_addr; 391185435Sbz bcopy(&ia, p, sizeof(struct in_addr)); 392185435Sbz p++; 393185435Sbz free(a4p); 394185435Sbz } 395185435Sbz 396185435Sbz return (ip4s); 397185435Sbz} 398185435Sbz 399185435Sbz#ifdef INET6 400185435Sbzstatic struct in6_addr * 401185435Sbzcopy_addr6(void) 402185435Sbz{ 403185435Sbz size_t len; 404185435Sbz struct in6_addr *ip6s, *p; 405185435Sbz struct addr6entry *a6p; 406185435Sbz 407185435Sbz if (STAILQ_EMPTY(&addr6)) 408185435Sbz return NULL; 409185435Sbz 410185435Sbz len = STAILQ_FIRST(&addr6)->count * sizeof(struct in6_addr); 411185435Sbz 412185435Sbz ip6s = p = (struct in6_addr *)malloc(len); 413185435Sbz if (ip6s == NULL) 414185435Sbz return (NULL); 415185435Sbz 416185435Sbz bzero(p, len); 417185435Sbz 418185435Sbz while (!STAILQ_EMPTY(&addr6)) { 419185435Sbz a6p = STAILQ_FIRST(&addr6); 420185435Sbz STAILQ_REMOVE_HEAD(&addr6, addr6entries); 421185435Sbz bcopy(&a6p->ip6, p, sizeof(struct in6_addr)); 422185435Sbz p++; 423185435Sbz free(a6p); 424185435Sbz } 425185435Sbz 426185435Sbz return (ip6s); 427185435Sbz} 428185435Sbz#endif 429185435Sbz 430