Index: share/man/man4/mac_ipacl.4 =================================================================== --- /dev/null +++ share/man/man4/mac_ipacl.4 @@ -0,0 +1,183 @@ +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.\" Copyright (c) 2003-2004 Networks Associates Technology, Inc. +.\" Copyright (c) 2006 SPARTA, Inc. +.\" Copyright (c) 2019 Shivank Garg +.\" +.\" All rights reserved. +.\" +.\" This code was developed as a Google Summer of Code 2019 project +.\" under the guidance of Mr. Bjoern A. Zeeb. +.\" +.\" 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 AUTHORS 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 AUTHORS 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. +.\" +.\" $FreeBSD$ +.\" +.Dd July 25, 2019 +.Dt MAC_IPACL 4 +.Os +.Sh NAME +.Nm mac_ipacl +.Nd "IP Address access control policy" +.Sh SYNOPSIS +To compile the IP address access control policy into +your kernel, add the following lines in your kernel configuration file: +.Bd -ragged -offset indent +.Cd "options MAC" +.Cd "options MAC_IPACL" +.Ed +.Pp +The +.Nm +module can be loaded at boot time, add the following line in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "options MAC" +.Ed +.Pp +and in +.Xr loader.conf 5 : +.Pp +.Dl "mac_ipacl_load=""YES""" +.Sh DESCRIPTION +The +.Nm +policy allows the root of the host to limit the +.Xr VNET 9 +jail's privileges +of setting +.Tn IPv4 +and +.Tn IPv6 +addresses via +.Xr sysctl 8 +interface. +So, the host can +define rules for jails and their interfaces about IP addresses +with +.Xr sysctl 8 +MIBs. +.Pp +Its default behaviour is to deny all IP addresses for the jail if +.Nm +policy is enforced and allow/deny IP(or subnets) according to +.Va security.mac.ipacl.rules +string specified with +.Xr sysctl 8 +.Ss Runtime Configuration +The following +.Xr sysctl 8 +MIBs are used to control enforcement and behavior of this MAC POlicy. +.Bl -tag -width indent +.It Va security.mac.ipacl.ipv4 +Enforce +.Nm +for IPv4 addresses. +(Default: 1). +.It Va security.mac.ipacl.ipv6 +Enforce +.Nm +for IPv6 addresses. +(Default: 1). +.It Va security.mac.ipacl.rules +The IP address access control list is specified in the following format: +.Pp +.Sm off +.D1 Ar jid @ allow @ interface @ addr_family @ IP_addr @ prefix Op , Ar jid @ ... +.Sm on +.Bl -tag -width ".Ar interface" +.It Ar jid +Describe the jail id of the jail for which the rule is written. +.It Ar allow +1 for allow and 0 for deny. +Decides action to perform for the rule. +.It Ar interface +Name of the interface the rule is enforced for. +Interface is +left empty(ie, NULL) then it is a wildcard to enforce rule +for all interfaces. +.It Ar addr_family +Address family of the IP_addr. +The input to be given as AF_INET or AF_INET6 +string only. +.It Ar IP_addr +IP address(or subnet) to be allowed/deny. +Action depends on the +prefix length. +.It Ar prefix +Prefix length of the subnet to be enforced by the policy. -1 implies +the policy is enforced for individual IP address. +For non-negative value, +a range of IP address(present in subnet) which calculated as +subnet = IP_addr & mask +.Sh EXAMPLES +.Pp +.Bl -tag indent +.It Va 1. +.Pp +Assign ipv4=1, ipv6=0 and rules="1@1@@AF_INET@169.254.123.123@-1" +.Pp +It allow only 169.254.123.123 IPv4 address for all interfaces (wildcard) of jail 1. +It allow all IPv6 address since policy is not enforced for IPv6. +.It Va 2. +.Pp +Assign ipv4=1, ipv6=1 and rules="1@1@epair0b@AF_INET6@fe80::@32,1@0@epair0b@AF_INET6@fe80::abcd@-1" +.Pp +It deny all IPv4 address as policy is enforced but no rules are specified +about it. +It allow all IPv6 address in the subnet- fe80::/32 except +fe80::abcd for interface epair0b only. +.It Va 3. +.Pp +Assign ipv4=1, ipv6=1, rules="2@1@@AF_INET6@fc00::@7,2@0@@AF_INET6@fc00::1111:2200@120,2@1@@AF_INET6@fc00::1111:2299@-1,1@1@@AF_INET@198.51.100.0@24" +.Pp +It allow IPv4 in the subnet 198.51.100.0/24 for jail 2 and +all interfaces. +It allow IPv6 address in the subnet fc00::/7 but +deny the subnet fc00::1111:2200/120, and allow individual IP +fc00::1111:2299 from the denied subnet for all interfaces in the jail 2. +.Sh TEST SCRIPTS +.It +Test scripts are not completely automatic :( So, the user has to create +edit the scripts to enter the jid of the test jails and interface. +.It +After editing the scripts run make && make install, which then install +the scripts in /usr/tests/sys/mac/ipacl. +you may also need to create that directory if it gives error. +.Sh LIMITATIONS/PRECATIONS +.It +Rules are checked in the same sequence they are given. +If many rules are there for a IP(or a set of IP), +the result depends on final rule. +.Sh FUTURE WORKS +.It +Rules are given with sysctl interface which gets very complex to give them +all in command line. +It has to be simplified with a better way to input those rules. +.Sh SEE ALSO +.Xr mac 4 , +.Xr mac 9 +.Sh AUTHORS +The +.Nm +policy module was developed as a Google Summer of Code Project in 2019 +by Shivank Garg under the guidance of Mr. Bjoern A. Zeeb. Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -4844,6 +4844,7 @@ security/mac_bsdextended/ugidfw_system.c optional mac_bsdextended security/mac_bsdextended/ugidfw_vnode.c optional mac_bsdextended security/mac_ifoff/mac_ifoff.c optional mac_ifoff +security/mac_ipacl/mac_ipacl.c optional mac_ipacl security/mac_lomac/mac_lomac.c optional mac_lomac security/mac_mls/mac_mls.c optional mac_mls security/mac_none/mac_none.c optional mac_none Index: sys/modules/Makefile =================================================================== --- sys/modules/Makefile +++ sys/modules/Makefile @@ -215,6 +215,7 @@ mac_biba \ mac_bsdextended \ mac_ifoff \ + mac_ipacl \ mac_lomac \ mac_mls \ mac_none \ Index: sys/modules/mac_ipacl/Makefile =================================================================== --- /dev/null +++ sys/modules/mac_ipacl/Makefile @@ -0,0 +1,9 @@ +# $FreeBSD$ + +.PATH: ${SRCTOP}/sys/security/mac_ipacl + +KMOD= mac_ipacl +SRCS= mac_ipacl.c +SRCS+= opt_inet.h opt_inet6.h + +.include Index: sys/netinet/in.c =================================================================== --- sys/netinet/in.c +++ sys/netinet/in.c @@ -72,6 +72,10 @@ #include #include +#ifdef MAC +#include +#endif + static int in_aifaddr_ioctl(u_long, caddr_t, struct ifnet *, struct thread *); static int in_difaddr_ioctl(u_long, caddr_t, struct ifnet *, struct thread *); @@ -372,6 +376,15 @@ /* * See whether address already exist. */ + +#ifdef MAC + /* Check if a MAC policy disallows setting the IPv4 address. */ + error = mac_inet_check_SIOCAIFADDR(td->td_ucred, + &addr->sin_addr, ifp); + if (error) + return (error); +#endif + iaIsFirst = true; ia = NULL; NET_EPOCH_ENTER(et); Index: sys/netinet6/in6.c =================================================================== --- sys/netinet6/in6.c +++ sys/netinet6/in6.c @@ -112,6 +112,9 @@ #include #include +#ifdef MAC +#include +#endif /* * struct in6_ifreq and struct ifreq must be type punnable for common members @@ -557,6 +560,13 @@ struct nd_prefixctl pr0; struct nd_prefix *pr; +#ifdef MAC + /* Check if a MAC policy disallows setting the IPv6 address. */ + error = mac_inet6_check_SIOCAIFADDR(td->td_ucred, + &sa6->sin6_addr, ifp); + if (error) + goto out; +#endif /* * first, make or update the interface address structure, * and link it to the list. Index: sys/security/mac/mac_framework.h =================================================================== --- sys/security/mac/mac_framework.h +++ sys/security/mac/mac_framework.h @@ -89,6 +89,9 @@ struct vnode; struct vop_setlabel_args; +struct in_addr; +struct in6_addr; + #include /* XXX acl_type_t */ #include /* accmode_t */ @@ -153,6 +156,12 @@ int mac_ifnet_ioctl_set(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifp); +/* Check if the IP address is allowed for the interface. */ +int mac_inet_check_SIOCAIFADDR(struct ucred *cred, + const struct in_addr *ia, struct ifnet *ifp); +int mac_inet6_check_SIOCAIFADDR(struct ucred *cred, + const struct in6_addr *ia6, struct ifnet *ifp); + int mac_inpcb_check_deliver(struct inpcb *inp, struct mbuf *m); int mac_inpcb_check_visible(struct ucred *cred, struct inpcb *inp); void mac_inpcb_create(struct socket *so, struct inpcb *inp); Index: sys/security/mac/mac_inet.c =================================================================== --- sys/security/mac/mac_inet.c +++ sys/security/mac/mac_inet.c @@ -108,6 +108,17 @@ return (0); } +/* Check with rules in module if the IPv4 address is allowed. */ +int +mac_inet_check_SIOCAIFADDR(struct ucred *cred, const struct in_addr *ia, + struct ifnet *ifp) +{ + int error; + + MAC_POLICY_CHECK(ip4_check_jail, cred, ia, ifp); + return (error); +} + static struct label * mac_ipq_label_alloc(int flag) { Index: sys/security/mac/mac_inet6.c =================================================================== --- sys/security/mac/mac_inet6.c +++ sys/security/mac/mac_inet6.c @@ -173,6 +173,17 @@ q6->ip6q_label); } +/* Check with rules in module if the IPv6 address is allowed. */ +int +mac_inet6_check_SIOCAIFADDR(struct ucred *cred, const struct in6_addr *ia6, + struct ifnet *ifp) +{ + int error; + + MAC_POLICY_CHECK(ip6_check_jail, cred, ia6, ifp); + return (error); +} + void mac_netinet6_nd6_send(struct ifnet *ifp, struct mbuf *m) { Index: sys/security/mac/mac_policy.h =================================================================== --- sys/security/mac/mac_policy.h +++ sys/security/mac/mac_policy.h @@ -100,6 +100,9 @@ struct vattr; struct vnode; +struct in_addr; +struct in6_addr; + /* * Policy module operations. */ @@ -238,6 +241,12 @@ typedef void (*mpo_ip6q_update_t)(struct mbuf *m, struct label *mlabel, struct ip6q *q6, struct label *q6label); +/* Policy ops checking IPv4 and IPv6 address for ipacl.*/ +typedef int (*mpo_ip4_check_jail_t)(struct ucred *cred, + const struct in_addr *ia, struct ifnet *ifp); +typedef int (*mpo_ip6_check_jail_t)(struct ucred *cred, + const struct in6_addr *ia6, struct ifnet *ifp); + typedef void (*mpo_ipq_create_t)(struct mbuf *m, struct label *mlabel, struct ipq *q, struct label *qlabel); typedef void (*mpo_ipq_destroy_label_t)(struct label *label); @@ -746,6 +755,9 @@ mpo_inpcb_destroy_label_t mpo_inpcb_destroy_label; mpo_inpcb_init_label_t mpo_inpcb_init_label; mpo_inpcb_sosetlabel_t mpo_inpcb_sosetlabel; + + mpo_ip4_check_jail_t mpo_ip4_check_jail; + mpo_ip6_check_jail_t mpo_ip6_check_jail; mpo_ip6q_create_t mpo_ip6q_create; mpo_ip6q_destroy_label_t mpo_ip6q_destroy_label; Index: sys/security/mac_ipacl/mac_ipacl.c =================================================================== --- /dev/null +++ sys/security/mac_ipacl/mac_ipacl.c @@ -0,0 +1,469 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2003-2004 Networks Associates Technology, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * Copyright (c) 2019 Shivank Garg + * + * All rights reserved. + * + * This code was developed as a Google Summer of Code 2019 project + * under the guidance of Mr. Bjoern A. Zeeb. + * + * 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. + * + * $FreeBSD$ + */ + +/* + * mac_ipacl allows the root of the host to limit the VNET jail's privileges + * of setting IPv4 and IPv6 addresses via sysctl(8) interface. So, the host + * can define rules for jails and their interfaces about IP addresses. + * + * sysctl(8) is to be used to modify the rules string in following format- + * "jail_id@allow@interface@address_family@IP_addr@prefix_length[,jail_id@...]" + */ + +#include "opt_inet.h" +#include "opt_inet6.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +SYSCTL_DECL(_security_mac); + +static SYSCTL_NODE(_security_mac, OID_AUTO, ipacl, CTLFLAG_RW, 0, + "TrustedBSD mac_ipacl policy controls"); + +#ifdef INET +static int ipacl_ipv4 = 1; +SYSCTL_INT(_security_mac_ipacl, OID_AUTO, ipv4, CTLFLAG_RWTUN, + &ipacl_ipv4, 0, "Enforce mac_ipacl for IPv4 addresses"); +#endif + +#ifdef INET6 +static int ipacl_ipv6 = 1; +SYSCTL_INT(_security_mac_ipacl, OID_AUTO, ipv6, CTLFLAG_RWTUN, + &ipacl_ipv6, 0, "Enforce mac_ipacl for IPv6 addresses"); +#endif + +static MALLOC_DEFINE(M_IPACL, "ipacl_rule", "Rules for mac_ipacl"); + +#define MAC_RULE_STRING_LEN 1024 + +struct ipacl_addr { + union { +#ifdef INET + struct in_addr ipv4; +#endif +#ifdef INET6 + struct in6_addr ipv6; +#endif + u_int8_t addr8[16]; + u_int16_t addr16[8]; + u_int32_t addr32[4]; + } ipa; /* 128 bit address*/ +#ifdef INET +#define v4 ipa.ipv4 +#endif +#ifdef INET6 +#define v6 ipa.ipv6 +#endif +#define addr8 ipa.addr8 +#define addr16 ipa.addr16 +#define addr32 ipa.addr32 +}; + +struct ip_rule { + int jid; + bool allow; + bool subnet_apply; /* Apply rule on whole subnet. */ + char if_name[IFNAMSIZ]; + int af; /* Address family. */ + struct ipacl_addr addr; + struct ipacl_addr mask; + TAILQ_ENTRY(ip_rule) r_entries; +}; + +static struct mtx rule_mtx; +static TAILQ_HEAD(rulehead, ip_rule) rule_head; +static char rule_string[MAC_RULE_STRING_LEN]; + +static void +destroy_rules(struct rulehead *head) +{ + struct ip_rule *rule; + + while ((rule = TAILQ_FIRST(head)) != NULL) { + TAILQ_REMOVE(head, rule, r_entries); + free(rule, M_IPACL); + } +} + +static void +ipacl_init(struct mac_policy_conf *conf) +{ + + mtx_init(&rule_mtx, "rule_mtx", NULL, MTX_DEF); + TAILQ_INIT(&rule_head); +} + +static void +ipacl_destroy(struct mac_policy_conf *conf) +{ + + mtx_destroy(&rule_mtx); + destroy_rules(&rule_head); +} + +/* + * Note: parsing routines are destructive on the passed string. + */ +static int +parse_rule_element(char *element, struct ip_rule **rule) +{ + char *jid, *allow, *if_name, *fam, *ip_addr, *mask, *p; + struct ip_rule *new; + int error, prefix, i; + + error = 0; + new = malloc(sizeof(*new), M_IPACL, M_ZERO | M_WAITOK); + /* Should we support a jail wildcard? */ + jid = strsep(&element, "@"); + if (jid == NULL) { + error = EINVAL; + goto out; + } + new->jid = strtol(jid, &p, 10); + if (*p != '\0') { + error = EINVAL; + goto out; + } + allow = strsep(&element, "@"); + if (allow == NULL) { + error = EINVAL; + goto out; + } + new->allow = strtol(allow, &p, 10); + if (*p != '\0') { + error = EINVAL; + goto out; + } + if_name = strsep(&element, "@"); + if (sizeof(if_name) > IFNAMSIZ) { + error = EINVAL; + goto out; + } + /* Empty interface name is wildcard to all interfaces. */ + bzero(new->if_name, IFNAMSIZ); + bcopy(if_name, new->if_name, strlen(if_name)); + fam = strsep(&element, "@"); + if (fam == NULL) { + error = EINVAL; + goto out; + } + new->af = (strcmp(fam, "AF_INET") == 0) ? AF_INET : + (strcmp(fam, "AF_INET6") == 0) ? AF_INET6 : -1; + if (new->af == -1) { + error = EINVAL; + goto out; + } + ip_addr = strsep(&element, "@"); + if (ip_addr == NULL) { + error = EINVAL; + goto out; + } + if (inet_pton(new->af, ip_addr, new->addr.addr32) != 1) { + error = EINVAL; + goto out; + } + mask = element; + if (mask == NULL) { + error = EINVAL; + goto out; + } + prefix = strtol(mask, &p, 10); + if (*p != '\0') { + error = EINVAL; + goto out; + } + /* Value -1 for prefix make policy applicable to individual IP only. */ + if (prefix == -1) + new->subnet_apply = false; + else { + new->subnet_apply = true; + switch (new->af) { +#ifdef INET + case AF_INET: + if (prefix < 0 || prefix > 32) { + error = EINVAL; + goto out; + } + if (prefix == 0) + new->mask.addr32[0] = htonl(0); + else + new->mask.addr32[0] = + htonl(~((1 << (32 - prefix)) - 1)); + new->addr.addr32[0] &= new->mask.addr32[0]; + break; +#endif +#ifdef INET6 + case AF_INET6: + if (prefix < 0 || prefix > 128) { + error = EINVAL; + goto out; + } + for (i = 0; prefix > 0; prefix -= 8, i++) + new->mask.addr8[i] = prefix >= 8 ? 0xFF : + (u_int8_t)((0xFFU << (8 - prefix)) & 0xFFU); + for (i=0; i<16; i++) + new->addr.addr8[i] &= new->mask.addr8[i]; + break; +#endif + } + } +out: + if (error != 0) { + free(new, M_IPACL); + *rule = NULL; + } else + *rule = new; + return (error); +} + +/* + * Format of Rule- jid@allow@interface_name@addr_family@ip_addr@subnet_mask + * Example: sysctl security.mac.ipacl.rules=1@1@epair0b@AF_INET@192.0.2.2@24 + */ +static int +parse_rules(char *string, struct rulehead *head) +{ + struct ip_rule *new; + char *element; + int error; + + error = 0; + while ((element = strsep(&string, ",")) != NULL) { + if (strlen(element) == 0) + continue; + error = parse_rule_element(element, &new); + if (error) + goto out; + TAILQ_INSERT_TAIL(head, new, r_entries); + } +out: + if (error != 0) + destroy_rules(head); + return (error); +} + +static int +sysctl_rules(SYSCTL_HANDLER_ARGS) +{ + char *string, *copy_string, *new_string; + struct rulehead head, save_head; + int error; + + new_string = NULL; + if (req->newptr != NULL) { + new_string = malloc(MAC_RULE_STRING_LEN, M_IPACL, + M_WAITOK | M_ZERO); + mtx_lock(&rule_mtx); + strcpy(new_string, rule_string); + mtx_unlock(&rule_mtx); + string = new_string; + } else + string = rule_string; + + error = sysctl_handle_string(oidp, string, MAC_RULE_STRING_LEN, req); + if (error) + goto out; + + if (req->newptr != NULL) { + copy_string = strdup(string, M_IPACL); + TAILQ_INIT(&head); + error = parse_rules(copy_string, &head); + free(copy_string, M_IPACL); + if (error) + goto out; + + TAILQ_INIT(&save_head); + mtx_lock(&rule_mtx); + TAILQ_CONCAT(&save_head, &rule_head, r_entries); + TAILQ_CONCAT(&rule_head, &head, r_entries); + strcpy(rule_string, string); + mtx_unlock(&rule_mtx); + destroy_rules(&save_head); + } +out: + if (new_string != NULL) + free(new_string, M_IPACL); + return (error); +} +SYSCTL_PROC(_security_mac_ipacl, OID_AUTO, rules, + CTLTYPE_STRING|CTLFLAG_RW, 0, 0, sysctl_rules, "A", "IP ACL Rules"); + +static int +rules_check(struct ucred *cred, + struct ipacl_addr *ip_addr, struct ifnet *ifp) +{ + struct ip_rule *rule; + int error, i; + bool same_subnet; + + error = EPERM; + + mtx_lock(&rule_mtx); + + for (rule = TAILQ_FIRST(&rule_head); + rule != NULL; + rule = TAILQ_NEXT(rule, r_entries)) { + + /* Skip if current rule applies to different jail. */ + if (cred->cr_prison->pr_id != rule->jid) + continue; + + if (strcmp(rule->if_name, "\0") && + strcmp(rule->if_name, ifp->if_xname)) + continue; + + switch (rule->af) { +#ifdef INET + case AF_INET: + if (rule->subnet_apply) { + if (rule->addr.v4.s_addr != + (ip_addr->v4.s_addr & rule->mask.v4.s_addr)) + continue; + } else + if (ip_addr->v4.s_addr != rule->addr.v4.s_addr) + continue; + break; +#endif +#ifdef INET6 + case AF_INET6: + if (rule->subnet_apply) { + same_subnet=true; + for (i=0 ; i<16 ; i++) + if (rule->addr.v6.s6_addr[i] != + (ip_addr->v6.s6_addr[i] & + rule->mask.v6.s6_addr[i])) { + same_subnet=false; + break; + } + if (!same_subnet) + continue; + } else + if (bcmp(&rule->addr, ip_addr, + sizeof(*ip_addr))) + continue; + break; +#endif + } + + if (rule->allow) + error = 0; + else + error = EPERM; + } + + mtx_unlock(&rule_mtx); + + return (error); +} + +/* Feature request: Can we make this a sysctl policy as well defaulting + * to jails only, but if changed also applying to the base system? + */ +#ifdef INET +static int +ipacl_ip4_check_jail(struct ucred *cred, + const struct in_addr *ia, struct ifnet *ifp) +{ + struct ipacl_addr ip4_addr; + + ip4_addr.v4 = *ia; + + if (!jailed(cred)) + return 0; + + /* Checks with the policy only when it is enforced for ipv4. */ + if (ipacl_ipv4) + return rules_check(cred, &ip4_addr, ifp); + + return 0; +} +#endif + +#ifdef INET6 +static int +ipacl_ip6_check_jail(struct ucred *cred, + const struct in6_addr *ia6, struct ifnet *ifp) +{ + struct ipacl_addr ip6_addr; + + ip6_addr.v6 = *ia6; /* Make copy to not alter the original. */ + in6_clearscope(&ip6_addr.v6); /* Clear the scope id. */ + + if (!jailed(cred)) + return 0; + + /* Checks with the policy when it is enforced for ipv6. */ + if (ipacl_ipv6) + return rules_check(cred, &ip6_addr, ifp); + + return 0; +} +#endif + +static struct mac_policy_ops ipacl_ops = +{ + .mpo_init = ipacl_init, + .mpo_destroy = ipacl_destroy, +#ifdef INET + .mpo_ip4_check_jail = ipacl_ip4_check_jail, +#endif +#ifdef INET6 + .mpo_ip6_check_jail = ipacl_ip6_check_jail, +#endif +}; + +MAC_POLICY_SET(&ipacl_ops, mac_ipacl, "TrustedBSD MAC/ipacl", + MPC_LOADTIME_FLAG_UNLOADOK, NULL); Index: sys/security/mac_ipacl/tests/Makefile =================================================================== --- /dev/null +++ sys/security/mac_ipacl/tests/Makefile @@ -0,0 +1,17 @@ +# $FreeBSD$ + +PACKAGE= tests + +TESTSDIR= ${TESTSBASE}/sys/mac/ipacl + +${PACKAGE}FILES+= ipacl_script.sh + +TAP_TESTS_SH+= ip4_test +TAP_TESTS_SH+= ip6_test + +.for t in ${TAP_TESTS_SH} +TEST_METADATA.$t+= required_user="root" +TEST_METADATA.$t+= timeout="450" +.endfor + +.include Index: sys/security/mac_ipacl/tests/ip4_test.sh =================================================================== --- /dev/null +++ sys/security/mac_ipacl/tests/ip4_test.sh @@ -0,0 +1,110 @@ +#!/bin/sh + +#- +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Shivank Garg +# +# All rights reserved. +# +# This code was developed as a Google Summer of Code 2019 project +# under the guidance of Mr. Bjoern A. Zeeb. +# +# 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. + +# $FreeBSD$ + +dir=`dirname $0` +. ${dir}/ipacl_script.sh + +echo "1..32" + +jid1=1 +jid2=2 + +if1_host="epair0a" +if1_jail1="epair0b" +if2_jail1="lo0" +if1_jail2="epair1b" +if2_jail2="lo0" + +# This test scripts require user to manually create two jails and bind +# those jails with VNET interfaces. Update variables defined above with +# respective value according to your settings. + +# Verify effect of changing security.mac.ipacl.ipv4 +sysctl security.mac.ipacl.ipv4=0 >/dev/null +exec_test ok ipv4 ${if1_host} '192.168.43.26' 16 0 +exec_test ok ipv4 ${if1_jail1} '192.168.43.26' 16 1 +exec_test ok ipv4 ${if1_host} '127.1.32.31' 24 0 +exec_test ok ipv4 ${if1_jail1} '198.18.0.12' 15 1 + +sysctl security.mac.ipacl.ipv4=1 >/dev/null +sysctl security.mac.ipacl.rules= >/dev/null + +exec_test ok ipv4 ${if1_host} '192.168.43.26' 16 0 +exec_test fl ipv4 ${if1_jail1} '192.168.43.26' 16 1 +exec_test ok ipv4 ${if1_host} '127.1.32.31' 24 0 +exec_test fl ipv4 ${if1_jail1} '198.18.0.12' 15 1 + +# rule: jid@allow@interface_name@addr_family@ip_addr@subnet_mask +sysctl security.mac.ipacl.rules=${jid1}@1@${if1_jail1}@AF_INET@192.168.42.2@-1,${jid2}@1@${if1_jail2}@AF_INET@127.1.32.1@-1,${jid2}@1@@AF_INET@198.18.0.1@15,${jid2}@0@@AF_INET@198.18.0.12@-1 >/dev/null + +# Verify if security.mac.ipacl.rules allow jail to set certain IPv4 address +exec_test ok ipv4 ${if1_jail1} '192.168.42.2' 16 ${jid1} +exec_test fl ipv4 ${if1_jail1} '192.168.42.3' 16 ${jid1} +exec_test ok ipv4 ${if1_jail2} '127.1.32.1' 24 ${jid2} +exec_test fl ipv4 ${if2_jail2} '127.1.32.1' 24 ${jid2} + +# Verify if scurity.mac.ipacl.rules allow jail to set any address in subnet +exec_test ok ipv4 ${if1_jail2} '198.18.0.192' 15 ${jid2} +exec_test ok ipv4 ${if1_jail2} '198.18.132.121' 15 ${jid2} +exec_test fl ipv4 ${if1_jail2} '197.1.123.123' 15 ${jid2} +exec_test fl ipv4 ${if1_jail2} '198.18.0.12' 15 ${jid2} #last rule disllow the ip in that subnet + +# Verify if security.mac.ipacl.rules (interface wildcard) allow jail to set certain IPv4 address +exec_test ok ipv4 ${if2_jail2} '198.18.0.192' 15 ${jid2} +exec_test ok ipv4 ${if2_jail2} '198.18.132.121' 15 ${jid2} +exec_test fl ipv4 ${if2_jail2} '197.1.123.123' 15 ${jid2} +exec_test fl ipv4 ${if2_jail2} '198.18.0.12' 15 ${jid2} #last rule disllow the ip in that subnet + +# Add more checks on subnet +sysctl security.mac.ipacl.rules=${jid2}@1@@AF_INET@10.0.0.0@16,${jid2}@1@@AF_INET@10.12.0.0@16 >/dev/null + +exec_test fl ipv4 ${if2_jail2} '10.1.0.0' 16 ${jid2} +exec_test ok ipv4 ${if2_jail2} '10.0.10.10' 16 ${jid2} +exec_test fl ipv4 ${if2_jail2} '10.13.0.0' 24 ${jid2} +exec_test fl ipv4 ${if2_jail2} '10.11.0.10' 24 ${jid2} #last rule disllow the ip in that subnet + +sysctl security.mac.ipacl.rules=${jid1}@1@@AF_INET@169.254.0.0@16,${jid1}@0@@AF_INET@169.254.123.0@24,${jid1}@1@@AF_INET@169.254.123.123@-1,${jid1}@1@@AF_INET@198.51.100.0@24,${jid1}@0@@AF_INET@198.51.100.100@-1 >/dev/null +# Add more tests from Link-Local space and Documentation(TEST-NET-3) +exec_test ok ipv4 ${if1_jail1} '169.254.121.121' 16 ${jid1} +exec_test fl ipv4 ${if1_jail1} '169.254.123.121' 16 ${jid1} +exec_test ok ipv4 ${if1_jail1} '169.254.123.123' 16 ${jid1} +exec_test fl ipv4 ${if1_jail1} '169.253.121.121' 16 ${jid1} + +exec_test ok ipv4 ${if2_jail1} '198.51.100.001' 24 ${jid1} +exec_test ok ipv4 ${if2_jail1} '198.51.100.254' 24 ${jid1} +exec_test fl ipv4 ${if1_jail1} '198.51.100.100' 24 ${jid1} +exec_test fl ipv4 ${if1_jail1} '198.151.100.100' 24 ${jid1} + +sysctl security.mac.ipacl.rules= >/dev/null Index: sys/security/mac_ipacl/tests/ip6_test.sh =================================================================== --- /dev/null +++ sys/security/mac_ipacl/tests/ip6_test.sh @@ -0,0 +1,118 @@ +#!/bin/sh + +#- +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Shivank Garg +# +# All rights reserved. +# +# This code was developed as a Google Summer of Code 2019 project +# under the guidance of Mr. Bjoern A. Zeeb. +# +# 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. + +# $FreeBSD$ + +dir=`dirname $0` +. ${dir}/ipacl_script.sh + +echo "1..36" + +jid1=1 +jid2=2 + +if1_host="epair0a" +if1_jail1="epair0b" +if2_jail1="lo0" +if1_jail2="epair1b" +if2_jail2="lo0" + +# This test scripts require user to manually create two jails and bind +# those jails with VNET interfaces. Update variables defined above with +# respective value according to your settings. + +# Verify effect of changing security.mac.ipacl.ipv4 +sysctl security.mac.ipacl.ipv6=0 >/dev/null +exec_test ok ipv6 ${if1_host} '2001:db8::1111' 32 0 +exec_test ok ipv6 ${if1_jail1} '2001:db8::1112' 64 1 +exec_test ok ipv6 ${if1_host} '2001:2::abcd' 24 0 +exec_test ok ipv6 ${if1_jail1} '001:470:1e01:5ea::11' 48 1 + +sysctl security.mac.ipacl.ipv6=1 >/dev/null +sysctl security.mac.ipacl.rules= >/dev/null + +exec_test ok ipv6 ${if1_host} '2001:db8::1111' 32 0 +exec_test fl ipv6 ${if1_jail1} '2001:db8::1112' 64 1 +exec_test ok ipv6 ${if1_host} '2001:2::abcd' 24 0 +exec_test fl ipv6 ${if1_jail1} '001:470:1e01:5ea::11' 48 1 + +# rule: jid@allow@interface_name@addr_family@ip_addr@subnet_mask +sysctl security.mac.ipacl.rules=${jid1}@1@epair0b@AF_INET6@2001:db8::1111@-1,${jid2}@1@epair1b@AF_INET6@2001:2::1234:1234@-1,${jid2}@1@@AF_INET6@fe80::@32,${jid2}@0@@AF_INET6@fe80::abcd@-1 >/dev/null + +# Verify if security.mac.ipacl.rules allow jail to set certain IPv4 address +exec_test ok ipv6 ${if1_jail1} '2001:db8::1111' 16 ${jid1} +exec_test fl ipv6 ${if1_jail1} '2001:db8::1112' 16 ${jid1} +exec_test ok ipv6 ${if1_jail2} '2001:2::1234:1234' 48 ${jid2} +exec_test fl ipv6 ${if1_jail1} '2001:2::1234:1234' 48 ${jid1} + +# Verify if scurity.mac.ipacl.rules allow jail to set any address in subnet +exec_test ok ipv6 ${if1_jail2} 'FE80::1101:1221' 15 ${jid2} +exec_test ok ipv6 ${if1_jail2} 'FE80::abab' 15 ${jid2} +exec_test ok ipv6 ${if1_jail2} 'FE80::1' 64 ${jid2} +exec_test fl ipv6 ${if1_jail2} 'FE80::abcd' 15 ${jid2} #last rule disllow the ip in that subnet + +# Verify if security.mac.ipacl.rules (interface wildcard) allow jail to set certain IPv4 address +exec_test ok ipv6 ${if2_jail2} 'FE80::1101:1221' 15 ${jid2} +exec_test ok ipv6 ${if2_jail2} 'FE80::abab' 32 ${jid2} +exec_test fl ipv6 ${if2_jail2} 'FE81::1' 64 ${jid2} +exec_test fl ipv6 ${if2_jail2} 'FE80::abcd' 32 ${jid2} #last rule disllow the ip in that subnet + + +# add more tests for subnet +sysctl security.mac.ipacl.rules=${jid2}@1@@AF_INET6@2001:2::@48,${jid2}@1@@AF_INET6@2001:3::@32 >/dev/null +exec_test fl ipv6 ${if2_jail2} '2001:2:0001::1' 64 ${jid2} +exec_test fl ipv6 ${if2_jail2} '2001:2:1000::1' 32 ${jid2} +exec_test ok ipv6 ${if2_jail2} '2001:3:0001::1' 64 ${jid2} +exec_test fl ipv6 ${if2_jail2} '2001:4::1' 64 ${jid2} + + +#add more tests of ULA address space +#allow subnet fc00::/7 except subnet fc00::1111:22xx but allow fc00::1111:2281 +sysctl security.mac.ipacl.rules=${jid1}@1@@AF_INET6@fc00::@7,${jid1}@0@@AF_INET6@fc00::1111:2200@120,${jid1}@1@@AF_INET6@fc00::1111:2299@-1,${jid1}@1@@AF_INET6@2001:db8::@32,${jid1}@0@@AF_INET6@2001:db8::abcd@-1 >/dev/null +exec_test ok ipv6 ${if1_jail1} 'fc00::0000:1234' 48 ${jid1} +exec_test ok ipv6 ${if1_jail1} 'fc00::1112:1234' 48 ${jid1} +exec_test fl ipv6 ${if1_jail1} 'f800::2222:2200' 48 ${jid1} +exec_test fl ipv6 ${if1_jail1} 'f800::2222:22ff' 48 ${jid1} + +exec_test ok ipv6 ${if1_jail1} 'fc00::1111:2111' 64 ${jid1} +exec_test fl ipv6 ${if1_jail1} 'fc00::1111:2211' 64 ${jid1} +exec_test fl ipv6 ${if1_jail1} 'fc00::1111:22aa' 48 ${jid1} +exec_test ok ipv6 ${if1_jail1} 'fc00::1111:2299' 48 ${jid1} + +#add more tests of documentation range IPV6 +exec_test ok ipv6 ${if1_jail1} '2001:db8:abcd:bcde:cdef:def1:ef12:f123' 32 ${jid1} +exec_test ok ipv6 ${if1_jail1} '2001:db8:1111:2222:3333:4444:5555:6666' 32 ${jid1} +exec_test fl ipv6 ${if1_jail1} '2000:db9:1111:2222:3333:4444:5555:6666' 32 ${jid1} +exec_test fl ipv6 ${if2_jail1} '2001:db8::abcd' 32 ${jid1} + +sysctl security.mac.ipacl.rules= >/dev/null Index: sys/security/mac_ipacl/tests/ipacl_script.sh =================================================================== --- /dev/null +++ sys/security/mac_ipacl/tests/ipacl_script.sh @@ -0,0 +1,101 @@ +#!/bin/sh + +#- +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2019 Shivank Garg +# +# All rights reserved. +# +# This code was developed as a Google Summer of Code 2019 project +# under the guidance of Mr. Bjoern A. Zeeb. +# +# 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. + +# $FreeBSD$ + +sysctl security.mac.ipacl >/dev/null 2>&1 +if [ $? -ne 0 ]; then + echo "1..0 # SKIP MAC_IPACL is unavailable." + exit 0 +fi + +if [ "$(id -u)" -ne 0 ]; then + echo "1..0 # SKIP testcases must be run as root" + exit 0 +fi + +ntest=1 + +test_ip() { + local proto interface address prefix jid + + proto=${1} + interface=${2} + address=${3} + prefix=${4} + jid=${5} #if jid = 0 then assume host + + if [ "${proto}" = "ipv4" ]; then + if [ "${jid}" -eq 0 ]; then + echo | ifconfig ${interface} ${address}/${prefix} up + RetVal=$? + else + echo | jexec ${jid} ifconfig ${interface} ${address}/${prefix} up + RetVal=$? + fi + elif [ "${proto}" = "ipv6" ]; then + if [ "${jid}" -eq 0 ]; then + echo | ifconfig ${interface} inet6 ${address} prefixlen ${prefix} + RetVal=$? + else + echo | jexec ${jid} ifconfig ${interface} inet6 ${address} prefixlen ${prefix} + RetVal=$? + fi + fi + if [ ${RetVal} -eq 0 ]; then + echo ok + else + echo fl + fi + +} + +exec_test() { + local expect_with_rule proto interface address prefix jid + expect_with_rule=${1} + proto=${2} + interface=${3} + address=${4} + prefix=${5} + jid=${6} + + out=$(test_ip "${proto}" "${interface}" "${address}" "${prefix}" "${jid}") + if [ "${out}" = "${expect_with_rule}" ]; then + echo "ok : PASS ${ntest}" + elif [ "${out}" = "ok" ] || [ "${out}" = "fl" ]; then + echo "not ok : FAIL ${ntest} # '${out}' != '${expect_with_rule}'" + else + echo "not ok : FAIL ${ntest} # unexpected output: '${out}'" + fi + : $(( ntest += 1 )) +}