! ! MFC: ! IPv4/v6/no-IP jail patches and follow-up bug and PR fixes. ! ! Generated: Wed Jan 28 13:15:51 UTC 2009 ! SVN Revision: 187819 ! by: /home/bz/public_html/20090118-01-jail7-merge.sh ! ! WARNING: the patch already bumps __FreeBSD_version ! ! Property changes on: usr.bin/cpuset ___________________________________________________________________ Modified: svn:mergeinfo Merged /head/usr.bin/cpuset:r185435 Index: usr.bin/cpuset/cpuset.1 =================================================================== --- usr.bin/cpuset/cpuset.1 (revision 187819) +++ usr.bin/cpuset/cpuset.1 (working copy) @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd March 2, 2008 +.Dd November 29, 2008 .Dt CPUSET 1 .Os .Sh NAME @@ -43,10 +43,10 @@ .Nm .Op Fl cr .Op Fl l Ar cpu-list -.Op Fl p Ar pid | Fl r Ar tid | Fl s Ar setid +.Op Fl j Ar jailid | Fl p Ar pid | Fl r Ar tid | Fl s Ar setid | Fl x Ar irq .Nm .Op Fl cgir -.Op Fl p Ar pid | Fl r Ar tid | Fl s Ar setid +.Op Fl j Ar jailid | Fl p Ar pid | Fl r Ar tid | Fl s Ar setid | Fl x Ar irq .Sh DESCRIPTION The .Nm @@ -56,8 +56,8 @@ about processor binding, sets, and available proce .Pp .Nm requires a target to modify or query. -The target may be specified as a command, process id, thread id, or a -cpuset id. +The target may be specified as a command, process id, thread id, a +cpuset id, an irq or a jail id. Using .Fl g the target's set id or mask may be queried. @@ -111,6 +111,8 @@ the id of the target. When used with the .Fl g option print the id rather than the valid mask of the target. +.It Fl j Ar jailid +Specifies a jail id as the target of the operation. .It Fl l Ar cpu-list Specifies a list of CPUs to apply to a target. Specification may include @@ -124,6 +126,8 @@ The requested operation should reference the root target specifier. .It Fl t Ar tid Specifies a thread id as the target of the operation. +.It Fl x Ar irq +Specifies an irq as the target of the operation. .El .Sh EXIT STATUS .Ex -std Index: usr.bin/cpuset/cpuset.c =================================================================== --- usr.bin/cpuset/cpuset.c (revision 187819) +++ usr.bin/cpuset/cpuset.c (working copy) @@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$"); int cflag; int gflag; int iflag; +int jflag; int lflag; int pflag; int rflag; @@ -149,7 +150,7 @@ printset(cpuset_t *mask) printf("\n"); } -const char *whichnames[] = { NULL, "tid", "pid", "cpuset" }; +const char *whichnames[] = { NULL, "tid", "pid", "cpuset", "N/A", "jail" }; const char *levelnames[] = { NULL, " root", " cpuset", "" }; static void @@ -194,7 +195,7 @@ main(int argc, char *argv[]) level = CPU_LEVEL_WHICH; which = CPU_WHICH_PID; id = pid = tid = setid = -1; - while ((ch = getopt(argc, argv, "cgil:p:rs:t:")) != -1) { + while ((ch = getopt(argc, argv, "cgij:l:p:rs:t:")) != -1) { switch (ch) { case 'c': if (rflag) @@ -208,6 +209,11 @@ main(int argc, char *argv[]) case 'i': iflag = 1; break; + case 'j': + jflag = 1; + which = CPU_WHICH_JAIL; + id = atoi(optarg); + break; case 'l': lflag = 1; parselist(optarg, &mask); @@ -243,7 +249,7 @@ main(int argc, char *argv[]) if (argc || lflag) usage(); /* Only one identity specifier. */ - if (sflag + pflag + tflag > 1) + if (jflag + sflag + pflag + tflag > 1) usage(); if (iflag) printsetid(); @@ -257,7 +263,7 @@ main(int argc, char *argv[]) * The user wants to run a command with a set and possibly cpumask. */ if (argc) { - if (pflag | rflag | tflag) + if (pflag | rflag | tflag | jflag) usage(); if (sflag) { if (cpuset_setid(CPU_WHICH_PID, -1, setid)) @@ -283,7 +289,7 @@ main(int argc, char *argv[]) if (!lflag && !sflag) usage(); /* You can only set a mask on a thread. */ - if (tflag && (sflag || pflag)) + if (tflag && (sflag | pflag | jflag)) usage(); if (pflag && sflag) { if (cpuset_setid(CPU_WHICH_PID, pid, setid)) @@ -313,8 +319,8 @@ usage(void) fprintf(stderr, " cpuset [-l cpu-list] [-s setid] -p pid\n"); fprintf(stderr, - " cpuset [-cr] [-l cpu-list] [-p pid | -t tid | -s setid]\n"); + " cpuset [-cr] [-l cpu-list] [-j jailid | -p pid | -t tid | -s setid]\n"); fprintf(stderr, - " cpuset [-cgir] [-p pid | -t tid | -s setid]\n"); + " cpuset [-cgir] [-j jailid | -p pid | -t tid | -s setid]\n"); exit(1); } Property changes on: share/man/man4 ___________________________________________________________________ Modified: svn:mergeinfo Merged /head/share/man/man4:r185435 Property changes on: share/man/man4/igb.4 ___________________________________________________________________ Modified: svn:mergeinfo Merged /head/share/man/man4/igb.4:r185435 Index: share/man/man4/ddb.4 =================================================================== --- share/man/man4/ddb.4 (revision 187819) +++ share/man/man4/ddb.4 (working copy) @@ -60,7 +60,7 @@ .\" .\" $FreeBSD$ .\" -.Dd December 26, 2007 +.Dd November 29, 2008 .Dt DDB 4 .Os .Sh NAME @@ -539,7 +539,17 @@ If the .Ar addr is given, displays details about the given GEOM object (class, geom, provider or consumer). +.\" .Pp +.It Ic show Cm jails +Show the list of +.Xr jail 8 +instances. +In addition to what +.Xr jls 8 +shows, also list kernel internal details. +.\" +.Pp .It Ic show Cm map Ns Oo Li / Ns Cm f Oc Ar addr Prints the VM map at .Ar addr . Property changes on: share/man/man5 ___________________________________________________________________ Modified: svn:mergeinfo Merged /head/share/man/man5:r187708 Index: share/man/man5/rc.conf.5 =================================================================== --- share/man/man5/rc.conf.5 (revision 187819) +++ share/man/man5/rc.conf.5 (working copy) @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd January 9, 2009 +.Dd January 25, 2009 .Dt RC.CONF 5 .Os .Sh NAME @@ -3398,8 +3398,38 @@ Set to the fully qualified domain name (FQDN) assi .It Va jail_ Ns Ao Ar jname Ac Ns Va _ip .Pq Vt str Unset by default. -Set to the IP address assigned to jail -.Va jname . +Set to the (primary) IPv4 and/or IPv6 address(es) assigned to the jail. +The argument can be a sole address or a comma separated list of addresses. +Additionally each address can be prefixed by the name of an interface +followed by a pipe to overwrite +.Va jail_ Ns Ao Ar jname Ac Ns Va _interface +or +.Va jail_interface +and/or suffixed by a netmask, prefixlen or prefix. +In case no netmask, prefixlen or prefix is given, +.Sq /32 +will be used for IPv4 and +.Sq /128 +will be used for an IPv6 address. +If no address is given for the jail then the jail will be started with +no networking support. +.It Va jail_ Ns Ao Ar jname Ac Ns Va _ip_multi Ns Aq Ar n +.Pq Vt str +Unset by default. +Set additional IPv4 and/or IPv6 address(es) assigned to the jail. +The sequence starts with +.Dq Li _multi0 +and the numbers have to be strictly ascending. +These entries follow the same syntax as their primary +.Va jail_ Ns Ao Ar jname Ac Ns Va _ip +entry. +The order of the entries can be important as the first address for +each address family found will be the primary address of the jail. +See +.Va ip-addresses +option in +.Xr jail 8 +for more details. .It Va jail_ Ns Ao Ar jname Ac Ns Va _flags .Pq Vt str Set to Property changes on: usr.sbin/jls ___________________________________________________________________ Added: svn:mergeinfo Merged /head/usr.sbin/jls:r178940-178941,178946,179308,181430,183242,183387,183422,183629,185435,185899,186085,186844-186848 Index: usr.sbin/jls/jls.c =================================================================== --- usr.sbin/jls/jls.c (revision 187819) +++ usr.sbin/jls/jls.c (working copy) @@ -1,5 +1,6 @@ /*- * Copyright (c) 2003 Mike Barcroft + * Copyright (c) 2008 Bjoern A. Zeeb * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,57 +28,230 @@ */ #include +#include #include #include +#include +#include #include #include #include #include #include #include +#include +#include +#define FLAG_A 0x00001 +#define FLAG_V 0x00002 +#ifdef SUPPORT_OLD_XPRISON +static +char *print_xprison_v1(void *p, char *end, unsigned flags) +{ + struct xprison_v1 *xp; + struct in_addr in; + + if ((char *)p + sizeof(struct xprison_v1) > end) + errx(1, "Invalid length for jail"); + + xp = (struct xprison_v1 *)p; + if (flags & FLAG_V) { + printf("%6d %-29.29s %.74s\n", + xp->pr_id, xp->pr_host, xp->pr_path); + /* We are not printing an empty line here for state and name. */ + /* We are not printing an empty line here for cpusetid. */ + /* IPv4 address. */ + in.s_addr = htonl(xp->pr_ip); + printf("%6s %-15.15s\n", "", inet_ntoa(in)); + } else { + printf("%6d %-15.15s %-29.29s %.74s\n", + xp->pr_id, inet_ntoa(in), xp->pr_host, xp->pr_path); + } + + return ((char *)(xp + 1)); +} +#endif + +static +char *print_xprison_v3(void *p, char *end, unsigned flags) +{ + struct xprison *xp; + struct in_addr *iap, in; + struct in6_addr *ia6p; + char buf[INET6_ADDRSTRLEN]; + const char *state; + char *q; + uint32_t i; + + if ((char *)p + sizeof(struct xprison) > end) + errx(1, "Invalid length for jail"); + xp = (struct xprison *)p; + + if (xp->pr_state < 0 || xp->pr_state >= (int) + ((sizeof(prison_states) / sizeof(struct prison_state)))) + state = "(bogus)"; + else + state = prison_states[xp->pr_state].state_name; + + /* See if we should print non-ACTIVE jails. No? */ + if ((flags & FLAG_A) == 0 && strcmp(state, "ALIVE")) { + q = (char *)(xp + 1); + q += (xp->pr_ip4s * sizeof(struct in_addr)); + if (q > end) + errx(1, "Invalid length for jail"); + q += (xp->pr_ip6s * sizeof(struct in6_addr)); + if (q > end) + errx(1, "Invalid length for jail"); + return (q); + } + + if (flags & FLAG_V) + printf("%6d %-29.29s %.74s\n", + xp->pr_id, xp->pr_host, xp->pr_path); + + /* Jail state and name. */ + if (flags & FLAG_V) + printf("%6s %-29.29s %.74s\n", + "", (xp->pr_name[0] != '\0') ? xp->pr_name : "", state); + + /* cpusetid. */ + if (flags & FLAG_V) + printf("%6s %-6d\n", + "", xp->pr_cpusetid); + + q = (char *)(xp + 1); + /* IPv4 addresses. */ + iap = (struct in_addr *)(void *)q; + q += (xp->pr_ip4s * sizeof(struct in_addr)); + if (q > end) + errx(1, "Invalid length for jail"); + in.s_addr = 0; + for (i = 0; i < xp->pr_ip4s; i++) { + if (i == 0 || flags & FLAG_V) + in.s_addr = iap[i].s_addr; + if (flags & FLAG_V) + printf("%6s %-15.15s\n", "", inet_ntoa(in)); + } + /* IPv6 addresses. */ + ia6p = (struct in6_addr *)(void *)q; + q += (xp->pr_ip6s * sizeof(struct in6_addr)); + if (q > end) + errx(1, "Invalid length for jail"); + for (i = 0; i < xp->pr_ip6s; i++) { + if (flags & FLAG_V) { + inet_ntop(AF_INET6, &ia6p[i], buf, sizeof(buf)); + printf("%6s %s\n", "", buf); + } + } + + /* If requested print the old style single line version. */ + if (!(flags & FLAG_V)) + printf("%6d %-15.15s %-29.29s %.74s\n", + xp->pr_id, (in.s_addr) ? inet_ntoa(in) : "", + xp->pr_host, xp->pr_path); + + return (q); +} + +static void +usage(void) +{ + + (void)fprintf(stderr, "usage: jls [-av]\n"); + exit(1); +} + int -main(void) +main(int argc, char *argv[]) { - struct xprison *sxp, *xp; - struct in_addr in; - size_t i, len; + int ch, version; + unsigned flags; + size_t i, j, len; + void *p, *q; + flags = 0; + while ((ch = getopt(argc, argv, "av")) != -1) { + switch (ch) { + case 'a': + flags |= FLAG_A; + break; + case 'v': + flags |= FLAG_V; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + if (sysctlbyname("security.jail.list", NULL, &len, NULL, 0) == -1) err(1, "sysctlbyname(): security.jail.list"); + j = len; for (i = 0; i < 4; i++) { if (len <= 0) exit(0); - sxp = xp = malloc(len); - if (sxp == NULL) + p = q = malloc(len); + if (p == NULL) err(1, "malloc()"); - if (sysctlbyname("security.jail.list", xp, &len, NULL, 0) == -1) { + if (sysctlbyname("security.jail.list", q, &len, NULL, 0) == -1) { if (errno == ENOMEM) { - free(sxp); - sxp = NULL; + free(p); + p = NULL; + len += j; continue; } err(1, "sysctlbyname(): security.jail.list"); } break; } - if (sxp == NULL) + if (p == NULL) err(1, "sysctlbyname(): security.jail.list"); - if (len < sizeof(*xp) || len % sizeof(*xp) || - xp->pr_version != XPRISON_VERSION) - errx(1, "Kernel and userland out of sync"); + if (len < sizeof(int)) + errx(1, "This is no prison. Kernel and userland out of sync?"); + version = *(int *)p; + if (version > XPRISON_VERSION) + errx(1, "Sci-Fi prison. Kernel/userland out of sync?"); - printf(" JID IP Address Hostname Path\n"); - for (i = 0; i < len / sizeof(*xp); i++) { - in.s_addr = ntohl(xp->pr_ip); - printf("%6d %-15.15s %-29.29s %.74s\n", - xp->pr_id, inet_ntoa(in), xp->pr_host, xp->pr_path); - xp++; + if (flags & FLAG_V) { + printf(" JID Hostname Path\n"); + printf(" Name State\n"); + printf(" CPUSetID\n"); + printf(" IP Address(es)\n"); + } else { + printf(" JID IP Address Hostname" + " Path\n"); } - free(sxp); + for (; q != NULL && (char *)q + sizeof(int) < (char *)p + len;) { + version = *(int *)q; + if (version > XPRISON_VERSION) + errx(1, "Sci-Fi prison. Kernel/userland out of sync?"); + switch (version) { +#ifdef SUPPORT_OLD_XPRISON + case 1: + q = print_xprison_v1(q, (char *)p + len, flags); + break; + case 2: + errx(1, "Version 2 was used by multi-IPv4 jail " + "implementations that never made it into the " + "official kernel."); + /* NOTREACHED */ + break; +#endif + case 3: + q = print_xprison_v3(q, (char *)p + len, flags); + break; + default: + errx(1, "Prison unknown. Kernel/userland out of sync?"); + /* NOTREACHED */ + break; + } + } + + free(p); exit(0); } Index: usr.sbin/jls/Makefile =================================================================== --- usr.sbin/jls/Makefile (revision 187819) +++ usr.sbin/jls/Makefile (working copy) @@ -4,4 +4,6 @@ PROG= jls MAN= jls.8 WARNS?= 6 +CFLAGS+= -DSUPPORT_OLD_XPRISON + .include Index: usr.sbin/jls/jls.8 =================================================================== --- usr.sbin/jls/jls.8 (revision 187819) +++ usr.sbin/jls/jls.8 (working copy) @@ -25,20 +25,44 @@ .\" .\" $FreeBSD$ .\" -.Dd April 8, 2003 +.Dd November 29, 2008 .Dt JLS 8 .Os .Sh NAME .Nm jls -.Nd "list active jails" +.Nd "list jails" .Sh SYNOPSIS .Nm +.Op Fl av .Sh DESCRIPTION The .Nm -utility lists all active jails. -Each jail is represented by one row which contains the following columns: -jail identifier (JID), IP address, hostname, and path. +utility lists all jails. +By default only active jails are listed. +.Pp +The options are as follows: +.Bl -tag -width ".Fl a" +.It Fl a +Show jails in all states, not only active ones. +.It Fl v +Show more verbose information. +This also lists cpusets, jail state, multi-IP, etc. instead of the +classic single-IP jail output. +.El +.Pp +Each jail is represented by rows which, depending on +.Fl v , +contain the following columns: +.Bl -item -offset indent -compact +.It +jail identifier (JID), hostname and path +.It +jail state and name +.It +jail cpuset +.It +followed by one IP adddress per line. +.El .Sh SEE ALSO .Xr jail 2 , .Xr jail 8 , Property changes on: usr.sbin/jexec ___________________________________________________________________ Added: svn:mergeinfo Merged /head/usr.sbin/jexec:r178940-178941,178946,179308,181430,183242,183387,183422,183629,185435,185899,186844-186848 Index: usr.sbin/jexec/jexec.c =================================================================== --- usr.sbin/jexec/jexec.c (revision 187819) +++ usr.sbin/jexec/jexec.c (working copy) @@ -1,5 +1,6 @@ /*- * Copyright (c) 2003 Mike Barcroft + * Copyright (c) 2008 Bjoern A. Zeeb * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,16 +30,171 @@ #include #include +#include #include #include #include #include #include +#include #include #include static void usage(void); +#ifdef SUPPORT_OLD_XPRISON +static +char *lookup_xprison_v1(void *p, char *end, int *id) +{ + struct xprison_v1 *xp; + + if (id == NULL) + errx(1, "Internal error. Invalid ID pointer."); + + if ((char *)p + sizeof(struct xprison_v1) > end) + errx(1, "Invalid length for jail"); + + xp = (struct xprison_v1 *)p; + + *id = xp->pr_id; + return ((char *)(xp + 1)); +} +#endif + +static +char *lookup_xprison_v3(void *p, char *end, int *id, char *jailname) +{ + struct xprison *xp; + char *q; + int ok; + + if (id == NULL) + errx(1, "Internal error. Invalid ID pointer."); + + if ((char *)p + sizeof(struct xprison) > end) + errx(1, "Invalid length for jail"); + + xp = (struct xprison *)p; + ok = 1; + + /* Jail state and name. */ + if (xp->pr_state < 0 || xp->pr_state >= + (int)((sizeof(prison_states) / sizeof(struct prison_state)))) + errx(1, "Invalid jail state."); + else if (xp->pr_state != PRISON_STATE_ALIVE) + ok = 0; + if (jailname != NULL) { + if (xp->pr_name[0] == '\0') + ok = 0; + else if (strcmp(jailname, xp->pr_name) != 0) + ok = 0; + } + + q = (char *)(xp + 1); + /* IPv4 addresses. */ + q += (xp->pr_ip4s * sizeof(struct in_addr)); + if ((char *)q > end) + errx(1, "Invalid length for jail"); + /* IPv6 addresses. */ + q += (xp->pr_ip6s * sizeof(struct in6_addr)); + if ((char *)q > end) + errx(1, "Invalid length for jail"); + + if (ok) + *id = xp->pr_id; + return (q); +} + +static int +lookup_jail(int jid, char *jailname) +{ + size_t i, j, len; + void *p, *q; + int version, id, xid, count; + + if (sysctlbyname("security.jail.list", NULL, &len, NULL, 0) == -1) + err(1, "sysctlbyname(): security.jail.list"); + + j = len; + for (i = 0; i < 4; i++) { + if (len <= 0) + exit(0); + p = q = malloc(len); + if (p == NULL) + err(1, "malloc()"); + + if (sysctlbyname("security.jail.list", q, &len, NULL, 0) == -1) { + if (errno == ENOMEM) { + free(p); + p = NULL; + len += j; + continue; + } + err(1, "sysctlbyname(): security.jail.list"); + } + break; + } + if (p == NULL) + err(1, "sysctlbyname(): security.jail.list"); + if (len < sizeof(int)) + errx(1, "This is no prison. Kernel and userland out of sync?"); + version = *(int *)p; + if (version > XPRISON_VERSION) + errx(1, "Sci-Fi prison. Kernel/userland out of sync?"); + + count = 0; + xid = -1; + for (; q != NULL && (char *)q + sizeof(int) < (char *)p + len;) { + version = *(int *)q; + if (version > XPRISON_VERSION) + errx(1, "Sci-Fi prison. Kernel/userland out of sync?"); + id = -1; + switch (version) { +#ifdef SUPPORT_OLD_XPRISON + case 1: + if (jailname != NULL) + errx(1, "Version 1 prisons did not " + "support jail names."); + q = lookup_xprison_v1(q, (char *)p + len, &id); + break; + case 2: + errx(1, "Version 2 was used by multi-IPv4 jail " + "implementations that never made it into the " + "official kernel."); + /* NOTREACHED */ + break; +#endif + case 3: + q = lookup_xprison_v3(q, (char *)p + len, &id, jailname); + break; + default: + errx(1, "Prison unknown. Kernel/userland out of sync?"); + /* NOTREACHED */ + break; + } + /* Possible match. */ + if (id > 0) { + /* Do we have a jail ID to match as well? */ + if (jid > 0) { + if (jid == id) { + xid = id; + count++; + } + } else { + xid = id; + count++; + } + } + } + + free(p); + + if (count != 1) + errx(1, "Could not uniquely identify the jail."); + + return (xid); +} + #define GET_USER_INFO do { \ pwd = getpwnam(username); \ if (pwd == NULL) { \ @@ -63,12 +219,17 @@ main(int argc, char *argv[]) struct passwd *pwd = NULL; gid_t groups[NGROUPS]; int ch, ngroups, uflag, Uflag; - char *username; + char *jailname, *username; + ch = uflag = Uflag = 0; - username = NULL; + jailname = username = NULL; + jid = -1; - while ((ch = getopt(argc, argv, "u:U:")) != -1) { + while ((ch = getopt(argc, argv, "i:n:u:U:")) != -1) { switch (ch) { + case 'n': + jailname = optarg; + break; case 'u': username = optarg; uflag = 1; @@ -85,11 +246,22 @@ main(int argc, char *argv[]) argv += optind; if (argc < 2) usage(); + if (strlen(argv[0]) > 0) { + jid = (int)strtol(argv[0], NULL, 10); + if (errno) + err(1, "Unable to parse jail ID."); + } + if (jid <= 0 && jailname == NULL) { + fprintf(stderr, "Neither jail ID nor jail name given.\n"); + usage(); + } if (uflag && Uflag) usage(); if (uflag) GET_USER_INFO; - jid = (int)strtol(argv[0], NULL, 10); + jid = lookup_jail(jid, jailname); + if (jid <= 0) + errx(1, "Cannot identify jail."); if (jail_attach(jid) == -1) err(1, "jail_attach(): %d", jid); if (chdir("/") == -1) @@ -117,6 +289,6 @@ usage(void) fprintf(stderr, "%s%s\n", "usage: jexec [-u username | -U username]", - " jid command ..."); + " [-n jailname] jid command ..."); exit(1); } Index: usr.sbin/jexec/jexec.8 =================================================================== --- usr.sbin/jexec/jexec.8 (revision 187819) +++ usr.sbin/jexec/jexec.8 (working copy) @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd April 19, 2006 +.Dd November 29, 2008 .Dt JEXEC 8 .Os .Sh NAME @@ -34,17 +34,36 @@ .Sh SYNOPSIS .Nm .Op Fl u Ar username | Fl U Ar username +.Op Fl n Ar jailname .Ar jid command ... .Sh DESCRIPTION The .Nm utility executes .Ar command -inside the jail identified by -.Ar jid . +inside the jail identified by either +.Ar jailname +or +.Ar jid +or both. .Pp +If the jail cannot be identified uniquely by the given parameters, +an error message is printed. +.Nm +will also check the state of the jail (once supported) to be +.Dv ALIVE +and ignore jails in other states. +The mandatory argument +.Ar jid +is the unique jail identifier as given by +.Xr jls 8 . +In case you only want to match on other criteria, give an empty string. +.Pp The following options are available: .Bl -tag -width indent +.It Fl n Ar jailname +The name of the jail, if given upon creation of the jail. +This is not the hostname of the jail. .It Fl u Ar username The user name from host environment as whom the .Ar command @@ -63,3 +82,13 @@ The .Nm utility was added in .Fx 5.1 . +.Sh BUGS +If the jail is not identified by +.Ar jid +there is a possible race in between the lookup of the jail +and executing the command inside the jail. +Giving a +.Ar jid +has a similar race as another process can stop the jail and +start another one after the user looked up the +.Ar jid . Index: usr.sbin/jexec/Makefile =================================================================== --- usr.sbin/jexec/Makefile (revision 187819) +++ usr.sbin/jexec/Makefile (working copy) @@ -6,4 +6,6 @@ DPADD= ${LIBUTIL} LDADD= -lutil WARNS?= 6 +CFLAGS+= -DSUPPORT_OLD_XPRISON + .include Property changes on: usr.sbin/jail ___________________________________________________________________ Added: svn:mergeinfo Merged /head/usr.sbin/jail:r178940-178941,178946,179308,181430,183242,183387,183422,183629,185435,186844-186848,187059,187092,187365,187669-187670 Index: usr.sbin/jail/jail.c =================================================================== --- usr.sbin/jail/jail.c (revision 187819) +++ usr.sbin/jail/jail.c (working copy) @@ -12,10 +12,14 @@ __FBSDID("$FreeBSD$"); #include #include +#include +#include #include +#include #include #include +#include #include #include @@ -25,12 +29,34 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include -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 + Index: usr.sbin/jail/jail.8 =================================================================== --- usr.sbin/jail/jail.8 (revision 187819) +++ usr.sbin/jail/jail.8 (working copy) @@ -33,7 +33,7 @@ .\" .\" $FreeBSD$ .\" -.Dd April 5, 2007 +.Dd January 24, 2009 .Dt JAIL 8 .Os .Sh NAME @@ -41,11 +41,12 @@ .Nd "imprison process and its descendants" .Sh SYNOPSIS .Nm -.Op Fl i +.Op Fl hi +.Op Fl n Ar jailname .Op Fl J Ar jid_file .Op Fl s Ar securelevel .Op Fl l u Ar username | Fl U Ar username -.Ar path hostname ip-number command ... +.Ar path hostname [ip[,..]] command ... .Sh DESCRIPTION The .Nm @@ -53,8 +54,28 @@ utility imprisons a process and all future descend .Pp The options are as follows: .Bl -tag -width ".Fl u Ar username" +.It Fl h +Resolve +.Va hostname +and add all IP addresses returned by the resolver +to the list of +.Va ip-addresses +for this prison. +This may affect default address selection for outgoing IPv4 connections +of prisons. +The address first returned by the resolver for each address family +will be used as primary address. +See +.Va ip-addresses +further down for details. .It Fl i Output the jail identifier of the newly created jail. +.It Fl n Ar jailname +Assign and administrative name to the jail that can be used for management +or auditing purposes. +The system will +.Sy not enforce +the name to be unique. .It Fl J Ar jid_file Write a .Ar jid_file @@ -92,8 +113,14 @@ should run. Directory which is to be the root of the prison. .It Ar hostname Hostname of the prison. -.It Ar ip-number -IP number assigned to the prison. +.It Ar ip-addresses +None, one or more IPv4 and IPv6 addresses assigned to the prison. +The first address of each address family that was assigned to the jail will +be used as the source address in case source address selection on unbound +sockets cannot find a better match. +It is only possible to start multiple jails with the same IP address, +if none of the jails has more than this single overlapping IP address +assigned to itself for the address family in question. .It Ar command Pathname of the program which is to be executed. .El @@ -179,7 +206,7 @@ is to disable IP services on the host system that IP addresses for a service. If a network service is present in the host environment that binds all available IP addresses rather than specific IP addresses, it may service -requests sent to jail IP addresses. +requests sent to jail IP addresses if the jail did not bind the port. This means changing .Xr inetd 8 to only listen on the @@ -455,6 +482,29 @@ pkill -j 3 or: .Pp .Dl "killall -j 3" +.Ss "Jails and File Systems" +It is not possible to +.Xr mount 8 +or +.Xr umount 8 +any file system inside a jail unless the file system is marked +jail-friendly. +See +.Va security.jail.mount_allowed +in the +.Va "Sysctl MIB Entries" +section. +.Pp +Multiple jails sharing the same file system can influence each other. +For example a user in one jail can fill the file system also +leaving no space for processes in the other jail. +Trying to use +.Xr quota 1 +to prevent this will not work either as the file system quotas +are not aware of jails but only look at the user and group IDs. +This means the same user ID in two jails share the same file +system quota. +One would need to use one file system per jail to make this working. .Ss "Sysctl MIB Entries" Certain aspects of the jail containments environment may be modified from the host environment using @@ -555,6 +605,9 @@ command can be used to find file system types avai a jail. This functionality is disabled by default, but can be enabled by setting this MIB entry to 1. +.It Va security.jail.jail_max_af_ips +This MIB entry determines how may address per address family a prison +may have. The default is 255. .El .Pp The read-only sysctl variable @@ -586,6 +639,7 @@ and .Xr pgrep 1 , .Xr pkill 1 , .Xr ps 1 , +.Xr quota 1 , .Xr chroot 2 , .Xr jail 2 , .Xr jail_attach 2 , @@ -604,7 +658,8 @@ and .Xr sendmail 8 , .Xr shutdown 8 , .Xr sysctl 8 , -.Xr syslogd 8 +.Xr syslogd 8 , +.Xr umount 8 .Sh HISTORY The .Nm @@ -622,6 +677,12 @@ who contributed it to .An Robert Watson wrote the extended documentation, found a few bugs, added a few new features, and cleaned up the userland jail environment. +.Pp +.An Bjoern A. Zeeb +added multi-IP jail support for IPv4 and IPv6 based on a patch +originally done by +.An Pawel Jakub Dawidek +for IPv4. .Sh BUGS Jail currently lacks the ability to allow access to specific jail information via Index: usr.sbin/jail/Makefile =================================================================== --- usr.sbin/jail/Makefile (revision 187819) +++ usr.sbin/jail/Makefile (working copy) @@ -1,5 +1,7 @@ # $FreeBSD$ +.include + PROG= jail MAN= jail.8 DPADD= ${LIBUTIL} @@ -7,4 +9,8 @@ LDADD= -lutil WARNS?= 6 +.if ${MK_INET6_SUPPORT} != "no" +CFLAGS+= -DINET6 +.endif + .include Property changes on: lib/libc ___________________________________________________________________ Modified: svn:mergeinfo Merged /head/lib/libc:r185435,186834 Property changes on: lib/libc/string/ffsll.c ___________________________________________________________________ Modified: svn:mergeinfo Merged /head/lib/libc/string/ffsll.c:r185435,186834 Property changes on: lib/libc/string/flsll.c ___________________________________________________________________ Modified: svn:mergeinfo Merged /head/lib/libc/string/flsll.c:r185435,186834 Index: lib/libc/sys/cpuset_getaffinity.2 =================================================================== --- lib/libc/sys/cpuset_getaffinity.2 (revision 187819) +++ lib/libc/sys/cpuset_getaffinity.2 (working copy) @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd March 29, 2008 +.Dd November 29, 2008 .Dt CPUSET 2 .Os .Sh NAME @@ -46,7 +46,7 @@ and .Fn cpuset_setaffinity allow the manipulation of sets of CPUs available to processes, threads, -interrupts and other resources. +interrupts, jails and other resources. These functions may manipulate sets of CPUs that contain many processes or per-object anonymous masks that effect only a single object. .Pp Index: lib/libc/sys/jail.2 =================================================================== --- lib/libc/sys/jail.2 (revision 187819) +++ lib/libc/sys/jail.2 (working copy) @@ -8,7 +8,7 @@ .\" .\" $FreeBSD$ .\" -.Dd April 8, 2003 +.Dd January 6, 2009 .Dt JAIL 2 .Os .Sh NAME @@ -32,15 +32,20 @@ The argument is a pointer to a structure describin .Bd -literal -offset indent struct jail { u_int32_t version; - char *path; - char *hostname; - u_int32_t ip_number; + char *path; + char *hostname; + char *jailname; + unsigned int ip4s; + unsigned int ip6s; + struct in_addr *ip4; + struct in6_addr *ip6; }; .Ed .Pp .Dq Li version defines the version of the API in use. -It should be set to zero at this time. +.Dv JAIL_API_VERSION +is defined for the current version. .Pp The .Dq Li path @@ -54,10 +59,26 @@ This can be changed from the inside of the prison. .Pp The -.Dq Li ip_number -can be set to the IP number assigned to the prison. +.Dq Li jailname +pointer is an optional name that can be assigned to the jail +for example for managment purposes. .Pp The +.Dq Li ip4s +and +.Dq Li ip6s +give the numbers of IPv4 and IPv6 addresses that will be passed +via their respective pointers. +.Pp +The +.Dq Li ip4 +and +.Dq Li ip6 +pointers can be set to an arrays of IPv4 and IPv6 addresses to be assigned to +the prison, or NULL if none. +IPv4 addresses must be in network byte order. +.Pp +The .Fn jail_attach system call attaches the current process to an existing jail, identified by @@ -97,6 +118,12 @@ or, if present, the per-jail .Pp All IP activity will be forced to happen to/from the IP number specified, which should be an alias on one of the network interfaces. +All connections to/from the loopback address +.Pf ( Li 127.0.0.1 +for IPv4, +.Li ::1 +for IPv6) will be changed to be to/from the primary address +of the jail for the given address family. .Pp It is possible to identify a process as jailed by examining .Dq Li /proc//status : Property changes on: lib/libkvm ___________________________________________________________________ Modified: svn:mergeinfo Merged /head/lib/libkvm:r185435 Index: lib/libkvm/kvm_proc.c =================================================================== --- lib/libkvm/kvm_proc.c (revision 187819) +++ lib/libkvm/kvm_proc.c (working copy) @@ -54,10 +54,11 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include +#include +#include #define _WANT_PRISON /* make jail.h give us 'struct prison' */ #include -#include -#include #include #include #include Property changes on: etc ___________________________________________________________________ Modified: svn:mergeinfo Merged /head/etc:r186841,187708 Property changes on: etc/periodic/weekly ___________________________________________________________________ Modified: svn:mergeinfo Merged /head/etc/periodic/weekly:r186841,187708 Index: etc/rc.d/jail =================================================================== --- etc/rc.d/jail (revision 187819) +++ etc/rc.d/jail (working copy) @@ -93,6 +93,7 @@ init_variables() debug "$_j mount enable: $_mount" debug "$_j hostname: $_hostname" debug "$_j ip: $_ip" + jail_show_addresses ${_j} debug "$_j interface: $_interface" debug "$_j fib: $_fib" debug "$_j root: $_rootdir" @@ -126,10 +127,6 @@ init_variables() if [ -z "${_rootdir}" ]; then err 3 "$name: No root directory has been defined for ${_j}" fi - if [ -z "${_ip}" ]; then - err 3 "$name: No IP address has been defined for ${_j}" - fi - } # set_sysctl rc_knob mib msg @@ -275,6 +272,208 @@ jail_mount_fstab() mount -a -F "${_fstab}" } +# jail_show_addresses jail +# Debug print the input for the given _multi aliases +# for a jail for init_variables(). +# +jail_show_addresses() +{ + local _j _type alias + _j="$1" + alias=0 + + if [ -z "${_j}" ]; then + warn "jail_show_addresses: you must specify a jail" + return + fi + + while : ; do + eval _addr=\"\$jail_${_j}_ip_multi${alias}\" + if [ -n "${_addr}" ]; then + debug "${_j} ip_multi${alias}: $_addr" + alias=$((${alias} + 1)) + else + break + fi + done +} + +# jail_extract_address argument +# The second argument is the string from one of the _ip +# or the _multi variables. In case of a comma separated list +# only one argument must be passed in at a time. +# The function alters the _type, _iface, _addr and _mask variables. +# +jail_extract_address() +{ + local _i + _i=$1 + + if [ -z "${_i}" ]; then + warn "jail_extract_address: called without input" + return + fi + + # Check if we have an interface prefix given and split into + # iFace and rest. + case "${_i}" in + *\|*) # ifN|.. prefix there + _iface=${_i%%|*} + _r=${_i##*|} + ;; + *) _iface="" + _r=${_i} + ;; + esac + + # In case the IP has no interface given, check if we have a global one. + _iface=${_iface:-${_interface}} + + # Set address, cut off any prefix/netmask/prefixlen. + _addr=${_r} + _addr=${_addr%%[/ ]*} + + # Theoretically we can return here if interface is not set, + # as we only care about the _mask if we call ifconfig. + # This is not done because we may want to santize IP addresses + # based on _type later, and optionally change the type as well. + + # Extract the prefix/netmask/prefixlen part by cutting off the address. + _mask=${_r} + _mask=`expr "${_mask}" : "${_addr}\(.*\)"` + + # Identify type {inet,inet6}. + case "${_addr}" in + *\.*\.*\.*) _type="inet" ;; + *:*) _type="inet6" ;; + *) warn "jail_extract_address: type not identified" + ;; + esac + + # Handle the special /netmask instead of /prefix or + # "netmask xxx" case for legacy IP. + # We do NOT support shortend class-full netmasks. + if [ "${_type}" = "inet" ]; then + case "${_mask}" in + /*\.*\.*\.*) _mask=" netmask ${_mask#/}" ;; + *) ;; + esac + + # In case _mask is still not set use /32. + _mask=${_mask:-/32} + + elif [ "${_type}" = "inet6" ]; then + # In case _maske is not set for IPv6, use /128. + _mask=${_mask:-/128} + fi +} + +# jail_handle_ips_option {add,del} input +# Handle a single argument imput which can be a comma separated +# list of addresses (theoretically with an option interface and +# prefix/netmask/prefixlen). +# +jail_handle_ips_option() +{ + local _x _action _type _i + _action=$1 + _x=$2 + + if [ -z "${_x}" ]; then + # No IP given. This can happen for the primary address + # of each address family. + return + fi + + # Loop, in case we find a comma separated list, we need to handle + # each argument on its own. + while [ ${#_x} -gt 0 ]; do + case "${_x}" in + *,*) # Extract the first argument and strip it off the list. + _i=`expr "${_x}" : '^\([^,]*\)'` + _x=`expr "${_x}" : "^[^,]*,\(.*\)"` + ;; + *) _i=${_x} + _x="" + ;; + esac + + _type="" + _iface="" + _addr="" + _mask="" + jail_extract_address "${_i}" + + # make sure we got an address. + case "${_addr}" in + "") continue ;; + *) ;; + esac + + # Append address to list of addresses for the jail command. + case "${_addrl}" in + "") _addrl="${_addr}" ;; + *) _addrl="${_addrl},${_addr}" ;; + esac + + # Configure interface alias if requested by a given interface + # and if we could correctly parse everything. + case "${_iface}" in + "") continue ;; + esac + case "${_type}" in + inet) ;; + inet6) ;; + *) warn "Could not determine address family. Not going" \ + "to ${_action} address '${_addr}' for ${_jail}." + continue + ;; + esac + case "${_action}" in + add) ifconfig ${_iface} ${_type} ${_addr}${_mask} alias + ;; + del) # When removing the IP, ignore the _mask. + ifconfig ${_iface} ${_type} ${_addr} -alias + ;; + esac + done +} + +# jail_ips {add,del} +# Extract the comma separated list of addresses and return them +# for the jail command. +# Handle more than one address via the _multi option as well. +# If an interface is given also add/remove an alias for the +# address with an optional netmask. +# +jail_ips() +{ + local _action + _action=$1 + + case "${_action}" in + add) ;; + del) ;; + *) warn "jail_ips: invalid action '${_action}'" + return + ;; + esac + + # Handle addresses. + jail_handle_ips_option ${_action} "${_ip}" + # Handle jail_xxx_ip_multi + alias=0 + while : ; do + eval _x=\"\$jail_${_jail}_ip_multi${alias}\" + case "${_x}" in + "") break ;; + *) jail_handle_ips_option ${_action} "${_x}" + alias=$((${alias} + 1)) + ;; + esac + done +} + jail_start() { echo -n 'Configuring jails:' @@ -296,9 +495,8 @@ jail_start() echo -n " [${_hostname} already running (/var/run/jail_${_jail}.id exists)]" continue; fi - if [ -n "${_interface}" ]; then - ifconfig ${_interface} alias ${_ip} netmask 255.255.255.255 - fi + _addrl="" + jail_ips "add" if [ -n "${_fib}" ]; then _setfib="setfib -F '${_fib}'" else @@ -358,7 +556,7 @@ jail_start() fi _tmp_jail=${_tmp_dir}/jail.$$ eval ${_setfib} jail ${_flags} -i ${_rootdir} ${_hostname} \ - ${_ip} ${_exec_start} > ${_tmp_jail} 2>&1 + \"${_addrl}\" ${_exec_start} > ${_tmp_jail} 2>&1 if [ "$?" -eq 0 ] ; then _jail_id=$(head -1 ${_tmp_jail}) @@ -379,9 +577,7 @@ jail_start() echo ${_jail_id} > /var/run/jail_${_jail}.id else jail_umount_fs - if [ -n "${_interface}" ]; then - ifconfig ${_interface} -alias ${_ip} - fi + jail_ips "del" echo " cannot start jail \"${_jail}\": " tail +2 ${_tmp_jail} fi @@ -410,9 +606,7 @@ jail_stop() jail_umount_fs echo -n " $_hostname" fi - if [ -n "${_interface}" ]; then - ifconfig ${_interface} -alias ${_ip} - fi + jail_ips "del" rm /var/run/jail_${_jail}.id else echo " cannot stop jail ${_jail}. No jail id in /var/run" Index: etc/defaults/rc.conf =================================================================== --- etc/defaults/rc.conf (revision 187819) +++ etc/defaults/rc.conf (working copy) @@ -615,24 +615,27 @@ jail_sysvipc_allow="NO" # Allow SystemV IPC use fr # each jail, specified in jail_list, with the following variables. # NOTES: # - replace 'example' with the jail's name. -# - except rootdir, hostname and ip, all of the following variables may be made -# global jail variables if you don't specify a jail name (ie. jail_interface). +# - except rootdir, hostname, ip and the _multi addresses, +# all of the following variables may be made global jail variables +# if you don't specify a jail name (ie. jail_interface, jail_devfs_ruleset). # #jail_example_rootdir="/usr/jail/default" # Jail's root directory #jail_example_hostname="default.domain.com" # Jail's hostname -#jail_example_ip="192.168.0.10" # Jail's IP number -#jail_example_interface="" # Interface to create the IP alias on -#jail_example_fib="0" # routing table for setfib(1) +#jail_example_interface="" # Jail's interface variable to create IP aliases on +#jail_example_fib="0" # Routing table for setfib(1) +#jail_example_ip="192.0.2.10,2001:db8::17" # Jail's primary IPv4 and IPv6 address +#jail_example_ip_multi0="2001:db8::10" # and another IPv6 address #jail_example_exec_start="/bin/sh /etc/rc" # command to execute in jail for starting #jail_example_exec_afterstart0="/bin/sh command" # command to execute after the one for # starting the jail. More than one can be # specified using a trailing number #jail_example_exec_stop="/bin/sh /etc/rc.shutdown" # command to execute in jail for stopping #jail_example_devfs_enable="NO" # mount devfs in the jail +#jail_example_devfs_ruleset="ruleset_name" # devfs ruleset to apply to jail - + # usually you want "devfsrules_jail". #jail_example_fdescfs_enable="NO" # mount fdescfs in the jail #jail_example_procfs_enable="NO" # mount procfs in jail #jail_example_mount_enable="NO" # mount/umount jail's fs -#jail_example_devfs_ruleset="ruleset_name" # devfs ruleset to apply to jail #jail_example_fstab="" # fstab(5) for mount/umount #jail_example_flags="-l -U root" # flags for jail(8) Property changes on: sys ___________________________________________________________________ Modified: svn:mergeinfo Merged /head/sys:r183571,185404,185435-185436,185441,185899,186086,186606,186948,186956,186980,186986,187684 Index: sys/kern/kern_jail.c =================================================================== --- sys/kern/kern_jail.c (revision 187819) +++ sys/kern/kern_jail.c (working copy) @@ -1,8 +1,26 @@ /*- - * ---------------------------------------------------------------------------- - * "THE BEER-WARE LICENSE" (Revision 42): - * wrote this file. As long as you retain this notice you - * can do whatever you want with this stuff. If we meet some day, and you think - * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp - * ---------------------------------------------------------------------------- + * Copyright (c) 1999 Poul-Henning Kamp. + * Copyright (c) 2008 Bjoern A. Zeeb. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ @@ -26,4 +26,7 @@ +#include "opt_ddb.h" +#include "opt_inet.h" +#include "opt_inet6.h" #include "opt_mac.h" #include @@ -35,6 +56,12 @@ __FBSDID("$FreeBSD$"); #include #include #include +#ifdef DDB +#include +#ifdef INET6 +#include +#endif /* INET6 */ +#endif /* DDB */ #include @@ -51,7 +78,7 @@ SYSCTL_INT(_security_jail, OID_AUTO, set_hostname_ int jail_socket_unixiproute_only = 1; SYSCTL_INT(_security_jail, OID_AUTO, socket_unixiproute_only, CTLFLAG_RW, &jail_socket_unixiproute_only, 0, - "Processes in jail are limited to creating UNIX/IPv4/route sockets only"); + "Processes in jail are limited to creating UNIX/IP/route sockets only"); int jail_sysvipc_allowed = 0; SYSCTL_INT(_security_jail, OID_AUTO, sysvipc_allowed, CTLFLAG_RW, @@ -78,6 +105,11 @@ SYSCTL_INT(_security_jail, OID_AUTO, mount_allowed &jail_mount_allowed, 0, "Processes in jail can mount/unmount jail-friendly file systems"); +int jail_max_af_ips = 255; +SYSCTL_INT(_security_jail, OID_AUTO, jail_max_af_ips, CTLFLAG_RW, + &jail_max_af_ips, 0, + "Number of IP addresses a jail may have at most per address family"); + /* allprison, lastprid, and prisoncount are protected by allprison_lock. */ struct prisonlist allprison; struct sx allprison_lock; @@ -103,6 +135,12 @@ struct prison_service { static void init_prison(void *); static void prison_complete(void *context, int pending); static int sysctl_jail_list(SYSCTL_HANDLER_ARGS); +#ifdef INET +static int _prison_check_ip4(struct prison *, struct in_addr *); +#endif +#ifdef INET6 +static int _prison_check_ip6(struct prison *, struct in6_addr *); +#endif static void init_prison(void *data __unused) @@ -114,6 +152,278 @@ init_prison(void *data __unused) SYSINIT(prison, SI_SUB_INTRINSIC, SI_ORDER_ANY, init_prison, NULL); +#ifdef INET +static int +qcmp_v4(const void *ip1, const void *ip2) +{ + in_addr_t iaa, iab; + + /* + * We need to compare in HBO here to get the list sorted as expected + * by the result of the code. Sorting NBO addresses gives you + * interesting results. If you do not understand, do not try. + */ + iaa = ntohl(((const struct in_addr *)ip1)->s_addr); + iab = ntohl(((const struct in_addr *)ip2)->s_addr); + + /* + * Do not simply return the difference of the two numbers, the int is + * not wide enough. + */ + if (iaa > iab) + return (1); + else if (iaa < iab) + return (-1); + else + return (0); +} +#endif + +#ifdef INET6 +static int +qcmp_v6(const void *ip1, const void *ip2) +{ + const struct in6_addr *ia6a, *ia6b; + int i, rc; + + ia6a = (const struct in6_addr *)ip1; + ia6b = (const struct in6_addr *)ip2; + + rc = 0; + for (i=0; rc == 0 && i < sizeof(struct in6_addr); i++) { + if (ia6a->s6_addr[i] > ia6b->s6_addr[i]) + rc = 1; + else if (ia6a->s6_addr[i] < ia6b->s6_addr[i]) + rc = -1; + } + return (rc); +} +#endif + +#if defined(INET) || defined(INET6) +static int +prison_check_conflicting_ips(struct prison *p) +{ + struct prison *pr; + int i; + + sx_assert(&allprison_lock, SX_LOCKED); + + if (p->pr_ip4s == 0 && p->pr_ip6s == 0) + return (0); + + LIST_FOREACH(pr, &allprison, pr_list) { + /* + * Skip 'dying' prisons to avoid problems when + * restarting multi-IP jails. + */ + if (pr->pr_state == PRISON_STATE_DYING) + continue; + + /* + * We permit conflicting IPs if there is no + * more than 1 IP on eeach jail. + * In case there is one duplicate on a jail with + * more than one IP stop checking and return error. + */ +#ifdef INET + if ((p->pr_ip4s >= 1 && pr->pr_ip4s > 1) || + (p->pr_ip4s > 1 && pr->pr_ip4s >= 1)) { + for (i = 0; i < p->pr_ip4s; i++) { + if (_prison_check_ip4(pr, &p->pr_ip4[i])) + return (EINVAL); + } + } +#endif +#ifdef INET6 + if ((p->pr_ip6s >= 1 && pr->pr_ip6s > 1) || + (p->pr_ip6s > 1 && pr->pr_ip6s >= 1)) { + for (i = 0; i < p->pr_ip6s; i++) { + if (_prison_check_ip6(pr, &p->pr_ip6[i])) + return (EINVAL); + } + } +#endif + } + + return (0); +} + +static int +jail_copyin_ips(struct jail *j) +{ +#ifdef INET + struct in_addr *ip4; +#endif +#ifdef INET6 + struct in6_addr *ip6; +#endif + int error, i; + + /* + * Copy in addresses, check for duplicate addresses and do some + * simple 0 and broadcast checks. If users give other bogus addresses + * it is their problem. + * + * IP addresses are all sorted but ip[0] to preserve the primary IP + * address as given from userland. This special IP is used for + * unbound outgoing connections as well for "loopback" traffic. + */ +#ifdef INET + ip4 = NULL; +#endif +#ifdef INET6 + ip6 = NULL; +#endif +#ifdef INET + if (j->ip4s > 0) { + ip4 = (struct in_addr *)malloc(j->ip4s * sizeof(struct in_addr), + M_PRISON, M_WAITOK | M_ZERO); + error = copyin(j->ip4, ip4, j->ip4s * sizeof(struct in_addr)); + if (error) + goto e_free_ip; + /* Sort all but the first IPv4 address. */ + if (j->ip4s > 1) + qsort((ip4 + 1), j->ip4s - 1, + sizeof(struct in_addr), qcmp_v4); + + /* + * We do not have to care about byte order for these checks + * so we will do them in NBO. + */ + for (i=0; iip4s; i++) { + if (ip4[i].s_addr == htonl(INADDR_ANY) || + ip4[i].s_addr == htonl(INADDR_BROADCAST)) { + error = EINVAL; + goto e_free_ip; + } + if ((i+1) < j->ip4s && + (ip4[0].s_addr == ip4[i+1].s_addr || + ip4[i].s_addr == ip4[i+1].s_addr)) { + error = EINVAL; + goto e_free_ip; + } + } + + j->ip4 = ip4; + } else + j->ip4 = NULL; +#endif +#ifdef INET6 + if (j->ip6s > 0) { + ip6 = (struct in6_addr *)malloc(j->ip6s * sizeof(struct in6_addr), + M_PRISON, M_WAITOK | M_ZERO); + error = copyin(j->ip6, ip6, j->ip6s * sizeof(struct in6_addr)); + if (error) + goto e_free_ip; + /* Sort all but the first IPv6 address. */ + if (j->ip6s > 1) + qsort((ip6 + 1), j->ip6s - 1, + sizeof(struct in6_addr), qcmp_v6); + for (i=0; iip6s; i++) { + if (IN6_IS_ADDR_UNSPECIFIED(&ip6[i])) { + error = EINVAL; + goto e_free_ip; + } + if ((i+1) < j->ip6s && + (IN6_ARE_ADDR_EQUAL(&ip6[0], &ip6[i+1]) || + IN6_ARE_ADDR_EQUAL(&ip6[i], &ip6[i+1]))) { + error = EINVAL; + goto e_free_ip; + } + } + + j->ip6 = ip6; + } else + j->ip6 = NULL; +#endif + return (0); + +e_free_ip: +#ifdef INET6 + free(ip6, M_PRISON); +#endif +#ifdef INET + free(ip4, M_PRISON); +#endif + return (error); +} +#endif /* INET || INET6 */ + +static int +jail_handle_ips(struct jail *j) +{ +#if defined(INET) || defined(INET6) + int error; +#endif + + /* + * Finish conversion for older versions, copyin and setup IPs. + */ + switch (j->version) { + case 0: + { +#ifdef INET + /* FreeBSD single IPv4 jails. */ + struct in_addr *ip4; + + if (j->ip4s == INADDR_ANY || j->ip4s == INADDR_BROADCAST) + return (EINVAL); + ip4 = (struct in_addr *)malloc(sizeof(struct in_addr), + M_PRISON, M_WAITOK | M_ZERO); + + /* + * Jail version 0 still used HBO for the IPv4 address. + */ + ip4->s_addr = htonl(j->ip4s); + j->ip4s = 1; + j->ip4 = ip4; + break; +#else + return (EINVAL); +#endif + } + + case 1: + /* + * Version 1 was used by multi-IPv4 jail implementations + * that never made it into the official kernel. + * We should never hit this here; jail() should catch it. + */ + return (EINVAL); + + case 2: /* JAIL_API_VERSION */ + /* FreeBSD multi-IPv4/IPv6,noIP jails. */ +#if defined(INET) || defined(INET6) +#ifdef INET + if (j->ip4s > jail_max_af_ips) + return (EINVAL); +#else + if (j->ip4s != 0) + return (EINVAL); +#endif +#ifdef INET6 + if (j->ip6s > jail_max_af_ips) + return (EINVAL); +#else + if (j->ip6s != 0) + return (EINVAL); +#endif + error = jail_copyin_ips(j); + if (error) + return (error); +#endif + break; + + default: + /* Sci-Fi jails are not supported, sorry. */ + return (EINVAL); + } + + return (0); +} + + /* * struct jail_args { * struct jail *jail; @@ -122,23 +432,73 @@ SYSINIT(prison, SI_SUB_INTRINSIC, SI_ORDER_ANY, in int jail(struct thread *td, struct jail_args *uap) { + uint32_t version; + int error; + struct jail j; + + error = copyin(uap->jail, &version, sizeof(uint32_t)); + if (error) + return (error); + + switch (version) { + case 0: + /* FreeBSD single IPv4 jails. */ + { + struct jail_v0 j0; + + bzero(&j, sizeof(struct jail)); + error = copyin(uap->jail, &j0, sizeof(struct jail_v0)); + if (error) + return (error); + j.version = j0.version; + j.path = j0.path; + j.hostname = j0.hostname; + j.ip4s = j0.ip_number; + break; + } + + case 1: + /* + * Version 1 was used by multi-IPv4 jail implementations + * that never made it into the official kernel. + */ + return (EINVAL); + + case 2: /* JAIL_API_VERSION */ + /* FreeBSD multi-IPv4/IPv6,noIP jails. */ + error = copyin(uap->jail, &j, sizeof(struct jail)); + if (error) + return (error); + break; + + default: + /* Sci-Fi jails are not supported, sorry. */ + return (EINVAL); + } + return (kern_jail(td, &j)); +} + +int +kern_jail(struct thread *td, struct jail *j) +{ struct nameidata nd; struct prison *pr, *tpr; struct prison_service *psrv; - struct jail j; struct jail_attach_args jaa; int vfslocked, error, tryprid; - error = copyin(uap->jail, &j, sizeof(j)); + KASSERT(j != NULL, ("%s: j is NULL", __func__)); + + /* Handle addresses - convert old structs, copyin, check IPs. */ + error = jail_handle_ips(j); if (error) return (error); - if (j.version != 0) - return (EINVAL); - MALLOC(pr, struct prison *, sizeof(*pr), M_PRISON, M_WAITOK | M_ZERO); + /* Allocate struct prison and fill it with life. */ + pr = malloc(sizeof(*pr), M_PRISON, M_WAITOK | M_ZERO); mtx_init(&pr->pr_mtx, "jail mutex", NULL, MTX_DEF); pr->pr_ref = 1; - error = copyinstr(j.path, &pr->pr_path, sizeof(pr->pr_path), 0); + error = copyinstr(j->path, &pr->pr_path, sizeof(pr->pr_path), NULL); if (error) goto e_killmtx; NDINIT(&nd, LOOKUP, MPSAFE | FOLLOW | LOCKLEAF, UIO_SYSSPACE, @@ -151,10 +511,25 @@ jail(struct thread *td, struct jail_args *uap) VOP_UNLOCK(nd.ni_vp, 0, td); NDFREE(&nd, NDF_ONLY_PNBUF); VFS_UNLOCK_GIANT(vfslocked); - error = copyinstr(j.hostname, &pr->pr_host, sizeof(pr->pr_host), 0); + error = copyinstr(j->hostname, &pr->pr_host, sizeof(pr->pr_host), NULL); if (error) goto e_dropvnref; - pr->pr_ip = j.ip_number; + if (j->jailname != NULL) { + error = copyinstr(j->jailname, &pr->pr_name, + sizeof(pr->pr_name), NULL); + if (error) + goto e_dropvnref; + } + if (j->ip4s > 0) { + pr->pr_ip4 = j->ip4; + pr->pr_ip4s = j->ip4s; + } +#ifdef INET6 + if (j->ip6s > 0) { + pr->pr_ip6 = j->ip6; + pr->pr_ip6s = j->ip6s; + } +#endif pr->pr_linux = NULL; pr->pr_securelevel = securelevel; if (prison_service_slots == 0) @@ -164,8 +539,29 @@ jail(struct thread *td, struct jail_args *uap) M_PRISON, M_ZERO | M_WAITOK); } + /* + * Pre-set prison state to ALIVE upon cration. This is needed so we + * can later attach the process to it, etc (avoiding another extra + * state for ther process of creation, complicating things). + */ + pr->pr_state = PRISON_STATE_ALIVE; + + /* Allocate a dedicated cpuset for each jail. */ + error = cpuset_create_root(td, &pr->pr_cpuset); + if (error) + goto e_dropvnref; + + sx_xlock(&allprison_lock); + /* Make sure we cannot run into problems with ambiguous bind()ings. */ +#if defined(INET) || defined(INET6) + error = prison_check_conflicting_ips(pr); + if (error) { + sx_xunlock(&allprison_lock); + goto e_dropcpuset; + } +#endif + /* Determine next pr_id and add prison to allprison list. */ - sx_xlock(&allprison_lock); tryprid = lastprid + 1; if (tryprid == JAIL_MAX) tryprid = 1; @@ -176,7 +572,7 @@ next: if (tryprid == JAIL_MAX) { sx_xunlock(&allprison_lock); error = EAGAIN; - goto e_dropvnref; + goto e_dropcpuset; } goto next; } @@ -207,15 +603,23 @@ e_dropprref: psrv->ps_destroy(psrv, pr); } sx_sunlock(&allprison_lock); +e_dropcpuset: + cpuset_rel(pr->pr_cpuset); e_dropvnref: if (pr->pr_slots != NULL) - FREE(pr->pr_slots, M_PRISON); + free(pr->pr_slots, M_PRISON); vfslocked = VFS_LOCK_GIANT(pr->pr_root->v_mount); vrele(pr->pr_root); VFS_UNLOCK_GIANT(vfslocked); e_killmtx: mtx_destroy(&pr->pr_mtx); - FREE(pr, M_PRISON); + free(pr, M_PRISON); +#ifdef INET6 + free(j->ip6, M_PRISON); +#endif +#ifdef INET + free(j->ip4, M_PRISON); +#endif return (error); } @@ -251,10 +655,27 @@ jail_attach(struct thread *td, struct jail_attach_ sx_sunlock(&allprison_lock); return (EINVAL); } + + /* + * Do not allow a process to attach to a prison that is not + * considered to be "ALIVE". + */ + if (pr->pr_state != PRISON_STATE_ALIVE) { + mtx_unlock(&pr->pr_mtx); + sx_sunlock(&allprison_lock); + return (EINVAL); + } pr->pr_ref++; mtx_unlock(&pr->pr_mtx); sx_sunlock(&allprison_lock); + /* + * Reparent the newly attached process to this jail. + */ + error = cpuset_setproc_update_set(p, pr->pr_cpuset); + if (error) + goto e_unref; + vfslocked = VFS_LOCK_GIANT(pr->pr_root->v_mount); vn_lock(pr->pr_root, LK_EXCLUSIVE | LK_RETRY, td); if ((error = change_dir(pr->pr_root, td)) != 0) @@ -274,12 +695,14 @@ jail_attach(struct thread *td, struct jail_attach_ crcopy(newcred, oldcred); newcred->cr_prison = pr; p->p_ucred = newcred; + prison_proc_hold(pr); PROC_UNLOCK(p); crfree(oldcred); return (0); e_unlock: VOP_UNLOCK(pr->pr_root, 0, td); VFS_UNLOCK_GIANT(vfslocked); +e_unref: mtx_lock(&pr->pr_mtx); pr->pr_ref--; mtx_unlock(&pr->pr_mtx); @@ -309,10 +732,10 @@ prison_find(int prid) } void -prison_free(struct prison *pr) +prison_free_locked(struct prison *pr) { - mtx_lock(&pr->pr_mtx); + mtx_assert(&pr->pr_mtx, MA_OWNED); pr->pr_ref--; if (pr->pr_ref == 0) { mtx_unlock(&pr->pr_mtx); @@ -323,6 +746,14 @@ void mtx_unlock(&pr->pr_mtx); } +void +prison_free(struct prison *pr) +{ + + mtx_lock(&pr->pr_mtx); + prison_free_locked(pr); +} + static void prison_complete(void *context, int pending) { @@ -340,103 +771,404 @@ prison_complete(void *context, int pending) psrv->ps_destroy(psrv, pr); } sx_sunlock(&allprison_lock); + + cpuset_rel(pr->pr_cpuset); + if (pr->pr_slots != NULL) - FREE(pr->pr_slots, M_PRISON); + free(pr->pr_slots, M_PRISON); vfslocked = VFS_LOCK_GIANT(pr->pr_root->v_mount); vrele(pr->pr_root); VFS_UNLOCK_GIANT(vfslocked); mtx_destroy(&pr->pr_mtx); - if (pr->pr_linux != NULL) - FREE(pr->pr_linux, M_PRISON); - FREE(pr, M_PRISON); + free(pr->pr_linux, M_PRISON); +#ifdef INET6 + free(pr->pr_ip6, M_PRISON); +#endif +#ifdef INET + free(pr->pr_ip4, M_PRISON); +#endif + free(pr, M_PRISON); } void -prison_hold(struct prison *pr) +prison_hold_locked(struct prison *pr) { - mtx_lock(&pr->pr_mtx); + mtx_assert(&pr->pr_mtx, MA_OWNED); KASSERT(pr->pr_ref > 0, ("Trying to hold dead prison (id=%d).", pr->pr_id)); pr->pr_ref++; +} + +void +prison_hold(struct prison *pr) +{ + + mtx_lock(&pr->pr_mtx); + prison_hold_locked(pr); mtx_unlock(&pr->pr_mtx); } -u_int32_t -prison_getip(struct ucred *cred) +void +prison_proc_hold(struct prison *pr) { - return (cred->cr_prison->pr_ip); + mtx_lock(&pr->pr_mtx); + KASSERT(pr->pr_state == PRISON_STATE_ALIVE, + ("Cannot add a process to a non-alive prison (id=%d).", pr->pr_id)); + pr->pr_nprocs++; + mtx_unlock(&pr->pr_mtx); } +void +prison_proc_free(struct prison *pr) +{ + + mtx_lock(&pr->pr_mtx); + KASSERT(pr->pr_state == PRISON_STATE_ALIVE && pr->pr_nprocs > 0, + ("Trying to kill a process in a dead prison (id=%d).", pr->pr_id)); + pr->pr_nprocs--; + if (pr->pr_nprocs == 0) + pr->pr_state = PRISON_STATE_DYING; + mtx_unlock(&pr->pr_mtx); +} + + +#ifdef INET +/* + * Pass back primary IPv4 address of this jail. + * + * If not jailed return success but do not alter the address. Caller has to + * make sure to intialize it correctly (INADDR_ANY). + * + * Returns 0 on success, 1 on error. Address returned in NBO. + */ int -prison_ip(struct ucred *cred, int flag, u_int32_t *ip) +prison_get_ip4(struct ucred *cred, struct in_addr *ia) { - u_int32_t tmp; + KASSERT(cred != NULL, ("%s: cred is NULL", __func__)); + KASSERT(ia != NULL, ("%s: ia is NULL", __func__)); + if (!jailed(cred)) + /* Do not change address passed in. */ return (0); - if (flag) - tmp = *ip; - else - tmp = ntohl(*ip); - if (tmp == INADDR_ANY) { - if (flag) - *ip = cred->cr_prison->pr_ip; - else - *ip = htonl(cred->cr_prison->pr_ip); + + if (cred->cr_prison->pr_ip4 == NULL) + return (1); + + ia->s_addr = cred->cr_prison->pr_ip4[0].s_addr; + return (0); +} + +/* + * Make sure our (source) address is set to something meaningful to this + * jail. + * + * Returns 0 on success, 1 on error. Address passed in in NBO and returned + * in NBO. + */ +int +prison_local_ip4(struct ucred *cred, struct in_addr *ia) +{ + struct in_addr ia0; + + KASSERT(cred != NULL, ("%s: cred is NULL", __func__)); + KASSERT(ia != NULL, ("%s: ia is NULL", __func__)); + + if (!jailed(cred)) return (0); + if (cred->cr_prison->pr_ip4 == NULL) + return (1); + + ia0.s_addr = ntohl(ia->s_addr); + if (ia0.s_addr == INADDR_LOOPBACK) { + ia->s_addr = cred->cr_prison->pr_ip4[0].s_addr; + return (0); } - if (tmp == INADDR_LOOPBACK) { - if (flag) - *ip = cred->cr_prison->pr_ip; + + /* + * In case there is only 1 IPv4 address, bind directly. + */ + if (ia0.s_addr == INADDR_ANY && cred->cr_prison->pr_ip4s == 1) { + ia->s_addr = cred->cr_prison->pr_ip4[0].s_addr; + return (0); + } + + if (ia0.s_addr == INADDR_ANY || prison_check_ip4(cred, ia)) + return (0); + + return (1); +} + +/* + * Rewrite destination address in case we will connect to loopback address. + * + * Returns 0 on success, 1 on error. Address passed in in NBO and returned + * in NBO. + */ +int +prison_remote_ip4(struct ucred *cred, struct in_addr *ia) +{ + + KASSERT(cred != NULL, ("%s: cred is NULL", __func__)); + KASSERT(ia != NULL, ("%s: ia is NULL", __func__)); + + if (!jailed(cred)) + return (0); + if (cred->cr_prison->pr_ip4 == NULL) + return (1); + if (ntohl(ia->s_addr) == INADDR_LOOPBACK) { + ia->s_addr = cred->cr_prison->pr_ip4[0].s_addr; + return (0); + } + + /* + * Return success because nothing had to be changed. + */ + return (0); +} + +/* + * Check if given address belongs to the jail referenced by cred. + * + * Returns 1 if address belongs to jail, 0 if not. Address passed in in NBO. + */ +static int +_prison_check_ip4(struct prison *pr, struct in_addr *ia) +{ + int i, a, z, d; + + if (pr->pr_ip4 == NULL) + return (0); + + /* + * Check the primary IP. + */ + if (pr->pr_ip4[0].s_addr == ia->s_addr) + return (1); + + /* + * All the other IPs are sorted so we can do a binary search. + */ + a = 0; + z = pr->pr_ip4s - 2; + while (a <= z) { + i = (a + z) / 2; + d = qcmp_v4(&pr->pr_ip4[i+1], ia); + if (d > 0) + z = i - 1; + else if (d < 0) + a = i + 1; else - *ip = htonl(cred->cr_prison->pr_ip); + return (1); + } + return (0); +} + +int +prison_check_ip4(struct ucred *cred, struct in_addr *ia) +{ + + KASSERT(cred != NULL, ("%s: cred is NULL", __func__)); + KASSERT(ia != NULL, ("%s: ia is NULL", __func__)); + + if (!jailed(cred)) + return (1); + + return (_prison_check_ip4(cred->cr_prison, ia)); +} +#endif + +#ifdef INET6 +/* + * Pass back primary IPv6 address for this jail. + * + * If not jailed return success but do not alter the address. Caller has to + * make sure to intialize it correctly (IN6ADDR_ANY_INIT). + * + * Returns 0 on success, 1 on error. + */ +int +prison_get_ip6(struct ucred *cred, struct in6_addr *ia6) +{ + + KASSERT(cred != NULL, ("%s: cred is NULL", __func__)); + KASSERT(ia6 != NULL, ("%s: ia6 is NULL", __func__)); + + if (!jailed(cred)) return (0); + if (cred->cr_prison->pr_ip6 == NULL) + return (1); + bcopy(&cred->cr_prison->pr_ip6[0], ia6, sizeof(struct in6_addr)); + return (0); +} + +/* + * Make sure our (source) address is set to something meaningful to this jail. + * + * v6only should be set based on (inp->inp_flags & IN6P_IPV6_V6ONLY != 0) + * when needed while binding. + * + * Returns 0 on success, 1 on error. + */ +int +prison_local_ip6(struct ucred *cred, struct in6_addr *ia6, int v6only) +{ + + KASSERT(cred != NULL, ("%s: cred is NULL", __func__)); + KASSERT(ia6 != NULL, ("%s: ia6 is NULL", __func__)); + + if (!jailed(cred)) + return (0); + if (cred->cr_prison->pr_ip6 == NULL) + return (1); + if (IN6_IS_ADDR_LOOPBACK(ia6)) { + bcopy(&cred->cr_prison->pr_ip6[0], ia6, + sizeof(struct in6_addr)); + return (0); } - if (cred->cr_prison->pr_ip != tmp) + + /* + * In case there is only 1 IPv6 address, and v6only is true, then + * bind directly. + */ + if (v6only != 0 && IN6_IS_ADDR_UNSPECIFIED(ia6) && + cred->cr_prison->pr_ip6s == 1) { + bcopy(&cred->cr_prison->pr_ip6[0], ia6, + sizeof(struct in6_addr)); + return (0); + } + if (IN6_IS_ADDR_UNSPECIFIED(ia6) || prison_check_ip6(cred, ia6)) + return (0); + return (1); +} + +/* + * Rewrite destination address in case we will connect to loopback address. + * + * Returns 0 on success, 1 on error. + */ +int +prison_remote_ip6(struct ucred *cred, struct in6_addr *ia6) +{ + + KASSERT(cred != NULL, ("%s: cred is NULL", __func__)); + KASSERT(ia6 != NULL, ("%s: ia6 is NULL", __func__)); + + if (!jailed(cred)) + return (0); + if (cred->cr_prison->pr_ip6 == NULL) return (1); + if (IN6_IS_ADDR_LOOPBACK(ia6)) { + bcopy(&cred->cr_prison->pr_ip6[0], ia6, + sizeof(struct in6_addr)); + return (0); + } + + /* + * Return success because nothing had to be changed. + */ return (0); } -void -prison_remote_ip(struct ucred *cred, int flag, u_int32_t *ip) +/* + * Check if given address belongs to the jail referenced by cred. + * + * Returns 1 if address belongs to jail, 0 if not. + */ +static int +_prison_check_ip6(struct prison *pr, struct in6_addr *ia6) { - u_int32_t tmp; + int i, a, z, d; - if (!jailed(cred)) - return; - if (flag) - tmp = *ip; - else - tmp = ntohl(*ip); - if (tmp == INADDR_LOOPBACK) { - if (flag) - *ip = cred->cr_prison->pr_ip; + if (pr->pr_ip6 == NULL) + return (0); + + /* + * Check the primary IP. + */ + if (IN6_ARE_ADDR_EQUAL(&pr->pr_ip6[0], ia6)) + return (1); + + /* + * All the other IPs are sorted so we can do a binary search. + */ + a = 0; + z = pr->pr_ip6s - 2; + while (a <= z) { + i = (a + z) / 2; + d = qcmp_v6(&pr->pr_ip6[i+1], ia6); + if (d > 0) + z = i - 1; + else if (d < 0) + a = i + 1; else - *ip = htonl(cred->cr_prison->pr_ip); - return; + return (1); } - return; + return (0); } int +prison_check_ip6(struct ucred *cred, struct in6_addr *ia6) +{ + + KASSERT(cred != NULL, ("%s: cred is NULL", __func__)); + KASSERT(ia6 != NULL, ("%s: ia6 is NULL", __func__)); + + if (!jailed(cred)) + return (1); + + return (_prison_check_ip6(cred->cr_prison, ia6)); +} +#endif + +/* + * Check if given address belongs to the jail referenced by cred (wrapper to + * prison_check_ip[46]). + * + * Returns 1 if address belongs to jail, 0 if not. IPv4 Address passed in in + * NBO. + */ +int prison_if(struct ucred *cred, struct sockaddr *sa) { +#ifdef INET struct sockaddr_in *sai; +#endif +#ifdef INET6 + struct sockaddr_in6 *sai6; +#endif int ok; - sai = (struct sockaddr_in *)sa; - if ((sai->sin_family != AF_INET) && jail_socket_unixiproute_only) - ok = 1; - else if (sai->sin_family != AF_INET) - ok = 0; - else if (cred->cr_prison->pr_ip != ntohl(sai->sin_addr.s_addr)) - ok = 1; - else - ok = 0; + KASSERT(cred != NULL, ("%s: cred is NULL", __func__)); + KASSERT(sa != NULL, ("%s: sa is NULL", __func__)); + + ok = 0; + switch(sa->sa_family) + { +#ifdef INET + case AF_INET: + sai = (struct sockaddr_in *)sa; + if (prison_check_ip4(cred, &sai->sin_addr)) + ok = 1; + break; + +#endif +#ifdef INET6 + case AF_INET6: + sai6 = (struct sockaddr_in6 *)sa; + if (prison_check_ip6(cred, (struct in6_addr *)&sai6->sin6_addr)) + ok = 1; + break; + +#endif + default: + if (!jail_socket_unixiproute_only) + ok = 1; + } return (ok); } @@ -652,6 +1384,7 @@ prison_priv_check(struct ucred *cred, int priv) * processes in the same jail. Likewise for signalling. */ case PRIV_SCHED_DIFFCRED: + case PRIV_SCHED_CPUSET: case PRIV_SIGNAL_DIFFCRED: case PRIV_SIGNAL_SUGID: @@ -947,6 +1680,8 @@ sysctl_jail_list(SYSCTL_HANDLER_ARGS) { struct xprison *xp, *sxp; struct prison *pr; + char *p; + size_t len; int count, error; if (jailed(req->td->td_ucred)) @@ -958,21 +1693,54 @@ sysctl_jail_list(SYSCTL_HANDLER_ARGS) return (0); } - sxp = xp = malloc(sizeof(*xp) * count, M_TEMP, M_WAITOK | M_ZERO); + len = sizeof(*xp) * count; + LIST_FOREACH(pr, &allprison, pr_list) { +#ifdef INET + len += pr->pr_ip4s * sizeof(struct in_addr); +#endif +#ifdef INET6 + len += pr->pr_ip6s * sizeof(struct in6_addr); +#endif + } + sxp = xp = malloc(len, M_TEMP, M_WAITOK | M_ZERO); + LIST_FOREACH(pr, &allprison, pr_list) { xp->pr_version = XPRISON_VERSION; xp->pr_id = pr->pr_id; - xp->pr_ip = pr->pr_ip; + xp->pr_state = pr->pr_state; + xp->pr_cpusetid = pr->pr_cpuset->cs_id; strlcpy(xp->pr_path, pr->pr_path, sizeof(xp->pr_path)); mtx_lock(&pr->pr_mtx); strlcpy(xp->pr_host, pr->pr_host, sizeof(xp->pr_host)); + strlcpy(xp->pr_name, pr->pr_name, sizeof(xp->pr_name)); mtx_unlock(&pr->pr_mtx); - xp++; +#ifdef INET + xp->pr_ip4s = pr->pr_ip4s; +#endif +#ifdef INET6 + xp->pr_ip6s = pr->pr_ip6s; +#endif + p = (char *)(xp + 1); +#ifdef INET + if (pr->pr_ip4s > 0) { + bcopy(pr->pr_ip4, (struct in_addr *)p, + pr->pr_ip4s * sizeof(struct in_addr)); + p += (pr->pr_ip4s * sizeof(struct in_addr)); + } +#endif +#ifdef INET6 + if (pr->pr_ip6s > 0) { + bcopy(pr->pr_ip6, (struct in6_addr *)p, + pr->pr_ip6s * sizeof(struct in6_addr)); + p += (pr->pr_ip6s * sizeof(struct in6_addr)); + } +#endif + xp = (struct xprison *)p; } sx_sunlock(&allprison_lock); - error = SYSCTL_OUT(req, sxp, sizeof(*sxp) * count); + error = SYSCTL_OUT(req, sxp, len); free(sxp, M_TEMP); return (error); } @@ -992,3 +1760,60 @@ sysctl_jail_jailed(SYSCTL_HANDLER_ARGS) } SYSCTL_PROC(_security_jail, OID_AUTO, jailed, CTLTYPE_INT | CTLFLAG_RD, NULL, 0, sysctl_jail_jailed, "I", "Process in jail?"); + +#ifdef DDB +DB_SHOW_COMMAND(jails, db_show_jails) +{ + struct prison *pr; +#ifdef INET + struct in_addr ia; +#endif +#ifdef INET6 + char ip6buf[INET6_ADDRSTRLEN]; +#endif + const char *state; +#if defined(INET) || defined(INET6) + int i; +#endif + + db_printf( + " JID pr_ref pr_nprocs pr_ip4s pr_ip6s\n"); + db_printf( + " Hostname Path\n"); + db_printf( + " Name State\n"); + db_printf( + " Cpusetid\n"); + db_printf( + " IP Address(es)\n"); + LIST_FOREACH(pr, &allprison, pr_list) { + db_printf("%6d %6d %9d %7d %7d\n", + pr->pr_id, pr->pr_ref, pr->pr_nprocs, + pr->pr_ip4s, pr->pr_ip6s); + db_printf("%6s %-29.29s %.74s\n", + "", pr->pr_host, pr->pr_path); + if (pr->pr_state < 0 || pr->pr_state >= (int)((sizeof( + prison_states) / sizeof(struct prison_state)))) + state = "(bogus)"; + else + state = prison_states[pr->pr_state].state_name; + db_printf("%6s %-29.29s %.74s\n", + "", (pr->pr_name[0] != '\0') ? pr->pr_name : "", state); + db_printf("%6s %-6d\n", + "", pr->pr_cpuset->cs_id); +#ifdef INET + for (i=0; i < pr->pr_ip4s; i++) { + ia.s_addr = pr->pr_ip4[i].s_addr; + db_printf("%6s %s\n", "", inet_ntoa(ia)); + } +#endif +#ifdef INET6 + for (i=0; i < pr->pr_ip6s; i++) + db_printf("%6s %s\n", + "", ip6_sprintf(ip6buf, &pr->pr_ip6[i])); +#endif /* INET6 */ + if (db_pager_quit) + break; + } +} +#endif /* DDB */ Index: sys/kern/uipc_socket.c =================================================================== --- sys/kern/uipc_socket.c (revision 187819) +++ sys/kern/uipc_socket.c (working copy) @@ -98,6 +98,7 @@ __FBSDID("$FreeBSD$"); #include "opt_inet.h" +#include "opt_inet6.h" #include "opt_mac.h" #include "opt_zero.h" #include "opt_compat.h" @@ -347,6 +348,9 @@ socreate(int dom, struct socket **aso, int type, i if (jailed(cred) && jail_socket_unixiproute_only && prp->pr_domain->dom_family != PF_LOCAL && prp->pr_domain->dom_family != PF_INET && +#ifdef INET6 + prp->pr_domain->dom_family != PF_INET6 && +#endif prp->pr_domain->dom_family != PF_ROUTE) { return (EPROTONOSUPPORT); } Index: sys/kern/kern_exit.c =================================================================== --- sys/kern/kern_exit.c (revision 187819) +++ sys/kern/kern_exit.c (working copy) @@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -461,6 +462,10 @@ retry: p->p_xstat = rv; p->p_xthread = td; + /* In case we are jailed tell the prison that we are gone. */ + if (jailed(p->p_ucred)) + prison_proc_free(p->p_ucred->cr_prison); + #ifdef KDTRACE_HOOKS /* * Tell the DTrace fasttrap provider about the exit if it Index: sys/kern/kern_fork.c =================================================================== --- sys/kern/kern_fork.c (revision 187819) +++ sys/kern/kern_fork.c (working copy) @@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -442,6 +443,11 @@ again: __rangeof(struct proc, p_startzero, p_endzero)); p2->p_ucred = crhold(td->td_ucred); + + /* In case we are jailed tell the prison that we exist. */ + if (jailed(p2->p_ucred)) + prison_proc_hold(p2->p_ucred->cr_prison); + PROC_UNLOCK(p2); /* Index: sys/kern/kern_cpuset.c =================================================================== --- sys/kern/kern_cpuset.c (revision 187819) +++ sys/kern/kern_cpuset.c (working copy) @@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include /* Must come after sys/proc.h */ #include @@ -208,7 +209,7 @@ cpuset_rel_complete(struct cpuset *set) * Find a set based on an id. Returns it with a ref. */ static struct cpuset * -cpuset_lookup(cpusetid_t setid) +cpuset_lookup(cpusetid_t setid, struct thread *td) { struct cpuset *set; @@ -221,6 +222,28 @@ static struct cpuset * if (set) cpuset_ref(set); mtx_unlock_spin(&cpuset_lock); + + KASSERT(td != NULL, ("[%s:%d] td is NULL", __func__, __LINE__)); + if (set != NULL && jailed(td->td_ucred)) { + struct cpuset *rset, *jset; + struct prison *pr; + + rset = cpuset_refroot(set); + + pr = td->td_ucred->cr_prison; + mtx_lock(&pr->pr_mtx); + cpuset_ref(pr->pr_cpuset); + jset = pr->pr_cpuset; + mtx_unlock(&pr->pr_mtx); + + if (jset->cs_id != rset->cs_id) { + cpuset_rel(set); + set = NULL; + } + cpuset_rel(jset); + cpuset_rel(rset); + } + return (set); } @@ -414,12 +437,38 @@ cpuset_which(cpuwhich_t which, id_t id, struct pro set = cpuset_refbase(curthread->td_cpuset); thread_unlock(curthread); } else - set = cpuset_lookup(id); + set = cpuset_lookup(id, curthread); if (set) { *setp = set; return (0); } return (ESRCH); + case CPU_WHICH_JAIL: + { + /* Find `set' for prison with given id. */ + struct prison *pr; + + sx_slock(&allprison_lock); + pr = prison_find(id); + sx_sunlock(&allprison_lock); + if (pr == NULL) + return (ESRCH); + if (jailed(curthread->td_ucred)) { + if (curthread->td_ucred->cr_prison == pr) { + cpuset_ref(pr->pr_cpuset); + set = pr->pr_cpuset; + } + } else { + cpuset_ref(pr->pr_cpuset); + set = pr->pr_cpuset; + } + mtx_unlock(&pr->pr_mtx); + if (set) { + *setp = set; + return (0); + } + return (ESRCH); + } default: return (EINVAL); } @@ -668,6 +717,59 @@ cpuset_thread0(void) } /* + * Create a cpuset, which would be cpuset_create() but + * mark the new 'set' as root. + * + * We are not going to reparent the td to it. Use cpuset_reparentproc() for that. + * + * In case of no error, returns the set in *setp locked with a reference. + */ +int +cpuset_create_root(struct thread *td, struct cpuset **setp) +{ + struct cpuset *root; + struct cpuset *set; + int error; + + KASSERT(td != NULL, ("[%s:%d] invalid td", __func__, __LINE__)); + KASSERT(setp != NULL, ("[%s:%d] invalid setp", __func__, __LINE__)); + + thread_lock(td); + root = cpuset_refroot(td->td_cpuset); + thread_unlock(td); + + error = cpuset_create(setp, td->td_cpuset, &root->cs_mask); + cpuset_rel(root); + if (error) + return (error); + + KASSERT(*setp != NULL, ("[%s:%d] cpuset_create returned invalid data", + __func__, __LINE__)); + + /* Mark the set as root. */ + set = *setp; + set->cs_flags |= CPU_SET_ROOT; + + return (0); +} + +int +cpuset_setproc_update_set(struct proc *p, struct cpuset *set) +{ + int error; + + KASSERT(p != NULL, ("[%s:%d] invalid proc", __func__, __LINE__)); + KASSERT(set != NULL, ("[%s:%d] invalid set", __func__, __LINE__)); + + cpuset_ref(set); + error = cpuset_setproc(p->p_pid, set, NULL); + if (error) + return (error); + cpuset_rel(set); + return (0); +} + +/* * This is called once the final set of system cpus is known. Modifies * the root set and all children and mark the root readonly. */ @@ -732,7 +834,7 @@ cpuset_setid(struct thread *td, struct cpuset_seti */ if (uap->which != CPU_WHICH_PID) return (EINVAL); - set = cpuset_lookup(uap->setid); + set = cpuset_lookup(uap->setid, td); if (set == NULL) return (ESRCH); error = cpuset_setproc(uap->id, set, NULL); @@ -771,6 +873,7 @@ cpuset_getid(struct thread *td, struct cpuset_geti PROC_UNLOCK(p); break; case CPU_WHICH_CPUSET: + case CPU_WHICH_JAIL: break; } switch (uap->level) { @@ -831,6 +934,7 @@ cpuset_getaffinity(struct thread *td, struct cpuse thread_unlock(ttd); break; case CPU_WHICH_CPUSET: + case CPU_WHICH_JAIL: break; } if (uap->level == CPU_LEVEL_ROOT) @@ -857,6 +961,7 @@ cpuset_getaffinity(struct thread *td, struct cpuse PROC_SUNLOCK(p); break; case CPU_WHICH_CPUSET: + case CPU_WHICH_JAIL: CPU_COPY(&set->cs_mask, mask); break; } @@ -934,6 +1039,7 @@ cpuset_setaffinity(struct thread *td, struct cpuse PROC_UNLOCK(p); break; case CPU_WHICH_CPUSET: + case CPU_WHICH_JAIL: break; } if (uap->level == CPU_LEVEL_ROOT) @@ -953,7 +1059,8 @@ cpuset_setaffinity(struct thread *td, struct cpuse error = cpuset_setproc(uap->id, NULL, mask); break; case CPU_WHICH_CPUSET: - error = cpuset_which(CPU_WHICH_CPUSET, uap->id, &p, + case CPU_WHICH_JAIL: + error = cpuset_which(uap->which, uap->id, &p, &ttd, &set); if (error == 0) { error = cpuset_modify(set, mask); Index: sys/netinet/in.c =================================================================== --- sys/netinet/in.c (revision 187819) +++ sys/netinet/in.c (working copy) @@ -41,7 +41,9 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include +#include #include #include @@ -252,13 +254,19 @@ in_control(struct socket *so, u_long cmd, caddr_t LIST_FOREACH(iap, INADDR_HASH(dst.s_addr), ia_hash) if (iap->ia_ifp == ifp && iap->ia_addr.sin_addr.s_addr == dst.s_addr) { - ia = iap; + if (td == NULL || prison_check_ip4( + td->td_ucred, &dst)) + ia = iap; break; } if (ia == NULL) TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { iap = ifatoia(ifa); if (iap->ia_addr.sin_family == AF_INET) { + if (td != NULL && + !prison_check_ip4(td->td_ucred, + &iap->ia_addr.sin_addr)) + continue; ia = iap; break; } Index: sys/netinet/raw_ip.c =================================================================== --- sys/netinet/raw_ip.c (revision 187819) +++ sys/netinet/raw_ip.c (working copy) @@ -265,10 +265,9 @@ rip_input(struct mbuf *m, int off) continue; if (inp->inp_faddr.s_addr != ip->ip_src.s_addr) continue; - if (jailed(inp->inp_cred) && - (htonl(prison_getip(inp->inp_cred)) != - ip->ip_dst.s_addr)) { - continue; + if (jailed(inp->inp_cred)) { + if (!prison_check_ip4(inp->inp_cred, &ip->ip_dst)) + continue; } if (last) { struct mbuf *n; @@ -296,10 +295,9 @@ rip_input(struct mbuf *m, int off) if (inp->inp_faddr.s_addr && inp->inp_faddr.s_addr != ip->ip_src.s_addr) continue; - if (jailed(inp->inp_cred) && - (htonl(prison_getip(inp->inp_cred)) != - ip->ip_dst.s_addr)) { - continue; + if (jailed(inp->inp_cred)) { + if (!prison_check_ip4(inp->inp_cred, &ip->ip_dst)) + continue; } if (last) { struct mbuf *n; @@ -360,11 +358,15 @@ rip_output(struct mbuf *m, struct socket *so, u_lo ip->ip_off = 0; ip->ip_p = inp->inp_ip_p; ip->ip_len = m->m_pkthdr.len; - if (jailed(inp->inp_cred)) - ip->ip_src.s_addr = - htonl(prison_getip(inp->inp_cred)); - else + if (jailed(inp->inp_cred)) { + if (prison_get_ip4(inp->inp_cred, &ip->ip_src) != 0) { + INP_RUNLOCK(inp); + m_freem(m); + return (EPERM); + } + } else { ip->ip_src = inp->inp_laddr; + } ip->ip_dst.s_addr = dst; ip->ip_ttl = inp->inp_ip_ttl; } else { @@ -374,13 +376,10 @@ rip_output(struct mbuf *m, struct socket *so, u_lo } INP_RLOCK(inp); ip = mtod(m, struct ip *); - if (jailed(inp->inp_cred)) { - if (ip->ip_src.s_addr != - htonl(prison_getip(inp->inp_cred))) { - INP_RUNLOCK(inp); - m_freem(m); - return (EPERM); - } + if (!prison_check_ip4(inp->inp_cred, &ip->ip_src)) { + INP_RUNLOCK(inp); + m_freem(m); + return (EPERM); } /* @@ -788,13 +787,8 @@ rip_bind(struct socket *so, struct sockaddr *nam, if (nam->sa_len != sizeof(*addr)) return (EINVAL); - if (jailed(td->td_ucred)) { - if (addr->sin_addr.s_addr == INADDR_ANY) - addr->sin_addr.s_addr = - htonl(prison_getip(td->td_ucred)); - if (htonl(prison_getip(td->td_ucred)) != addr->sin_addr.s_addr) - return (EADDRNOTAVAIL); - } + if (!prison_check_ip4(td->td_ucred, &addr->sin_addr)) + return (EADDRNOTAVAIL); if (TAILQ_EMPTY(&ifnet) || (addr->sin_family != AF_INET && addr->sin_family != AF_IMPLINK) || Index: sys/netinet/in_pcb.c =================================================================== --- sys/netinet/in_pcb.c (revision 187819) +++ sys/netinet/in_pcb.c (working copy) @@ -279,7 +279,7 @@ in_pcbbind_setup(struct inpcb *inp, struct sockadd struct in_addr laddr; u_short lport = 0; int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); - int error, prison = 0; + int error; int dorandom; /* @@ -308,9 +308,8 @@ in_pcbbind_setup(struct inpcb *inp, struct sockadd if (sin->sin_family != AF_INET) return (EAFNOSUPPORT); #endif - if (sin->sin_addr.s_addr != INADDR_ANY) - if (prison_ip(cred, 0, &sin->sin_addr.s_addr)) - return(EINVAL); + if (prison_local_ip4(cred, &sin->sin_addr)) + return (EINVAL); if (sin->sin_port != *lportp) { /* Don't allow the port to change. */ if (*lportp != 0) @@ -345,14 +344,11 @@ in_pcbbind_setup(struct inpcb *inp, struct sockadd priv_check_cred(cred, PRIV_NETINET_RESERVEDPORT, 0)) return (EACCES); - if (jailed(cred)) - prison = 1; if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) && priv_check_cred(inp->inp_cred, PRIV_NETINET_REUSEPORT, 0) != 0) { t = in_pcblookup_local(pcbinfo, sin->sin_addr, - lport, prison ? 0 : INPLOOKUP_WILDCARD, - cred); + lport, INPLOOKUP_WILDCARD, cred); /* * XXX * This entire block sorely needs a rewrite. @@ -369,10 +365,10 @@ in_pcbbind_setup(struct inpcb *inp, struct sockadd t->inp_cred->cr_uid)) return (EADDRINUSE); } - if (prison && prison_ip(cred, 0, &sin->sin_addr.s_addr)) + if (prison_local_ip4(cred, &sin->sin_addr)) return (EADDRNOTAVAIL); t = in_pcblookup_local(pcbinfo, sin->sin_addr, - lport, prison ? 0 : wild, cred); + lport, wild, cred); if (t && (t->inp_vflag & INP_TIMEWAIT)) { /* * XXXRW: If an incpb has had its timewait @@ -404,9 +400,8 @@ in_pcbbind_setup(struct inpcb *inp, struct sockadd u_short first, last; int count; - if (laddr.s_addr != INADDR_ANY) - if (prison_ip(cred, 0, &laddr.s_addr)) - return (EINVAL); + if (prison_local_ip4(cred, &laddr)) + return (EINVAL); if (inp->inp_flags & INP_HIGHPORT) { first = ipport_hifirstauto; /* sysctl */ @@ -490,7 +485,7 @@ in_pcbbind_setup(struct inpcb *inp, struct sockadd wild, cred)); } } - if (prison_ip(cred, 0, &laddr.s_addr)) + if (prison_local_ip4(cred, &laddr)) return (EINVAL); *laddrp = laddr.s_addr; *lportp = lport; @@ -545,6 +540,211 @@ in_pcbconnect(struct inpcb *inp, struct sockaddr * } /* + * Do proper source address selection on an unbound socket in case + * of connect. Take jails into account as well. + */ +static int +in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr, + struct ucred *cred) +{ + struct in_ifaddr *ia; + struct ifaddr *ifa; + struct sockaddr *sa; + struct sockaddr_in *sin; + struct route sro; + int error; + + KASSERT(laddr != NULL, ("%s: laddr NULL", __func__)); + + error = 0; + ia = NULL; + bzero(&sro, sizeof(sro)); + + sin = (struct sockaddr_in *)&sro.ro_dst; + sin->sin_family = AF_INET; + sin->sin_len = sizeof(struct sockaddr_in); + sin->sin_addr.s_addr = faddr->s_addr; + + /* + * If route is known our src addr is taken from the i/f, + * else punt. + * + * Find out route to destination. + */ + if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0) + in_rtalloc_ign(&sro, RTF_CLONING, inp->inp_inc.inc_fibnum); + + /* + * If we found a route, use the address corresponding to + * the outgoing interface. + * + * Otherwise assume faddr is reachable on a directly connected + * network and try to find a corresponding interface to take + * the source address from. + */ + if (sro.ro_rt == NULL || sro.ro_rt->rt_ifp == NULL) { + struct ifnet *ifp; + + ia = ifatoia(ifa_ifwithdstaddr((struct sockaddr *)sin)); + if (ia == NULL) + ia = ifatoia(ifa_ifwithnet((struct sockaddr *)sin)); + if (ia == NULL) { + error = ENETUNREACH; + goto done; + } + + if (cred == NULL || !jailed(cred)) { + laddr->s_addr = ia->ia_addr.sin_addr.s_addr; + goto done; + } + + ifp = ia->ia_ifp; + ia = NULL; + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + + sa = ifa->ifa_addr; + if (sa->sa_family != AF_INET) + continue; + sin = (struct sockaddr_in *)sa; + if (prison_check_ip4(cred, &sin->sin_addr)) { + ia = (struct in_ifaddr *)ifa; + break; + } + } + if (ia != NULL) { + laddr->s_addr = ia->ia_addr.sin_addr.s_addr; + goto done; + } + + /* 3. As a last resort return the 'default' jail address. */ + if (prison_get_ip4(cred, laddr) != 0) + error = EADDRNOTAVAIL; + goto done; + } + + /* + * If the outgoing interface on the route found is not + * a loopback interface, use the address from that interface. + * In case of jails do those three steps: + * 1. check if the interface address belongs to the jail. If so use it. + * 2. check if we have any address on the outgoing interface + * belonging to this jail. If so use it. + * 3. as a last resort return the 'default' jail address. + */ + if ((sro.ro_rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) { + + /* If not jailed, use the default returned. */ + if (cred == NULL || !jailed(cred)) { + ia = (struct in_ifaddr *)sro.ro_rt->rt_ifa; + laddr->s_addr = ia->ia_addr.sin_addr.s_addr; + goto done; + } + + /* Jailed. */ + /* 1. Check if the iface address belongs to the jail. */ + sin = (struct sockaddr_in *)sro.ro_rt->rt_ifa->ifa_addr; + if (prison_check_ip4(cred, &sin->sin_addr)) { + ia = (struct in_ifaddr *)sro.ro_rt->rt_ifa; + laddr->s_addr = ia->ia_addr.sin_addr.s_addr; + goto done; + } + + /* + * 2. Check if we have any address on the outgoing interface + * belonging to this jail. + */ + TAILQ_FOREACH(ifa, &sro.ro_rt->rt_ifp->if_addrhead, ifa_link) { + + sa = ifa->ifa_addr; + if (sa->sa_family != AF_INET) + continue; + sin = (struct sockaddr_in *)sa; + if (prison_check_ip4(cred, &sin->sin_addr)) { + ia = (struct in_ifaddr *)ifa; + break; + } + } + if (ia != NULL) { + laddr->s_addr = ia->ia_addr.sin_addr.s_addr; + goto done; + } + + /* 3. As a last resort return the 'default' jail address. */ + if (prison_get_ip4(cred, laddr) != 0) + error = EADDRNOTAVAIL; + goto done; + } + + /* + * The outgoing interface is marked with 'loopback net', so a route + * to ourselves is here. + * Try to find the interface of the destination address and then + * take the address from there. That interface is not necessarily + * a loopback interface. + * In case of jails, check that it is an address of the jail + * and if we cannot find, fall back to the 'default' jail address. + */ + if ((sro.ro_rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) { + struct sockaddr_in sain; + + bzero(&sain, sizeof(struct sockaddr_in)); + sain.sin_family = AF_INET; + sain.sin_len = sizeof(struct sockaddr_in); + sain.sin_addr.s_addr = faddr->s_addr; + + ia = ifatoia(ifa_ifwithdstaddr(sintosa(&sain))); + if (ia == NULL) + ia = ifatoia(ifa_ifwithnet(sintosa(&sain))); + + if (cred == NULL || !jailed(cred)) { +#if __FreeBSD_version < 800000 + if (ia == NULL) + ia = (struct in_ifaddr *)sro.ro_rt->rt_ifa; +#endif + if (ia == NULL) { + error = ENETUNREACH; + goto done; + } + laddr->s_addr = ia->ia_addr.sin_addr.s_addr; + goto done; + } + + /* Jailed. */ + if (ia != NULL) { + struct ifnet *ifp; + + ifp = ia->ia_ifp; + ia = NULL; + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + + sa = ifa->ifa_addr; + if (sa->sa_family != AF_INET) + continue; + sin = (struct sockaddr_in *)sa; + if (prison_check_ip4(cred, &sin->sin_addr)) { + ia = (struct in_ifaddr *)ifa; + break; + } + } + if (ia != NULL) { + laddr->s_addr = ia->ia_addr.sin_addr.s_addr; + goto done; + } + } + + /* 3. As a last resort return the 'default' jail address. */ + if (prison_get_ip4(cred, laddr) != 0) + error = EADDRNOTAVAIL; + goto done; + } + +done: + if (sro.ro_rt != NULL) + RTFREE(sro.ro_rt); + return (error); +} + +/* * Set up for a connect from a socket to the specified address. * On entry, *laddrp and *lportp should contain the current local * address and port for the PCB; these are updated to the values @@ -566,10 +766,8 @@ in_pcbconnect_setup(struct inpcb *inp, struct sock { struct sockaddr_in *sin = (struct sockaddr_in *)nam; struct in_ifaddr *ia; - struct sockaddr_in sa; - struct ucred *socred; struct inpcb *oinp; - struct in_addr laddr, faddr; + struct in_addr laddr, faddr, jailia; u_short lport, fport; int error; @@ -592,17 +790,7 @@ in_pcbconnect_setup(struct inpcb *inp, struct sock lport = *lportp; faddr = sin->sin_addr; fport = sin->sin_port; - socred = inp->inp_socket->so_cred; - if (laddr.s_addr == INADDR_ANY && jailed(socred)) { - bzero(&sa, sizeof(sa)); - sa.sin_addr.s_addr = htonl(prison_getip(socred)); - sa.sin_len = sizeof(sa); - sa.sin_family = AF_INET; - error = in_pcbbind_setup(inp, (struct sockaddr *)&sa, - &laddr.s_addr, &lport, cred); - if (error) - return (error); - } + if (!TAILQ_EMPTY(&in_ifaddrhead)) { /* * If the destination address is INADDR_ANY, @@ -611,44 +799,27 @@ in_pcbconnect_setup(struct inpcb *inp, struct sock * and the primary interface supports broadcast, * choose the broadcast address for that interface. */ - if (faddr.s_addr == INADDR_ANY) - faddr = IA_SIN(TAILQ_FIRST(&in_ifaddrhead))->sin_addr; - else if (faddr.s_addr == (u_long)INADDR_BROADCAST && + if (faddr.s_addr == INADDR_ANY) { + if (cred != NULL && jailed(cred)) { + if (prison_get_ip4(cred, &jailia) != 0) + return (EADDRNOTAVAIL); + faddr.s_addr = jailia.s_addr; + } else { + faddr = + IA_SIN(TAILQ_FIRST(&in_ifaddrhead))-> + sin_addr; + } + } else if (faddr.s_addr == (u_long)INADDR_BROADCAST && (TAILQ_FIRST(&in_ifaddrhead)->ia_ifp->if_flags & IFF_BROADCAST)) faddr = satosin(&TAILQ_FIRST( &in_ifaddrhead)->ia_broadaddr)->sin_addr; } if (laddr.s_addr == INADDR_ANY) { - ia = NULL; - /* - * If route is known our src addr is taken from the i/f, - * else punt. - * - * Find out route to destination - */ - if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0) - ia = ip_rtaddr(faddr, inp->inp_inc.inc_fibnum); - /* - * If we found a route, use the address corresponding to - * the outgoing interface. - * - * Otherwise assume faddr is reachable on a directly connected - * network and try to find a corresponding interface to take - * the source address from. - */ - if (ia == NULL) { - bzero(&sa, sizeof(sa)); - sa.sin_addr = faddr; - sa.sin_len = sizeof(sa); - sa.sin_family = AF_INET; + error = in_pcbladdr(inp, &faddr, &laddr, cred); + if (error) + return (error); - ia = ifatoia(ifa_ifwithdstaddr(sintosa(&sa))); - if (ia == NULL) - ia = ifatoia(ifa_ifwithnet(sintosa(&sa))); - if (ia == NULL) - return (ENETUNREACH); - } /* * If the destination address is multicast and an outgoing * interface has been set as a multicast option, use the @@ -667,9 +838,9 @@ in_pcbconnect_setup(struct inpcb *inp, struct sock break; if (ia == NULL) return (EADDRNOTAVAIL); + laddr = ia->ia_addr.sin_addr; } } - laddr = ia->ia_addr.sin_addr; } oinp = in_pcblookup_hash(inp->inp_pcbinfo, faddr, fport, laddr, lport, @@ -948,6 +1119,7 @@ in_pcblookup_local(struct inpcbinfo *pcbinfo, stru 0, pcbinfo->ipi_hashmask)]; LIST_FOREACH(inp, head, inp_hash) { #ifdef INET6 + /* XXX inp locking */ if ((inp->inp_vflag & INP_IPV4) == 0) continue; #endif @@ -955,9 +1127,11 @@ in_pcblookup_local(struct inpcbinfo *pcbinfo, stru inp->inp_laddr.s_addr == laddr.s_addr && inp->inp_lport == lport) { /* - * Found. + * Found? */ - return (inp); + if (cred == NULL || + inp->inp_cred->cr_prison == cred->cr_prison) + return (inp); } } /* @@ -987,7 +1161,11 @@ in_pcblookup_local(struct inpcbinfo *pcbinfo, stru */ LIST_FOREACH(inp, &phd->phd_pcblist, inp_portlist) { wildcard = 0; + if (cred != NULL && + inp->inp_cred->cr_prison != cred->cr_prison) + continue; #ifdef INET6 + /* XXX inp locking */ if ((inp->inp_vflag & INP_IPV4) == 0) continue; /* @@ -1020,9 +1198,8 @@ in_pcblookup_local(struct inpcbinfo *pcbinfo, stru if (wildcard < matchwild) { match = inp; matchwild = wildcard; - if (matchwild == 0) { + if (matchwild == 0) break; - } } } } @@ -1040,7 +1217,7 @@ in_pcblookup_hash(struct inpcbinfo *pcbinfo, struc struct ifnet *ifp) { struct inpcbhead *head; - struct inpcb *inp; + struct inpcb *inp, *tmpinp; u_short fport = fport_arg, lport = lport_arg; INP_INFO_LOCK_ASSERT(pcbinfo); @@ -1048,60 +1225,108 @@ in_pcblookup_hash(struct inpcbinfo *pcbinfo, struc /* * First look for an exact match. */ + tmpinp = NULL; head = &pcbinfo->ipi_hashbase[INP_PCBHASH(faddr.s_addr, lport, fport, pcbinfo->ipi_hashmask)]; LIST_FOREACH(inp, head, inp_hash) { #ifdef INET6 + /* XXX inp locking */ if ((inp->inp_vflag & INP_IPV4) == 0) continue; #endif if (inp->inp_faddr.s_addr == faddr.s_addr && inp->inp_laddr.s_addr == laddr.s_addr && inp->inp_fport == fport && - inp->inp_lport == lport) - return (inp); + inp->inp_lport == lport) { + /* + * XXX We should be able to directly return + * the inp here, without any checks. + * Well unless both bound with SO_REUSEPORT? + */ + if (jailed(inp->inp_cred)) + return (inp); + if (tmpinp == NULL) + tmpinp = inp; + } } + if (tmpinp != NULL) + return (tmpinp); /* * Then look for a wildcard match, if requested. */ - if (wildcard) { - struct inpcb *local_wild = NULL; + if (wildcard == INPLOOKUP_WILDCARD) { + struct inpcb *local_wild = NULL, *local_exact = NULL; #ifdef INET6 struct inpcb *local_wild_mapped = NULL; #endif + struct inpcb *jail_wild = NULL; + int injail; + /* + * Order of socket selection - we always prefer jails. + * 1. jailed, non-wild. + * 2. jailed, wild. + * 3. non-jailed, non-wild. + * 4. non-jailed, wild. + */ + head = &pcbinfo->ipi_hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->ipi_hashmask)]; LIST_FOREACH(inp, head, inp_hash) { #ifdef INET6 + /* XXX inp locking */ if ((inp->inp_vflag & INP_IPV4) == 0) continue; #endif - if (inp->inp_faddr.s_addr == INADDR_ANY && - inp->inp_lport == lport) { - if (ifp && ifp->if_type == IFT_FAITH && - (inp->inp_flags & INP_FAITH) == 0) + if (inp->inp_faddr.s_addr != INADDR_ANY || + inp->inp_lport != lport) + continue; + + /* XXX inp locking */ + if (ifp && ifp->if_type == IFT_FAITH && + (inp->inp_flags & INP_FAITH) == 0) + continue; + + injail = jailed(inp->inp_cred); + if (injail) { + if (!prison_check_ip4(inp->inp_cred, &laddr)) continue; - if (inp->inp_laddr.s_addr == laddr.s_addr) + } else { + if (local_exact != NULL) + continue; + } + + if (inp->inp_laddr.s_addr == laddr.s_addr) { + if (injail) return (inp); - else if (inp->inp_laddr.s_addr == INADDR_ANY) { + else + local_exact = inp; + } else if (inp->inp_laddr.s_addr == INADDR_ANY) { #ifdef INET6 - if (INP_CHECK_SOCKAF(inp->inp_socket, - AF_INET6)) - local_wild_mapped = inp; + /* XXX inp locking, NULL check */ + if (inp->inp_vflag & INP_IPV6PROTO) + local_wild_mapped = inp; + else +#endif /* INET6 */ + if (injail) + jail_wild = inp; else -#endif local_wild = inp; - } } - } + } /* LIST_FOREACH */ + if (jail_wild != NULL) + return (jail_wild); + if (local_exact != NULL) + return (local_exact); + if (local_wild != NULL) + return (local_wild); #ifdef INET6 - if (local_wild == NULL) + if (local_wild_mapped != NULL) return (local_wild_mapped); -#endif - return (local_wild); - } +#endif /* defined(INET6) */ + } /* if (wildcard == INPLOOKUP_WILDCARD) */ + return (NULL); } Index: sys/netinet/sctp_usrreq.c =================================================================== --- sys/netinet/sctp_usrreq.c (revision 187819) +++ sys/netinet/sctp_usrreq.c (working copy) @@ -507,11 +507,10 @@ sctp_attach(struct socket *so, int proto, struct t struct inpcb *ip_inp; int error; uint32_t vrf_id = SCTP_DEFAULT_VRFID; - #ifdef IPSEC uint32_t flags; +#endif -#endif inp = (struct sctp_inpcb *)so->so_pcb; if (inp != 0) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); @@ -3949,12 +3948,8 @@ sctp_setopt(struct socket *so, int optname, void * struct sctp_getaddresses *addrs; size_t sz; struct thread *td; - int prison = 0; td = (struct thread *)p; - if (jailed(td->td_ucred)) { - prison = 1; - } SCTP_CHECK_AND_CAST(addrs, optval, struct sctp_getaddresses, optsize); if (addrs->addr->sa_family == AF_INET) { @@ -3964,10 +3959,12 @@ sctp_setopt(struct socket *so, int optname, void * error = EINVAL; break; } - if (prison && prison_ip(td->td_ucred, 0, &(((struct sockaddr_in *)(addrs->addr))->sin_addr.s_addr))) { + if (td != NULL && prison_local_ip4(td->td_ucred, &(((struct sockaddr_in *)(addrs->addr))->sin_addr))) { SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_USRREQ, EADDRNOTAVAIL); error = EADDRNOTAVAIL; + break; } +#ifdef INET6 } else if (addrs->addr->sa_family == AF_INET6) { sz = sizeof(struct sctp_getaddresses) - sizeof(struct sockaddr) + sizeof(struct sockaddr_in6); if (optsize < sz) { @@ -3975,7 +3972,16 @@ sctp_setopt(struct socket *so, int optname, void * error = EINVAL; break; } - /* JAIL XXXX Add else here for V6 */ + if (td != NULL && prison_local_ip6(td->td_ucred, &(((struct sockaddr_in6 *)(addrs->addr))->sin6_addr), + (SCTP_IPV6_V6ONLY(inp) != 0)) != 0) { + SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_USRREQ, EADDRNOTAVAIL); + error = EADDRNOTAVAIL; + break; + } +#endif + } else { + error = EAFNOSUPPORT; + break; } sctp_bindx_add_address(so, inp, addrs->addr, addrs->sget_assoc_id, vrf_id, @@ -3987,12 +3993,9 @@ sctp_setopt(struct socket *so, int optname, void * struct sctp_getaddresses *addrs; size_t sz; struct thread *td; - int prison = 0; td = (struct thread *)p; - if (jailed(td->td_ucred)) { - prison = 1; - } + SCTP_CHECK_AND_CAST(addrs, optval, struct sctp_getaddresses, optsize); if (addrs->addr->sa_family == AF_INET) { sz = sizeof(struct sctp_getaddresses) - sizeof(struct sockaddr) + sizeof(struct sockaddr_in); @@ -4001,10 +4004,12 @@ sctp_setopt(struct socket *so, int optname, void * error = EINVAL; break; } - if (prison && prison_ip(td->td_ucred, 0, &(((struct sockaddr_in *)(addrs->addr))->sin_addr.s_addr))) { + if (td != NULL && prison_local_ip4(td->td_ucred, &(((struct sockaddr_in *)(addrs->addr))->sin_addr))) { SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_USRREQ, EADDRNOTAVAIL); error = EADDRNOTAVAIL; + break; } +#ifdef INET6 } else if (addrs->addr->sa_family == AF_INET6) { sz = sizeof(struct sctp_getaddresses) - sizeof(struct sockaddr) + sizeof(struct sockaddr_in6); if (optsize < sz) { @@ -4012,7 +4017,16 @@ sctp_setopt(struct socket *so, int optname, void * error = EINVAL; break; } - /* JAIL XXXX Add else here for V6 */ + if (td != NULL && prison_local_ip6(td->td_ucred, &(((struct sockaddr_in6 *)(addrs->addr))->sin6_addr), + (SCTP_IPV6_V6ONLY(inp) != 0)) != 0) { + SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_USRREQ, EADDRNOTAVAIL); + error = EADDRNOTAVAIL; + break; + } +#endif + } else { + error = EAFNOSUPPORT; + break; } sctp_bindx_delete_address(so, inp, addrs->addr, addrs->sget_assoc_id, vrf_id, @@ -4104,14 +4118,35 @@ sctp_connect(struct socket *so, struct sockaddr *a SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); return EINVAL; } - if ((addr->sa_family == AF_INET6) && (addr->sa_len != sizeof(struct sockaddr_in6))) { - SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); - return (EINVAL); +#ifdef INET6 + if (addr->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6p; + if (addr->sa_len != sizeof(struct sockaddr_in6)) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); + return (EINVAL); + } + sin6p = (struct sockaddr_in6 *)addr; + if (p != NULL && prison_remote_ip6(p->td_ucred, &sin6p->sin6_addr) != 0) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); + return (EINVAL); + } + } else +#endif + if (addr->sa_family == AF_INET) { + struct sockaddr_in *sinp; + if (addr->sa_len != sizeof(struct sockaddr_in)) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); + return (EINVAL); + } + sinp = (struct sockaddr_in *)addr; + if (p != NULL && prison_remote_ip4(p->td_ucred, &sinp->sin_addr) != 0) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); + return (EINVAL); + } + } else { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EAFNOSUPPORT); + return (EAFNOSUPPORT); } - if ((addr->sa_family == AF_INET) && (addr->sa_len != sizeof(struct sockaddr_in))) { - SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); - return (EINVAL); - } SCTP_INP_INCR_REF(inp); SCTP_ASOC_CREATE_LOCK(inp); create_lock_on = 1; Index: sys/netinet/tcp_usrreq.c =================================================================== --- sys/netinet/tcp_usrreq.c (revision 187819) +++ sys/netinet/tcp_usrreq.c (working copy) @@ -462,8 +462,8 @@ tcp_usr_connect(struct socket *so, struct sockaddr if (sinp->sin_family == AF_INET && IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) return (EAFNOSUPPORT); - if (jailed(td->td_ucred)) - prison_remote_ip(td->td_ucred, 0, &sinp->sin_addr.s_addr); + if (prison_remote_ip4(td->td_ucred, &sinp->sin_addr) != 0) + return (EINVAL); TCPDEBUG0; INP_INFO_WLOCK(&tcbinfo); @@ -528,6 +528,10 @@ tcp6_usr_connect(struct socket *so, struct sockadd in6_sin6_2_sin(&sin, sin6p); inp->inp_vflag |= INP_IPV4; inp->inp_vflag &= ~INP_IPV6; + if (prison_remote_ip4(td->td_ucred, &sin.sin_addr) != 0) { + error = EINVAL; + goto out; + } if ((error = tcp_connect(tp, (struct sockaddr *)&sin, td)) != 0) goto out; error = tcp_output_connect(so, nam); @@ -536,6 +540,10 @@ tcp6_usr_connect(struct socket *so, struct sockadd inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; inp->inp_inc.inc_isipv6 = 1; + if (prison_remote_ip6(td->td_ucred, &sin6p->sin6_addr) != 0) { + error = EINVAL; + goto out; + } if ((error = tcp6_connect(tp, nam, td)) != 0) goto out; error = tcp_output_connect(so, nam); Index: sys/netinet/udp_usrreq.c =================================================================== --- sys/netinet/udp_usrreq.c (revision 187819) +++ sys/netinet/udp_usrreq.c (working copy) @@ -938,9 +938,10 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru * Jail may rewrite the destination address, so let it do * that before we use it. */ - if (jailed(td->td_ucred)) - prison_remote_ip(td->td_ucred, 0, - &sin->sin_addr.s_addr); + if (prison_remote_ip4(td->td_ucred, &sin->sin_addr) != 0) { + error = EINVAL; + goto release; + } /* * If a local address or port hasn't yet been selected, or if @@ -1187,8 +1188,11 @@ udp_connect(struct socket *so, struct sockaddr *na return (EISCONN); } sin = (struct sockaddr_in *)nam; - if (jailed(td->td_ucred)) - prison_remote_ip(td->td_ucred, 0, &sin->sin_addr.s_addr); + if (prison_remote_ip4(td->td_ucred, &sin->sin_addr) != 0) { + INP_WUNLOCK(inp); + INP_INFO_WUNLOCK(&udbinfo); + return (EAFNOSUPPORT); + } error = in_pcbconnect(inp, nam, td->td_ucred); if (error == 0) soisconnected(so); Index: sys/netinet/sctp_pcb.c =================================================================== --- sys/netinet/sctp_pcb.c (revision 187819) +++ sys/netinet/sctp_pcb.c (working copy) @@ -2553,7 +2553,6 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr struct inpcb *ip_inp; int port_reuse_active = 0; int bindall; - int prison = 0; uint16_t lport; int error; uint32_t vrf_id; @@ -2580,9 +2579,6 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr if (p == NULL) panic("null proc/thread"); #endif - if (p && jailed(p->td_ucred)) { - prison = 1; - } if (addr != NULL) { switch (addr->sa_family) { case AF_INET: @@ -2600,18 +2596,13 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr } sin = (struct sockaddr_in *)addr; lport = sin->sin_port; - if (prison) { - /* - * For INADDR_ANY and LOOPBACK the - * prison_ip() call will transmute - * the ip address to the proper - * value (i.e. the IP address owned - * by the jail). - */ - if (prison_ip(p->td_ucred, 0, &sin->sin_addr.s_addr)) { - SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL); - return (EINVAL); - } + /* + * For LOOPBACK the prison_local_ip4() call will transmute the ip address + * to the proper value. + */ + if (p && prison_local_ip4(p->td_ucred, &sin->sin_addr) != 0) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL); + return (EINVAL); } if (sin->sin_addr.s_addr != INADDR_ANY) { bindall = 0; @@ -2634,12 +2625,16 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr return (EINVAL); } lport = sin6->sin6_port; + /* - * Jail checks for IPv6 should go HERE! i.e. - * add the prison_ip() equivilant in this - * postion to transmute the addresses to the - * proper one jailed. + * For LOOPBACK the prison_local_ip6() call will transmute the ipv6 address + * to the proper value. */ + if (p && prison_local_ip6(p->td_ucred, &sin6->sin6_addr, + (SCTP_IPV6_V6ONLY(inp) != 0)) != 0) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL); + return (EINVAL); + } if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { bindall = 0; /* KAME hack: embed scopeid */ Property changes on: sys/dev/cxgb ___________________________________________________________________ Modified: svn:mergeinfo Merged /head/sys/dev/cxgb:r183571,185404,185435-185436,185441,185899,186086,186606,186948,186956,186980,186986,187684 Property changes on: sys/dev/ath/ath_hal ___________________________________________________________________ Modified: svn:mergeinfo Merged /head/sys/dev/ath/ath_hal:r183571,185404,185435-185436,185441,186948,186956,186980,186986,187684 Index: sys/compat/freebsd32/freebsd32_syscall.h =================================================================== --- sys/compat/freebsd32/freebsd32_syscall.h (revision 187819) +++ sys/compat/freebsd32/freebsd32_syscall.h (working copy) @@ -264,7 +264,7 @@ #define FREEBSD32_SYS_utrace 335 #define FREEBSD32_SYS_freebsd4_freebsd32_sendfile 336 #define FREEBSD32_SYS_kldsym 337 -#define FREEBSD32_SYS_jail 338 +#define FREEBSD32_SYS_freebsd32_jail 338 #define FREEBSD32_SYS_sigprocmask 340 #define FREEBSD32_SYS_sigsuspend 341 #define FREEBSD32_SYS_freebsd4_freebsd32_sigaction 342 Index: sys/compat/freebsd32/freebsd32.h =================================================================== --- sys/compat/freebsd32/freebsd32.h (revision 187819) +++ sys/compat/freebsd32/freebsd32.h (working copy) @@ -153,6 +153,24 @@ struct stat32 { unsigned int :(8 / 2) * (16 - (int)sizeof(struct timespec32)); }; +struct jail32_v0 { + u_int32_t version; + uint32_t path; + uint32_t hostname; + u_int32_t ip_number; +}; + +struct jail32 { + uint32_t version; + uint32_t path; + uint32_t hostname; + uint32_t jailname; + uint32_t ip4s; + uint32_t ip6s; + uint32_t ip4; + uint32_t ip6; +}; + struct sigaction32 { u_int32_t sa_u; int sa_flags; Index: sys/compat/freebsd32/freebsd32_sysent.c =================================================================== --- sys/compat/freebsd32/freebsd32_sysent.c (revision 187819) +++ sys/compat/freebsd32/freebsd32_sysent.c (working copy) @@ -376,7 +376,7 @@ struct sysent freebsd32_sysent[] = { { AS(utrace_args), (sy_call_t *)utrace, AUE_NULL, NULL, 0, 0 }, /* 335 = utrace */ { compat4(AS(freebsd4_freebsd32_sendfile_args),freebsd32_sendfile), AUE_SENDFILE, NULL, 0, 0 }, /* 336 = old freebsd32_sendfile */ { AS(kldsym_args), (sy_call_t *)kldsym, AUE_NULL, NULL, 0, 0 }, /* 337 = kldsym */ - { AS(jail_args), (sy_call_t *)jail, AUE_JAIL, NULL, 0, 0 }, /* 338 = jail */ + { AS(freebsd32_jail_args), (sy_call_t *)freebsd32_jail, AUE_JAIL, NULL, 0, 0 }, /* 338 = freebsd32_jail */ { 0, (sy_call_t *)nosys, AUE_NULL, NULL, 0, 0 }, /* 339 = pioctl */ { AS(sigprocmask_args), (sy_call_t *)sigprocmask, AUE_SIGPROCMASK, NULL, 0, 0 }, /* 340 = sigprocmask */ { AS(sigsuspend_args), (sy_call_t *)sigsuspend, AUE_SIGSUSPEND, NULL, 0, 0 }, /* 341 = sigsuspend */ Index: sys/compat/freebsd32/syscalls.master =================================================================== --- sys/compat/freebsd32/syscalls.master (revision 187819) +++ sys/compat/freebsd32/syscalls.master (working copy) @@ -587,7 +587,7 @@ off_t *sbytes, int flags); } 337 AUE_NULL NOPROTO { int kldsym(int fileid, int cmd, \ void *data); } -338 AUE_JAIL NOPROTO { int jail(struct jail *jail); } +338 AUE_JAIL STD { int freebsd32_jail(struct jail32 *jail); } 339 AUE_NULL UNIMPL pioctl 340 AUE_SIGPROCMASK NOPROTO { int sigprocmask(int how, \ const sigset_t *set, sigset_t *oset); } Index: sys/compat/freebsd32/freebsd32_proto.h =================================================================== --- sys/compat/freebsd32/freebsd32_proto.h (revision 187819) +++ sys/compat/freebsd32/freebsd32_proto.h (working copy) @@ -274,6 +274,9 @@ struct freebsd32_olio_listio_args { char nent_l_[PADL_(int)]; int nent; char nent_r_[PADR_(int)]; char sig_l_[PADL_(struct osigevent32 *)]; struct osigevent32 * sig; char sig_r_[PADR_(struct osigevent32 *)]; }; +struct freebsd32_jail_args { + char jail_l_[PADL_(struct jail32 *)]; struct jail32 * jail; char jail_r_[PADR_(struct jail32 *)]; +}; struct freebsd32_sigtimedwait_args { char set_l_[PADL_(const sigset_t *)]; const sigset_t * set; char set_r_[PADR_(const sigset_t *)]; char info_l_[PADL_(siginfo_t *)]; siginfo_t * info; char info_r_[PADR_(siginfo_t *)]; @@ -469,6 +472,7 @@ int freebsd32_aio_error(struct thread *, struct fr int freebsd32_oaio_read(struct thread *, struct freebsd32_oaio_read_args *); int freebsd32_oaio_write(struct thread *, struct freebsd32_oaio_write_args *); int freebsd32_olio_listio(struct thread *, struct freebsd32_olio_listio_args *); +int freebsd32_jail(struct thread *, struct freebsd32_jail_args *); int freebsd32_sigtimedwait(struct thread *, struct freebsd32_sigtimedwait_args *); int freebsd32_sigwaitinfo(struct thread *, struct freebsd32_sigwaitinfo_args *); int freebsd32_aio_waitcomplete(struct thread *, struct freebsd32_aio_waitcomplete_args *); @@ -691,6 +695,7 @@ int freebsd6_freebsd32_ftruncate(struct thread *, #define FREEBSD32_SYS_AUE_freebsd32_oaio_read AUE_NULL #define FREEBSD32_SYS_AUE_freebsd32_oaio_write AUE_NULL #define FREEBSD32_SYS_AUE_freebsd32_olio_listio AUE_NULL +#define FREEBSD32_SYS_AUE_freebsd32_jail AUE_JAIL #define FREEBSD32_SYS_AUE_freebsd32_sigtimedwait AUE_SIGWAIT #define FREEBSD32_SYS_AUE_freebsd32_sigwaitinfo AUE_NULL #define FREEBSD32_SYS_AUE_freebsd32_aio_waitcomplete AUE_NULL Index: sys/compat/freebsd32/freebsd32_misc.c =================================================================== --- sys/compat/freebsd32/freebsd32_misc.c (revision 187819) +++ sys/compat/freebsd32/freebsd32_misc.c (working copy) @@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -1983,6 +1984,66 @@ done2: } int +freebsd32_jail(struct thread *td, struct freebsd32_jail_args *uap) +{ + uint32_t version; + int error; + struct jail j; + + error = copyin(uap->jail, &version, sizeof(uint32_t)); + if (error) + return (error); + switch (version) { + case 0: + { + /* FreeBSD single IPv4 jails. */ + struct jail32_v0 j32_v0; + + bzero(&j, sizeof(struct jail)); + error = copyin(uap->jail, &j32_v0, sizeof(struct jail32_v0)); + if (error) + return (error); + CP(j32_v0, j, version); + PTRIN_CP(j32_v0, j, path); + PTRIN_CP(j32_v0, j, hostname); + j.ip4s = j32_v0.ip_number; + break; + } + + case 1: + /* + * Version 1 was used by multi-IPv4 jail implementations + * that never made it into the official kernel. + */ + return (EINVAL); + + case 2: /* JAIL_API_VERSION */ + { + /* FreeBSD multi-IPv4/IPv6,noIP jails. */ + struct jail32 j32; + + error = copyin(uap->jail, &j32, sizeof(struct jail32)); + if (error) + return (error); + CP(j32, j, version); + PTRIN_CP(j32, j, path); + PTRIN_CP(j32, j, hostname); + PTRIN_CP(j32, j, jailname); + CP(j32, j, ip4s); + CP(j32, j, ip6s); + PTRIN_CP(j32, j, ip4); + PTRIN_CP(j32, j, ip6); + break; + } + + default: + /* Sci-Fi jails are not supported, sorry. */ + return (EINVAL); + } + return (kern_jail(td, &j)); +} + +int freebsd32_sigaction(struct thread *td, struct freebsd32_sigaction_args *uap) { struct sigaction32 s32; Index: sys/compat/freebsd32/freebsd32_syscalls.c =================================================================== --- sys/compat/freebsd32/freebsd32_syscalls.c (revision 187819) +++ sys/compat/freebsd32/freebsd32_syscalls.c (working copy) @@ -345,7 +345,7 @@ const char *freebsd32_syscallnames[] = { "utrace", /* 335 = utrace */ "compat4.freebsd32_sendfile", /* 336 = old freebsd32_sendfile */ "kldsym", /* 337 = kldsym */ - "jail", /* 338 = jail */ + "freebsd32_jail", /* 338 = freebsd32_jail */ "#339", /* 339 = pioctl */ "sigprocmask", /* 340 = sigprocmask */ "sigsuspend", /* 341 = sigsuspend */ Index: sys/net/if.c =================================================================== --- sys/net/if.c (revision 187819) +++ sys/net/if.c (working copy) @@ -2181,7 +2181,7 @@ again: struct sockaddr *sa = ifa->ifa_addr; if (jailed(curthread->td_ucred) && - prison_if(curthread->td_ucred, sa)) + !prison_if(curthread->td_ucred, sa)) continue; addrs++; #ifdef COMPAT_43 Index: sys/net/rtsock.c =================================================================== --- sys/net/rtsock.c (revision 187819) +++ sys/net/rtsock.c (working copy) @@ -30,10 +30,13 @@ * $FreeBSD$ */ #include "opt_sctp.h" +#include "opt_inet.h" +#include "opt_inet6.h" + #include #include +#include #include -#include #include #include #include @@ -51,6 +54,9 @@ #include #include +#ifdef INET6 +#include +#endif #ifdef SCTP extern void sctp_addr_change(struct ifaddr *ifa, int cmd); @@ -306,6 +312,136 @@ static struct pr_usrreqs route_usrreqs = { .pru_close = rts_close, }; +#ifndef _SOCKADDR_UNION_DEFINED +#define _SOCKADDR_UNION_DEFINED +/* + * The union of all possible address formats we handle. + */ +union sockaddr_union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; +}; +#endif /* _SOCKADDR_UNION_DEFINED */ + +static int +rtm_get_jailed(struct rt_addrinfo *info, struct ifnet *ifp, + struct rtentry *rt, union sockaddr_union *saun, struct ucred *cred) +{ + + switch (info->rti_info[RTAX_DST]->sa_family) { +#ifdef INET + case AF_INET: + { + struct in_addr ia; + + /* + * 1. Check if the returned address is part of the jail. + */ + ia = ((struct sockaddr_in *)rt->rt_ifa->ifa_addr)->sin_addr; + if (prison_check_ip4(cred, &ia) != 0) { + info->rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr; + + } else { + struct ifaddr *ifa; + int found; + + found = 0; + + /* + * 2. Try to find an address on the given outgoing + * interface that belongs to the jail. + */ + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + struct sockaddr *sa; + sa = ifa->ifa_addr; + if (sa->sa_family != AF_INET) + continue; + ia = ((struct sockaddr_in *)sa)->sin_addr; + if (prison_check_ip4(cred, &ia) != 0) { + found = 1; + break; + } + } + if (!found) { + /* + * 3. As a last resort return the 'default' + * jail address. + */ + if (prison_get_ip4(cred, &ia) != 0) + return (ESRCH); + } + bzero(&saun->sin, sizeof(struct sockaddr_in)); + saun->sin.sin_len = sizeof(struct sockaddr_in); + saun->sin.sin_family = AF_INET; + saun->sin.sin_addr.s_addr = ia.s_addr; + info->rti_info[RTAX_IFA] = + (struct sockaddr *)&saun->sin; + } + break; + } +#endif +#ifdef INET6 + case AF_INET6: + { + struct in6_addr ia6; + + /* + * 1. Check if the returned address is part of the jail. + */ + bcopy(&((struct sockaddr_in6 *)rt->rt_ifa->ifa_addr)->sin6_addr, + &ia6, sizeof(struct in6_addr)); + if (prison_check_ip6(cred, &ia6) != 0) { + info->rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr; + } else { + struct ifaddr *ifa; + int found; + + found = 0; + + /* + * 2. Try to find an address on the given outgoing + * interface that belongs to the jail. + */ + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + struct sockaddr *sa; + sa = ifa->ifa_addr; + if (sa->sa_family != AF_INET6) + continue; + bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr, + &ia6, sizeof(struct in6_addr)); + if (prison_check_ip6(cred, &ia6) != 0) { + found = 1; + break; + } + } + if (!found) { + /* + * 3. As a last resort return the 'default' + * jail address. + */ + if (prison_get_ip6(cred, &ia6) != 0) + return (ESRCH); + } + bzero(&saun->sin6, sizeof(struct sockaddr_in6)); + saun->sin6.sin6_len = sizeof(struct sockaddr_in6); + saun->sin6.sin6_family = AF_INET6; + bcopy(&ia6, &saun->sin6.sin6_addr, + sizeof(struct in6_addr)); + if (sa6_recoverscope(&saun->sin6) != 0) + return (ESRCH); + info->rti_info[RTAX_IFA] = + (struct sockaddr *)&saun->sin6; + } + break; + } +#endif + default: + return (ESRCH); + } + return (0); +} + /*ARGSUSED*/ static int route_output(struct mbuf *m, struct socket *so) @@ -317,7 +453,7 @@ route_output(struct mbuf *m, struct socket *so) struct rt_addrinfo info; int len, error = 0; struct ifnet *ifp = NULL; - struct sockaddr_in jail; + union sockaddr_union saun; #define senderr(e) { error = e; goto flush;} if (m == NULL || ((m->m_len < sizeof(long)) && @@ -449,6 +585,12 @@ route_output(struct mbuf *m, struct socket *so) case RTM_GET: report: RT_LOCK_ASSERT(rt); + if (jailed(curthread->td_ucred) && + ((rt->rt_flags & RTF_HOST) == 0 || + !prison_if(curthread->td_ucred, rt_key(rt)))) { + RT_UNLOCK(rt); + senderr(ESRCH); + } info.rti_info[RTAX_DST] = rt_key(rt); info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; info.rti_info[RTAX_NETMASK] = rt_mask(rt); @@ -458,17 +600,18 @@ route_output(struct mbuf *m, struct socket *so) if (ifp) { info.rti_info[RTAX_IFP] = ifp->if_addr->ifa_addr; - if (jailed(so->so_cred)) { - bzero(&jail, sizeof(jail)); - jail.sin_family = PF_INET; - jail.sin_len = sizeof(jail); - jail.sin_addr.s_addr = - htonl(prison_getip(so->so_cred)); + if (jailed(curthread->td_ucred)) { + error = rtm_get_jailed( + &info, ifp, rt, &saun, + curthread->td_ucred); + if (error != 0) { + RT_UNLOCK(rt); + senderr(ESRCH); + } + } else { info.rti_info[RTAX_IFA] = - (struct sockaddr *)&jail; - } else - info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr; + } if (ifp->if_flags & IFF_POINTOPOINT) info.rti_info[RTAX_BRD] = rt->rt_ifa->ifa_dstaddr; @@ -1087,6 +1230,10 @@ sysctl_dumpentry(struct radix_node *rn, void *vw) if (w->w_op == NET_RT_FLAGS && !(rt->rt_flags & w->w_arg)) return 0; + if (jailed(w->w_req->td->td_ucred) && + ((rt->rt_flags & RTF_HOST) == 0 || + !prison_if(w->w_req->td->td_ucred, rt_key(rt)))) + return (0); bzero((caddr_t)&info, sizeof(info)); info.rti_info[RTAX_DST] = rt_key(rt); info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; @@ -1146,8 +1293,8 @@ sysctl_iflist(int af, struct walkarg *w) while ((ifa = TAILQ_NEXT(ifa, ifa_link)) != NULL) { if (af && af != ifa->ifa_addr->sa_family) continue; - if (jailed(curthread->td_ucred) && - prison_if(curthread->td_ucred, ifa->ifa_addr)) + if (jailed(w->w_req->td->td_ucred) && + !prison_if(w->w_req->td->td_ucred, ifa->ifa_addr)) continue; info.rti_info[RTAX_IFA] = ifa->ifa_addr; info.rti_info[RTAX_NETMASK] = ifa->ifa_netmask; @@ -1174,7 +1321,7 @@ done: return (error); } -int +static int sysctl_ifmalist(int af, struct walkarg *w) { struct ifnet *ifp; @@ -1194,8 +1341,8 @@ sysctl_ifmalist(int af, struct walkarg *w) TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (af && af != ifma->ifma_addr->sa_family) continue; - if (jailed(curproc->p_ucred) && - prison_if(curproc->p_ucred, ifma->ifma_addr)) + if (jailed(w->w_req->td->td_ucred) && + !prison_if(w->w_req->td->td_ucred, ifma->ifma_addr)) continue; info.rti_info[RTAX_IFA] = ifma->ifma_addr; info.rti_info[RTAX_GATEWAY] = @@ -1260,7 +1407,7 @@ sysctl_rtsock(SYSCTL_HANDLER_ARGS) } else /* dump only one table */ i = lim = af; for (error = 0; error == 0 && i <= lim; i++) - if ((rnh = rt_tables[curthread->td_proc->p_fibnum][i]) != NULL) { + if ((rnh = rt_tables[req->td->td_proc->p_fibnum][i]) != NULL) { RADIX_NODE_HEAD_LOCK(rnh); error = rnh->rnh_walktree(rnh, sysctl_dumpentry, &w); Index: sys/netinet6/raw_ip6.c =================================================================== --- sys/netinet6/raw_ip6.c (revision 187819) +++ sys/netinet6/raw_ip6.c (working copy) @@ -67,6 +67,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -154,6 +155,7 @@ rip6_input(struct mbuf **mp, int *offp, int proto) INP_INFO_RLOCK(&ripcbinfo); LIST_FOREACH(in6p, &ripcb, inp_list) { + /* XXX inp locking */ if ((in6p->in6p_vflag & INP_IPV6) == 0) continue; if (in6p->in6p_ip6_nxt && @@ -165,6 +167,10 @@ rip6_input(struct mbuf **mp, int *offp, int proto) if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) && !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src)) continue; + if (jailed(in6p->inp_cred)) { + if (!prison_check_ip6(in6p->inp_cred, &ip6->ip6_dst)) + continue; + } INP_RLOCK(in6p); if (in6p->in6p_cksum != -1) { rip6stat.rip6s_isum++; @@ -391,6 +397,11 @@ rip6_output(m, va_alist) error = EADDRNOTAVAIL; goto bad; } + if (jailed(in6p->inp_cred)) + if (prison_get_ip6(in6p->inp_cred, in6a) != 0) { + error = EPERM; + goto bad; + } ip6->ip6_src = *in6a; if (oifp && scope_ambiguous) { @@ -649,6 +660,8 @@ rip6_bind(struct socket *so, struct sockaddr *nam, if (nam->sa_len != sizeof(*addr)) return (EINVAL); + if (!prison_check_ip6(td->td_ucred, &addr->sin6_addr)) + return (EADDRNOTAVAIL); if (TAILQ_EMPTY(&ifnet) || addr->sin6_family != AF_INET6) return (EADDRNOTAVAIL); if ((error = sa6_embedscope(addr, ip6_use_defzone)) != 0) Index: sys/netinet6/udp6_usrreq.c =================================================================== --- sys/netinet6/udp6_usrreq.c (revision 187819) +++ sys/netinet6/udp6_usrreq.c (working copy) @@ -71,6 +71,7 @@ __FBSDID("$FreeBSD$"); #include "opt_mac.h" #include +#include #include #include #include @@ -845,6 +846,12 @@ udp6_connect(struct socket *so, struct sockaddr *n goto out; } in6_sin6_2_sin(&sin, sin6_p); + if (td && jailed(td->td_ucred)) + if (prison_remote_ip4(td->td_ucred, + &sin.sin_addr) != 0) { + error = EAFNOSUPPORT; + goto out; + } error = in_pcbconnect(inp, (struct sockaddr *)&sin, td->td_ucred); if (error == 0) { @@ -859,6 +866,13 @@ udp6_connect(struct socket *so, struct sockaddr *n error = EISCONN; goto out; } + if (td && jailed(td->td_ucred)) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam; + if (prison_remote_ip6(td->td_ucred, &sin6->sin6_addr) != 0) { + error = EAFNOSUPPORT; + goto out; + } + } error = in6_pcbconnect(inp, nam, td->td_ucred); if (error == 0) { if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { Index: sys/netinet6/in6.c =================================================================== --- sys/netinet6/in6.c (revision 187819) +++ sys/netinet6/in6.c (working copy) @@ -68,6 +68,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -468,6 +469,9 @@ in6_control(struct socket *so, u_long cmd, caddr_t error = in6_setscope(&sa6->sin6_addr, ifp, NULL); if (error != 0) return (error); + if (td != NULL && !prison_check_ip6(td->td_ucred, + &sa6->sin6_addr)) + return (EADDRNOTAVAIL); ia = in6ifa_ifpwithaddr(ifp, &sa6->sin6_addr); } else ia = NULL; Index: sys/netinet6/in6_src.c =================================================================== --- sys/netinet6/in6_src.c (revision 187819) +++ sys/netinet6/in6_src.c (working copy) @@ -79,6 +79,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -224,6 +225,11 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct if (*errorp != 0) return (NULL); } + if (cred != NULL && prison_local_ip6(cred, &srcsock.sin6_addr, + (inp != NULL && (inp->inp_flags & IN6P_IPV6_V6ONLY) != 0)) != 0) { + *errorp = EADDRNOTAVAIL; + return (NULL); + } ia6 = (struct in6_ifaddr *)ifa_ifwithaddr((struct sockaddr *)(&srcsock)); if (ia6 == NULL || @@ -241,6 +247,11 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct * Otherwise, if the socket has already bound the source, just use it. */ if (inp != NULL && !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) { + if (cred != NULL && prison_local_ip6(cred, &inp->in6p_laddr, + ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0)) != 0) { + *errorp = EADDRNOTAVAIL; + return (NULL); + } return (&inp->in6p_laddr); } @@ -292,6 +303,12 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct if (!ip6_use_deprecated && IFA6_IS_DEPRECATED(ia)) continue; + if (cred != NULL && + prison_local_ip6(cred, &ia->ia_addr.sin6_addr, + (inp != NULL && + (inp->inp_flags & IN6P_IPV6_V6ONLY) != 0)) != 0) + continue; + /* Rule 1: Prefer same address */ if (IN6_ARE_ADDR_EQUAL(&dst, &ia->ia_addr.sin6_addr)) { ia_best = ia; @@ -765,6 +782,10 @@ in6_pcbsetport(struct in6_addr *laddr, struct inpc INP_INFO_WLOCK_ASSERT(pcbinfo); INP_WLOCK_ASSERT(inp); + if (prison_local_ip6(cred, laddr, + ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0)) != 0) + return(EINVAL); + /* XXX: this is redundant when called from in6_pcbbind */ if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) wild = INPLOOKUP_WILDCARD; Index: sys/netinet6/in6_pcb.c =================================================================== --- sys/netinet6/in6_pcb.c (revision 187819) +++ sys/netinet6/in6_pcb.c (working copy) @@ -145,6 +145,10 @@ in6_pcbbind(register struct inpcb *inp, struct soc if ((error = sa6_embedscope(sin6, ip6_use_defzone)) != 0) return(error); + if (prison_local_ip6(cred, &sin6->sin6_addr, + ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0)) != 0) + return (EINVAL); + lport = sin6->sin6_port; if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { /* @@ -219,6 +223,9 @@ in6_pcbbind(register struct inpcb *inp, struct soc return (EADDRINUSE); } } + if (prison_local_ip6(cred, &sin6->sin6_addr, + ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0)) != 0) + return (EADDRNOTAVAIL); t = in6_pcblookup_local(pcbinfo, &sin6->sin6_addr, lport, wild, cred); if (t && (reuseport & ((t->inp_vflag & INP_TIMEWAIT) ? @@ -251,6 +258,9 @@ in6_pcbbind(register struct inpcb *inp, struct soc } inp->in6p_laddr = sin6->sin6_addr; } + if (prison_local_ip6(cred, &inp->in6p_laddr, + ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0)) != 0) + return (EINVAL); if (lport == 0) { int e; if ((e = in6_pcbsetport(&inp->in6p_laddr, inp, cred)) != 0) @@ -309,6 +319,8 @@ in6_pcbladdr(register struct inpcb *inp, struct so if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) sin6->sin6_addr = in6addr_loopback; } + if (prison_remote_ip6(inp->inp_cred, &sin6->sin6_addr) != 0) + return (EADDRNOTAVAIL); /* * XXX: in6_selectsrc might replace the bound local address @@ -690,15 +702,16 @@ in6_pcblookup_local(struct inpcbinfo *pcbinfo, str head = &pcbinfo->ipi_hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->ipi_hashmask)]; LIST_FOREACH(inp, head, inp_hash) { + /* XXX inp locking */ if ((inp->inp_vflag & INP_IPV6) == 0) continue; if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) && IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr) && inp->inp_lport == lport) { - /* - * Found. - */ - return (inp); + /* Found. */ + if (cred == NULL || + inp->inp_cred->cr_prison == cred->cr_prison) + return (inp); } } /* @@ -728,6 +741,10 @@ in6_pcblookup_local(struct inpcbinfo *pcbinfo, str */ LIST_FOREACH(inp, &phd->phd_pcblist, inp_portlist) { wildcard = 0; + if (cred != NULL && + inp->inp_cred->cr_prison != cred->cr_prison) + continue; + /* XXX inp locking */ if ((inp->inp_vflag & INP_IPV6) == 0) continue; if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) @@ -737,7 +754,7 @@ in6_pcblookup_local(struct inpcbinfo *pcbinfo, str if (IN6_IS_ADDR_UNSPECIFIED(laddr)) wildcard++; else if (!IN6_ARE_ADDR_EQUAL( - &inp->in6p_laddr, laddr)) + &inp->in6p_laddr, laddr)) continue; } else { if (!IN6_IS_ADDR_UNSPECIFIED(laddr)) @@ -746,9 +763,8 @@ in6_pcblookup_local(struct inpcbinfo *pcbinfo, str if (wildcard < matchwild) { match = inp; matchwild = wildcard; - if (matchwild == 0) { + if (matchwild == 0) break; - } } } } @@ -831,11 +847,11 @@ in6_rtchange(struct inpcb *inp, int errno) */ struct inpcb * in6_pcblookup_hash(struct inpcbinfo *pcbinfo, struct in6_addr *faddr, - u_int fport_arg, struct in6_addr *laddr, u_int lport_arg, - int wildcard, struct ifnet *ifp) + u_int fport_arg, struct in6_addr *laddr, u_int lport_arg, int wildcard, + struct ifnet *ifp) { struct inpcbhead *head; - register struct inpcb *inp; + struct inpcb *inp, *tmpinp; u_short fport = fport_arg, lport = lport_arg; int faith; @@ -849,10 +865,12 @@ in6_pcblookup_hash(struct inpcbinfo *pcbinfo, stru /* * First look for an exact match. */ + tmpinp = NULL; head = &pcbinfo->ipi_hashbase[ INP_PCBHASH(faddr->s6_addr32[3] /* XXX */, lport, fport, pcbinfo->ipi_hashmask)]; LIST_FOREACH(inp, head, inp_hash) { + /* XXX inp locking */ if ((inp->inp_vflag & INP_IPV6) == 0) continue; if (IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, faddr) && @@ -860,33 +878,80 @@ in6_pcblookup_hash(struct inpcbinfo *pcbinfo, stru inp->inp_fport == fport && inp->inp_lport == lport) { /* - * Found. + * XXX We should be able to directly return + * the inp here, without any checks. + * Well unless both bound with SO_REUSEPORT? */ - return (inp); + if (jailed(inp->inp_cred)) + return (inp); + if (tmpinp == NULL) + tmpinp = inp; } } - if (wildcard) { - struct inpcb *local_wild = NULL; + if (tmpinp != NULL) + return (tmpinp); + /* + * Then look for a wildcard match, if requested. + */ + if (wildcard == INPLOOKUP_WILDCARD) { + struct inpcb *local_wild = NULL, *local_exact = NULL; + struct inpcb *jail_wild = NULL; + int injail; + + /* + * Order of socket selection - we always prefer jails. + * 1. jailed, non-wild. + * 2. jailed, wild. + * 3. non-jailed, non-wild. + * 4. non-jailed, wild. + */ head = &pcbinfo->ipi_hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->ipi_hashmask)]; LIST_FOREACH(inp, head, inp_hash) { + /* XXX inp locking */ if ((inp->inp_vflag & INP_IPV6) == 0) continue; - if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) && - inp->inp_lport == lport) { - if (faith && (inp->inp_flags & INP_FAITH) == 0) + + if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) || + inp->inp_lport != lport) { + continue; + } + + /* XXX inp locking */ + if (faith && (inp->inp_flags & INP_FAITH) == 0) + continue; + + injail = jailed(inp->inp_cred); + if (injail) { + if (!prison_check_ip6(inp->inp_cred, laddr)) continue; - if (IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, - laddr)) + } else { + if (local_exact != NULL) + continue; + } + + if (IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr)) { + if (injail) return (inp); - else if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) + else + local_exact = inp; + } else if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) { + if (injail) + jail_wild = inp; + else local_wild = inp; } - } - return (local_wild); - } + } /* LIST_FOREACH */ + if (jail_wild != NULL) + return (jail_wild); + if (local_exact != NULL) + return (local_exact); + if (local_wild != NULL) + return (local_wild); + } /* if (wildcard == INPLOOKUP_WILDCARD) */ + /* * Not found. */ Property changes on: sys/contrib/pf ___________________________________________________________________ Modified: svn:mergeinfo Merged /head/sys/contrib/pf:r183571,185404,185435-185436,185441,185899,186086,186606,186948,186956,186980,186986,187684 Index: sys/security/mac_bsdextended/mac_bsdextended.c =================================================================== --- sys/security/mac_bsdextended/mac_bsdextended.c (revision 187819) +++ sys/security/mac_bsdextended/mac_bsdextended.c (working copy) @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include Index: sys/sys/cpuset.h =================================================================== --- sys/sys/cpuset.h (revision 187819) +++ sys/sys/cpuset.h (working copy) @@ -131,6 +131,8 @@ typedef struct _cpuset { #define CPU_WHICH_TID 1 /* Specifies a thread id. */ #define CPU_WHICH_PID 2 /* Specifies a process id. */ #define CPU_WHICH_CPUSET 3 /* Specifies a set id. */ +/* CPU_WHICH_IRQ */ +#define CPU_WHICH_JAIL 5 /* Specifies a jail id. */ /* * Reserved cpuset identifiers. @@ -167,11 +169,15 @@ struct cpuset { #define CPU_SET_RDONLY 0x0002 /* No modification allowed. */ extern cpuset_t *cpuset_root; +struct proc; +struct thread; struct cpuset *cpuset_thread0(void); struct cpuset *cpuset_ref(struct cpuset *); void cpuset_rel(struct cpuset *); int cpuset_setthread(lwpid_t id, cpuset_t *); +int cpuset_create_root(struct thread *, struct cpuset **); +int cpuset_setproc_update_set(struct proc *, struct cpuset *); #else __BEGIN_DECLS Index: sys/sys/jail.h =================================================================== --- sys/sys/jail.h (revision 187819) +++ sys/sys/jail.h (working copy) @@ -13,22 +13,75 @@ #ifndef _SYS_JAIL_H_ #define _SYS_JAIL_H_ -struct jail { +#ifdef _KERNEL +struct jail_v0 { u_int32_t version; char *path; char *hostname; u_int32_t ip_number; }; +#endif +struct jail { + uint32_t version; + char *path; + char *hostname; + char *jailname; + uint32_t ip4s; + uint32_t ip6s; + struct in_addr *ip4; + struct in6_addr *ip6; +}; +#define JAIL_API_VERSION 2 + +/* + * For all xprison structs, always keep the pr_version an int and + * the first variable so userspace can easily distinguish them. + */ +#ifndef _KERNEL +struct xprison_v1 { + int pr_version; + int pr_id; + char pr_path[MAXPATHLEN]; + char pr_host[MAXHOSTNAMELEN]; + u_int32_t pr_ip; +}; +#endif + struct xprison { int pr_version; int pr_id; + int pr_state; + cpusetid_t pr_cpusetid; char pr_path[MAXPATHLEN]; char pr_host[MAXHOSTNAMELEN]; - u_int32_t pr_ip; + char pr_name[MAXHOSTNAMELEN]; + uint32_t pr_ip4s; + uint32_t pr_ip6s; +#if 0 + /* + * sizeof(xprison) will be malloced + size needed for all + * IPv4 and IPv6 addesses. Offsets are based numbers of addresses. + */ + struct in_addr pr_ip4[]; + struct in6_addr pr_ip6[]; +#endif }; -#define XPRISON_VERSION 1 +#define XPRISON_VERSION 3 +static const struct prison_state { + int pr_state; + const char * state_name; +} prison_states[] = { +#define PRISON_STATE_INVALID 0 + { PRISON_STATE_INVALID, "INVALID" }, +#define PRISON_STATE_ALIVE 1 + { PRISON_STATE_ALIVE, "ALIVE" }, +#define PRISON_STATE_DYING 2 + { PRISON_STATE_DYING, "DYING" }, +}; + + #ifndef _KERNEL int jail(struct jail *); @@ -48,6 +101,8 @@ MALLOC_DECLARE(M_PRISON); #endif #endif /* _KERNEL */ +struct cpuset; + /* * This structure describes a prison. It is pointed to by all struct * ucreds's of the inmates. pr_ref keeps track of them and is used to @@ -65,15 +120,22 @@ struct prison { LIST_ENTRY(prison) pr_list; /* (a) all prisons */ int pr_id; /* (c) prison id */ int pr_ref; /* (p) refcount */ + int pr_state; /* (p) prison state */ + int pr_nprocs; /* (p) process count */ char pr_path[MAXPATHLEN]; /* (c) chroot path */ + struct cpuset *pr_cpuset; /* (p) cpuset */ struct vnode *pr_root; /* (c) vnode to rdir */ char pr_host[MAXHOSTNAMELEN]; /* (p) jail hostname */ - u_int32_t pr_ip; /* (c) ip addr host */ + char pr_name[MAXHOSTNAMELEN]; /* (c) admin jail name */ void *pr_linux; /* (p) linux abi */ int pr_securelevel; /* (p) securelevel */ struct task pr_task; /* (d) destroy task */ struct mtx pr_mtx; void **pr_slots; /* (p) additional data */ + int pr_ip4s; /* (c) number of v4 IPs */ + struct in_addr *pr_ip4; /* (c) v4 IPs of jail */ + int pr_ip6s; /* (c) number of v6 IPs */ + struct in6_addr *pr_ip6; /* (c) v6 IPs of jail */ }; #endif /* _KERNEL || _WANT_PRISON */ @@ -101,6 +163,8 @@ struct ucred; struct mount; struct sockaddr; struct statfs; +struct thread; +int kern_jail(struct thread *, struct jail *); int jailed(struct ucred *cred); void getcredhostname(struct ucred *cred, char *, size_t); int prison_check(struct ucred *cred1, struct ucred *cred2); @@ -109,12 +173,23 @@ void prison_enforce_statfs(struct ucred *cred, str struct statfs *sp); struct prison *prison_find(int prid); void prison_free(struct prison *pr); -u_int32_t prison_getip(struct ucred *cred); +void prison_free_locked(struct prison *pr); void prison_hold(struct prison *pr); +void prison_hold_locked(struct prison *pr); +void prison_proc_hold(struct prison *); +void prison_proc_free(struct prison *); +int prison_get_ip4(struct ucred *cred, struct in_addr *ia); +int prison_local_ip4(struct ucred *cred, struct in_addr *ia); +int prison_remote_ip4(struct ucred *cred, struct in_addr *ia); +int prison_check_ip4(struct ucred *cred, struct in_addr *ia); +#ifdef INET6 +int prison_get_ip6(struct ucred *, struct in6_addr *); +int prison_local_ip6(struct ucred *, struct in6_addr *, int); +int prison_remote_ip6(struct ucred *, struct in6_addr *); +int prison_check_ip6(struct ucred *, struct in6_addr *); +#endif int prison_if(struct ucred *cred, struct sockaddr *sa); -int prison_ip(struct ucred *cred, int flag, u_int32_t *ip); int prison_priv_check(struct ucred *cred, int priv); -void prison_remote_ip(struct ucred *cred, int flags, u_int32_t *ip); /* * Kernel jail services. Index: sys/sys/param.h =================================================================== --- sys/sys/param.h (revision 187819) +++ sys/sys/param.h (working copy) @@ -57,7 +57,7 @@ * is created, otherwise 1. */ #undef __FreeBSD_version -#define __FreeBSD_version 701102 /* Master, propagated to newvers */ +#define __FreeBSD_version 701103 /* Master, propagated to newvers */ #ifndef LOCORE #include