diff -rup ./sys/kern/kern_jail.c /home/pjd/p4/jail/sys/kern/kern_jail.c --- ./sys/kern/kern_jail.c Mon Mar 15 13:10:34 2004 +++ /home/pjd/p4/jail/sys/kern/kern_jail.c Tue Mar 23 13:10:47 2004 @@ -99,7 +99,7 @@ jail(struct thread *td, struct jail_args error = copyin(uap->jail, &j, sizeof(j)); if (error) return (error); - if (j.version != 0) + if (j.version != 1) return (EINVAL); MALLOC(pr, struct prison *, sizeof(*pr), M_PRISON, M_WAITOK | M_ZERO); @@ -122,7 +122,14 @@ jail(struct thread *td, struct jail_args error = copyinstr(j.hostname, &pr->pr_host, sizeof(pr->pr_host), 0); if (error) goto e_dropvnref; - pr->pr_ip = j.ip_number; + if (j.nips >= JAIL_MAX_IPS) + goto e_dropvnref; + MALLOC(pr->pr_ips, u_int32_t *, sizeof(u_int32_t) * j.nips, M_PRISON, + M_WAITOK); + error = copyin(j.ips, pr->pr_ips, sizeof(u_int32_t) * j.nips); + if (error) + goto e_freeips; + pr->pr_nips = j.nips; pr->pr_linux = NULL; pr->pr_securelevel = securelevel; @@ -138,7 +145,7 @@ next: if (tryprid == JAIL_MAX) { mtx_unlock(&allprison_mtx); error = EAGAIN; - goto e_dropvnref; + goto e_freeips; } goto next; } @@ -161,6 +168,8 @@ e_dropprref: LIST_REMOVE(pr, pr_list); prisoncount--; mtx_unlock(&allprison_mtx); +e_freeips: + FREE(pr->pr_ips, M_PRISON); e_dropvnref: mtx_lock(&Giant); vrele(pr->pr_root); @@ -293,6 +302,7 @@ prison_complete(void *context, int pendi mtx_destroy(&pr->pr_mtx); if (pr->pr_linux != NULL) FREE(pr->pr_linux, M_PRISON); + FREE(pr->pr_ips, M_PRISON); FREE(pr, M_PRISON); } @@ -309,7 +319,7 @@ u_int32_t prison_getip(struct ucred *cred) { - return (cred->cr_prison->pr_ip); + return (cred->cr_prison->pr_ips[0]); } int @@ -323,23 +333,16 @@ prison_ip(struct ucred *cred, int 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); - return (0); - } if (tmp == INADDR_LOOPBACK) { if (flag) - *ip = cred->cr_prison->pr_ip; + *ip = cred->cr_prison->pr_ips[0]; else - *ip = htonl(cred->cr_prison->pr_ip); + *ip = htonl(cred->cr_prison->pr_ips[0]); return (0); } - if (cred->cr_prison->pr_ip != tmp) - return (1); - return (0); + if (tmp == INADDR_ANY || jailed_ip(cred, tmp)) + return (0); + return (1); } void @@ -355,9 +358,9 @@ prison_remote_ip(struct ucred *cred, int tmp = ntohl(*ip); if (tmp == INADDR_LOOPBACK) { if (flag) - *ip = cred->cr_prison->pr_ip; + *ip = cred->cr_prison->pr_ips[0]; else - *ip = htonl(cred->cr_prison->pr_ip); + *ip = htonl(cred->cr_prison->pr_ips[0]); return; } return; @@ -374,13 +377,34 @@ prison_if(struct ucred *cred, struct soc ok = 1; else if (sai->sin_family != AF_INET) ok = 0; - else if (cred->cr_prison->pr_ip != ntohl(sai->sin_addr.s_addr)) + else if (!jailed_ip(cred, ntohl(sai->sin_addr.s_addr))) ok = 1; else ok = 0; return (ok); } +int +jailed_ip(struct ucred *cred, u_int32_t ip) +{ + register struct prison *pr; + register u_int i; + + if (!jailed(cred)) + return(1); + + pr = cred->cr_prison; + /* + * XXX: This is really not optimal when there are many IPs in a jail. + */ + for (i = 0; i < pr->pr_nips; ++i) { + if (pr->pr_ips[i] == ip) + return (1); + } + + return (0); +} + /* * Return 0 if jails permit p1 to frob p2, otherwise ESRCH. */ @@ -470,7 +494,8 @@ retry: xp->pr_id = pr->pr_id; strlcpy(xp->pr_path, pr->pr_path, sizeof(xp->pr_path)); strlcpy(xp->pr_host, pr->pr_host, sizeof(xp->pr_host)); - xp->pr_ip = pr->pr_ip; + memcpy(xp->pr_ips, pr->pr_ips, sizeof(u_int32_t) * pr->pr_nips); + xp->pr_nips = pr->pr_nips; mtx_unlock(&pr->pr_mtx); xp++; } diff -rup ./sys/netinet/in_pcb.c /home/pjd/p4/jail/sys/netinet/in_pcb.c --- ./sys/netinet/in_pcb.c Wed Mar 10 11:22:22 2004 +++ /home/pjd/p4/jail/sys/netinet/in_pcb.c Tue Mar 23 13:05:33 2004 @@ -261,9 +261,10 @@ in_pcbbind_setup(inp, nam, laddrp, lport struct sockaddr_in *sin; struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; struct in_addr laddr; + struct ucred *cred = NULL; u_short lport = 0; int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); - int error, prison = 0; + int error; INP_INFO_WLOCK_ASSERT(pcbinfo); INP_LOCK_ASSERT(inp); @@ -275,6 +276,8 @@ in_pcbbind_setup(inp, nam, laddrp, lport return (EINVAL); if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) wild = 1; + if (td) + cred = td->td_ucred; if (nam) { sin = (struct sockaddr_in *)nam; if (nam->sa_len != sizeof (*sin)) @@ -290,6 +293,8 @@ in_pcbbind_setup(inp, nam, laddrp, lport if (sin->sin_addr.s_addr != INADDR_ANY) if (prison_ip(td->td_ucred, 0, &sin->sin_addr.s_addr)) return(EINVAL); + if (td && prison_ip(cred, 0, &sin->sin_addr.s_addr)) + return (EINVAL); if (sin->sin_port != *lportp) { /* Don't allow the port to change. */ if (*lportp != 0) @@ -319,15 +324,12 @@ in_pcbbind_setup(inp, nam, laddrp, lport /* GROSS */ if (ntohs(lport) <= ipport_reservedhigh && ntohs(lport) >= ipport_reservedlow && - td && suser_cred(td->td_ucred, PRISON_ROOT)) + td && suser_cred(cred, PRISON_ROOT)) return (EACCES); - if (td && jailed(td->td_ucred)) - prison = 1; if (so->so_cred->cr_uid != 0 && !IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) { - t = in_pcblookup_local(inp->inp_pcbinfo, - sin->sin_addr, lport, - prison ? 0 : INPLOOKUP_WILDCARD); + t = in_pcblookup_local(cred, inp->inp_pcbinfo, + sin->sin_addr, lport, INPLOOKUP_WILDCARD); /* * XXX * This entire block sorely needs a rewrite. @@ -357,11 +359,10 @@ in_pcbbind_setup(inp, nam, laddrp, lport return (EADDRINUSE); } } - if (prison && - prison_ip(td->td_ucred, 0, &sin->sin_addr.s_addr)) + if (td && prison_ip(cred, 0, &sin->sin_addr.s_addr)) return (EADDRNOTAVAIL); - t = in_pcblookup_local(pcbinfo, sin->sin_addr, - lport, prison ? 0 : wild); + t = in_pcblookup_local(cred, pcbinfo, sin->sin_addr, + lport, wild); if (t && (t->inp_vflag & INP_TIMEWAIT)) { if ((reuseport & intotw(t)->tw_so_options) == 0) return (EADDRINUSE); @@ -386,17 +387,14 @@ in_pcbbind_setup(inp, nam, laddrp, lport u_short first, last; int count; - if (laddr.s_addr != INADDR_ANY) - if (prison_ip(td->td_ucred, 0, &laddr.s_addr)) - return (EINVAL); - + if (td && prison_ip(cred, 0, &laddr.s_addr)) + return (EINVAL); if (inp->inp_flags & INP_HIGHPORT) { first = ipport_hifirstauto; /* sysctl */ last = ipport_hilastauto; lastport = &pcbinfo->lasthi; } else if (inp->inp_flags & INP_LOWPORT) { - if (td && (error = suser_cred(td->td_ucred, - PRISON_ROOT)) != 0) + if (td && (error = suser_cred(cred, PRISON_ROOT)) != 0) return error; first = ipport_lowfirstauto; /* 1023 */ last = ipport_lowlastauto; /* 600 */ @@ -426,7 +424,7 @@ in_pcbbind_setup(inp, nam, laddrp, lport if (*lastport > first || *lastport < last) *lastport = first; lport = htons(*lastport); - } while (in_pcblookup_local(pcbinfo, laddr, lport, + } while (in_pcblookup_local(cred, pcbinfo, laddr, lport, wild)); } else { /* @@ -441,11 +439,11 @@ in_pcbbind_setup(inp, nam, laddrp, lport if (*lastport < first || *lastport > last) *lastport = first; lport = htons(*lastport); - } while (in_pcblookup_local(pcbinfo, laddr, lport, + } while (in_pcblookup_local(cred, pcbinfo, laddr, lport, wild)); } } - if (prison_ip(td->td_ucred, 0, &laddr.s_addr)) + if (td && prison_ip(cred, 0, &laddr.s_addr)) return (EINVAL); *laddrp = laddr.s_addr; *lportp = lport; @@ -531,7 +529,7 @@ in_pcbconnect_setup(inp, nam, laddrp, lp struct sockaddr_in *sin = (struct sockaddr_in *)nam; struct in_ifaddr *ia; struct sockaddr_in sa; - struct ucred *cred; + struct ucred *cred = NULL; struct inpcb *oinp; struct in_addr laddr, faddr; u_short lport, fport; @@ -560,6 +558,7 @@ in_pcbconnect_setup(inp, nam, laddrp, lp if (error) return (error); } + if (!TAILQ_EMPTY(&in_ifaddrhead)) { /* * If the destination address is INADDR_ANY, @@ -568,9 +567,12 @@ in_pcbconnect_setup(inp, nam, laddrp, lp * 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)) + faddr.s_addr = htonl(prison_getip(cred)); + 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( @@ -873,7 +875,8 @@ in_pcbpurgeif0(pcbinfo, ifp) * Lookup a PCB based on the local address and port. */ struct inpcb * -in_pcblookup_local(pcbinfo, laddr, lport_arg, wild_okay) +in_pcblookup_local(cred, pcbinfo, laddr, lport_arg, wild_okay) + struct ucred *cred; struct inpcbinfo *pcbinfo; struct in_addr laddr; u_int lport_arg; @@ -901,9 +904,15 @@ in_pcblookup_local(pcbinfo, laddr, lport inp->inp_laddr.s_addr == laddr.s_addr && inp->inp_lport == lport) { /* - * Found. + * Found? */ - return (inp); + /* Those values could be NULL, really. */ + if (inp->inp_socket == NULL || cred == NULL) + return (inp); + if (inp->inp_socket->so_cred->cr_prison == + cred->cr_prison) { + return (inp); + } } } /* @@ -938,6 +947,11 @@ in_pcblookup_local(pcbinfo, laddr, lport if ((inp->inp_vflag & INP_IPV4) == 0) continue; #endif + if (cred != NULL && inp->inp_socket != NULL && + inp->inp_socket->so_cred->cr_prison != + cred->cr_prison) { + continue; + } /* * Clean out old time_wait sockets if they * are clogging up needed local ports. @@ -987,7 +1001,7 @@ in_pcblookup_hash(pcbinfo, faddr, fport_ struct ifnet *ifp; { struct inpcbhead *head; - register struct inpcb *inp; + register struct inpcb *inp, *jinp = NULL; u_short fport = fport_arg, lport = lport_arg; INP_INFO_RLOCK_ASSERT(pcbinfo); @@ -1005,17 +1019,34 @@ in_pcblookup_hash(pcbinfo, faddr, fport_ inp->inp_fport == fport && inp->inp_lport == lport) { /* - * Found. + * Found? */ - return (inp); + if (inp->inp_socket == NULL || + inp->inp_socket->so_cred->cr_prison == NULL) { + return (inp); + } else { + if (jinp == NULL) + jinp = inp; + } } } + if (jinp != NULL) + return (jinp); if (wildcard) { struct inpcb *local_wild = NULL; #if defined(INET6) struct inpcb *local_wild_mapped = NULL; #endif /* defined(INET6) */ + struct inpcb *jinp_wild = NULL; + struct ucred *cred; + /* + * Order of socket selection: + * 1. non-jailed, non-wild. + * 2. non-jailed, wild. + * 3. jailed, non-wild. + * 4. jailed, wild. + */ head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->hashmask)]; LIST_FOREACH(inp, head, inp_hash) { #ifdef INET6 @@ -1027,24 +1058,47 @@ in_pcblookup_hash(pcbinfo, faddr, fport_ if (ifp && ifp->if_type == IFT_FAITH && (inp->inp_flags & INP_FAITH) == 0) continue; - if (inp->inp_laddr.s_addr == laddr.s_addr) - return (inp); - else if (inp->inp_laddr.s_addr == INADDR_ANY) { + if (inp->inp_socket != NULL) + cred = inp->inp_socket->so_cred; + else + cred = NULL; + if (cred != NULL && jailed(cred)) { + if (jinp != NULL) + continue; + if (!jailed_ip(cred, + ntohl(laddr.s_addr))) { + continue; + } + } + if (inp->inp_laddr.s_addr == laddr.s_addr) { + if (cred != NULL && jailed(cred)) + jinp = inp; + else + return (inp); + } else if (inp->inp_laddr.s_addr == + INADDR_ANY) { #if defined(INET6) if (INP_CHECK_SOCKAF(inp->inp_socket, AF_INET6)) local_wild_mapped = inp; else #endif /* defined(INET6) */ - local_wild = inp; + if (cred != NULL && jailed(cred)) + jinp_wild = inp; + else + local_wild = inp; } } } + if (local_wild != NULL) + return (local_wild); #if defined(INET6) - if (local_wild == NULL) + if (local_wild_mapped != NULL) return (local_wild_mapped); #endif /* defined(INET6) */ - return (local_wild); + if (jinp != NULL) + return (jinp); + return (jinp_wild); } /* @@ -1186,7 +1240,7 @@ prison_xinpcb(struct thread *td, struct { if (!jailed(td->td_ucred)) return (0); - if (ntohl(inp->inp_laddr.s_addr) == prison_getip(td->td_ucred)) + if (jailed_ip(td->td_ucred, ntohl(inp->inp_laddr.s_addr))) return (0); return (1); } diff -rup ./sys/netinet/in_pcb.h /home/pjd/p4/jail/sys/netinet/in_pcb.h --- ./sys/netinet/in_pcb.h Wed Nov 26 02:40:43 2003 +++ /home/pjd/p4/jail/sys/netinet/in_pcb.h Tue Mar 23 01:47:33 2004 @@ -336,6 +336,8 @@ extern int ipport_lastauto; extern int ipport_hifirstauto; extern int ipport_hilastauto; +struct ucred; + void in_pcbpurgeif0(struct inpcbinfo *, struct ifnet *); int in_pcballoc(struct socket *, struct inpcbinfo *, struct thread *, const char *); @@ -350,7 +352,7 @@ void in_pcbdetach(struct inpcb *); void in_pcbdisconnect(struct inpcb *); int in_pcbinshash(struct inpcb *); struct inpcb * - in_pcblookup_local(struct inpcbinfo *, + in_pcblookup_local(struct ucred *, struct inpcbinfo *, struct in_addr, u_int, int); struct inpcb * in_pcblookup_hash(struct inpcbinfo *, struct in_addr, u_int, diff -rup ./sys/netinet6/in6_pcb.c /home/pjd/p4/jail/sys/netinet6/in6_pcb.c --- ./sys/netinet6/in6_pcb.c Mon Feb 16 10:34:50 2004 +++ /home/pjd/p4/jail/sys/netinet6/in6_pcb.c Tue Mar 23 01:47:33 2004 @@ -131,6 +131,7 @@ in6_pcbbind(inp, nam, td) struct socket *so = inp->inp_socket; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)NULL; struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; + struct ucred *cred = NULL; u_short lport = 0; int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); @@ -140,6 +141,8 @@ in6_pcbbind(inp, nam, td) return (EINVAL); if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) wild = 1; + if (td != NULL) + cred = td->td_ucred; if (nam) { sin6 = (struct sockaddr_in6 *)nam; if (nam->sa_len != sizeof(*sin6)) @@ -191,7 +194,7 @@ in6_pcbbind(inp, nam, td) /* GROSS */ if (ntohs(lport) < IPV6PORT_RESERVED && td && - suser_cred(td->td_ucred, PRISON_ROOT)) + suser_cred(cred, PRISON_ROOT)) return (EACCES); if (so->so_cred->cr_uid != 0 && !IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { @@ -217,9 +220,9 @@ in6_pcbbind(inp, nam, td) struct sockaddr_in sin; in6_sin6_2_sin(&sin, sin6); - t = in_pcblookup_local(pcbinfo, - sin.sin_addr, lport, - INPLOOKUP_WILDCARD); + t = in_pcblookup_local(cred, pcbinfo, + sin.sin_addr, lport, + INPLOOKUP_WILDCARD); if (t && (t->inp_vflag & INP_TIMEWAIT)) { if (so->so_cred->cr_uid != intotw(t)->tw_cred->cr_uid && @@ -251,8 +254,8 @@ in6_pcbbind(inp, nam, td) struct sockaddr_in sin; in6_sin6_2_sin(&sin, sin6); - t = in_pcblookup_local(pcbinfo, sin.sin_addr, - lport, wild); + t = in_pcblookup_local(cred, pcbinfo, + sin.sin_addr, lport, wild); if (t && t->inp_vflag & INP_TIMEWAIT) { if ((reuseport & intotw(t)->tw_so_options) == 0 && diff -rup ./sys/sys/jail.h /home/pjd/p4/jail/sys/sys/jail.h --- ./sys/sys/jail.h Mon Feb 16 10:35:23 2004 +++ /home/pjd/p4/jail/sys/sys/jail.h Tue Mar 23 12:46:52 2004 @@ -17,17 +17,21 @@ struct jail { u_int32_t version; char *path; char *hostname; - u_int32_t ip_number; + u_int32_t *ips; + u_int nips; }; +#define JAIL_MAX_IPS 256 + struct xprison { - int pr_version; - int pr_id; - char pr_path[MAXPATHLEN]; - char pr_host[MAXHOSTNAMELEN]; - u_int32_t pr_ip; + int pr_version; + int pr_id; + char pr_path[MAXPATHLEN]; + char pr_host[MAXHOSTNAMELEN]; + u_int32_t pr_ips[JAIL_MAX_IPS]; + u_int pr_nips; }; -#define XPRISON_VERSION 1 +#define XPRISON_VERSION 2 #ifndef _KERNEL @@ -66,7 +70,8 @@ struct prison { char pr_path[MAXPATHLEN]; /* (c) chroot path */ struct vnode *pr_root; /* (c) vnode to rdir */ char pr_host[MAXHOSTNAMELEN]; /* (p) jail hostname */ - u_int32_t pr_ip; /* (c) ip addr host */ + u_int32_t *pr_ips; /* (c) jail's ips */ + u_int pr_nips; /* (c) number of ips */ void *pr_linux; /* (p) linux abi */ int pr_securelevel; /* (p) securelevel */ struct task pr_task; /* (d) destroy task */ @@ -102,6 +107,7 @@ void prison_hold(struct prison *pr); int prison_if(struct ucred *cred, struct sockaddr *sa); int prison_ip(struct ucred *cred, int flag, u_int32_t *ip); void prison_remote_ip(struct ucred *cred, int flags, u_int32_t *ip); +int jailed_ip(struct ucred *cred, u_int32_t ip); #endif /* !_KERNEL */ #endif /* !_SYS_JAIL_H_ */ diff -rup ./usr.sbin/jail/jail.c /home/pjd/p4/jail/usr.sbin/jail/jail.c --- ./usr.sbin/jail/jail.c Sun Jul 6 14:44:11 2003 +++ /home/pjd/p4/jail/usr.sbin/jail/jail.c Mon Mar 22 17:47:57 2004 @@ -35,7 +35,7 @@ main(int argc, char **argv) struct passwd *pwd; struct in_addr in; int ch, groups[NGROUPS], i, iflag, ngroups; - char *username; + char *ip, *username; iflag = 0; username = NULL; @@ -71,12 +71,26 @@ main(int argc, char **argv) if (chdir(argv[0]) != 0) err(1, "chdir: %s", argv[0]); memset(&j, 0, sizeof(j)); - j.version = 0; + j.version = 1; j.path = argv[0]; 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); + + for (i = 1, ip = argv[2]; *ip; ip++) { + if (*ip == ',') + i++; + } + if ((j.ips = (u_int32_t *)malloc(sizeof(u_int32_t) * i)) == NULL) + errx(1, "malloc()"); + for (i = 0, ip = strtok(argv[2], ","); ip; + i++, ip = strtok(NULL, ",")) { + if (inet_aton(ip, &in) == 0) { + free(j.ips); + errx(1, "Couldn't make sense of ip-number: %s", ip); + } + j.ips[i] = ntohl(in.s_addr); + } + j.nips = i; + i = jail(&j); if (i == -1) err(1, "jail"); @@ -103,7 +117,7 @@ static void usage(void) { - (void)fprintf(stderr, - "usage: jail [-i] [-u username] path hostname ip-number command ...\n"); + (void)fprintf(stderr, "usage: jail [-i] [-u username] path hostname " + "ip1[,ip2[...]] command ...\n"); exit(1); } diff -rup ./usr.sbin/jls/jls.c /home/pjd/p4/jail/usr.sbin/jls/jls.c --- ./usr.sbin/jls/jls.c Tue Apr 22 15:24:56 2003 +++ /home/pjd/p4/jail/usr.sbin/jls/jls.c Mon Mar 22 17:47:57 2004 @@ -43,7 +43,7 @@ main(void) { struct xprison *sxp, *xp; struct in_addr in; - size_t i, len; + size_t i, j, len; if (sysctlbyname("security.jail.list", NULL, &len, NULL, 0) == -1) err(1, "sysctlbyname(): security.jail.list"); @@ -67,9 +67,13 @@ retry: printf(" JID IP Address Hostname Path\n"); for (i = 0; i < len / sizeof(*xp); i++) { - in.s_addr = ntohl(xp->pr_ip); + in.s_addr = ntohl(xp->pr_ips[0]); printf("%6d %-15.15s %-29.29s %.74s\n", xp->pr_id, inet_ntoa(in), xp->pr_host, xp->pr_path); + for (j = 1; j < xp->pr_nips; j++) { + in.s_addr = ntohl(xp->pr_ips[j]); + printf(" %-15.15s\n", inet_ntoa(in)); + } xp++; } free(sxp);