From owner-freebsd-bugs@FreeBSD.ORG Sat Jul 31 08:01:17 2004 Return-Path: Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 6AED116A4CE for ; Sat, 31 Jul 2004 08:01:17 +0000 (GMT) Received: from freefall.freebsd.org (freefall.freebsd.org [216.136.204.21]) by mx1.FreeBSD.org (Postfix) with ESMTP id 4A0DE43D49 for ; Sat, 31 Jul 2004 08:01:17 +0000 (GMT) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.12.11/8.12.11) with ESMTP id i6V80nQZ083259 for ; Sat, 31 Jul 2004 08:00:49 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.12.11/8.12.11/Submit) id i6V80kKF083253; Sat, 31 Jul 2004 08:00:46 GMT (envelope-from gnats) Date: Sat, 31 Jul 2004 08:00:46 GMT Message-Id: <200407310800.i6V80kKF083253@freefall.freebsd.org> To: freebsd-bugs@FreeBSD.org From: "Meno Abels" Subject: Re: kern/69064: No multiple ip4/6's could assigned to a jail. X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.1 Precedence: list Reply-To: Meno Abels List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 31 Jul 2004 08:01:17 -0000 The following reply was made to PR kern/69064; it has been noted by GNATS. From: "Meno Abels" To: FreeBSD-gnats-submit@freebsd.org Cc: Max Laier Subject: Re: kern/69064: No multiple ip4/6's could assigned to a jail. Date: Sat, 31 Jul 2004 09:54:15 +0200 I chosen the wrong patch format so here it again as a unified diff. It is a little diffrent to the last patch in fact i resolved some clashes with other changes to -CURRENT Meno Index: sys/kern/kern_jail.c =================================================================== RCS file: /usr/freebsd.cvs/src/sys/kern/kern_jail.c,v retrieving revision 1.44 diff -u -r1.44 kern_jail.c --- sys/kern/kern_jail.c 27 Jun 2004 09:03:21 -0000 1.44 +++ sys/kern/kern_jail.c 14 Jul 2004 19:12:39 -0000 @@ -33,8 +33,11 @@ #include #include #include +#include MALLOC_DEFINE(M_PRISON, "prison", "Prison structures"); +MALLOC_DEFINE(M_PRISON_IP4, "prison", "Prison ipv4 addresses"); +MALLOC_DEFINE(M_PRISON_IP6, "prison", "Prison ipv6 addresses"); SYSCTL_DECL(_security); SYSCTL_NODE(_security, OID_AUTO, jail, CTLFLAG_RW, 0, @@ -75,6 +78,11 @@ static void init_prison(void *); static void prison_complete(void *context, int pending); +static int prison_add_ip4(struct in_addr *tmpv4, struct prison *pr, void *pr_new, void **pr_old); +static int prison_add_ip6(struct in6_addr *tmpv4, struct prison *pr, void *pr_new, void **pr_old); +static int prison_del_ip4(struct in_addr *tmpv4, struct prison *pr, void *pr_new, void **pr_old); +static int prison_del_ip6(struct in6_addr *tmpv4, struct prison *pr, void *pr_new, void **pr_old); +static int prison_check_duplicate(char *hostname, char *path); static struct prison *prison_find(int); static int sysctl_jail_list(SYSCTL_HANDLER_ARGS); @@ -95,45 +103,179 @@ * struct jail *jail; * }; */ -int -jail(struct thread *td, struct jail_args *uap) +static int +jail_createjail(struct thread *td, char *user_hostname, char *user_path, struct prison **pr) { + int error; struct nameidata nd; - struct prison *pr, *tpr; - struct jail j; - struct jail_attach_args jaa; - int error, tryprid; - error = copyin(uap->jail, &j, sizeof(j)); + MALLOC(*pr, struct prison *, sizeof(**pr), M_PRISON, M_WAITOK | M_ZERO); + mtx_init(&(*pr)->pr_mtx, "jail mutex", NULL, MTX_DEF); + (*pr)->pr_ref = 1; + error = copyinstr(user_path, &(*pr)->pr_path, sizeof((*pr)->pr_path), 0); if (error) - return (error); - if (j.version != 0) - return (EINVAL); - - MALLOC(pr, struct prison *, 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); + goto e_killmtx; + error = copyinstr(user_hostname, &(*pr)->pr_host, sizeof((*pr)->pr_host), 0); if (error) goto e_killmtx; + if (prison_check_duplicate((*pr)->pr_host, (*pr)->pr_path)) { + error = EAGAIN; + goto e_killmtx; + } + mtx_lock(&Giant); - NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, pr->pr_path, td); + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, (*pr)->pr_path, td); error = namei(&nd); if (error) { mtx_unlock(&Giant); goto e_killmtx; } - pr->pr_root = nd.ni_vp; + (*pr)->pr_root = nd.ni_vp; VOP_UNLOCK(nd.ni_vp, 0, td); NDFREE(&nd, NDF_ONLY_PNBUF); mtx_unlock(&Giant); - error = copyinstr(j.hostname, &pr->pr_host, sizeof(pr->pr_host), 0); - if (error) - goto e_dropvnref; - pr->pr_ip = j.ip_number; - pr->pr_linux = NULL; - pr->pr_securelevel = securelevel; + (*pr)->pr_linux = NULL; + (*pr)->pr_securelevel = securelevel; + return (0); + +e_killmtx: + mtx_destroy(&(*pr)->pr_mtx); + FREE(*pr, M_PRISON); + return (error); + +} + + +static void +jail_dropvnref(struct prison *pr) +{ + mtx_lock(&Giant); + vrele(pr->pr_root); + mtx_unlock(&Giant); +} + +int +jail(struct thread *td, struct jail_args *uap) +{ + struct prison *pr, *tpr; + struct jail j; + struct jail_attach_args jaa; + int error, tryprid; + void *pr_new = 0; + void *pr_old = 0; + + error = copyin(uap->jail, &j, sizeof(j)); + if (error) + return (error); + if (j.version < XPRISON_VERSION) + { + struct in_addr tmpv4; + + error = jail_createjail(td, j.u.v1.hostname, j.u.v1.path, &pr); + if (error) { + return error; + } + MALLOC(pr_new, void *, sizeof(struct in_addr)*1, M_PRISON_IP4, M_WAITOK | M_ZERO); + tmpv4.s_addr = htonl(j.u.v1.ip_number); + mtx_lock(&pr->pr_mtx); + error = prison_add_ip4(&tmpv4, pr, pr_new, &pr_old); + mtx_unlock(&pr->pr_mtx); + if (error) { + jail_dropvnref(pr); + } + } + else + { + if (j.u.v2.function == CREATEJAIL) { + error = jail_createjail(td, j.u.v2.u.createjail.hostname, j.u.v2.u.createjail.path, &pr); + if (error) { + return error; + } + } + else { + int cnt = 0; + struct malloc_type *alloc_type; +retry_alloc: + mtx_lock(&allprison_mtx); + pr = prison_find(j.u.v2.u.add_del.id); + if (pr) { + cnt = (j.u.v2.function == ADDIP4 || j.u.v2.function == DELIP4) ? + pr->pr_ip4s : pr->pr_ip6s; + } + mtx_unlock(&pr->pr_mtx); + mtx_unlock(&allprison_mtx); + if (pr == NULL) { + return (EINVAL); + } + if (j.u.v2.function == ADDIP4 || j.u.v2.function == DELIP4) { + alloc_type=M_PRISON_IP4; + if (pr->pr_ip4s >= (j.u.v2.function == DELIP4 ? 2 : 0)) { + MALLOC(pr_new, void *, + sizeof(struct in_addr)*(pr->pr_ip4s+ + (j.u.v2.function == DELIP4 ? -1 : +1)), + alloc_type, M_WAITOK | M_ZERO); + /*printf("pr_ip4s:%d\n", pr->pr_ip4s);*/ + } + } + else { + alloc_type=M_PRISON_IP6; + if (pr->pr_ip6s >= (j.u.v2.function == DELIP6 ? 2 : 0)) { + MALLOC(pr_new, void *, + sizeof(struct in6_addr)*(pr->pr_ip6s+ + (j.u.v2.function == DELIP6 ? -1 : +1)), + alloc_type, M_WAITOK | M_ZERO); + /*printf("pr_ip4s:%d\n", pr->pr_ip6s);*/ + } + } + mtx_lock(&allprison_mtx); + pr = prison_find(j.u.v2.u.add_del.id); + if (pr) + { + if (cnt != ((j.u.v2.function == ADDIP4 || j.u.v2.function == DELIP4) ? + pr->pr_ip4s : pr->pr_ip6s)) + { + /* should i sleep ? */ + mtx_unlock(&pr->pr_mtx); + mtx_unlock(&allprison_mtx); + FREE(pr_new, alloc_type); + /*printf("jail retry alloc\n");*/ + goto retry_alloc; + } + mtx_unlock(&allprison_mtx); + } + else + { + mtx_unlock(&allprison_mtx); + return (EINVAL); + } + + switch (j.u.v2.function) { + case ADDIP4: + error = prison_add_ip4(&j.u.v2.u.add_del.v4_6.ip4_num, pr, pr_new, &pr_old); + break; + case ADDIP6: + error = prison_add_ip6(&j.u.v2.u.add_del.v4_6.ip6_num, pr, pr_new, &pr_old); + break; + case DELIP4: + error = prison_del_ip4(&j.u.v2.u.add_del.v4_6.ip4_num, pr, pr_new, &pr_old); + break; + case DELIP6: + error = prison_del_ip6(&j.u.v2.u.add_del.v4_6.ip6_num, pr, pr_new, &pr_old); + break; + default: + mtx_unlock(&pr->pr_mtx); + return EINVAL; + } + mtx_unlock(&pr->pr_mtx); + if (pr_old) { + /*printf("jail free:%p\n", pr_old);*/ + FREE(pr_old, alloc_type); + } + return (error); + } + } + /* REST of Create Code */ /* Determine next pr_id and add prison to allprison list. */ mtx_lock(&allprison_mtx); tryprid = lastprid + 1; @@ -146,7 +288,8 @@ if (tryprid == JAIL_MAX) { mtx_unlock(&allprison_mtx); error = EAGAIN; - goto e_dropvnref; + jail_dropvnref(pr); + return error; } goto next; } @@ -164,18 +307,13 @@ mtx_unlock(&pr->pr_mtx); td->td_retval[0] = jaa.jid; return (0); + e_dropprref: mtx_lock(&allprison_mtx); LIST_REMOVE(pr, pr_list); prisoncount--; mtx_unlock(&allprison_mtx); -e_dropvnref: - mtx_lock(&Giant); - vrele(pr->pr_root); - mtx_unlock(&Giant); -e_killmtx: - mtx_destroy(&pr->pr_mtx); - FREE(pr, M_PRISON); + jail_dropvnref(pr); return (error); } @@ -248,6 +386,32 @@ return (error); } +/* return 0 on not duplicate */ +static int +prison_check_duplicate(char *hostname, char *path) +{ + struct prison *pr; + mtx_lock(&allprison_mtx); + LIST_FOREACH(pr, &allprison, pr_list) { + if (!strncmp(hostname, pr->pr_host, sizeof(pr->pr_host))) { + mtx_unlock(&allprison_mtx); + return 1; + } + /* this is not perfect remove of trailing / or duplicated // + should be done, who knows the kernel method for this-:) + */ +/* + if (!strncmp(path, pr->pr_path, sizeof(pr->pr_path))) { + mtx_unlock(&allprison_mtx); + return 1; + } +*/ + } + mtx_unlock(&allprison_mtx); + return 0; +} + + /* * Returns a locked prison instance, or NULL on failure. */ @@ -299,6 +463,10 @@ mtx_unlock(&Giant); mtx_destroy(&pr->pr_mtx); + if (pr->pr_ip4 != NULL) + FREE(pr->pr_ip4, M_PRISON_IP4); + if (pr->pr_ip6 != NULL) + FREE(pr->pr_ip6, M_PRISON_IP6); if (pr->pr_linux != NULL) FREE(pr->pr_linux, M_PRISON); FREE(pr, M_PRISON); @@ -313,59 +481,130 @@ mtx_unlock(&pr->pr_mtx); } -u_int32_t -prison_getip(struct ucred *cred) +int +prison_first_ip4(struct ucred *cred, struct in_addr *out) { + int errno = 0; + mtx_lock(&cred->cr_prison->pr_mtx); + if (cred->cr_prison->pr_ip4s) + *out = cred->cr_prison->pr_ip4[0]; + else + errno = EADDRNOTAVAIL; + mtx_unlock(&cred->cr_prison->pr_mtx); + return errno; +} - return (cred->cr_prison->pr_ip); +int +prison_first_ip6(struct ucred *cred, struct in6_addr *out) +{ + int errno = 0; + mtx_lock(&cred->cr_prison->pr_mtx); + if (cred->cr_prison->pr_ip6s) + *out = cred->cr_prison->pr_ip6[0]; + else + errno = EADDRNOTAVAIL; + mtx_unlock(&cred->cr_prison->pr_mtx); + return errno; } int -prison_ip(struct ucred *cred, int flag, u_int32_t *ip) +prison_match_ip4(struct ucred *cred, struct in_addr *ip) { u_int32_t tmp; if (!jailed(cred)) 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); - return (0); - } - if (tmp == INADDR_LOOPBACK) { - if (flag) - *ip = cred->cr_prison->pr_ip; - else - *ip = htonl(cred->cr_prison->pr_ip); + + for (tmp = 0; tmp < cred->cr_prison->pr_ip4s; ++tmp) + { + if (cred->cr_prison->pr_ip4[tmp].s_addr == ip->s_addr) + { + return 0; + } + } + return (1); +} + +int +prison_redirect_ip4(struct ucred *cred, struct in_addr *ip) +{ + if (!jailed(cred)) + return (0); + if (cred->cr_prison->pr_ip4s <= 0) + return (0); + + if (ip->s_addr == htonl(INADDR_ANY)) { + *ip = cred->cr_prison->pr_ip4[0]; return (0); } - if (cred->cr_prison->pr_ip != tmp) - return (1); - return (0); + if (ip->s_addr == htonl(INADDR_LOOPBACK)) { + *ip = cred->cr_prison->pr_ip4[0]; + return (0); + } + return prison_match_ip4(cred, ip); } -void -prison_remote_ip(struct ucred *cred, int flag, u_int32_t *ip) +int +prison_match_ip6(struct ucred *cred, struct in6_addr *ip) { u_int32_t tmp; if (!jailed(cred)) + return (0); + + for (tmp = 0; tmp < cred->cr_prison->pr_ip6s; ++tmp) + { + if (IN6_ARE_ADDR_EQUAL(&(cred->cr_prison->pr_ip6[tmp]), ip)) + { + return 0; + } + } + return (1); +} + + +int +prison_redirect_ip6(struct ucred *cred, struct in6_addr *ip) +{ + if (!jailed(cred)) + return (0); + if (cred->cr_prison->pr_ip6s <= 0) + return (0); + + if (IN6_ARE_ADDR_EQUAL(ip, &in6addr_any)) { + *ip = cred->cr_prison->pr_ip6[0]; + return (0); + } + if (IN6_IS_ADDR_LOOPBACK(ip)) { + *ip = cred->cr_prison->pr_ip6[0]; + return (0); + } + return prison_match_ip6(cred, ip); +} + +void +prison_remote_ip4(struct ucred *cred, struct in_addr *ip) +{ + if (!jailed(cred)) return; - if (flag) - tmp = *ip; - else - tmp = ntohl(*ip); - if (tmp == INADDR_LOOPBACK) { - if (flag) - *ip = cred->cr_prison->pr_ip; - else - *ip = htonl(cred->cr_prison->pr_ip); + if (cred->cr_prison->pr_ip4s <= 0) + return; + if (ip->s_addr == INADDR_LOOPBACK) { + *ip = cred->cr_prison->pr_ip4[0]; + return; + } + return; +} + +void +prison_remote_ip6(struct ucred *cred, struct in6_addr *ip) +{ + if (!jailed(cred)) + return; + if (cred->cr_prison->pr_ip6s <= 0) + return; + if (IN6_IS_ADDR_LOOPBACK(ip)) { + *ip = cred->cr_prison->pr_ip6[0]; return; } return; @@ -374,18 +613,43 @@ int prison_if(struct ucred *cred, struct sockaddr *sa) { - struct sockaddr_in *sai; - int ok; + int ok = 0; - sai = (struct sockaddr_in *)sa; - if ((sai->sin_family != AF_INET) && jail_socket_unixiproute_only) + if (!(sa->sa_family == AF_INET || sa->sa_family == AF_INET6) + && 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; + else + { + if (sa->sa_family == AF_INET) + { + int tmp; + struct sockaddr_in *sai = (struct sockaddr_in *)sa; + ok = 1; + for (tmp = 0; tmp < cred->cr_prison->pr_ip4s; ++tmp) + { + if (cred->cr_prison->pr_ip4[tmp].s_addr == sai->sin_addr.s_addr) + { + ok = 0; + break; + } + } + } + else if (sa->sa_family == AF_INET6) + { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; + int tmp; + ok = 1; + for (tmp = 0; tmp < cred->cr_prison->pr_ip6s; ++tmp) + { + if (IN6_ARE_ADDR_EQUAL(&(cred->cr_prison->pr_ip6[tmp]), + &(sa6->sin6_addr))) + { + ok = 0; + break; + } + } + } + } return (ok); } @@ -407,6 +671,138 @@ } /* + * assumes that mtx_lock (pr->mtx) is done + */ +static int +prison_add_ip4(struct in_addr *ip, struct prison *pr, void *vpr_new, void **pr_free) +{ + struct in_addr *pr_new = vpr_new; + struct in_addr *wrk_new; + + wrk_new = pr_new; + if (pr->pr_ip4s > 0) + { + struct in_addr *pr_old = pr->pr_ip4; + for (;pr_old < &(pr->pr_ip4[pr->pr_ip4s]) ; ++pr_old) + { + if (pr_old->s_addr == ip->s_addr) + { + *pr_free = pr_new; + return 0; + } + *wrk_new++ = *pr_old; + } + } + *wrk_new = *ip; + ++(pr->pr_ip4s); + if (pr->pr_ip4) + *pr_free = pr->pr_ip4; + pr->pr_ip4 = pr_new; + return 0; +} + +static int +prison_add_ip6(struct in6_addr *ip, struct prison *pr, void *vpr_new, void **pr_free) +{ + struct in6_addr *pr_new = vpr_new; + struct in6_addr *wrk_new; + + wrk_new = pr_new; + if (pr->pr_ip6s > 0) + { + struct in6_addr *pr_old = pr->pr_ip6; + for (;pr_old < &(pr->pr_ip6[pr->pr_ip6s]) ; ++pr_old) + { + if (IN6_ARE_ADDR_EQUAL(pr_old, ip)) + { + *pr_free = pr_new; + return 0; + } + *wrk_new++ = *pr_old; + } + } + *wrk_new = *ip; + ++(pr->pr_ip6s); + if (pr->pr_ip6) + *pr_free = pr->pr_ip6; + pr->pr_ip6 = pr_new; + return 0; +} + +static int +prison_del_ip4(struct in_addr *ip, struct prison *pr, void *vpr_new, void **pr_free) +{ + struct in_addr *pr_new = vpr_new; + struct in_addr *wrk_new; + int errno = ENOENT; + + wrk_new = pr_new; + if (pr->pr_ip4s > 0) + { + struct in_addr *pr_old = pr->pr_ip4; + for (;pr_old < &(pr->pr_ip4[pr->pr_ip4s]) ; ++pr_old) + { + if (pr_old->s_addr != ip->s_addr) + { + if (wrk_new) { /* remove only if one or more elements remaining */ + *wrk_new++ = *pr_old; + } + } + else + { + errno = 0; /* found element to delete */ + } + } + } + if (errno == 0) + { + --(pr->pr_ip4s); + /* attach new array */ + if (pr->pr_ip4) + *pr_free = pr->pr_ip4; + pr->pr_ip4 = pr_new; + } + return errno; +} + +static int +prison_del_ip6(struct in6_addr *ip, struct prison *pr, void *vpr_new, void **pr_free) +{ + struct in6_addr *pr_new = vpr_new; + struct in6_addr *wrk_new; + int errno = ENOENT; + + wrk_new = pr_new; + if (pr->pr_ip6s > 0) + { + struct in6_addr *pr_old = pr->pr_ip6; + for (;pr_old < &(pr->pr_ip6[pr->pr_ip6s]) ; ++pr_old) + { + if (!IN6_ARE_ADDR_EQUAL(pr_old, ip)) + { + if (wrk_new) { /* remove only if one or more elements remaining */ + *wrk_new++ = *pr_old; + } + } + else + { + errno = 0; /* found element to delete */ + } + } + } + if (errno == 0) + { + --(pr->pr_ip6s); + /* attach new array */ + if (pr->pr_ip6) + *pr_free = pr->pr_ip6; + pr->pr_ip6 = pr_new; + } + return errno; +} + + +/* * Return 1 if the passed credential is in a jail, otherwise 0. */ int @@ -452,35 +848,67 @@ struct xprison *xp, *sxp; struct prison *pr; int count, error; + int prcount; mtx_assert(&Giant, MA_OWNED); if (jailed(req->td->td_ucred)) return (0); retry: mtx_lock(&allprison_mtx); - count = prisoncount; + count = 0; + prcount = 0; + LIST_FOREACH(pr, &allprison, pr_list) { + ++prcount; + if (pr->pr_ip4s || pr->pr_ip6s) { + count += max(pr->pr_ip4s, pr->pr_ip6s); + } + else { + ++count; + } + } mtx_unlock(&allprison_mtx); if (count == 0) return (0); + /*printf("jls:count=%d:%d\n", count, prcount);*/ sxp = xp = malloc(sizeof(*xp) * count, M_TEMP, M_WAITOK | M_ZERO); mtx_lock(&allprison_mtx); - if (count != prisoncount) { + if (prcount != prisoncount) { mtx_unlock(&allprison_mtx); free(sxp, M_TEMP); goto retry; } LIST_FOREACH(pr, &allprison, pr_list) { - mtx_lock(&pr->pr_mtx); - xp->pr_version = XPRISON_VERSION; - 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; - mtx_unlock(&pr->pr_mtx); - xp++; + int id = 0; + int i; + int maxcnt = max(pr->pr_ip4s, pr->pr_ip6s); + /*printf("jls:maxcnt=%d\n", maxcnt);*/ + for (i = 0; i < (maxcnt ? maxcnt : 1) ; ++i) + { + /*printf("jls:-0-:%d:%d\n", i, maxcnt);*/ + mtx_lock(&pr->pr_mtx); + xp->pr_version = XPRISON_VERSION; + 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)); + if (i < pr->pr_ip4s) { + xp->pr4_id = id; + xp->pr4_num = pr->pr_ip4[i]; + } + else + xp->pr4_id = -1; + if (i < pr->pr_ip6s) { + xp->pr6_id = id; + xp->pr6_num = pr->pr_ip6[i]; + } + else + xp->pr6_id = -1; + ++id; + mtx_unlock(&pr->pr_mtx); + xp++; + } } mtx_unlock(&allprison_mtx); Index: sys/net/rtsock.c =================================================================== RCS file: /usr/freebsd.cvs/src/sys/net/rtsock.c,v retrieving revision 1.112 diff -u -r1.112 rtsock.c --- sys/net/rtsock.c 6 Jul 2004 03:29:41 -0000 1.112 +++ sys/net/rtsock.c 14 Jul 2004 17:52:25 -0000 @@ -322,7 +322,8 @@ int len, error = 0; struct ifnet *ifp = NULL; struct ifaddr *ifa = NULL; - struct sockaddr_in jail; + struct sockaddr_in jail4; + struct sockaddr_in6 jail6; #define senderr(e) { error = e; goto flush;} if (m == NULL || ((m->m_len < sizeof(long)) && @@ -437,13 +438,32 @@ info.rti_info[RTAX_IFP] = ifaddr_byindex(ifp->if_index)->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)); - info.rti_info[RTAX_IFA] = - (struct sockaddr *)&jail; + if (rt->rt_ifa->ifa_addr->sa_family == PF_INET) { + bzero(&jail4, sizeof(jail4)); + jail4.sin_family = PF_INET; + jail4.sin_len = sizeof(jail4); + error = prison_first_ip4(so->so_cred, + &jail4.sin_addr); + if (error) + senderr(error); + info.rti_info[RTAX_IFA] = + (struct sockaddr *)&jail4; + } + else if (rt->rt_ifa->ifa_addr->sa_family == PF_INET6) { + bzero(&jail6, sizeof(jail6)); + jail6.sin6_family = PF_INET6; + jail6.sin6_len = sizeof(jail6); + error = prison_first_ip6(so->so_cred, + &jail6.sin6_addr); + if (error) + senderr(error); + info.rti_info[RTAX_IFA] = + (struct sockaddr *)&jail6; + } + else { + info.rti_info[RTAX_IFA] = + rt->rt_ifa->ifa_addr; + } } else info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr; Index: sys/netinet/in_pcb.c =================================================================== RCS file: /usr/freebsd.cvs/src/sys/netinet/in_pcb.c,v retrieving revision 1.152 diff -u -r1.152 in_pcb.c --- sys/netinet/in_pcb.c 28 Jul 2004 13:03:07 -0000 1.152 +++ sys/netinet/in_pcb.c 30 Jul 2004 22:46:26 -0000 @@ -290,7 +290,7 @@ return (EAFNOSUPPORT); #endif if (sin->sin_addr.s_addr != INADDR_ANY) - if (prison_ip(cred, 0, &sin->sin_addr.s_addr)) + if (prison_redirect_ip4(cred, &sin->sin_addr)) return(EINVAL); if (sin->sin_port != *lportp) { /* Don't allow the port to change. */ @@ -346,7 +346,7 @@ t->inp_socket->so_cred->cr_uid)) return (EADDRINUSE); } - if (prison && prison_ip(cred, 0, &sin->sin_addr.s_addr)) + if (prison && prison_redirect_ip4(cred, &sin->sin_addr)) return (EADDRNOTAVAIL); t = in_pcblookup_local(pcbinfo, sin->sin_addr, lport, prison ? 0 : wild); @@ -375,7 +375,7 @@ int count; if (laddr.s_addr != INADDR_ANY) - if (prison_ip(cred, 0, &laddr.s_addr)) + if (prison_redirect_ip4(cred, &laddr)) return (EINVAL); if (inp->inp_flags & INP_HIGHPORT) { @@ -438,7 +438,7 @@ wild)); } } - if (prison_ip(cred, 0, &laddr.s_addr)) + if (prison_redirect_ip4(cred, &laddr)) return (EINVAL); *laddrp = laddr.s_addr; *lportp = lport; @@ -545,7 +545,9 @@ 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)); + error = prison_first_ip4(socred, &sa.sin_addr); + if (error) + return (error); sa.sin_len = sizeof(sa); sa.sin_family = AF_INET; error = in_pcbbind_setup(inp, (struct sockaddr *)&sa, Index: sys/netinet/raw_ip.c =================================================================== RCS file: /usr/freebsd.cvs/src/sys/netinet/raw_ip.c,v retrieving revision 1.137 diff -u -r1.137 raw_ip.c --- sys/netinet/raw_ip.c 26 Jul 2004 07:24:03 -0000 1.137 +++ sys/netinet/raw_ip.c 30 Jul 2004 22:46:26 -0000 @@ -213,9 +213,9 @@ if (inp->inp_faddr.s_addr && inp->inp_faddr.s_addr != ip->ip_src.s_addr) goto docontinue; + if (jailed(inp->inp_socket->so_cred)) - if (htonl(prison_getip(inp->inp_socket->so_cred)) != - ip->ip_dst.s_addr) + if (prison_match_ip4(inp->inp_socket->so_cred, &(ip->ip_dst))) goto docontinue; if (last) { struct mbuf *n; @@ -272,8 +272,16 @@ ip->ip_p = inp->inp_ip_p; ip->ip_len = m->m_pkthdr.len; if (jailed(inp->inp_socket->so_cred)) - ip->ip_src.s_addr = - htonl(prison_getip(inp->inp_socket->so_cred)); + { + /* fallback to first ip */ + if (prison_match_ip4(inp->inp_socket->so_cred, &inp->inp_laddr)) { + if ((error = prison_first_ip4(inp->inp_socket->so_cred, + &ip->ip_src)) != 0) + return error; + } + else + ip->ip_src = inp->inp_laddr; + } else ip->ip_src = inp->inp_laddr; ip->ip_dst.s_addr = dst; @@ -286,8 +294,7 @@ INP_LOCK(inp); ip = mtod(m, struct ip *); if (jailed(inp->inp_socket->so_cred)) { - if (ip->ip_src.s_addr != - htonl(prison_getip(inp->inp_socket->so_cred))) { + if (prison_match_ip4(inp->inp_socket->so_cred, &ip->ip_src)) { INP_UNLOCK(inp); m_freem(m); return (EPERM); @@ -658,10 +665,11 @@ return EINVAL; if (jailed(td->td_ucred)) { + int error; 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) + if ((error = prison_first_ip4(td->td_ucred, &addr->sin_addr)) != 0) + return error; + if (prison_match_ip4(td->td_ucred, &addr->sin_addr)) return (EADDRNOTAVAIL); } Index: sys/netinet/tcp_usrreq.c =================================================================== RCS file: /usr/freebsd.cvs/src/sys/netinet/tcp_usrreq.c,v retrieving revision 1.105 diff -u -r1.105 tcp_usrreq.c --- sys/netinet/tcp_usrreq.c 26 Jul 2004 21:29:56 -0000 1.105 +++ sys/netinet/tcp_usrreq.c 30 Jul 2004 22:46:27 -0000 @@ -357,7 +357,7 @@ && IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) return (EAFNOSUPPORT); if (td && jailed(td->td_ucred)) - prison_remote_ip(td->td_ucred, 0, &sinp->sin_addr.s_addr); + prison_remote_ip4(td->td_ucred, &sinp->sin_addr); COMMON_START(); if ((error = tcp_connect(tp, nam, td)) != 0) @@ -386,6 +386,9 @@ && IN6_IS_ADDR_MULTICAST(&sin6p->sin6_addr)) return (EAFNOSUPPORT); + if (td && jailed(td->td_ucred)) + prison_remote_ip6(td->td_ucred, &sin6p->sin6_addr); + COMMON_START(); if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { struct sockaddr_in sin; Index: sys/netinet/udp_usrreq.c =================================================================== RCS file: /usr/freebsd.cvs/src/sys/netinet/udp_usrreq.c,v retrieving revision 1.157 diff -u -r1.157 udp_usrreq.c --- sys/netinet/udp_usrreq.c 26 Jul 2004 07:24:03 -0000 1.157 +++ sys/netinet/udp_usrreq.c 30 Jul 2004 22:46:27 -0000 @@ -806,7 +806,7 @@ if (addr) { sin = (struct sockaddr_in *)addr; if (td && jailed(td->td_ucred)) - prison_remote_ip(td->td_ucred, 0, &sin->sin_addr.s_addr); + prison_remote_ip4(td->td_ucred, &sin->sin_addr); if (inp->inp_faddr.s_addr != INADDR_ANY) { error = EISCONN; goto release; @@ -1002,7 +1002,7 @@ s = splnet(); sin = (struct sockaddr_in *)nam; if (td && jailed(td->td_ucred)) - prison_remote_ip(td->td_ucred, 0, &sin->sin_addr.s_addr); + prison_remote_ip4(td->td_ucred, &sin->sin_addr); error = in_pcbconnect(inp, nam, td->td_ucred); splx(s); if (error == 0) Index: sys/netinet6/in6_pcb.c =================================================================== RCS file: /usr/freebsd.cvs/src/sys/netinet6/in6_pcb.c,v retrieving revision 1.57 diff -u -r1.57 in6_pcb.c --- sys/netinet6/in6_pcb.c 28 Jul 2004 13:03:07 -0000 1.57 +++ sys/netinet6/in6_pcb.c 31 Jul 2004 07:38:54 -0000 @@ -128,6 +128,7 @@ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)NULL; struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; u_short lport = 0; + int prison = 0; int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); INP_INFO_WLOCK_ASSERT(pcbinfo); @@ -149,6 +150,10 @@ if (nam->sa_family != AF_INET6) return (EAFNOSUPPORT); + if (!IN6_ARE_ADDR_EQUAL(&in6addr_any, &sin6->sin6_addr)) + if (prison_redirect_ip6(cred, &sin6->sin6_addr)) + return (EINVAL); + /* KAME hack: embed scopeid */ if (in6_embedscope(&sin6->sin6_addr, sin6, inp, NULL) != 0) return EINVAL; @@ -192,12 +197,20 @@ if (ntohs(lport) < IPV6PORT_RESERVED && suser_cred(cred, SUSER_ALLOWJAIL)) return (EACCES); + prison = jailed(cred); if (so->so_cred->cr_uid != 0 && !IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { t = in6_pcblookup_local(pcbinfo, &sin6->sin6_addr, lport, - INPLOOKUP_WILDCARD); - if (t && + prison ? 0 : INPLOOKUP_WILDCARD); + if (t && (t->inp_vflag & INP_TIMEWAIT)) { + if ((!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) || + !IN6_IS_ADDR_UNSPECIFIED(&t->in6p_laddr) || + !(intotw(t)->tw_so_options & SO_REUSEPORT)) + && so->so_cred->cr_uid != + intotw(t)->tw_cred->cr_uid) + return (EADDRINUSE); + } else if (t && ((t->inp_vflag & INP_TIMEWAIT) == 0) && (so->so_type != SOCK_STREAM || IN6_IS_ADDR_UNSPECIFIED(&t->in6p_faddr)) && @@ -226,8 +239,10 @@ return (EADDRINUSE); } } + if (prison && prison_redirect_ip6(cred, &sin6->sin6_addr)) + return (EADDRNOTAVAIL); t = in6_pcblookup_local(pcbinfo, &sin6->sin6_addr, - lport, wild); + lport, prison ? 0 : wild); if (t && (reuseport & ((t->inp_vflag & INP_TIMEWAIT) ? intotw(t)->tw_so_options : t->inp_socket->so_options)) == 0) Index: sys/netinet6/raw_ip6.c =================================================================== RCS file: /usr/freebsd.cvs/src/sys/netinet6/raw_ip6.c,v retrieving revision 1.43 diff -u -r1.43 raw_ip6.c --- sys/netinet6/raw_ip6.c 27 Jul 2004 23:45:19 -0000 1.43 +++ sys/netinet6/raw_ip6.c 31 Jul 2004 07:41:50 -0000 @@ -68,6 +68,7 @@ #include #include #include +#include #include #include #include @@ -174,6 +175,11 @@ goto docontinue; } } + + if (jailed(in6p->in6p_socket->so_cred)) + if (prison_match_ip6(in6p->in6p_socket->so_cred, &(ip6->ip6_dst))) + continue; + if (last) { struct mbuf *n = m_copy(m, 0, (int)M_COPYALL); @@ -428,7 +434,20 @@ error = EADDRNOTAVAIL; goto bad; } - ip6->ip6_src = *in6a; + if (jailed(in6p->in6p_socket->so_cred)) + { + /* fallback to first ip */ + if (prison_match_ip6(in6p->in6p_socket->so_cred, in6a)) { + if ((error = prison_first_ip6(in6p->in6p_socket->so_cred, + &ip6->ip6_src)) != 0) + return error; + } + else + ip6->ip6_src = *in6a; + } + else + ip6->ip6_src = *in6a; + ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) | (in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK); ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) | @@ -573,6 +592,10 @@ INP_INFO_WUNLOCK(&ripcbinfo); return error; } + if (td && jailed(td->td_ucred) && !jail_allow_raw_sockets) { + return (EPERM); + } + error = soreserve(so, rip_sendspace, rip_recvspace); if (error) { INP_INFO_WUNLOCK(&ripcbinfo); @@ -650,6 +673,16 @@ if (nam->sa_len != sizeof(*addr)) return EINVAL; + + if (jailed(td->td_ucred)) { + if (IN6_ARE_ADDR_EQUAL(&(addr->sin6_addr), &in6addr_any)) { + int error; + if ((error = prison_first_ip6(td->td_ucred, &addr->sin6_addr)) != 0) + return error; + } + if (prison_match_ip6(td->td_ucred, &addr->sin6_addr)) + return (EADDRNOTAVAIL); + } if (TAILQ_EMPTY(&ifnet) || addr->sin6_family != AF_INET6) return EADDRNOTAVAIL; #ifdef ENABLE_DEFAULT_SCOPE Index: sys/netinet6/udp6_output.c =================================================================== RCS file: /usr/freebsd.cvs/src/sys/netinet6/udp6_output.c,v retrieving revision 1.18 diff -u -r1.18 udp6_output.c --- sys/netinet6/udp6_output.c 7 Apr 2004 20:46:16 -0000 1.18 +++ sys/netinet6/udp6_output.c 14 Jul 2004 18:48:13 -0000 @@ -67,6 +67,7 @@ #include #include +#include #include #include #include @@ -166,6 +167,8 @@ error = EISCONN; goto release; } + if (td && jailed(td->td_ucred)) + prison_remote_ip6(td->td_ucred, &sin6->sin6_addr); /* protect *sin6 from overwrites */ tmp = *sin6; Index: sys/netinet6/udp6_usrreq.c =================================================================== RCS file: /usr/freebsd.cvs/src/sys/netinet6/udp6_usrreq.c,v retrieving revision 1.50 diff -u -r1.50 udp6_usrreq.c --- sys/netinet6/udp6_usrreq.c 27 Jul 2004 23:45:19 -0000 1.50 +++ sys/netinet6/udp6_usrreq.c 30 Jul 2004 22:46:27 -0000 @@ -67,6 +67,7 @@ #include #include +#include #include #include #include @@ -604,6 +605,7 @@ { struct inpcb *inp; int s, error; + struct sockaddr_in6 *sin6_p; INP_INFO_WLOCK(&udbinfo); inp = sotoinpcb(so); @@ -614,8 +616,6 @@ INP_LOCK(inp); if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { - struct sockaddr_in6 *sin6_p; - sin6_p = (struct sockaddr_in6 *)nam; if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) { struct sockaddr_in sin; @@ -640,6 +640,10 @@ goto out; } s = splnet(); + sin6_p = (struct sockaddr_in6 *)nam; + if (td && jailed(td->td_ucred)) + prison_remote_ip6(td->td_ucred, &sin6_p->sin6_addr); + error = in6_pcbconnect(inp, nam, td->td_ucred); splx(s); if (error == 0) { Index: sys/nfsclient/nfs_vfsops.c =================================================================== RCS file: /usr/freebsd.cvs/src/sys/nfsclient/nfs_vfsops.c,v retrieving revision 1.157 diff -u -r1.157 nfs_vfsops.c --- sys/nfsclient/nfs_vfsops.c 28 Jul 2004 20:21:04 -0000 1.157 +++ sys/nfsclient/nfs_vfsops.c 30 Jul 2004 22:46:29 -0000 @@ -507,6 +507,7 @@ mp->mnt_kern_flag = 0; mp->mnt_flag = mountflag; nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK); + args->flags|=NFSMNT_NOLOCKD; if ((error = mountnfs(args, mp, nam, which, path, vpp, td->td_ucred)) != 0) { printf("nfs_mountroot: mount %s on %s: %d", path, which, error); Index: sys/sys/jail.h =================================================================== RCS file: /usr/freebsd.cvs/src/sys/sys/jail.h,v retrieving revision 1.21 diff -u -r1.21 jail.h --- sys/sys/jail.h 26 Apr 2004 19:46:52 -0000 1.21 +++ sys/sys/jail.h 14 Jul 2004 18:36:05 -0000 @@ -6,18 +6,59 @@ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * - * $FreeBSD$ + * $FreeBSD: src/sys/sys/jail.h,v 1.18 2003/04/09 02:55:18 mike Exp $ * */ #ifndef _SYS_JAIL_H_ #define _SYS_JAIL_H_ +#include + +/* + * to safe a system call i reuse the jail systemcall to + * to modify a jail. I will enable the ability to add + * and remove ip4/6 numbers to a jail. + * To get rid of it i playing around with version and + * function numbers. + * A jail id is only create on setup path and hostname + * these values are inmutable. The function number is + * CREATEJAIL + */ struct jail { - u_int32_t version; - char *path; - char *hostname; - u_int32_t ip_number; + u_int32_t version; + union { + struct v1_s { + char *path; + char *hostname; + u_int32_t ip_number; + } v1; + struct v2_s { + u_int32_t function; + #define CREATEJAIL 1 + #define ADDIP4 2 + #define DELIP4 3 + #define ADDIP6 4 + #define DELIP6 5 + union + { + struct + { + char *path; + char *hostname; + } createjail; + struct + { + int id; + union + { + struct in_addr ip4_num; + struct in6_addr ip6_num; + } v4_6; + } add_del; + } u; + } v2; + } u; }; struct xprison { @@ -25,9 +66,12 @@ int pr_id; char pr_path[MAXPATHLEN]; char pr_host[MAXHOSTNAMELEN]; - u_int32_t pr_ip; + int pr4_id; + struct in_addr pr4_num; /* null is empty */ + int pr6_id; + struct in6_addr pr6_num; /* null is empty */ }; -#define XPRISON_VERSION 1 +#define XPRISON_VERSION 6 #ifndef _KERNEL @@ -37,9 +81,9 @@ #else /* _KERNEL */ #include +#include #include #include -#include #define JAIL_MAX 999999 @@ -55,10 +99,12 @@ * Lock key: * (a) allprison_mutex * (p) locked by pr_mutex + * (d) set only during destruction of jail, no mutex needed * (c) set only during creation before the structure is shared, no mutex * required to read - * (d) set only during destruction of jail, no mutex needed */ + +struct mtx; struct prison { LIST_ENTRY(prison) pr_list; /* (a) all prisons */ int pr_id; /* (c) prison id */ @@ -66,10 +112,13 @@ 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 */ + int pr_ip4s; /* (p) ipv4 addr count */ + struct in_addr *pr_ip4; /* (p) ipv4 addr host */ + int pr_ip6s; /* (p) ipv6 addr count */ + struct in6_addr *pr_ip6; /* (p) ipv6 addr host */ + struct task pr_task; /* (d) destroy task */ void *pr_linux; /* (p) linux abi */ int pr_securelevel; /* (p) securelevel */ - struct task pr_task; /* (d) destroy task */ struct mtx pr_mtx; }; @@ -78,11 +127,11 @@ * * XXX MIB entries will need to be protected by a mutex. */ +extern int jail_getfsstat_jailrootonly; +extern int jail_allow_raw_sockets; extern int jail_set_hostname_allowed; extern int jail_socket_unixiproute_only; extern int jail_sysvipc_allowed; -extern int jail_getfsstat_jailrootonly; -extern int jail_allow_raw_sockets; LIST_HEAD(prisonlist, prison); extern struct prisonlist allprison; @@ -91,18 +140,24 @@ * Kernel support functions for jail(). */ struct ucred; -struct mount; struct sockaddr; +struct mount; int jailed(struct ucred *cred); void getcredhostname(struct ucred *cred, char *, size_t); -int prison_check(struct ucred *cred1, struct ucred *cred2); int prison_check_mount(struct ucred *cred, struct mount *mp); +int prison_check(struct ucred *cred1, struct ucred *cred2); void prison_free(struct prison *pr); -u_int32_t prison_getip(struct ucred *cred); +int prison_first_ip6(struct ucred *cred, struct in6_addr *out); +int prison_first_ip4(struct ucred *cred, struct in_addr *out); +int prison_match_ip4(struct ucred *cred, struct in_addr *in); +int prison_match_ip6(struct ucred *cred, struct in6_addr *in); 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 prison_redirect_ip4(struct ucred *cred, struct in_addr *ip); +int prison_redirect_ip6(struct ucred *cred, struct in6_addr *ip); +void prison_remote_ip4(struct ucred *cred, struct in_addr *ip); +void prison_remote_ip6(struct ucred *cred, struct in6_addr *ip); + #endif /* !_KERNEL */ #endif /* !_SYS_JAIL_H_ */