Bug #518 ยป jail-fixaddr.diff
| sys/jail.h 17 Jan 2007 12:45:47 -0000 | ||
|---|---|---|
| /* Used to store the IPs of the jail */ | ||
| struct jail_ip_storage { | ||
| 	SLIST_ENTRY(jail_ip_storage) entries; | ||
| 	struct sockaddr_storage ip; | ||
| 	SLIST_ENTRY(jail_ip_storage) entries; | ||
| }; | ||
| /* | ||
| ... | ... | |
| 	struct nchandle pr_root;			/* namecache entry of root */ | ||
| 	char 		pr_host[MAXHOSTNAMELEN];	/* host name */ | ||
| 	SLIST_HEAD(iplist, jail_ip_storage) pr_ips;	/* list of IP addresses */ | ||
| 	struct sockaddr_storage	*local_ip4;		/* cache for a loopback ipv4 address */ | ||
| 	struct sockaddr_storage	*nonlocal_ip4;		/* cache for a non loopback ipv4 address */ | ||
| 	struct sockaddr_storage	*local_ip6;		/* cache for a loopback ipv6 address */ | ||
| 	struct sockaddr_storage	*nonlocal_ip6;		/* cache for a non loopback ipv6 address */ | ||
| 	struct sockaddr_in	*local_ip4;		/* cache for a loopback ipv4 address */ | ||
| 	struct sockaddr_in	*nonlocal_ip4;		/* cache for a non loopback ipv4 address */ | ||
| 	struct sockaddr_in6	*local_ip6;		/* cache for a loopback ipv6 address */ | ||
| 	struct sockaddr_in6	*nonlocal_ip6;		/* cache for a non loopback ipv6 address */ | ||
| 	void		*pr_linux;			/* Linux ABI emulation */ | ||
| 	int		 pr_securelevel;		/* jail securelevel */ | ||
| 	struct varsymset pr_varsymset;			/* jail varsyms */ | ||
| ... | ... | |
| void	prison_hold(struct prison *); | ||
| void	prison_free(struct prison *); | ||
| int	jailed_ip(struct prison *, struct sockaddr *); | ||
| int	prison_get_local(struct prison *pr, struct sockaddr *); | ||
| int	prison_get_nonlocal(struct prison *pr, struct sockaddr *); | ||
| struct sockaddr * | ||
| 	prison_get_local(struct prison *pr, sa_family_t, struct sockaddr *); | ||
| struct sockaddr * | ||
| 	prison_get_nonlocal(struct prison *pr, sa_family_t, struct sockaddr *); | ||
| /* | ||
|  * Return 1 if the passed credential is in a jail, otherwise 0. | ||
| kern/kern_jail.c 17 Jan 2007 12:53:16 -0000 | ||
|---|---|---|
| #include <netinet6/in6_var.h> | ||
| static struct prison	*prison_find(int); | ||
| static void		prison_ipcache_init(struct prison *); | ||
| MALLOC_DEFINE(M_PRISON, "prison", "Prison structures"); | ||
| ... | ... | |
| 	cache_copy(&nd.nl_nch, &pr->pr_root); | ||
| 	varsymset_init(&pr->pr_varsymset, NULL); | ||
| 	prison_ipcache_init(pr); | ||
| 	tryprid = lastprid + 1; | ||
| 	if (tryprid == JAIL_MAX) | ||
| ... | ... | |
| 	return(kern_jail_attach(uap->jid)); | ||
| } | ||
| static void | ||
| prison_ipcache_init(struct prison *pr) | ||
| { | ||
| 	struct jail_ip_storage *jis; | ||
| 	struct sockaddr_in *ip4; | ||
| 	struct sockaddr_in6 *ip6; | ||
| 	SLIST_FOREACH(jis, &pr->pr_ips, entries) { | ||
| 		switch (jis->ip.ss_family) { | ||
| 		case AF_INET: | ||
| 			ip4 = (struct sockaddr_in *)&jis->ip; | ||
| 			if ((ntohl(ip4->sin_addr.s_addr) >> IN_CLASSA_NSHIFT) == | ||
| 			    IN_LOOPBACKNET) { | ||
| 				/* loopback address */ | ||
| 				if (pr->local_ip4 == NULL) | ||
| 					pr->local_ip4 = ip4; | ||
| 			} else { | ||
| 				/* public address */ | ||
| 				if (pr->nonlocal_ip4 == NULL) | ||
| 					pr->nonlocal_ip4 = ip4; | ||
| 			} | ||
| 			break; | ||
| 		case AF_INET6: | ||
| 			ip6 = (struct sockaddr_in6 *)&jis->ip; | ||
| 			if (IN6_IS_ADDR_LOOPBACK(&ip6->sin6_addr)) { | ||
| 				/* loopback address */ | ||
| 				if (pr->local_ip6 == NULL) | ||
| 					pr->local_ip6 = ip6; | ||
| 			} else { | ||
| 				/* public address */ | ||
| 				if (pr->nonlocal_ip6 == NULL) | ||
| 					pr->nonlocal_ip6 = ip6; | ||
| 			} | ||
| 			break; | ||
| 		} | ||
| 	} | ||
| } | ||
| /*  | ||
|  * Changes INADDR_LOOPBACK for a valid jail address. | ||
|  * ip is in network byte order. | ||
| ... | ... | |
| 	    ip4->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) || | ||
| 	    (ip->sa_family == AF_INET6 && | ||
| 	    IN6_IS_ADDR_LOOPBACK(&ip6->sin6_addr))) { | ||
| 		if (!prison_get_local(pr, ip) && !prison_get_nonlocal(pr, ip)) | ||
| 		if (!prison_get_local(pr, ip->sa_family, ip) && | ||
| 		    !prison_get_nonlocal(pr, ip->sa_family, ip)) | ||
| 			return(0); | ||
| 		else | ||
| 			return(1); | ||
| ... | ... | |
| 	    ip4->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) || | ||
| 	    (ip->sa_family == AF_INET6 && | ||
| 	    IN6_IS_ADDR_LOOPBACK(&ip6->sin6_addr))) { | ||
| 		if (!prison_get_local(pr, ip) && !prison_get_nonlocal(pr, ip)) | ||
| 		if (!prison_get_local(pr, ip->sa_family, ip) && | ||
| 		    !prison_get_nonlocal(pr, ip->sa_family, ip)) | ||
| 			return(0); | ||
| 		else | ||
| 			return(1); | ||
| ... | ... | |
| /* | ||
|  * Prison get non loopback ip: | ||
|  * Put on *ip the first IP address that is not a loopback address. | ||
|  * af is the address family of the ip we want (AF_INET|AF_INET6). | ||
|  * - af is the address family of the ip we want (AF_INET|AF_INET6). | ||
|  * - If ip != NULL, put the first IP address that is not a loopback address | ||
|  *   into *ip. | ||
|  * | ||
|  * ip is in network by order and we don't touch it unless we find a valid ip. | ||
|  * Return 1 if we've found a non loopback ip, else return 0. | ||
|  * No matter if ip == NULL or not, we return either a valid struct sockaddr *, | ||
|  * or NULL.  This struct may not be modified. | ||
|  */ | ||
| int | ||
| prison_get_nonlocal(struct prison *pr, struct sockaddr *ip) | ||
| struct sockaddr * | ||
| prison_get_nonlocal(struct prison *pr, sa_family_t af, struct sockaddr *ip) | ||
| { | ||
| 	struct jail_ip_storage *jis; | ||
| 	struct sockaddr_in *jip4, *ip4; | ||
| 	struct sockaddr_in6 *jip6, *ip6; | ||
| 	struct sockaddr_in *ip4 = (struct sockaddr_in *)ip; | ||
| 	struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)ip; | ||
| 	ip4 = (struct sockaddr_in *)ip; | ||
| 	ip6 = (struct sockaddr_in6 *)ip; | ||
| 	/* Check if it is cached */ | ||
| 	switch(ip->sa_family) { | ||
| 		case AF_INET: | ||
| 			/* -1 Means that we don't have any address */ | ||
| 			if (pr->nonlocal_ip4 == (struct sockaddr_storage *)-1) | ||
| 				return(0); | ||
| 			if (pr->nonlocal_ip4 != NULL) { | ||
| 				jip4 = (struct sockaddr_in *) pr->nonlocal_ip4; | ||
| 				ip4->sin_addr.s_addr = jip4->sin_addr.s_addr; | ||
| 			} | ||
| 		break; | ||
| 		case AF_INET6: | ||
| 			/* -1 Means that we don't have any address */ | ||
| 			if (pr->nonlocal_ip6 == (struct sockaddr_storage *)-1) | ||
| 				return(0); | ||
| 			if (pr->nonlocal_ip6 != NULL) { | ||
| 				jip6 = (struct sockaddr_in6 *) pr->nonlocal_ip6; | ||
| 				ip6->sin6_addr = jip6->sin6_addr; | ||
| 			} | ||
| 		break; | ||
| 	}; | ||
| 	SLIST_FOREACH(jis, &pr->pr_ips, entries) { | ||
| 		switch (ip->sa_family) { | ||
| 		case AF_INET: | ||
| 			jip4 = (struct sockaddr_in *) &jis->ip; | ||
| 			if (jip4->sin_family == AF_INET && | ||
|     ((ntohl(jip4->sin_addr.s_addr) >> IN_CLASSA_NSHIFT) != IN_LOOPBACKNET)) { | ||
| 				pr->nonlocal_ip4 = &jis->ip; | ||
| 				ip4->sin_addr.s_addr = jip4->sin_addr.s_addr; | ||
| 				return(1); | ||
| 			} | ||
| 			break; | ||
| 		case AF_INET6: | ||
| 			jip6 = (struct sockaddr_in6 *) &jis->ip; | ||
| 			if ( jip6->sin6_family == AF_INET6 && | ||
| 			     !IN6_IS_ADDR_LOOPBACK(&jip6->sin6_addr)) { | ||
| 				pr->nonlocal_ip6 = &jis->ip; | ||
| 				ip6->sin6_addr = jip6->sin6_addr; | ||
| 				return(1); | ||
| 			} | ||
| 			break; | ||
| 		} | ||
| 	switch(af) { | ||
| 	case AF_INET: | ||
| 		if (ip4 != NULL && pr->nonlocal_ip4 != NULL) | ||
| 			ip4->sin_addr.s_addr = pr->nonlocal_ip4->sin_addr.s_addr; | ||
| 		return (struct sockaddr *)pr->nonlocal_ip4; | ||
| 	case AF_INET6: | ||
| 		if (ip6 != NULL && pr->nonlocal_ip6 != NULL) | ||
| 			ip6->sin6_addr = pr->nonlocal_ip6->sin6_addr; | ||
| 		return (struct sockaddr *)pr->nonlocal_ip6; | ||
| 	} | ||
| 	if (ip->sa_family == AF_INET) | ||
| 		pr->nonlocal_ip4 = (struct sockaddr_storage *)-1; | ||
| 	else | ||
| 		pr->nonlocal_ip6 = (struct sockaddr_storage *)-1; | ||
| 	return(0); | ||
| 	/* NOTREACHED */ | ||
| 	return NULL; | ||
| } | ||
| /* | ||
|  * Prison get loopback ip. | ||
|  * Put on *ip the first loopback IP address. | ||
|  * af is the address family of the ip we want (AF_INET|PF_INET). | ||
|  * *ip is in network by order and we don't touch it unless we find a valid ip. | ||
|  * return 1 if we've found a loopback ip, else return 0. | ||
|  * - af is the address family of the ip we want (AF_INET|AF_INET6). | ||
|  * - If ip != NULL, put the first IP address that is not a loopback address | ||
|  *   into *ip. | ||
|  * | ||
|  * ip is in network by order and we don't touch it unless we find a valid ip. | ||
|  * No matter if ip == NULL or not, we return either a valid struct sockaddr *, | ||
|  * or NULL.  This struct may not be modified. | ||
|  */ | ||
| int | ||
| prison_get_local(struct prison *pr, struct sockaddr *ip) | ||
| struct sockaddr * | ||
| prison_get_local(struct prison *pr, sa_family_t af, struct sockaddr *ip) | ||
| { | ||
| 	struct jail_ip_storage *jis; | ||
| 	struct sockaddr_in *jip4, *ip4; | ||
| 	struct sockaddr_in6 *jip6, *ip6; | ||
| 	struct sockaddr_in *ip4 = (struct sockaddr_in *)ip; | ||
| 	struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)ip; | ||
| 	ip4 = (struct sockaddr_in *)ip; | ||
| 	ip6 = (struct sockaddr_in6 *)ip; | ||
| 	/* Check if it is cached */ | ||
| 	switch(ip->sa_family) { | ||
| 		case AF_INET: | ||
| 			/* -1 Means that we don't have any address */ | ||
| 			if (pr->local_ip4 == (struct sockaddr_storage *)-1) | ||
| 				return(0); | ||
| 			if (pr->local_ip4 != NULL) { | ||
| 				jip4 = (struct sockaddr_in *) pr->local_ip4; | ||
| 				ip4->sin_addr.s_addr = jip4->sin_addr.s_addr; | ||
| 			} | ||
| 		break; | ||
| 		case AF_INET6: | ||
| 			/* -1 Means that we don't have any address */ | ||
| 			if (pr->local_ip6 == (struct sockaddr_storage *)-1) | ||
| 				return(0); | ||
| 			if (pr->local_ip6 != NULL) { | ||
| 				jip6 = (struct sockaddr_in6 *) pr->local_ip6; | ||
| 				ip6->sin6_addr = jip6->sin6_addr; | ||
| 			} | ||
| 		break; | ||
| 	}; | ||
| 	SLIST_FOREACH(jis, &pr->pr_ips, entries) { | ||
| 		switch(ip->sa_family) { | ||
| 		case AF_INET: | ||
| 			jip4 = (struct sockaddr_in *) &jis->ip; | ||
| 			if (jip4->sin_family == AF_INET && | ||
| 			    ((ntohl(jip4->sin_addr.s_addr) >> IN_CLASSA_NSHIFT) | ||
| 			    == IN_LOOPBACKNET)) { | ||
| 				pr->local_ip4 = &jis->ip; | ||
| 				ip4->sin_addr.s_addr = jip4->sin_addr.s_addr; | ||
| 				return(1); | ||
| 			} | ||
| 			break; | ||
| 		case AF_INET6: | ||
| 			jip6 = (struct sockaddr_in6 *) &jis->ip; | ||
| 			if (jip6->sin6_family == AF_INET6 && | ||
| 			     IN6_IS_ADDR_LOOPBACK(&jip6->sin6_addr)) { | ||
| 				pr->local_ip6 = &jis->ip; | ||
| 				ip6->sin6_addr = jip6->sin6_addr; | ||
| 				return(1); | ||
| 			} | ||
| 			break; | ||
| 		} | ||
| 	switch(af) { | ||
| 	case AF_INET: | ||
| 		if (ip4 != NULL && pr->local_ip4 != NULL) | ||
| 			ip4->sin_addr.s_addr = pr->local_ip4->sin_addr.s_addr; | ||
| 		return (struct sockaddr *)pr->local_ip4; | ||
| 	case AF_INET6: | ||
| 		if (ip6 != NULL && pr->local_ip6 != NULL) | ||
| 			ip6->sin6_addr = pr->local_ip6->sin6_addr; | ||
| 		return (struct sockaddr *)pr->local_ip6; | ||
| 	} | ||
| 	if (ip->sa_family == AF_INET) | ||
| 		pr->local_ip4 = (struct sockaddr_storage *)-1; | ||
| 	else | ||
| 		pr->local_ip6 = (struct sockaddr_storage *)-1; | ||
| 	return(0); | ||
| 	/* NOTREACHED */ | ||
| 	return NULL; | ||
| } | ||
| /* Check if the IP is among ours, if it is return 1, else 0 */ | ||
| netinet/in_pcb.c 17 Jan 2007 12:58:07 -0000 | ||
|---|---|---|
| 	struct in_ifaddr *ia; | ||
| 	struct ucred *cred = NULL; | ||
| 	struct sockaddr_in *sin = (struct sockaddr_in *)nam; | ||
| 	struct sockaddr_in jsin; | ||
| 	struct sockaddr *jsin; | ||
| 	int jailed = 0; | ||
| 	if (nam->sa_len != sizeof *sin) | ||
| ... | ... | |
| 		 * Don't do pcblookup call here; return interface in plocal_sin | ||
| 		 * and exit to caller, that will do the lookup. | ||
| 		 */ | ||
| 		if (jailed) { | ||
| 			jsin.sin_family = AF_INET; | ||
| 			if (!prison_get_nonlocal(cred->cr_prison, | ||
| 			    sintosa(&jsin)) && | ||
| 			    !prison_get_local(cred->cr_prison, sintosa(&jsin))) | ||
| 					/* IPv6 only Jail */ | ||
| 					return (EADDRNOTAVAIL); | ||
| 			*plocal_sin = &jsin; | ||
| 		if (ia == NULL && jailed) { | ||
| 			if ((jsin = prison_get_nonlocal(cred->cr_prison, AF_INET, NULL)) != NULL || | ||
| 			    (jsin = prison_get_local(cred->cr_prison, AF_INET, NULL)) != NULL) | ||
| 				*plocal_sin = satosin(jsin); | ||
| 			else | ||
| 				/* IPv6 only Jail */ | ||
| 				return (EADDRNOTAVAIL); | ||
| 		} else { | ||
| 			*plocal_sin = &ia->ia_addr; | ||
| 		} | ||