diff options
Diffstat (limited to 'usr.sbin/jail/jail.c')
-rw-r--r-- | usr.sbin/jail/jail.c | 252 |
1 files changed, 237 insertions, 15 deletions
diff --git a/usr.sbin/jail/jail.c b/usr.sbin/jail/jail.c index e21fcc8..be337b7 100644 --- a/usr.sbin/jail/jail.c +++ b/usr.sbin/jail/jail.c @@ -12,10 +12,14 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/jail.h> +#include <sys/queue.h> +#include <sys/socket.h> #include <sys/sysctl.h> +#include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> +#include <netdb.h> #include <err.h> #include <errno.h> @@ -25,12 +29,34 @@ __FBSDID("$FreeBSD$"); #include <pwd.h> #include <stdio.h> #include <stdlib.h> +#include <strings.h> #include <string.h> #include <unistd.h> -static void usage(void); +static void usage(void); +static int add_addresses(struct addrinfo *); +static struct in_addr *copy_addr4(void); +#ifdef INET6 +static struct in6_addr *copy_addr6(void); +#endif + extern char **environ; +struct addr4entry { + STAILQ_ENTRY(addr4entry) addr4entries; + struct in_addr ip4; + int count; +}; +struct addr6entry { + STAILQ_ENTRY(addr6entry) addr6entries; +#ifdef INET6 + struct in6_addr ip6; +#endif + int count; +}; +STAILQ_HEAD(addr4head, addr4entry) addr4 = STAILQ_HEAD_INITIALIZER(addr4); +STAILQ_HEAD(addr6head, addr6entry) addr6 = STAILQ_HEAD_INITIALIZER(addr6); + #define GET_USER_INFO do { \ pwd = getpwnam(username); \ if (pwd == NULL) { \ @@ -53,22 +79,26 @@ main(int argc, char **argv) login_cap_t *lcap = NULL; struct jail j; struct passwd *pwd = NULL; - struct in_addr in; gid_t groups[NGROUPS]; - int ch, i, iflag, Jflag, lflag, ngroups, securelevel, uflag, Uflag; - char path[PATH_MAX], *ep, *username, *JidFile; + int ch, error, i, ngroups, securelevel; + int hflag, iflag, Jflag, lflag, uflag, Uflag; + char path[PATH_MAX], *jailname, *ep, *username, *JidFile, *ip; static char *cleanenv; const char *shell, *p = NULL; long ltmp; FILE *fp; + struct addrinfo hints, *res0; - iflag = Jflag = lflag = uflag = Uflag = 0; + hflag = iflag = Jflag = lflag = uflag = Uflag = 0; securelevel = -1; - username = JidFile = cleanenv = NULL; + jailname = username = JidFile = cleanenv = NULL; fp = NULL; - while ((ch = getopt(argc, argv, "ils:u:U:J:")) != -1) { + while ((ch = getopt(argc, argv, "hiln:s:u:U:J:")) != -1) { switch (ch) { + case 'h': + hflag = 1; + break; case 'i': iflag = 1; break; @@ -76,6 +106,9 @@ main(int argc, char **argv) JidFile = optarg; Jflag = 1; break; + case 'n': + jailname = optarg; + break; case 's': ltmp = strtol(optarg, &ep, 0); if (*ep || ep == optarg || ltmp > INT_MAX || !ltmp) @@ -111,13 +144,62 @@ main(int argc, char **argv) err(1, "realpath: %s", argv[0]); if (chdir(path) != 0) err(1, "chdir: %s", path); + /* Initialize struct jail. */ memset(&j, 0, sizeof(j)); - j.version = 0; + j.version = JAIL_API_VERSION; j.path = path; j.hostname = argv[1]; - if (inet_aton(argv[2], &in) == 0) - errx(1, "Could not make sense of ip-number: %s", argv[2]); - j.ip_number = ntohl(in.s_addr); + if (jailname != NULL) + j.jailname = jailname; + + /* Handle IP addresses. If requested resolve hostname too. */ + bzero(&hints, sizeof(struct addrinfo)); + hints.ai_protocol = IPPROTO_TCP; + hints.ai_socktype = SOCK_STREAM; + if (JAIL_API_VERSION < 2) + hints.ai_family = PF_INET; + else + hints.ai_family = PF_UNSPEC; + /* Handle hostname. */ + if (hflag != 0) { + error = getaddrinfo(j.hostname, NULL, &hints, &res0); + if (error != 0) + errx(1, "failed to handle hostname: %s", + gai_strerror(error)); + error = add_addresses(res0); + freeaddrinfo(res0); + if (error != 0) + errx(1, "failed to add addresses."); + } + /* Handle IP addresses. */ + hints.ai_flags = AI_NUMERICHOST; + ip = strtok(argv[2], ","); + while (ip != NULL) { + error = getaddrinfo(ip, NULL, &hints, &res0); + if (error != 0) + errx(1, "failed to handle ip: %s", gai_strerror(error)); + error = add_addresses(res0); + freeaddrinfo(res0); + if (error != 0) + errx(1, "failed to add addresses."); + ip = strtok(NULL, ","); + } + /* Count IP addresses and add them to struct jail. */ + if (!STAILQ_EMPTY(&addr4)) { + j.ip4s = STAILQ_FIRST(&addr4)->count; + j.ip4 = copy_addr4(); + if (j.ip4s > 0 && j.ip4 == NULL) + errx(1, "copy_addr4()"); + } +#ifdef INET6 + if (!STAILQ_EMPTY(&addr6)) { + j.ip6s = STAILQ_FIRST(&addr6)->count; + j.ip6 = copy_addr6(); + if (j.ip6s > 0 && j.ip6 == NULL) + errx(1, "copy_addr6()"); + } +#endif + if (Jflag) { fp = fopen(JidFile, "w"); if (fp == NULL) @@ -125,7 +207,7 @@ main(int argc, char **argv) } i = jail(&j); if (i == -1) - err(1, "jail"); + err(1, "syscall failed with"); if (iflag) { printf("%d\n", i); fflush(stdout); @@ -183,8 +265,148 @@ usage(void) { (void)fprintf(stderr, "%s%s%s\n", - "usage: jail [-i] [-J jid_file] [-s securelevel] [-l -u ", - "username | -U username]", - " path hostname ip-number command ..."); + "usage: jail [-hi] [-n jailname] [-J jid_file] ", + "[-s securelevel] [-l -u username | -U username] ", + "path hostname [ip[,..]] command ..."); exit(1); } + +static int +add_addresses(struct addrinfo *res0) +{ + int error; + struct addrinfo *res; + struct addr4entry *a4p; + struct sockaddr_in *sai; +#ifdef INET6 + struct addr6entry *a6p; + struct sockaddr_in6 *sai6; +#endif + int count; + + error = 0; + for (res = res0; res && error == 0; res = res->ai_next) { + switch (res->ai_family) { + case AF_INET: + sai = (struct sockaddr_in *)(void *)res->ai_addr; + STAILQ_FOREACH(a4p, &addr4, addr4entries) { + if (bcmp(&sai->sin_addr, &a4p->ip4, + sizeof(struct in_addr)) == 0) { + err(1, "Ignoring duplicate IPv4 address."); + break; + } + } + a4p = (struct addr4entry *) malloc( + sizeof(struct addr4entry)); + if (a4p == NULL) { + error = 1; + break; + } + bzero(a4p, sizeof(struct addr4entry)); + bcopy(&sai->sin_addr, &a4p->ip4, + sizeof(struct in_addr)); + if (!STAILQ_EMPTY(&addr4)) + count = STAILQ_FIRST(&addr4)->count; + else + count = 0; + STAILQ_INSERT_TAIL(&addr4, a4p, addr4entries); + STAILQ_FIRST(&addr4)->count = count + 1; + break; +#ifdef INET6 + case AF_INET6: + sai6 = (struct sockaddr_in6 *)(void *)res->ai_addr; + STAILQ_FOREACH(a6p, &addr6, addr6entries) { + if (bcmp(&sai6->sin6_addr, &a6p->ip6, + sizeof(struct in6_addr)) == 0) { + err(1, "Ignoring duplicate IPv6 address."); + break; + } + } + a6p = (struct addr6entry *) malloc( + sizeof(struct addr6entry)); + if (a6p == NULL) { + error = 1; + break; + } + bzero(a6p, sizeof(struct addr6entry)); + bcopy(&sai6->sin6_addr, &a6p->ip6, + sizeof(struct in6_addr)); + if (!STAILQ_EMPTY(&addr6)) + count = STAILQ_FIRST(&addr6)->count; + else + count = 0; + STAILQ_INSERT_TAIL(&addr6, a6p, addr6entries); + STAILQ_FIRST(&addr6)->count = count + 1; + break; +#endif + default: + err(1, "Address family %d not supported. Ignoring.\n", + res->ai_family); + break; + } + } + + return (error); +} + +static struct in_addr * +copy_addr4(void) +{ + size_t len; + struct in_addr *ip4s, *p, ia; + struct addr4entry *a4p; + + if (STAILQ_EMPTY(&addr4)) + return NULL; + + len = STAILQ_FIRST(&addr4)->count * sizeof(struct in_addr); + + ip4s = p = (struct in_addr *)malloc(len); + if (ip4s == NULL) + return (NULL); + + bzero(p, len); + + while (!STAILQ_EMPTY(&addr4)) { + a4p = STAILQ_FIRST(&addr4); + STAILQ_REMOVE_HEAD(&addr4, addr4entries); + ia.s_addr = a4p->ip4.s_addr; + bcopy(&ia, p, sizeof(struct in_addr)); + p++; + free(a4p); + } + + return (ip4s); +} + +#ifdef INET6 +static struct in6_addr * +copy_addr6(void) +{ + size_t len; + struct in6_addr *ip6s, *p; + struct addr6entry *a6p; + + if (STAILQ_EMPTY(&addr6)) + return NULL; + + len = STAILQ_FIRST(&addr6)->count * sizeof(struct in6_addr); + + ip6s = p = (struct in6_addr *)malloc(len); + if (ip6s == NULL) + return (NULL); + + bzero(p, len); + + while (!STAILQ_EMPTY(&addr6)) { + a6p = STAILQ_FIRST(&addr6); + STAILQ_REMOVE_HEAD(&addr6, addr6entries); + bcopy(&a6p->ip6, p, sizeof(struct in6_addr)); + p++; + free(a6p); + } + + return (ip6s); +} +#endif + |