aboutsummaryrefslogtreecommitdiff
path: root/sys/security
diff options
context:
space:
mode:
authorShivank Garg <shivank@freebsd.org>2023-07-25 20:27:06 +0000
committerBjoern A. Zeeb <bz@FreeBSD.org>2023-07-26 00:07:57 +0000
commit215bab7924f6c8e133a96431b3e2176d5fae1eff (patch)
tree78c32ee058e2181259105895e111211a534bd038 /sys/security
parenta1b67573130114257fdd443c4ec9b54fbe2e5843 (diff)
Diffstat (limited to 'sys/security')
-rw-r--r--sys/security/mac/mac_framework.h9
-rw-r--r--sys/security/mac/mac_inet.c11
-rw-r--r--sys/security/mac/mac_inet6.c11
-rw-r--r--sys/security/mac/mac_policy.h12
-rw-r--r--sys/security/mac_ipacl/mac_ipacl.c453
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);