summaryrefslogtreecommitdiffstats
path: root/usr.sbin/jail/jail.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/jail/jail.c')
-rw-r--r--usr.sbin/jail/jail.c252
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
+
OpenPOWER on IntegriCloud