diff options
| author | Shivank Garg <shivank@freebsd.org> | 2023-07-25 20:27:06 +0000 |
|---|---|---|
| committer | Bjoern A. Zeeb <bz@FreeBSD.org> | 2023-07-26 00:07:57 +0000 |
| commit | 215bab7924f6c8e133a96431b3e2176d5fae1eff (patch) | |
| tree | 78c32ee058e2181259105895e111211a534bd038 /sys/security | |
| parent | a1b67573130114257fdd443c4ec9b54fbe2e5843 (diff) | |
Diffstat (limited to 'sys/security')
| -rw-r--r-- | sys/security/mac/mac_framework.h | 9 | ||||
| -rw-r--r-- | sys/security/mac/mac_inet.c | 11 | ||||
| -rw-r--r-- | sys/security/mac/mac_inet6.c | 11 | ||||
| -rw-r--r-- | sys/security/mac/mac_policy.h | 12 | ||||
| -rw-r--r-- | sys/security/mac_ipacl/mac_ipacl.c | 453 |
5 files changed, 496 insertions, 0 deletions
diff --git a/sys/security/mac/mac_framework.h b/sys/security/mac/mac_framework.h index 8a1de6fe13e1..6ffd8e27bcb7 100644 --- a/sys/security/mac/mac_framework.h +++ b/sys/security/mac/mac_framework.h @@ -90,6 +90,9 @@ struct vattr; struct vnode; struct vop_setlabel_args; +struct in_addr; +struct in6_addr; + #include <sys/acl.h> /* XXX acl_type_t */ #include <sys/types.h> /* accmode_t */ @@ -191,6 +194,12 @@ int mac_ifnet_ioctl_get(struct ucred *cred, struct ifreq *ifr, 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_add_addr(struct ucred *cred, + const struct in_addr *ia, struct ifnet *ifp); +int mac_inet6_check_add_addr(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); diff --git a/sys/security/mac/mac_inet.c b/sys/security/mac/mac_inet.c index dd77a6825204..7dcdfd1decf4 100644 --- a/sys/security/mac/mac_inet.c +++ b/sys/security/mac/mac_inet.c @@ -108,6 +108,17 @@ mac_inpcb_init(struct inpcb *inp, int flag) return (0); } +/* Check with rules in module if the IPv4 address is allowed. */ +int +mac_inet_check_add_addr(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) { diff --git a/sys/security/mac/mac_inet6.c b/sys/security/mac/mac_inet6.c index cb0812bab785..16c78ee1a6bd 100644 --- a/sys/security/mac/mac_inet6.c +++ b/sys/security/mac/mac_inet6.c @@ -173,6 +173,17 @@ mac_ip6q_update(struct mbuf *m, struct ip6q *q6) q6->ip6q_label); } +/* Check with rules in module if the IPv6 address is allowed. */ +int +mac_inet6_check_add_addr(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) { diff --git a/sys/security/mac/mac_policy.h b/sys/security/mac/mac_policy.h index 1f9a5485b136..1a6b19b79f85 100644 --- a/sys/security/mac/mac_policy.h +++ b/sys/security/mac/mac_policy.h @@ -104,6 +104,9 @@ struct ucred; struct vattr; struct vnode; +struct in_addr; +struct in6_addr; + /* * Policy module operations. */ @@ -248,6 +251,12 @@ typedef void (*mpo_ip6q_reassemble)(struct ip6q *q6, struct label *q6label, 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); @@ -762,6 +771,9 @@ struct mac_policy_ops { 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; mpo_ip6q_init_label_t mpo_ip6q_init_label; diff --git a/sys/security/mac_ipacl/mac_ipacl.c b/sys/security/mac_ipacl/mac_ipacl.c new file mode 100644 index 000000000000..ed207f38dbb8 --- /dev/null +++ b/sys/security/mac_ipacl/mac_ipacl.c @@ -0,0 +1,453 @@ +/*- + * Copyright (c) 2003-2004 Networks Associates Technology, Inc. + * Copyright (c) 2006 SPARTA, Inc. + * Copyright (c) 2019, 2023 Shivank Garg <shivank@FreeBSD.org> + * + * This software was developed for the FreeBSD Project by Network + * Associates Laboratories, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), + * as part of the DARPA CHATS research program. + * + * This software was enhanced by SPARTA ISSO under SPAWAR contract + * N66001-04-C-6019 ("SEFOS"). + * + * This code was developed as a Google Summer of Code 2019 project + * under the guidance of 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$ + */ + +/* + * The IP address access control policy module - 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 <sys/param.h> +#include <sys/module.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/mutex.h> +#include <sys/priv.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/systm.h> +#include <sys/types.h> +#include <sys/ucred.h> +#include <sys/jail.h> + +#include <net/if.h> +#include <net/if_var.h> + +#include <netinet/in.h> +#include <netinet6/scope6_var.h> + +#include <security/mac/mac_policy.h> + +SYSCTL_DECL(_security_mac); + +static SYSCTL_NODE(_security_mac, OID_AUTO, ipacl, CTLFLAG_RW | CTLFLAG_MPSAFE, 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 *tok, *p; + int prefix; +#ifdef INET6 + int i; +#endif + + /* Should we support a jail wildcard? */ + tok = strsep(&element, ","); + if (tok == NULL) + return (EINVAL); + rule->jid = strtol(tok, &p, 10); + if (*p != '\0') + return (EINVAL); + tok = strsep(&element, ","); + if (tok == NULL) + return (EINVAL); + rule->allow = strtol(tok, &p, 10); + if (*p != '\0') + return (EINVAL); + tok = strsep(&element, ","); + if (strlen(tok) + 1 > IFNAMSIZ) + return (EINVAL); + /* Empty interface name is wildcard to all interfaces. */ + strlcpy(rule->if_name, tok, strlen(tok) + 1); + tok = strsep(&element, ","); + if (tok == NULL) + return (EINVAL); + rule->af = (strcmp(tok, "AF_INET") == 0) ? AF_INET : + (strcmp(tok, "AF_INET6") == 0) ? AF_INET6 : -1; + if (rule->af == -1) + return (EINVAL); + tok = strsep(&element, "/"); + if (tok == NULL) + return (EINVAL); + if (inet_pton(rule->af, tok, rule->addr.addr32) != 1) + return (EINVAL); + tok = element; + if (tok == NULL) + return (EINVAL); + prefix = strtol(tok, &p, 10); + if (*p != '\0') + return (EINVAL); + /* Value -1 for prefix make policy applicable to individual IP only. */ + if (prefix == -1) + rule->subnet_apply = false; + else { + rule->subnet_apply = true; + switch (rule->af) { +#ifdef INET + case AF_INET: + if (prefix < 0 || prefix > 32) + return (EINVAL); + + if (prefix == 0) + rule->mask.addr32[0] = htonl(0); + else + rule->mask.addr32[0] = + htonl(~((1 << (32 - prefix)) - 1)); + rule->addr.addr32[0] &= rule->mask.addr32[0]; + break; +#endif +#ifdef INET6 + case AF_INET6: + if (prefix < 0 || prefix > 128) + return (EINVAL); + + for (i = 0; prefix > 0; prefix -= 8, i++) + rule->mask.addr8[i] = prefix >= 8 ? 0xFF : + (u_int8_t)((0xFFU << (8 - prefix)) & 0xFFU); + for (i = 0; i < 16; i++) + rule->addr.addr8[i] &= rule->mask.addr8[i]; + break; +#endif + } + } + return (0); +} + +/* + * 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; + + new = malloc(sizeof(*new), M_IPACL, M_ZERO | M_WAITOK); + error = parse_rule_element(element, new); + if (error != 0) { + free(new, M_IPACL); + 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 | CTLFLAG_MPSAFE, 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; +#ifdef INET6 + int i; + bool same_subnet; +#endif + + error = EPERM; + + mtx_lock(&rule_mtx); + + /* + * In the case where multiple rules are applicable to an IP address or + * a set of IP addresses, the rule that is defined later in the list + * determines the outcome, disregarding any previous rule for that IP + * address. + * Walk the policy rules list in reverse order until rule applicable + * to the requested IP address is found. + */ + TAILQ_FOREACH_REVERSE(rule, &rule_head, rulehead, 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; + break; + } + + mtx_unlock(&rule_mtx); + + return (error); +} + +/* + * Feature request: Can we make this sysctl policy apply to jails by default, + * but also allow it to be changed to apply 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); |
