Index: stable/6/sys/contrib/pf/net/pf.c =================================================================== --- stable/6/sys/contrib/pf/net/pf.c (revision 162447) +++ stable/6/sys/contrib/pf/net/pf.c (revision 162448) @@ -1,7081 +1,7136 @@ /* $FreeBSD$ */ /* $OpenBSD: pf.c,v 1.483 2005/03/15 17:38:43 dhartmei Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier * Copyright (c) 2002,2003 Henning Brauer * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - 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 COPYRIGHT HOLDERS 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 * COPYRIGHT HOLDERS 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. * * Effort sponsored in part by the Defense Advanced Research Projects * Agency (DARPA) and Air Force Research Laboratory, Air Force * Materiel Command, USAF, under agreement number F30602-01-2-0537. * */ #ifdef __FreeBSD__ #include "opt_inet.h" #include "opt_inet6.h" #endif #ifdef __FreeBSD__ +#include "opt_mac.h" #include "opt_bpf.h" #include "opt_pf.h" #define NBPFILTER DEV_BPF #define NPFLOG DEV_PFLOG #define NPFSYNC DEV_PFSYNC #else #include "bpfilter.h" #include "pflog.h" #include "pfsync.h" #endif #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ +#include #include #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef __FreeBSD__ #include #endif #include #include #if NPFSYNC > 0 #include #endif /* NPFSYNC > 0 */ #ifdef INET6 #include #include #include #include #ifdef __FreeBSD__ #include #include #endif #endif /* INET6 */ #ifdef __FreeBSD__ #include #include #include extern int ip_optcopy(struct ip *, struct ip *); #endif #define DPFPRINTF(n, x) if (pf_status.debug >= (n)) printf x /* * Global variables */ struct pf_anchor_global pf_anchors; struct pf_ruleset pf_main_ruleset; struct pf_altqqueue pf_altqs[2]; struct pf_palist pf_pabuf; struct pf_altqqueue *pf_altqs_active; struct pf_altqqueue *pf_altqs_inactive; struct pf_status pf_status; u_int32_t ticket_altqs_active; u_int32_t ticket_altqs_inactive; int altqs_inactive_open; u_int32_t ticket_pabuf; #ifdef __FreeBSD__ struct callout pf_expire_to; /* expire timeout */ #else struct timeout pf_expire_to; /* expire timeout */ #endif struct pf_anchor_stackframe { struct pf_ruleset *rs; struct pf_rule *r; struct pf_anchor_node *parent; struct pf_anchor *child; } pf_anchor_stack[64]; #ifdef __FreeBSD__ uma_zone_t pf_src_tree_pl, pf_rule_pl; uma_zone_t pf_state_pl, pf_altq_pl, pf_pooladdr_pl; #else struct pool pf_src_tree_pl, pf_rule_pl; struct pool pf_state_pl, pf_altq_pl, pf_pooladdr_pl; #endif void pf_print_host(struct pf_addr *, u_int16_t, u_int8_t); void pf_init_threshold(struct pf_threshold *, u_int32_t, u_int32_t); void pf_add_threshold(struct pf_threshold *); int pf_check_threshold(struct pf_threshold *); void pf_change_ap(struct pf_addr *, u_int16_t *, u_int16_t *, u_int16_t *, struct pf_addr *, u_int16_t, u_int8_t, sa_family_t); #ifdef INET6 void pf_change_a6(struct pf_addr *, u_int16_t *, struct pf_addr *, u_int8_t); #endif /* INET6 */ void pf_change_icmp(struct pf_addr *, u_int16_t *, struct pf_addr *, struct pf_addr *, u_int16_t, u_int16_t *, u_int16_t *, u_int16_t *, u_int16_t *, u_int8_t, sa_family_t); +#ifdef __FreeBSD__ +void pf_send_tcp(struct mbuf *, + const struct pf_rule *, sa_family_t, +#else void pf_send_tcp(const struct pf_rule *, sa_family_t, +#endif const struct pf_addr *, const struct pf_addr *, u_int16_t, u_int16_t, u_int32_t, u_int32_t, u_int8_t, u_int16_t, u_int16_t, u_int8_t, int, struct ether_header *, struct ifnet *); void pf_send_icmp(struct mbuf *, u_int8_t, u_int8_t, sa_family_t, struct pf_rule *); struct pf_rule *pf_match_translation(struct pf_pdesc *, struct mbuf *, int, int, struct pfi_kif *, struct pf_addr *, u_int16_t, struct pf_addr *, u_int16_t, int); struct pf_rule *pf_get_translation(struct pf_pdesc *, struct mbuf *, int, int, struct pfi_kif *, struct pf_src_node **, struct pf_addr *, u_int16_t, struct pf_addr *, u_int16_t, struct pf_addr *, u_int16_t *); int pf_test_tcp(struct pf_rule **, struct pf_state **, int, struct pfi_kif *, struct mbuf *, int, void *, struct pf_pdesc *, struct pf_rule **, #ifdef __FreeBSD__ struct pf_ruleset **, struct ifqueue *, struct inpcb *); #else struct pf_ruleset **, struct ifqueue *); #endif int pf_test_udp(struct pf_rule **, struct pf_state **, int, struct pfi_kif *, struct mbuf *, int, void *, struct pf_pdesc *, struct pf_rule **, #ifdef __FreeBSD__ struct pf_ruleset **, struct ifqueue *, struct inpcb *); #else struct pf_ruleset **, struct ifqueue *); #endif int pf_test_icmp(struct pf_rule **, struct pf_state **, int, struct pfi_kif *, struct mbuf *, int, void *, struct pf_pdesc *, struct pf_rule **, struct pf_ruleset **, struct ifqueue *); int pf_test_other(struct pf_rule **, struct pf_state **, int, struct pfi_kif *, struct mbuf *, int, void *, struct pf_pdesc *, struct pf_rule **, struct pf_ruleset **, struct ifqueue *); int pf_test_fragment(struct pf_rule **, int, struct pfi_kif *, struct mbuf *, void *, struct pf_pdesc *, struct pf_rule **, struct pf_ruleset **); int pf_test_state_tcp(struct pf_state **, int, struct pfi_kif *, struct mbuf *, int, void *, struct pf_pdesc *, u_short *); int pf_test_state_udp(struct pf_state **, int, struct pfi_kif *, struct mbuf *, int, void *, struct pf_pdesc *); int pf_test_state_icmp(struct pf_state **, int, struct pfi_kif *, struct mbuf *, int, void *, struct pf_pdesc *, u_short *); int pf_test_state_other(struct pf_state **, int, struct pfi_kif *, struct pf_pdesc *); struct pf_tag *pf_get_tag(struct mbuf *); int pf_match_tag(struct mbuf *, struct pf_rule *, struct pf_tag **, int *); void pf_hash(struct pf_addr *, struct pf_addr *, struct pf_poolhashkey *, sa_family_t); int pf_map_addr(u_int8_t, struct pf_rule *, struct pf_addr *, struct pf_addr *, struct pf_addr *, struct pf_src_node **); int pf_get_sport(sa_family_t, u_int8_t, struct pf_rule *, struct pf_addr *, struct pf_addr *, u_int16_t, struct pf_addr *, u_int16_t*, u_int16_t, u_int16_t, struct pf_src_node **); void pf_route(struct mbuf **, struct pf_rule *, int, struct ifnet *, struct pf_state *); void pf_route6(struct mbuf **, struct pf_rule *, int, struct ifnet *, struct pf_state *); #ifdef __FreeBSD__ int pf_socket_lookup(uid_t *, gid_t *, int, struct pf_pdesc *, struct inpcb *); #else int pf_socket_lookup(uid_t *, gid_t *, int, struct pf_pdesc *); #endif u_int8_t pf_get_wscale(struct mbuf *, int, u_int16_t, sa_family_t); u_int16_t pf_get_mss(struct mbuf *, int, u_int16_t, sa_family_t); u_int16_t pf_calc_mss(struct pf_addr *, sa_family_t, u_int16_t); void pf_set_rt_ifp(struct pf_state *, struct pf_addr *); int pf_check_proto_cksum(struct mbuf *, int, int, u_int8_t, sa_family_t); int pf_addr_wrap_neq(struct pf_addr_wrap *, struct pf_addr_wrap *); static int pf_add_mbuf_tag(struct mbuf *, u_int); struct pf_state *pf_find_state_recurse(struct pfi_kif *, struct pf_state *, u_int8_t); int pf_src_connlimit(struct pf_state **); int pf_check_congestion(struct ifqueue *); #ifdef __FreeBSD__ int in4_cksum(struct mbuf *m, u_int8_t nxt, int off, int len); struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX]; #else struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX] = { { &pf_state_pl, PFSTATE_HIWAT }, { &pf_src_tree_pl, PFSNODE_HIWAT }, { &pf_frent_pl, PFFRAG_FRENT_HIWAT } }; #endif #define STATE_LOOKUP() \ do { \ if (direction == PF_IN) \ *state = pf_find_state_recurse( \ kif, &key, PF_EXT_GWY); \ else \ *state = pf_find_state_recurse( \ kif, &key, PF_LAN_EXT); \ if (*state == NULL || (*state)->timeout == PFTM_PURGE) \ return (PF_DROP); \ if (direction == PF_OUT && \ (((*state)->rule.ptr->rt == PF_ROUTETO && \ (*state)->rule.ptr->direction == PF_OUT) || \ ((*state)->rule.ptr->rt == PF_REPLYTO && \ (*state)->rule.ptr->direction == PF_IN)) && \ (*state)->rt_kif != NULL && \ (*state)->rt_kif != kif) \ return (PF_PASS); \ } while (0) #define STATE_TRANSLATE(s) \ (s)->lan.addr.addr32[0] != (s)->gwy.addr.addr32[0] || \ ((s)->af == AF_INET6 && \ ((s)->lan.addr.addr32[1] != (s)->gwy.addr.addr32[1] || \ (s)->lan.addr.addr32[2] != (s)->gwy.addr.addr32[2] || \ (s)->lan.addr.addr32[3] != (s)->gwy.addr.addr32[3])) || \ (s)->lan.port != (s)->gwy.port #define BOUND_IFACE(r, k) (((r)->rule_flag & PFRULE_IFBOUND) ? (k) : \ ((r)->rule_flag & PFRULE_GRBOUND) ? (k)->pfik_parent : \ (k)->pfik_parent->pfik_parent) #define STATE_INC_COUNTERS(s) \ do { \ s->rule.ptr->states++; \ if (s->anchor.ptr != NULL) \ s->anchor.ptr->states++; \ if (s->nat_rule.ptr != NULL) \ s->nat_rule.ptr->states++; \ } while (0) #define STATE_DEC_COUNTERS(s) \ do { \ if (s->nat_rule.ptr != NULL) \ s->nat_rule.ptr->states--; \ if (s->anchor.ptr != NULL) \ s->anchor.ptr->states--; \ s->rule.ptr->states--; \ } while (0) #ifndef __FreeBSD__ static __inline int pf_src_compare(struct pf_src_node *, struct pf_src_node *); static __inline int pf_state_compare_lan_ext(struct pf_state *, struct pf_state *); static __inline int pf_state_compare_ext_gwy(struct pf_state *, struct pf_state *); static __inline int pf_state_compare_id(struct pf_state *, struct pf_state *); static __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *); #else static int pf_src_compare(struct pf_src_node *, struct pf_src_node *); static int pf_state_compare_lan_ext(struct pf_state *, struct pf_state *); static int pf_state_compare_ext_gwy(struct pf_state *, struct pf_state *); static int pf_state_compare_id(struct pf_state *, struct pf_state *); static int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *); #endif struct pf_src_tree tree_src_tracking; struct pf_state_tree_id tree_id; struct pf_state_queue state_updates; RB_GENERATE(pf_src_tree, pf_src_node, entry, pf_src_compare); RB_GENERATE(pf_state_tree_lan_ext, pf_state, u.s.entry_lan_ext, pf_state_compare_lan_ext); RB_GENERATE(pf_state_tree_ext_gwy, pf_state, u.s.entry_ext_gwy, pf_state_compare_ext_gwy); RB_GENERATE(pf_state_tree_id, pf_state, u.s.entry_id, pf_state_compare_id); RB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare); RB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare); #ifdef __FreeBSD__ static int #else static __inline int #endif pf_src_compare(struct pf_src_node *a, struct pf_src_node *b) { int diff; if (a->rule.ptr > b->rule.ptr) return (1); if (a->rule.ptr < b->rule.ptr) return (-1); if ((diff = a->af - b->af) != 0) return (diff); switch (a->af) { #ifdef INET case AF_INET: if (a->addr.addr32[0] > b->addr.addr32[0]) return (1); if (a->addr.addr32[0] < b->addr.addr32[0]) return (-1); break; #endif /* INET */ #ifdef INET6 case AF_INET6: if (a->addr.addr32[3] > b->addr.addr32[3]) return (1); if (a->addr.addr32[3] < b->addr.addr32[3]) return (-1); if (a->addr.addr32[2] > b->addr.addr32[2]) return (1); if (a->addr.addr32[2] < b->addr.addr32[2]) return (-1); if (a->addr.addr32[1] > b->addr.addr32[1]) return (1); if (a->addr.addr32[1] < b->addr.addr32[1]) return (-1); if (a->addr.addr32[0] > b->addr.addr32[0]) return (1); if (a->addr.addr32[0] < b->addr.addr32[0]) return (-1); break; #endif /* INET6 */ } return (0); } #ifdef __FreeBSD__ static int #else static __inline int #endif pf_state_compare_lan_ext(struct pf_state *a, struct pf_state *b) { int diff; if ((diff = a->proto - b->proto) != 0) return (diff); if ((diff = a->af - b->af) != 0) return (diff); switch (a->af) { #ifdef INET case AF_INET: if (a->lan.addr.addr32[0] > b->lan.addr.addr32[0]) return (1); if (a->lan.addr.addr32[0] < b->lan.addr.addr32[0]) return (-1); if (a->ext.addr.addr32[0] > b->ext.addr.addr32[0]) return (1); if (a->ext.addr.addr32[0] < b->ext.addr.addr32[0]) return (-1); break; #endif /* INET */ #ifdef INET6 case AF_INET6: if (a->lan.addr.addr32[3] > b->lan.addr.addr32[3]) return (1); if (a->lan.addr.addr32[3] < b->lan.addr.addr32[3]) return (-1); if (a->ext.addr.addr32[3] > b->ext.addr.addr32[3]) return (1); if (a->ext.addr.addr32[3] < b->ext.addr.addr32[3]) return (-1); if (a->lan.addr.addr32[2] > b->lan.addr.addr32[2]) return (1); if (a->lan.addr.addr32[2] < b->lan.addr.addr32[2]) return (-1); if (a->ext.addr.addr32[2] > b->ext.addr.addr32[2]) return (1); if (a->ext.addr.addr32[2] < b->ext.addr.addr32[2]) return (-1); if (a->lan.addr.addr32[1] > b->lan.addr.addr32[1]) return (1); if (a->lan.addr.addr32[1] < b->lan.addr.addr32[1]) return (-1); if (a->ext.addr.addr32[1] > b->ext.addr.addr32[1]) return (1); if (a->ext.addr.addr32[1] < b->ext.addr.addr32[1]) return (-1); if (a->lan.addr.addr32[0] > b->lan.addr.addr32[0]) return (1); if (a->lan.addr.addr32[0] < b->lan.addr.addr32[0]) return (-1); if (a->ext.addr.addr32[0] > b->ext.addr.addr32[0]) return (1); if (a->ext.addr.addr32[0] < b->ext.addr.addr32[0]) return (-1); break; #endif /* INET6 */ } if ((diff = a->lan.port - b->lan.port) != 0) return (diff); if ((diff = a->ext.port - b->ext.port) != 0) return (diff); return (0); } #ifdef __FreeBSD__ static int #else static __inline int #endif pf_state_compare_ext_gwy(struct pf_state *a, struct pf_state *b) { int diff; if ((diff = a->proto - b->proto) != 0) return (diff); if ((diff = a->af - b->af) != 0) return (diff); switch (a->af) { #ifdef INET case AF_INET: if (a->ext.addr.addr32[0] > b->ext.addr.addr32[0]) return (1); if (a->ext.addr.addr32[0] < b->ext.addr.addr32[0]) return (-1); if (a->gwy.addr.addr32[0] > b->gwy.addr.addr32[0]) return (1); if (a->gwy.addr.addr32[0] < b->gwy.addr.addr32[0]) return (-1); break; #endif /* INET */ #ifdef INET6 case AF_INET6: if (a->ext.addr.addr32[3] > b->ext.addr.addr32[3]) return (1); if (a->ext.addr.addr32[3] < b->ext.addr.addr32[3]) return (-1); if (a->gwy.addr.addr32[3] > b->gwy.addr.addr32[3]) return (1); if (a->gwy.addr.addr32[3] < b->gwy.addr.addr32[3]) return (-1); if (a->ext.addr.addr32[2] > b->ext.addr.addr32[2]) return (1); if (a->ext.addr.addr32[2] < b->ext.addr.addr32[2]) return (-1); if (a->gwy.addr.addr32[2] > b->gwy.addr.addr32[2]) return (1); if (a->gwy.addr.addr32[2] < b->gwy.addr.addr32[2]) return (-1); if (a->ext.addr.addr32[1] > b->ext.addr.addr32[1]) return (1); if (a->ext.addr.addr32[1] < b->ext.addr.addr32[1]) return (-1); if (a->gwy.addr.addr32[1] > b->gwy.addr.addr32[1]) return (1); if (a->gwy.addr.addr32[1] < b->gwy.addr.addr32[1]) return (-1); if (a->ext.addr.addr32[0] > b->ext.addr.addr32[0]) return (1); if (a->ext.addr.addr32[0] < b->ext.addr.addr32[0]) return (-1); if (a->gwy.addr.addr32[0] > b->gwy.addr.addr32[0]) return (1); if (a->gwy.addr.addr32[0] < b->gwy.addr.addr32[0]) return (-1); break; #endif /* INET6 */ } if ((diff = a->ext.port - b->ext.port) != 0) return (diff); if ((diff = a->gwy.port - b->gwy.port) != 0) return (diff); return (0); } #ifdef __FreeBSD__ static int #else static __inline int #endif pf_state_compare_id(struct pf_state *a, struct pf_state *b) { if (a->id > b->id) return (1); if (a->id < b->id) return (-1); if (a->creatorid > b->creatorid) return (1); if (a->creatorid < b->creatorid) return (-1); return (0); } #ifdef __FreeBSD__ static int #else static __inline int #endif pf_anchor_compare(struct pf_anchor *a, struct pf_anchor *b) { int c = strcmp(a->path, b->path); return (c ? (c < 0 ? -1 : 1) : 0); } #ifdef INET6 void pf_addrcpy(struct pf_addr *dst, struct pf_addr *src, sa_family_t af) { switch (af) { #ifdef INET case AF_INET: dst->addr32[0] = src->addr32[0]; break; #endif /* INET */ case AF_INET6: dst->addr32[0] = src->addr32[0]; dst->addr32[1] = src->addr32[1]; dst->addr32[2] = src->addr32[2]; dst->addr32[3] = src->addr32[3]; break; } } #endif /* INET6 */ struct pf_state * pf_find_state_byid(struct pf_state *key) { pf_status.fcounters[FCNT_STATE_SEARCH]++; return (RB_FIND(pf_state_tree_id, &tree_id, key)); } struct pf_state * pf_find_state_recurse(struct pfi_kif *kif, struct pf_state *key, u_int8_t tree) { struct pf_state *s; pf_status.fcounters[FCNT_STATE_SEARCH]++; switch (tree) { case PF_LAN_EXT: for (; kif != NULL; kif = kif->pfik_parent) { s = RB_FIND(pf_state_tree_lan_ext, &kif->pfik_lan_ext, key); if (s != NULL) return (s); } return (NULL); case PF_EXT_GWY: for (; kif != NULL; kif = kif->pfik_parent) { s = RB_FIND(pf_state_tree_ext_gwy, &kif->pfik_ext_gwy, key); if (s != NULL) return (s); } return (NULL); default: panic("pf_find_state_recurse"); } } struct pf_state * pf_find_state_all(struct pf_state *key, u_int8_t tree, int *more) { struct pf_state *s, *ss = NULL; struct pfi_kif *kif; pf_status.fcounters[FCNT_STATE_SEARCH]++; switch (tree) { case PF_LAN_EXT: TAILQ_FOREACH(kif, &pfi_statehead, pfik_w_states) { s = RB_FIND(pf_state_tree_lan_ext, &kif->pfik_lan_ext, key); if (s == NULL) continue; if (more == NULL) return (s); ss = s; (*more)++; } return (ss); case PF_EXT_GWY: TAILQ_FOREACH(kif, &pfi_statehead, pfik_w_states) { s = RB_FIND(pf_state_tree_ext_gwy, &kif->pfik_ext_gwy, key); if (s == NULL) continue; if (more == NULL) return (s); ss = s; (*more)++; } return (ss); default: panic("pf_find_state_all"); } } void pf_init_threshold(struct pf_threshold *threshold, u_int32_t limit, u_int32_t seconds) { threshold->limit = limit * PF_THRESHOLD_MULT; threshold->seconds = seconds; threshold->count = 0; threshold->last = time_second; } void pf_add_threshold(struct pf_threshold *threshold) { u_int32_t t = time_second, diff = t - threshold->last; if (diff >= threshold->seconds) threshold->count = 0; else threshold->count -= threshold->count * diff / threshold->seconds; threshold->count += PF_THRESHOLD_MULT; threshold->last = t; } int pf_check_threshold(struct pf_threshold *threshold) { return (threshold->count > threshold->limit); } int pf_src_connlimit(struct pf_state **state) { struct pf_state *s; int bad = 0; (*state)->src_node->conn++; #ifdef __FreeBSD__ (*state)->local_flags |= PFSTATE_SRC_CONN; #endif pf_add_threshold(&(*state)->src_node->conn_rate); if ((*state)->rule.ptr->max_src_conn && (*state)->rule.ptr->max_src_conn < (*state)->src_node->conn) { pf_status.lcounters[LCNT_SRCCONN]++; bad++; } if ((*state)->rule.ptr->max_src_conn_rate.limit && pf_check_threshold(&(*state)->src_node->conn_rate)) { pf_status.lcounters[LCNT_SRCCONNRATE]++; bad++; } if (!bad) return (0); if ((*state)->rule.ptr->overload_tbl) { struct pfr_addr p; u_int32_t killed = 0; pf_status.lcounters[LCNT_OVERLOAD_TABLE]++; if (pf_status.debug >= PF_DEBUG_MISC) { printf("pf_src_connlimit: blocking address "); pf_print_host(&(*state)->src_node->addr, 0, (*state)->af); } bzero(&p, sizeof(p)); p.pfra_af = (*state)->af; switch ((*state)->af) { #ifdef INET case AF_INET: p.pfra_net = 32; p.pfra_ip4addr = (*state)->src_node->addr.v4; break; #endif /* INET */ #ifdef INET6 case AF_INET6: p.pfra_net = 128; p.pfra_ip6addr = (*state)->src_node->addr.v6; break; #endif /* INET6 */ } pfr_insert_kentry((*state)->rule.ptr->overload_tbl, &p, time_second); /* kill existing states if that's required. */ if ((*state)->rule.ptr->flush) { pf_status.lcounters[LCNT_OVERLOAD_FLUSH]++; RB_FOREACH(s, pf_state_tree_id, &tree_id) { /* * Kill states from this source. (Only those * from the same rule if PF_FLUSH_GLOBAL is not * set) */ if (s->af == (*state)->af && (((*state)->direction == PF_OUT && PF_AEQ(&(*state)->src_node->addr, &s->lan.addr, s->af)) || ((*state)->direction == PF_IN && PF_AEQ(&(*state)->src_node->addr, &s->ext.addr, s->af))) && ((*state)->rule.ptr->flush & PF_FLUSH_GLOBAL || (*state)->rule.ptr == s->rule.ptr)) { s->timeout = PFTM_PURGE; s->src.state = s->dst.state = TCPS_CLOSED; killed++; } } if (pf_status.debug >= PF_DEBUG_MISC) printf(", %u states killed", killed); } if (pf_status.debug >= PF_DEBUG_MISC) printf("\n"); } /* kill this state */ (*state)->timeout = PFTM_PURGE; (*state)->src.state = (*state)->dst.state = TCPS_CLOSED; return (1); } int pf_insert_src_node(struct pf_src_node **sn, struct pf_rule *rule, struct pf_addr *src, sa_family_t af) { struct pf_src_node k; if (*sn == NULL) { k.af = af; PF_ACPY(&k.addr, src, af); if (rule->rule_flag & PFRULE_RULESRCTRACK || rule->rpool.opts & PF_POOL_STICKYADDR) k.rule.ptr = rule; else k.rule.ptr = NULL; pf_status.scounters[SCNT_SRC_NODE_SEARCH]++; *sn = RB_FIND(pf_src_tree, &tree_src_tracking, &k); } if (*sn == NULL) { if (!rule->max_src_nodes || rule->src_nodes < rule->max_src_nodes) (*sn) = pool_get(&pf_src_tree_pl, PR_NOWAIT); else pf_status.lcounters[LCNT_SRCNODES]++; if ((*sn) == NULL) return (-1); bzero(*sn, sizeof(struct pf_src_node)); pf_init_threshold(&(*sn)->conn_rate, rule->max_src_conn_rate.limit, rule->max_src_conn_rate.seconds); (*sn)->af = af; if (rule->rule_flag & PFRULE_RULESRCTRACK || rule->rpool.opts & PF_POOL_STICKYADDR) (*sn)->rule.ptr = rule; else (*sn)->rule.ptr = NULL; PF_ACPY(&(*sn)->addr, src, af); if (RB_INSERT(pf_src_tree, &tree_src_tracking, *sn) != NULL) { if (pf_status.debug >= PF_DEBUG_MISC) { printf("pf: src_tree insert failed: "); pf_print_host(&(*sn)->addr, 0, af); printf("\n"); } pool_put(&pf_src_tree_pl, *sn); return (-1); } (*sn)->creation = time_second; (*sn)->ruletype = rule->action; if ((*sn)->rule.ptr != NULL) (*sn)->rule.ptr->src_nodes++; pf_status.scounters[SCNT_SRC_NODE_INSERT]++; pf_status.src_nodes++; } else { if (rule->max_src_states && (*sn)->states >= rule->max_src_states) { pf_status.lcounters[LCNT_SRCSTATES]++; return (-1); } } return (0); } int pf_insert_state(struct pfi_kif *kif, struct pf_state *state) { /* Thou MUST NOT insert multiple duplicate keys */ state->u.s.kif = kif; if (RB_INSERT(pf_state_tree_lan_ext, &kif->pfik_lan_ext, state)) { if (pf_status.debug >= PF_DEBUG_MISC) { printf("pf: state insert failed: tree_lan_ext"); printf(" lan: "); pf_print_host(&state->lan.addr, state->lan.port, state->af); printf(" gwy: "); pf_print_host(&state->gwy.addr, state->gwy.port, state->af); printf(" ext: "); pf_print_host(&state->ext.addr, state->ext.port, state->af); if (state->sync_flags & PFSTATE_FROMSYNC) printf(" (from sync)"); printf("\n"); } return (-1); } if (RB_INSERT(pf_state_tree_ext_gwy, &kif->pfik_ext_gwy, state)) { if (pf_status.debug >= PF_DEBUG_MISC) { printf("pf: state insert failed: tree_ext_gwy"); printf(" lan: "); pf_print_host(&state->lan.addr, state->lan.port, state->af); printf(" gwy: "); pf_print_host(&state->gwy.addr, state->gwy.port, state->af); printf(" ext: "); pf_print_host(&state->ext.addr, state->ext.port, state->af); if (state->sync_flags & PFSTATE_FROMSYNC) printf(" (from sync)"); printf("\n"); } RB_REMOVE(pf_state_tree_lan_ext, &kif->pfik_lan_ext, state); return (-1); } if (state->id == 0 && state->creatorid == 0) { state->id = htobe64(pf_status.stateid++); state->creatorid = pf_status.hostid; } if (RB_INSERT(pf_state_tree_id, &tree_id, state) != NULL) { if (pf_status.debug >= PF_DEBUG_MISC) { #ifdef __FreeBSD__ printf("pf: state insert failed: " "id: %016llx creatorid: %08x", (long long)be64toh(state->id), ntohl(state->creatorid)); #else printf("pf: state insert failed: " "id: %016llx creatorid: %08x", betoh64(state->id), ntohl(state->creatorid)); #endif if (state->sync_flags & PFSTATE_FROMSYNC) printf(" (from sync)"); printf("\n"); } RB_REMOVE(pf_state_tree_lan_ext, &kif->pfik_lan_ext, state); RB_REMOVE(pf_state_tree_ext_gwy, &kif->pfik_ext_gwy, state); return (-1); } TAILQ_INSERT_HEAD(&state_updates, state, u.s.entry_updates); pf_status.fcounters[FCNT_STATE_INSERT]++; pf_status.states++; pfi_attach_state(kif); #if NPFSYNC pfsync_insert_state(state); #endif return (0); } void pf_purge_timeout(void *arg) { #ifdef __FreeBSD__ struct callout *to = arg; #else struct timeout *to = arg; #endif int s; #ifdef __FreeBSD__ PF_LOCK(); #endif s = splsoftnet(); pf_purge_expired_states(); pf_purge_expired_fragments(); pf_purge_expired_src_nodes(); splx(s); #ifdef __FreeBSD__ PF_UNLOCK(); #endif #ifdef __FreeBSD__ callout_reset(to, pf_default_rule.timeout[PFTM_INTERVAL] * hz, pf_purge_timeout, to); #else timeout_add(to, pf_default_rule.timeout[PFTM_INTERVAL] * hz); #endif } u_int32_t pf_state_expires(const struct pf_state *state) { u_int32_t timeout; u_int32_t start; u_int32_t end; u_int32_t states; /* handle all PFTM_* > PFTM_MAX here */ if (state->timeout == PFTM_PURGE) return (time_second); if (state->timeout == PFTM_UNTIL_PACKET) return (0); #ifdef __FreeBSD__ KASSERT((state->timeout < PFTM_MAX), ("pf_state_expires: timeout > PFTM_MAX")); #else KASSERT(state->timeout < PFTM_MAX); #endif timeout = state->rule.ptr->timeout[state->timeout]; if (!timeout) timeout = pf_default_rule.timeout[state->timeout]; start = state->rule.ptr->timeout[PFTM_ADAPTIVE_START]; if (start) { end = state->rule.ptr->timeout[PFTM_ADAPTIVE_END]; states = state->rule.ptr->states; } else { start = pf_default_rule.timeout[PFTM_ADAPTIVE_START]; end = pf_default_rule.timeout[PFTM_ADAPTIVE_END]; states = pf_status.states; } if (end && states > start && start < end) { if (states < end) return (state->expire + timeout * (end - states) / (end - start)); else return (time_second); } return (state->expire + timeout); } void pf_purge_expired_src_nodes(void) { struct pf_src_node *cur, *next; for (cur = RB_MIN(pf_src_tree, &tree_src_tracking); cur; cur = next) { next = RB_NEXT(pf_src_tree, &tree_src_tracking, cur); if (cur->states <= 0 && cur->expire <= time_second) { if (cur->rule.ptr != NULL) { cur->rule.ptr->src_nodes--; if (cur->rule.ptr->states <= 0 && cur->rule.ptr->max_src_nodes <= 0) pf_rm_rule(NULL, cur->rule.ptr); } RB_REMOVE(pf_src_tree, &tree_src_tracking, cur); pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; pf_status.src_nodes--; pool_put(&pf_src_tree_pl, cur); } } } void pf_src_tree_remove_state(struct pf_state *s) { u_int32_t timeout; if (s->src_node != NULL) { if (s->proto == IPPROTO_TCP) { #ifdef __FreeBSD__ if (s->local_flags & PFSTATE_SRC_CONN) #else if (s->src.state == PF_TCPS_PROXY_DST || s->timeout >= PFTM_TCP_ESTABLISHED) #endif --s->src_node->conn; } if (--s->src_node->states <= 0) { timeout = s->rule.ptr->timeout[PFTM_SRC_NODE]; if (!timeout) timeout = pf_default_rule.timeout[PFTM_SRC_NODE]; s->src_node->expire = time_second + timeout; } } if (s->nat_src_node != s->src_node && s->nat_src_node != NULL) { if (--s->nat_src_node->states <= 0) { timeout = s->rule.ptr->timeout[PFTM_SRC_NODE]; if (!timeout) timeout = pf_default_rule.timeout[PFTM_SRC_NODE]; s->nat_src_node->expire = time_second + timeout; } } s->src_node = s->nat_src_node = NULL; } void pf_purge_expired_state(struct pf_state *cur) { #ifdef __FreeBSD__ if (cur->local_flags & PFSTATE_EXPIRING) return; cur->local_flags |= PFSTATE_EXPIRING; #endif if (cur->src.state == PF_TCPS_PROXY_DST) +#ifdef __FreeBSD__ + pf_send_tcp(NULL, cur->rule.ptr, cur->af, +#else pf_send_tcp(cur->rule.ptr, cur->af, +#endif &cur->ext.addr, &cur->lan.addr, cur->ext.port, cur->lan.port, cur->src.seqhi, cur->src.seqlo + 1, TH_RST|TH_ACK, 0, 0, 0, 1, NULL, NULL); RB_REMOVE(pf_state_tree_ext_gwy, &cur->u.s.kif->pfik_ext_gwy, cur); RB_REMOVE(pf_state_tree_lan_ext, &cur->u.s.kif->pfik_lan_ext, cur); RB_REMOVE(pf_state_tree_id, &tree_id, cur); #if NPFSYNC pfsync_delete_state(cur); #endif pf_src_tree_remove_state(cur); if (--cur->rule.ptr->states <= 0 && cur->rule.ptr->src_nodes <= 0) pf_rm_rule(NULL, cur->rule.ptr); if (cur->nat_rule.ptr != NULL) if (--cur->nat_rule.ptr->states <= 0 && cur->nat_rule.ptr->src_nodes <= 0) pf_rm_rule(NULL, cur->nat_rule.ptr); if (cur->anchor.ptr != NULL) if (--cur->anchor.ptr->states <= 0) pf_rm_rule(NULL, cur->anchor.ptr); pf_normalize_tcp_cleanup(cur); pfi_detach_state(cur->u.s.kif); TAILQ_REMOVE(&state_updates, cur, u.s.entry_updates); if (cur->tag) pf_tag_unref(cur->tag); pool_put(&pf_state_pl, cur); pf_status.fcounters[FCNT_STATE_REMOVALS]++; pf_status.states--; } void pf_purge_expired_states(void) { struct pf_state *cur, *next; for (cur = RB_MIN(pf_state_tree_id, &tree_id); cur; cur = next) { next = RB_NEXT(pf_state_tree_id, &tree_id, cur); if (pf_state_expires(cur) <= time_second) pf_purge_expired_state(cur); } } int pf_tbladdr_setup(struct pf_ruleset *rs, struct pf_addr_wrap *aw) { if (aw->type != PF_ADDR_TABLE) return (0); if ((aw->p.tbl = pfr_attach_table(rs, aw->v.tblname)) == NULL) return (1); return (0); } void pf_tbladdr_remove(struct pf_addr_wrap *aw) { if (aw->type != PF_ADDR_TABLE || aw->p.tbl == NULL) return; pfr_detach_table(aw->p.tbl); aw->p.tbl = NULL; } void pf_tbladdr_copyout(struct pf_addr_wrap *aw) { struct pfr_ktable *kt = aw->p.tbl; if (aw->type != PF_ADDR_TABLE || kt == NULL) return; if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE) && kt->pfrkt_root != NULL) kt = kt->pfrkt_root; aw->p.tbl = NULL; aw->p.tblcnt = (kt->pfrkt_flags & PFR_TFLAG_ACTIVE) ? kt->pfrkt_cnt : -1; } void pf_print_host(struct pf_addr *addr, u_int16_t p, sa_family_t af) { switch (af) { #ifdef INET case AF_INET: { u_int32_t a = ntohl(addr->addr32[0]); printf("%u.%u.%u.%u", (a>>24)&255, (a>>16)&255, (a>>8)&255, a&255); if (p) { p = ntohs(p); printf(":%u", p); } break; } #endif /* INET */ #ifdef INET6 case AF_INET6: { u_int16_t b; u_int8_t i, curstart = 255, curend = 0, maxstart = 0, maxend = 0; for (i = 0; i < 8; i++) { if (!addr->addr16[i]) { if (curstart == 255) curstart = i; else curend = i; } else { if (curstart) { if ((curend - curstart) > (maxend - maxstart)) { maxstart = curstart; maxend = curend; curstart = 255; } } } } for (i = 0; i < 8; i++) { if (i >= maxstart && i <= maxend) { if (maxend != 7) { if (i == maxstart) printf(":"); } else { if (i == maxend) printf(":"); } } else { b = ntohs(addr->addr16[i]); printf("%x", b); if (i < 7) printf(":"); } } if (p) { p = ntohs(p); printf("[%u]", p); } break; } #endif /* INET6 */ } } void pf_print_state(struct pf_state *s) { switch (s->proto) { case IPPROTO_TCP: printf("TCP "); break; case IPPROTO_UDP: printf("UDP "); break; case IPPROTO_ICMP: printf("ICMP "); break; case IPPROTO_ICMPV6: printf("ICMPV6 "); break; default: printf("%u ", s->proto); break; } pf_print_host(&s->lan.addr, s->lan.port, s->af); printf(" "); pf_print_host(&s->gwy.addr, s->gwy.port, s->af); printf(" "); pf_print_host(&s->ext.addr, s->ext.port, s->af); printf(" [lo=%u high=%u win=%u modulator=%u", s->src.seqlo, s->src.seqhi, s->src.max_win, s->src.seqdiff); if (s->src.wscale && s->dst.wscale) printf(" wscale=%u", s->src.wscale & PF_WSCALE_MASK); printf("]"); printf(" [lo=%u high=%u win=%u modulator=%u", s->dst.seqlo, s->dst.seqhi, s->dst.max_win, s->dst.seqdiff); if (s->src.wscale && s->dst.wscale) printf(" wscale=%u", s->dst.wscale & PF_WSCALE_MASK); printf("]"); printf(" %u:%u", s->src.state, s->dst.state); } void pf_print_flags(u_int8_t f) { if (f) printf(" "); if (f & TH_FIN) printf("F"); if (f & TH_SYN) printf("S"); if (f & TH_RST) printf("R"); if (f & TH_PUSH) printf("P"); if (f & TH_ACK) printf("A"); if (f & TH_URG) printf("U"); if (f & TH_ECE) printf("E"); if (f & TH_CWR) printf("W"); } #define PF_SET_SKIP_STEPS(i) \ do { \ while (head[i] != cur) { \ head[i]->skip[i].ptr = cur; \ head[i] = TAILQ_NEXT(head[i], entries); \ } \ } while (0) void pf_calc_skip_steps(struct pf_rulequeue *rules) { struct pf_rule *cur, *prev, *head[PF_SKIP_COUNT]; int i; cur = TAILQ_FIRST(rules); prev = cur; for (i = 0; i < PF_SKIP_COUNT; ++i) head[i] = cur; while (cur != NULL) { if (cur->kif != prev->kif || cur->ifnot != prev->ifnot) PF_SET_SKIP_STEPS(PF_SKIP_IFP); if (cur->direction != prev->direction) PF_SET_SKIP_STEPS(PF_SKIP_DIR); if (cur->af != prev->af) PF_SET_SKIP_STEPS(PF_SKIP_AF); if (cur->proto != prev->proto) PF_SET_SKIP_STEPS(PF_SKIP_PROTO); if (cur->src.neg != prev->src.neg || pf_addr_wrap_neq(&cur->src.addr, &prev->src.addr)) PF_SET_SKIP_STEPS(PF_SKIP_SRC_ADDR); if (cur->src.port[0] != prev->src.port[0] || cur->src.port[1] != prev->src.port[1] || cur->src.port_op != prev->src.port_op) PF_SET_SKIP_STEPS(PF_SKIP_SRC_PORT); if (cur->dst.neg != prev->dst.neg || pf_addr_wrap_neq(&cur->dst.addr, &prev->dst.addr)) PF_SET_SKIP_STEPS(PF_SKIP_DST_ADDR); if (cur->dst.port[0] != prev->dst.port[0] || cur->dst.port[1] != prev->dst.port[1] || cur->dst.port_op != prev->dst.port_op) PF_SET_SKIP_STEPS(PF_SKIP_DST_PORT); prev = cur; cur = TAILQ_NEXT(cur, entries); } for (i = 0; i < PF_SKIP_COUNT; ++i) PF_SET_SKIP_STEPS(i); } int pf_addr_wrap_neq(struct pf_addr_wrap *aw1, struct pf_addr_wrap *aw2) { if (aw1->type != aw2->type) return (1); switch (aw1->type) { case PF_ADDR_ADDRMASK: if (PF_ANEQ(&aw1->v.a.addr, &aw2->v.a.addr, 0)) return (1); if (PF_ANEQ(&aw1->v.a.mask, &aw2->v.a.mask, 0)) return (1); return (0); case PF_ADDR_DYNIFTL: return (aw1->p.dyn->pfid_kt != aw2->p.dyn->pfid_kt); case PF_ADDR_NOROUTE: return (0); case PF_ADDR_TABLE: return (aw1->p.tbl != aw2->p.tbl); default: printf("invalid address type: %d\n", aw1->type); return (1); } } u_int16_t pf_cksum_fixup(u_int16_t cksum, u_int16_t old, u_int16_t new, u_int8_t udp) { u_int32_t l; if (udp && !cksum) return (0x0000); l = cksum + old - new; l = (l >> 16) + (l & 65535); l = l & 65535; if (udp && !l) return (0xFFFF); return (l); } void pf_change_ap(struct pf_addr *a, u_int16_t *p, u_int16_t *ic, u_int16_t *pc, struct pf_addr *an, u_int16_t pn, u_int8_t u, sa_family_t af) { struct pf_addr ao; u_int16_t po = *p; PF_ACPY(&ao, a, af); PF_ACPY(a, an, af); *p = pn; switch (af) { #ifdef INET case AF_INET: *ic = pf_cksum_fixup(pf_cksum_fixup(*ic, ao.addr16[0], an->addr16[0], 0), ao.addr16[1], an->addr16[1], 0); *p = pn; *pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc, ao.addr16[0], an->addr16[0], u), ao.addr16[1], an->addr16[1], u), po, pn, u); break; #endif /* INET */ #ifdef INET6 case AF_INET6: *pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc, ao.addr16[0], an->addr16[0], u), ao.addr16[1], an->addr16[1], u), ao.addr16[2], an->addr16[2], u), ao.addr16[3], an->addr16[3], u), ao.addr16[4], an->addr16[4], u), ao.addr16[5], an->addr16[5], u), ao.addr16[6], an->addr16[6], u), ao.addr16[7], an->addr16[7], u), po, pn, u); break; #endif /* INET6 */ } } /* Changes a u_int32_t. Uses a void * so there are no align restrictions */ void pf_change_a(void *a, u_int16_t *c, u_int32_t an, u_int8_t u) { u_int32_t ao; memcpy(&ao, a, sizeof(ao)); memcpy(a, &an, sizeof(u_int32_t)); *c = pf_cksum_fixup(pf_cksum_fixup(*c, ao / 65536, an / 65536, u), ao % 65536, an % 65536, u); } #ifdef INET6 void pf_change_a6(struct pf_addr *a, u_int16_t *c, struct pf_addr *an, u_int8_t u) { struct pf_addr ao; PF_ACPY(&ao, a, AF_INET6); PF_ACPY(a, an, AF_INET6); *c = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( pf_cksum_fixup(pf_cksum_fixup(*c, ao.addr16[0], an->addr16[0], u), ao.addr16[1], an->addr16[1], u), ao.addr16[2], an->addr16[2], u), ao.addr16[3], an->addr16[3], u), ao.addr16[4], an->addr16[4], u), ao.addr16[5], an->addr16[5], u), ao.addr16[6], an->addr16[6], u), ao.addr16[7], an->addr16[7], u); } #endif /* INET6 */ void pf_change_icmp(struct pf_addr *ia, u_int16_t *ip, struct pf_addr *oa, struct pf_addr *na, u_int16_t np, u_int16_t *pc, u_int16_t *h2c, u_int16_t *ic, u_int16_t *hc, u_int8_t u, sa_family_t af) { struct pf_addr oia, ooa; PF_ACPY(&oia, ia, af); PF_ACPY(&ooa, oa, af); /* Change inner protocol port, fix inner protocol checksum. */ if (ip != NULL) { u_int16_t oip = *ip; u_int32_t opc = 0; /* make the compiler happy */ if (pc != NULL) opc = *pc; *ip = np; if (pc != NULL) *pc = pf_cksum_fixup(*pc, oip, *ip, u); *ic = pf_cksum_fixup(*ic, oip, *ip, 0); if (pc != NULL) *ic = pf_cksum_fixup(*ic, opc, *pc, 0); } /* Change inner ip address, fix inner ip and icmp checksums. */ PF_ACPY(ia, na, af); switch (af) { #ifdef INET case AF_INET: { u_int32_t oh2c = *h2c; *h2c = pf_cksum_fixup(pf_cksum_fixup(*h2c, oia.addr16[0], ia->addr16[0], 0), oia.addr16[1], ia->addr16[1], 0); *ic = pf_cksum_fixup(pf_cksum_fixup(*ic, oia.addr16[0], ia->addr16[0], 0), oia.addr16[1], ia->addr16[1], 0); *ic = pf_cksum_fixup(*ic, oh2c, *h2c, 0); break; } #endif /* INET */ #ifdef INET6 case AF_INET6: *ic = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( pf_cksum_fixup(pf_cksum_fixup(*ic, oia.addr16[0], ia->addr16[0], u), oia.addr16[1], ia->addr16[1], u), oia.addr16[2], ia->addr16[2], u), oia.addr16[3], ia->addr16[3], u), oia.addr16[4], ia->addr16[4], u), oia.addr16[5], ia->addr16[5], u), oia.addr16[6], ia->addr16[6], u), oia.addr16[7], ia->addr16[7], u); break; #endif /* INET6 */ } /* Change outer ip address, fix outer ip or icmpv6 checksum. */ PF_ACPY(oa, na, af); switch (af) { #ifdef INET case AF_INET: *hc = pf_cksum_fixup(pf_cksum_fixup(*hc, ooa.addr16[0], oa->addr16[0], 0), ooa.addr16[1], oa->addr16[1], 0); break; #endif /* INET */ #ifdef INET6 case AF_INET6: *ic = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( pf_cksum_fixup(pf_cksum_fixup(*ic, ooa.addr16[0], oa->addr16[0], u), ooa.addr16[1], oa->addr16[1], u), ooa.addr16[2], oa->addr16[2], u), ooa.addr16[3], oa->addr16[3], u), ooa.addr16[4], oa->addr16[4], u), ooa.addr16[5], oa->addr16[5], u), ooa.addr16[6], oa->addr16[6], u), ooa.addr16[7], oa->addr16[7], u); break; #endif /* INET6 */ } } void +#ifdef __FreeBSD__ +pf_send_tcp(struct mbuf *replyto, const struct pf_rule *r, sa_family_t af, +#else pf_send_tcp(const struct pf_rule *r, sa_family_t af, +#endif const struct pf_addr *saddr, const struct pf_addr *daddr, u_int16_t sport, u_int16_t dport, u_int32_t seq, u_int32_t ack, u_int8_t flags, u_int16_t win, u_int16_t mss, u_int8_t ttl, int tag, struct ether_header *eh, struct ifnet *ifp) { struct mbuf *m; int len = 0, tlen; /* make the compiler happy */ #ifdef INET struct ip *h = NULL; /* make the compiler happy */ #endif /* INET */ #ifdef INET6 struct ip6_hdr *h6 = NULL; /* make the compiler happy */ #endif /* INET6 */ struct tcphdr *th = NULL; /* make the compiler happy */ char *opt; /* maximum segment size tcp option */ tlen = sizeof(struct tcphdr); if (mss) tlen += 4; switch (af) { #ifdef INET case AF_INET: len = sizeof(struct ip) + tlen; break; #endif /* INET */ #ifdef INET6 case AF_INET6: len = sizeof(struct ip6_hdr) + tlen; break; #endif /* INET6 */ } /* create outgoing mbuf */ m = m_gethdr(M_DONTWAIT, MT_HEADER); if (m == NULL) return; +#ifdef __FreeBSD__ +#ifdef MAC + if (replyto) + mac_create_mbuf_netlayer(replyto, m); + else + mac_create_mbuf_from_firewall(m); +#else + (void)replyto; +#endif +#endif if (tag) { #ifdef __FreeBSD__ m->m_flags |= M_SKIP_FIREWALL; #else struct m_tag *mtag; mtag = m_tag_get(PACKET_TAG_PF_GENERATED, 0, M_NOWAIT); if (mtag == NULL) { m_freem(m); return; } m_tag_prepend(m, mtag); #endif } #ifdef ALTQ if (r != NULL && r->qid) { struct m_tag *mtag; struct altq_tag *atag; mtag = m_tag_get(PACKET_TAG_PF_QID, sizeof(*atag), M_NOWAIT); if (mtag != NULL) { atag = (struct altq_tag *)(mtag + 1); atag->qid = r->qid; /* add hints for ecn */ atag->af = af; atag->hdr = mtod(m, struct ip *); m_tag_prepend(m, mtag); } } #endif /* ALTQ */ m->m_data += max_linkhdr; m->m_pkthdr.len = m->m_len = len; m->m_pkthdr.rcvif = NULL; bzero(m->m_data, len); switch (af) { #ifdef INET case AF_INET: h = mtod(m, struct ip *); /* IP header fields included in the TCP checksum */ h->ip_p = IPPROTO_TCP; h->ip_len = htons(tlen); h->ip_src.s_addr = saddr->v4.s_addr; h->ip_dst.s_addr = daddr->v4.s_addr; th = (struct tcphdr *)((caddr_t)h + sizeof(struct ip)); break; #endif /* INET */ #ifdef INET6 case AF_INET6: h6 = mtod(m, struct ip6_hdr *); /* IP header fields included in the TCP checksum */ h6->ip6_nxt = IPPROTO_TCP; h6->ip6_plen = htons(tlen); memcpy(&h6->ip6_src, &saddr->v6, sizeof(struct in6_addr)); memcpy(&h6->ip6_dst, &daddr->v6, sizeof(struct in6_addr)); th = (struct tcphdr *)((caddr_t)h6 + sizeof(struct ip6_hdr)); break; #endif /* INET6 */ } /* TCP header */ th->th_sport = sport; th->th_dport = dport; th->th_seq = htonl(seq); th->th_ack = htonl(ack); th->th_off = tlen >> 2; th->th_flags = flags; th->th_win = htons(win); if (mss) { opt = (char *)(th + 1); opt[0] = TCPOPT_MAXSEG; opt[1] = 4; HTONS(mss); bcopy((caddr_t)&mss, (caddr_t)(opt + 2), 2); } switch (af) { #ifdef INET case AF_INET: /* TCP checksum */ th->th_sum = in_cksum(m, len); /* Finish the IP header */ h->ip_v = 4; h->ip_hl = sizeof(*h) >> 2; h->ip_tos = IPTOS_LOWDELAY; #ifdef __FreeBSD__ h->ip_off = path_mtu_discovery ? IP_DF : 0; h->ip_len = len; #else h->ip_off = htons(ip_mtudisc ? IP_DF : 0); h->ip_len = htons(len); #endif h->ip_ttl = ttl ? ttl : ip_defttl; h->ip_sum = 0; if (eh == NULL) { #ifdef __FreeBSD__ PF_UNLOCK(); ip_output(m, (void *)NULL, (void *)NULL, 0, (void *)NULL, (void *)NULL); PF_LOCK(); #else /* ! __FreeBSD__ */ ip_output(m, (void *)NULL, (void *)NULL, 0, (void *)NULL, (void *)NULL); #endif } else { struct route ro; struct rtentry rt; struct ether_header *e = (void *)ro.ro_dst.sa_data; if (ifp == NULL) { m_freem(m); return; } rt.rt_ifp = ifp; ro.ro_rt = &rt; ro.ro_dst.sa_len = sizeof(ro.ro_dst); ro.ro_dst.sa_family = pseudo_AF_HDRCMPLT; bcopy(eh->ether_dhost, e->ether_shost, ETHER_ADDR_LEN); bcopy(eh->ether_shost, e->ether_dhost, ETHER_ADDR_LEN); e->ether_type = eh->ether_type; #ifdef __FreeBSD__ PF_UNLOCK(); /* XXX_IMPORT: later */ ip_output(m, (void *)NULL, &ro, 0, (void *)NULL, (void *)NULL); PF_LOCK(); #else /* ! __FreeBSD__ */ ip_output(m, (void *)NULL, &ro, IP_ROUTETOETHER, (void *)NULL, (void *)NULL); #endif } break; #endif /* INET */ #ifdef INET6 case AF_INET6: /* TCP checksum */ th->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(struct ip6_hdr), tlen); h6->ip6_vfc |= IPV6_VERSION; h6->ip6_hlim = IPV6_DEFHLIM; #ifdef __FreeBSD__ PF_UNLOCK(); ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); PF_LOCK(); #else ip6_output(m, NULL, NULL, 0, NULL, NULL); #endif break; #endif /* INET6 */ } } void pf_send_icmp(struct mbuf *m, u_int8_t type, u_int8_t code, sa_family_t af, struct pf_rule *r) { #ifdef ALTQ struct m_tag *mtag; #endif struct mbuf *m0; #ifdef __FreeBSD__ struct ip *ip; #endif #ifdef __FreeBSD__ m0 = m_copypacket(m, M_DONTWAIT); if (m0 == NULL) return; m0->m_flags |= M_SKIP_FIREWALL; #else mtag = m_tag_get(PACKET_TAG_PF_GENERATED, 0, M_NOWAIT); if (mtag == NULL) return; m0 = m_copy(m, 0, M_COPYALL); if (m0 == NULL) { m_tag_free(mtag); return; } m_tag_prepend(m0, mtag); #endif #ifdef ALTQ if (r->qid) { struct altq_tag *atag; mtag = m_tag_get(PACKET_TAG_PF_QID, sizeof(*atag), M_NOWAIT); if (mtag != NULL) { atag = (struct altq_tag *)(mtag + 1); atag->qid = r->qid; /* add hints for ecn */ atag->af = af; atag->hdr = mtod(m0, struct ip *); m_tag_prepend(m0, mtag); } } #endif /* ALTQ */ switch (af) { #ifdef INET case AF_INET: #ifdef __FreeBSD__ /* icmp_error() expects host byte ordering */ ip = mtod(m0, struct ip *); NTOHS(ip->ip_len); NTOHS(ip->ip_off); PF_UNLOCK(); icmp_error(m0, type, code, 0, 0); PF_LOCK(); #else icmp_error(m0, type, code, 0, (void *)NULL); #endif break; #endif /* INET */ #ifdef INET6 case AF_INET6: #ifdef __FreeBSD__ PF_UNLOCK(); #endif icmp6_error(m0, type, code, 0); #ifdef __FreeBSD__ PF_LOCK(); #endif break; #endif /* INET6 */ } } /* * Return 1 if the addresses a and b match (with mask m), otherwise return 0. * If n is 0, they match if they are equal. If n is != 0, they match if they * are different. */ int pf_match_addr(u_int8_t n, struct pf_addr *a, struct pf_addr *m, struct pf_addr *b, sa_family_t af) { int match = 0; switch (af) { #ifdef INET case AF_INET: if ((a->addr32[0] & m->addr32[0]) == (b->addr32[0] & m->addr32[0])) match++; break; #endif /* INET */ #ifdef INET6 case AF_INET6: if (((a->addr32[0] & m->addr32[0]) == (b->addr32[0] & m->addr32[0])) && ((a->addr32[1] & m->addr32[1]) == (b->addr32[1] & m->addr32[1])) && ((a->addr32[2] & m->addr32[2]) == (b->addr32[2] & m->addr32[2])) && ((a->addr32[3] & m->addr32[3]) == (b->addr32[3] & m->addr32[3]))) match++; break; #endif /* INET6 */ } if (match) { if (n) return (0); else return (1); } else { if (n) return (1); else return (0); } } int pf_match(u_int8_t op, u_int32_t a1, u_int32_t a2, u_int32_t p) { switch (op) { case PF_OP_IRG: return ((p > a1) && (p < a2)); case PF_OP_XRG: return ((p < a1) || (p > a2)); case PF_OP_RRG: return ((p >= a1) && (p <= a2)); case PF_OP_EQ: return (p == a1); case PF_OP_NE: return (p != a1); case PF_OP_LT: return (p < a1); case PF_OP_LE: return (p <= a1); case PF_OP_GT: return (p > a1); case PF_OP_GE: return (p >= a1); } return (0); /* never reached */ } int pf_match_port(u_int8_t op, u_int16_t a1, u_int16_t a2, u_int16_t p) { NTOHS(a1); NTOHS(a2); NTOHS(p); return (pf_match(op, a1, a2, p)); } int pf_match_uid(u_int8_t op, uid_t a1, uid_t a2, uid_t u) { if (u == UID_MAX && op != PF_OP_EQ && op != PF_OP_NE) return (0); return (pf_match(op, a1, a2, u)); } int pf_match_gid(u_int8_t op, gid_t a1, gid_t a2, gid_t g) { if (g == GID_MAX && op != PF_OP_EQ && op != PF_OP_NE) return (0); return (pf_match(op, a1, a2, g)); } struct pf_tag * pf_get_tag(struct mbuf *m) { struct m_tag *mtag; if ((mtag = m_tag_find(m, PACKET_TAG_PF_TAG, NULL)) != NULL) return ((struct pf_tag *)(mtag + 1)); else return (NULL); } int pf_match_tag(struct mbuf *m, struct pf_rule *r, struct pf_tag **pftag, int *tag) { if (*tag == -1) { /* find mbuf tag */ *pftag = pf_get_tag(m); if (*pftag != NULL) *tag = (*pftag)->tag; else *tag = 0; } return ((!r->match_tag_not && r->match_tag == *tag) || (r->match_tag_not && r->match_tag != *tag)); } int pf_tag_packet(struct mbuf *m, struct pf_tag *pftag, int tag) { struct m_tag *mtag; if (tag <= 0) return (0); if (pftag == NULL) { mtag = m_tag_get(PACKET_TAG_PF_TAG, sizeof(*pftag), M_NOWAIT); if (mtag == NULL) return (1); ((struct pf_tag *)(mtag + 1))->tag = tag; m_tag_prepend(m, mtag); } else pftag->tag = tag; return (0); } static void pf_step_into_anchor(int *depth, struct pf_ruleset **rs, int n, struct pf_rule **r, struct pf_rule **a) { struct pf_anchor_stackframe *f; if (*depth >= sizeof(pf_anchor_stack) / sizeof(pf_anchor_stack[0])) { printf("pf_step_into_anchor: stack overflow\n"); *r = TAILQ_NEXT(*r, entries); return; } else if (*depth == 0 && a != NULL) *a = *r; f = pf_anchor_stack + (*depth)++; f->rs = *rs; f->r = *r; if ((*r)->anchor_wildcard) { f->parent = &(*r)->anchor->children; if ((f->child = RB_MIN(pf_anchor_node, f->parent)) == NULL) { *r = NULL; return; } *rs = &f->child->ruleset; } else { f->parent = NULL; f->child = NULL; *rs = &(*r)->anchor->ruleset; } *r = TAILQ_FIRST((*rs)->rules[n].active.ptr); } static void pf_step_out_of_anchor(int *depth, struct pf_ruleset **rs, int n, struct pf_rule **r, struct pf_rule **a) { struct pf_anchor_stackframe *f; do { if (*depth <= 0) break; f = pf_anchor_stack + *depth - 1; if (f->parent != NULL && f->child != NULL) { f->child = RB_NEXT(pf_anchor_node, f->parent, f->child); if (f->child != NULL) { *rs = &f->child->ruleset; *r = TAILQ_FIRST((*rs)->rules[n].active.ptr); if (*r == NULL) continue; else break; } } (*depth)--; if (*depth == 0 && a != NULL) *a = NULL; *rs = f->rs; *r = TAILQ_NEXT(f->r, entries); } while (*r == NULL); } #ifdef INET6 void pf_poolmask(struct pf_addr *naddr, struct pf_addr *raddr, struct pf_addr *rmask, struct pf_addr *saddr, sa_family_t af) { switch (af) { #ifdef INET case AF_INET: naddr->addr32[0] = (raddr->addr32[0] & rmask->addr32[0]) | ((rmask->addr32[0] ^ 0xffffffff ) & saddr->addr32[0]); break; #endif /* INET */ case AF_INET6: naddr->addr32[0] = (raddr->addr32[0] & rmask->addr32[0]) | ((rmask->addr32[0] ^ 0xffffffff ) & saddr->addr32[0]); naddr->addr32[1] = (raddr->addr32[1] & rmask->addr32[1]) | ((rmask->addr32[1] ^ 0xffffffff ) & saddr->addr32[1]); naddr->addr32[2] = (raddr->addr32[2] & rmask->addr32[2]) | ((rmask->addr32[2] ^ 0xffffffff ) & saddr->addr32[2]); naddr->addr32[3] = (raddr->addr32[3] & rmask->addr32[3]) | ((rmask->addr32[3] ^ 0xffffffff ) & saddr->addr32[3]); break; } } void pf_addr_inc(struct pf_addr *addr, sa_family_t af) { switch (af) { #ifdef INET case AF_INET: addr->addr32[0] = htonl(ntohl(addr->addr32[0]) + 1); break; #endif /* INET */ case AF_INET6: if (addr->addr32[3] == 0xffffffff) { addr->addr32[3] = 0; if (addr->addr32[2] == 0xffffffff) { addr->addr32[2] = 0; if (addr->addr32[1] == 0xffffffff) { addr->addr32[1] = 0; addr->addr32[0] = htonl(ntohl(addr->addr32[0]) + 1); } else addr->addr32[1] = htonl(ntohl(addr->addr32[1]) + 1); } else addr->addr32[2] = htonl(ntohl(addr->addr32[2]) + 1); } else addr->addr32[3] = htonl(ntohl(addr->addr32[3]) + 1); break; } } #endif /* INET6 */ #define mix(a,b,c) \ do { \ a -= b; a -= c; a ^= (c >> 13); \ b -= c; b -= a; b ^= (a << 8); \ c -= a; c -= b; c ^= (b >> 13); \ a -= b; a -= c; a ^= (c >> 12); \ b -= c; b -= a; b ^= (a << 16); \ c -= a; c -= b; c ^= (b >> 5); \ a -= b; a -= c; a ^= (c >> 3); \ b -= c; b -= a; b ^= (a << 10); \ c -= a; c -= b; c ^= (b >> 15); \ } while (0) /* * hash function based on bridge_hash in if_bridge.c */ void pf_hash(struct pf_addr *inaddr, struct pf_addr *hash, struct pf_poolhashkey *key, sa_family_t af) { u_int32_t a = 0x9e3779b9, b = 0x9e3779b9, c = key->key32[0]; switch (af) { #ifdef INET case AF_INET: a += inaddr->addr32[0]; b += key->key32[1]; mix(a, b, c); hash->addr32[0] = c + key->key32[2]; break; #endif /* INET */ #ifdef INET6 case AF_INET6: a += inaddr->addr32[0]; b += inaddr->addr32[2]; mix(a, b, c); hash->addr32[0] = c; a += inaddr->addr32[1]; b += inaddr->addr32[3]; c += key->key32[1]; mix(a, b, c); hash->addr32[1] = c; a += inaddr->addr32[2]; b += inaddr->addr32[1]; c += key->key32[2]; mix(a, b, c); hash->addr32[2] = c; a += inaddr->addr32[3]; b += inaddr->addr32[0]; c += key->key32[3]; mix(a, b, c); hash->addr32[3] = c; break; #endif /* INET6 */ } } int pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr, struct pf_addr *naddr, struct pf_addr *init_addr, struct pf_src_node **sn) { unsigned char hash[16]; struct pf_pool *rpool = &r->rpool; struct pf_addr *raddr = &rpool->cur->addr.v.a.addr; struct pf_addr *rmask = &rpool->cur->addr.v.a.mask; struct pf_pooladdr *acur = rpool->cur; struct pf_src_node k; if (*sn == NULL && r->rpool.opts & PF_POOL_STICKYADDR && (r->rpool.opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) { k.af = af; PF_ACPY(&k.addr, saddr, af); if (r->rule_flag & PFRULE_RULESRCTRACK || r->rpool.opts & PF_POOL_STICKYADDR) k.rule.ptr = r; else k.rule.ptr = NULL; pf_status.scounters[SCNT_SRC_NODE_SEARCH]++; *sn = RB_FIND(pf_src_tree, &tree_src_tracking, &k); if (*sn != NULL && !PF_AZERO(&(*sn)->raddr, af)) { PF_ACPY(naddr, &(*sn)->raddr, af); if (pf_status.debug >= PF_DEBUG_MISC) { printf("pf_map_addr: src tracking maps "); pf_print_host(&k.addr, 0, af); printf(" to "); pf_print_host(naddr, 0, af); printf("\n"); } return (0); } } if (rpool->cur->addr.type == PF_ADDR_NOROUTE) return (1); if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) { switch (af) { #ifdef INET case AF_INET: if (rpool->cur->addr.p.dyn->pfid_acnt4 < 1 && (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN) return (1); raddr = &rpool->cur->addr.p.dyn->pfid_addr4; rmask = &rpool->cur->addr.p.dyn->pfid_mask4; break; #endif /* INET */ #ifdef INET6 case AF_INET6: if (rpool->cur->addr.p.dyn->pfid_acnt6 < 1 && (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN) return (1); raddr = &rpool->cur->addr.p.dyn->pfid_addr6; rmask = &rpool->cur->addr.p.dyn->pfid_mask6; break; #endif /* INET6 */ } } else if (rpool->cur->addr.type == PF_ADDR_TABLE) { if ((rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN) return (1); /* unsupported */ } else { raddr = &rpool->cur->addr.v.a.addr; rmask = &rpool->cur->addr.v.a.mask; } switch (rpool->opts & PF_POOL_TYPEMASK) { case PF_POOL_NONE: PF_ACPY(naddr, raddr, af); break; case PF_POOL_BITMASK: PF_POOLMASK(naddr, raddr, rmask, saddr, af); break; case PF_POOL_RANDOM: if (init_addr != NULL && PF_AZERO(init_addr, af)) { switch (af) { #ifdef INET case AF_INET: rpool->counter.addr32[0] = htonl(arc4random()); break; #endif /* INET */ #ifdef INET6 case AF_INET6: if (rmask->addr32[3] != 0xffffffff) rpool->counter.addr32[3] = htonl(arc4random()); else break; if (rmask->addr32[2] != 0xffffffff) rpool->counter.addr32[2] = htonl(arc4random()); else break; if (rmask->addr32[1] != 0xffffffff) rpool->counter.addr32[1] = htonl(arc4random()); else break; if (rmask->addr32[0] != 0xffffffff) rpool->counter.addr32[0] = htonl(arc4random()); break; #endif /* INET6 */ } PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af); PF_ACPY(init_addr, naddr, af); } else { PF_AINC(&rpool->counter, af); PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af); } break; case PF_POOL_SRCHASH: pf_hash(saddr, (struct pf_addr *)&hash, &rpool->key, af); PF_POOLMASK(naddr, raddr, rmask, (struct pf_addr *)&hash, af); break; case PF_POOL_ROUNDROBIN: if (rpool->cur->addr.type == PF_ADDR_TABLE) { if (!pfr_pool_get(rpool->cur->addr.p.tbl, &rpool->tblidx, &rpool->counter, &raddr, &rmask, af)) goto get_addr; } else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) { if (!pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt, &rpool->tblidx, &rpool->counter, &raddr, &rmask, af)) goto get_addr; } else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af)) goto get_addr; try_next: if ((rpool->cur = TAILQ_NEXT(rpool->cur, entries)) == NULL) rpool->cur = TAILQ_FIRST(&rpool->list); if (rpool->cur->addr.type == PF_ADDR_TABLE) { rpool->tblidx = -1; if (pfr_pool_get(rpool->cur->addr.p.tbl, &rpool->tblidx, &rpool->counter, &raddr, &rmask, af)) { /* table contains no address of type 'af' */ if (rpool->cur != acur) goto try_next; return (1); } } else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) { rpool->tblidx = -1; if (pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt, &rpool->tblidx, &rpool->counter, &raddr, &rmask, af)) { /* table contains no address of type 'af' */ if (rpool->cur != acur) goto try_next; return (1); } } else { raddr = &rpool->cur->addr.v.a.addr; rmask = &rpool->cur->addr.v.a.mask; PF_ACPY(&rpool->counter, raddr, af); } get_addr: PF_ACPY(naddr, &rpool->counter, af); if (init_addr != NULL && PF_AZERO(init_addr, af)) PF_ACPY(init_addr, naddr, af); PF_AINC(&rpool->counter, af); break; } if (*sn != NULL) PF_ACPY(&(*sn)->raddr, naddr, af); if (pf_status.debug >= PF_DEBUG_MISC && (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) { printf("pf_map_addr: selected address "); pf_print_host(naddr, 0, af); printf("\n"); } return (0); } int pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r, struct pf_addr *saddr, struct pf_addr *daddr, u_int16_t dport, struct pf_addr *naddr, u_int16_t *nport, u_int16_t low, u_int16_t high, struct pf_src_node **sn) { struct pf_state key; struct pf_addr init_addr; u_int16_t cut; bzero(&init_addr, sizeof(init_addr)); if (pf_map_addr(af, r, saddr, naddr, &init_addr, sn)) return (1); if (proto == IPPROTO_ICMP) { low = 1; high = 65535; } do { key.af = af; key.proto = proto; PF_ACPY(&key.ext.addr, daddr, key.af); PF_ACPY(&key.gwy.addr, naddr, key.af); key.ext.port = dport; /* * port search; start random, step; * similar 2 portloop in in_pcbbind */ if (!(proto == IPPROTO_TCP || proto == IPPROTO_UDP || proto == IPPROTO_ICMP)) { key.gwy.port = dport; if (pf_find_state_all(&key, PF_EXT_GWY, NULL) == NULL) return (0); } else if (low == 0 && high == 0) { key.gwy.port = *nport; if (pf_find_state_all(&key, PF_EXT_GWY, NULL) == NULL) return (0); } else if (low == high) { key.gwy.port = htons(low); if (pf_find_state_all(&key, PF_EXT_GWY, NULL) == NULL) { *nport = htons(low); return (0); } } else { u_int16_t tmp; if (low > high) { tmp = low; low = high; high = tmp; } /* low < high */ cut = htonl(arc4random()) % (1 + high - low) + low; /* low <= cut <= high */ for (tmp = cut; tmp <= high; ++(tmp)) { key.gwy.port = htons(tmp); if (pf_find_state_all(&key, PF_EXT_GWY, NULL) == NULL) { *nport = htons(tmp); return (0); } } for (tmp = cut - 1; tmp >= low; --(tmp)) { key.gwy.port = htons(tmp); if (pf_find_state_all(&key, PF_EXT_GWY, NULL) == NULL) { *nport = htons(tmp); return (0); } } } switch (r->rpool.opts & PF_POOL_TYPEMASK) { case PF_POOL_RANDOM: case PF_POOL_ROUNDROBIN: if (pf_map_addr(af, r, saddr, naddr, &init_addr, sn)) return (1); break; case PF_POOL_NONE: case PF_POOL_SRCHASH: case PF_POOL_BITMASK: default: return (1); } } while (! PF_AEQ(&init_addr, naddr, af) ); return (1); /* none available */ } struct pf_rule * pf_match_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction, struct pfi_kif *kif, struct pf_addr *saddr, u_int16_t sport, struct pf_addr *daddr, u_int16_t dport, int rs_num) { struct pf_rule *r, *rm = NULL; struct pf_ruleset *ruleset = NULL; struct pf_tag *pftag = NULL; int tag = -1; int asd = 0; r = TAILQ_FIRST(pf_main_ruleset.rules[rs_num].active.ptr); while (r && rm == NULL) { struct pf_rule_addr *src = NULL, *dst = NULL; struct pf_addr_wrap *xdst = NULL; if (r->action == PF_BINAT && direction == PF_IN) { src = &r->dst; if (r->rpool.cur != NULL) xdst = &r->rpool.cur->addr; } else { src = &r->src; dst = &r->dst; } r->evaluations++; if (r->kif != NULL && (r->kif != kif && r->kif != kif->pfik_parent) == !r->ifnot) r = r->skip[PF_SKIP_IFP].ptr; else if (r->direction && r->direction != direction) r = r->skip[PF_SKIP_DIR].ptr; else if (r->af && r->af != pd->af) r = r->skip[PF_SKIP_AF].ptr; else if (r->proto && r->proto != pd->proto) r = r->skip[PF_SKIP_PROTO].ptr; else if (PF_MISMATCHAW(&src->addr, saddr, pd->af, src->neg)) r = r->skip[src == &r->src ? PF_SKIP_SRC_ADDR : PF_SKIP_DST_ADDR].ptr; else if (src->port_op && !pf_match_port(src->port_op, src->port[0], src->port[1], sport)) r = r->skip[src == &r->src ? PF_SKIP_SRC_PORT : PF_SKIP_DST_PORT].ptr; else if (dst != NULL && PF_MISMATCHAW(&dst->addr, daddr, pd->af, dst->neg)) r = r->skip[PF_SKIP_DST_ADDR].ptr; else if (xdst != NULL && PF_MISMATCHAW(xdst, daddr, pd->af, 0)) r = TAILQ_NEXT(r, entries); else if (dst != NULL && dst->port_op && !pf_match_port(dst->port_op, dst->port[0], dst->port[1], dport)) r = r->skip[PF_SKIP_DST_PORT].ptr; else if (r->match_tag && !pf_match_tag(m, r, &pftag, &tag)) r = TAILQ_NEXT(r, entries); else if (r->os_fingerprint != PF_OSFP_ANY && (pd->proto != IPPROTO_TCP || !pf_osfp_match(pf_osfp_fingerprint(pd, m, off, pd->hdr.tcp), r->os_fingerprint))) r = TAILQ_NEXT(r, entries); else { if (r->tag) tag = r->tag; if (r->anchor == NULL) { rm = r; } else pf_step_into_anchor(&asd, &ruleset, rs_num, &r, NULL); } if (r == NULL) pf_step_out_of_anchor(&asd, &ruleset, rs_num, &r, NULL); } if (pf_tag_packet(m, pftag, tag)) return (NULL); if (rm != NULL && (rm->action == PF_NONAT || rm->action == PF_NORDR || rm->action == PF_NOBINAT)) return (NULL); return (rm); } struct pf_rule * pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction, struct pfi_kif *kif, struct pf_src_node **sn, struct pf_addr *saddr, u_int16_t sport, struct pf_addr *daddr, u_int16_t dport, struct pf_addr *naddr, u_int16_t *nport) { struct pf_rule *r = NULL; if (direction == PF_OUT) { r = pf_match_translation(pd, m, off, direction, kif, saddr, sport, daddr, dport, PF_RULESET_BINAT); if (r == NULL) r = pf_match_translation(pd, m, off, direction, kif, saddr, sport, daddr, dport, PF_RULESET_NAT); } else { r = pf_match_translation(pd, m, off, direction, kif, saddr, sport, daddr, dport, PF_RULESET_RDR); if (r == NULL) r = pf_match_translation(pd, m, off, direction, kif, saddr, sport, daddr, dport, PF_RULESET_BINAT); } if (r != NULL) { switch (r->action) { case PF_NONAT: case PF_NOBINAT: case PF_NORDR: return (NULL); case PF_NAT: if (pf_get_sport(pd->af, pd->proto, r, saddr, daddr, dport, naddr, nport, r->rpool.proxy_port[0], r->rpool.proxy_port[1], sn)) { DPFPRINTF(PF_DEBUG_MISC, ("pf: NAT proxy port allocation " "(%u-%u) failed\n", r->rpool.proxy_port[0], r->rpool.proxy_port[1])); return (NULL); } break; case PF_BINAT: switch (direction) { case PF_OUT: if (r->rpool.cur->addr.type == PF_ADDR_DYNIFTL){ switch (pd->af) { #ifdef INET case AF_INET: if (r->rpool.cur->addr.p.dyn-> pfid_acnt4 < 1) return (NULL); PF_POOLMASK(naddr, &r->rpool.cur->addr.p.dyn-> pfid_addr4, &r->rpool.cur->addr.p.dyn-> pfid_mask4, saddr, AF_INET); break; #endif /* INET */ #ifdef INET6 case AF_INET6: if (r->rpool.cur->addr.p.dyn-> pfid_acnt6 < 1) return (NULL); PF_POOLMASK(naddr, &r->rpool.cur->addr.p.dyn-> pfid_addr6, &r->rpool.cur->addr.p.dyn-> pfid_mask6, saddr, AF_INET6); break; #endif /* INET6 */ } } else PF_POOLMASK(naddr, &r->rpool.cur->addr.v.a.addr, &r->rpool.cur->addr.v.a.mask, saddr, pd->af); break; case PF_IN: if (r->src.addr.type == PF_ADDR_DYNIFTL) { switch (pd->af) { #ifdef INET case AF_INET: if (r->src.addr.p.dyn-> pfid_acnt4 < 1) return (NULL); PF_POOLMASK(naddr, &r->src.addr.p.dyn-> pfid_addr4, &r->src.addr.p.dyn-> pfid_mask4, daddr, AF_INET); break; #endif /* INET */ #ifdef INET6 case AF_INET6: if (r->src.addr.p.dyn-> pfid_acnt6 < 1) return (NULL); PF_POOLMASK(naddr, &r->src.addr.p.dyn-> pfid_addr6, &r->src.addr.p.dyn-> pfid_mask6, daddr, AF_INET6); break; #endif /* INET6 */ } } else PF_POOLMASK(naddr, &r->src.addr.v.a.addr, &r->src.addr.v.a.mask, daddr, pd->af); break; } break; case PF_RDR: { if (pf_map_addr(pd->af, r, saddr, naddr, NULL, sn)) return (NULL); if ((r->rpool.opts & PF_POOL_TYPEMASK) == PF_POOL_BITMASK) PF_POOLMASK(naddr, naddr, &r->rpool.cur->addr.v.a.mask, daddr, pd->af); if (r->rpool.proxy_port[1]) { u_int32_t tmp_nport; tmp_nport = ((ntohs(dport) - ntohs(r->dst.port[0])) % (r->rpool.proxy_port[1] - r->rpool.proxy_port[0] + 1)) + r->rpool.proxy_port[0]; /* wrap around if necessary */ if (tmp_nport > 65535) tmp_nport -= 65535; *nport = htons((u_int16_t)tmp_nport); } else if (r->rpool.proxy_port[0]) *nport = htons(r->rpool.proxy_port[0]); break; } default: return (NULL); } } return (r); } int #ifdef __FreeBSD__ pf_socket_lookup(uid_t *uid, gid_t *gid, int direction, struct pf_pdesc *pd, struct inpcb *inp_arg) #else pf_socket_lookup(uid_t *uid, gid_t *gid, int direction, struct pf_pdesc *pd) #endif { struct pf_addr *saddr, *daddr; u_int16_t sport, dport; #ifdef __FreeBSD__ struct inpcbinfo *pi; #else struct inpcbtable *tb; #endif struct inpcb *inp; *uid = UID_MAX; *gid = GID_MAX; #ifdef __FreeBSD__ if (inp_arg != NULL) { INP_LOCK_ASSERT(inp_arg); if (inp_arg->inp_socket) { *uid = inp_arg->inp_socket->so_cred->cr_uid; *gid = inp_arg->inp_socket->so_cred->cr_groups[0]; return (1); } else return (0); } #endif switch (pd->proto) { case IPPROTO_TCP: sport = pd->hdr.tcp->th_sport; dport = pd->hdr.tcp->th_dport; #ifdef __FreeBSD__ pi = &tcbinfo; #else tb = &tcbtable; #endif break; case IPPROTO_UDP: sport = pd->hdr.udp->uh_sport; dport = pd->hdr.udp->uh_dport; #ifdef __FreeBSD__ pi = &udbinfo; #else tb = &udbtable; #endif break; default: return (0); } if (direction == PF_IN) { saddr = pd->src; daddr = pd->dst; } else { u_int16_t p; p = sport; sport = dport; dport = p; saddr = pd->dst; daddr = pd->src; } switch (pd->af) { #ifdef INET case AF_INET: #ifdef __FreeBSD__ INP_INFO_RLOCK(pi); /* XXX LOR */ inp = in_pcblookup_hash(pi, saddr->v4, sport, daddr->v4, dport, 0, NULL); if (inp == NULL) { inp = in_pcblookup_hash(pi, saddr->v4, sport, daddr->v4, dport, INPLOOKUP_WILDCARD, NULL); if(inp == NULL) { INP_INFO_RUNLOCK(pi); return (0); } } #else inp = in_pcbhashlookup(tb, saddr->v4, sport, daddr->v4, dport); if (inp == NULL) { inp = in_pcblookup_listen(tb, daddr->v4, dport, 0); if (inp == NULL) return (0); } #endif break; #endif /* INET */ #ifdef INET6 case AF_INET6: #ifdef __FreeBSD__ INP_INFO_RLOCK(pi); inp = in6_pcblookup_hash(pi, &saddr->v6, sport, &daddr->v6, dport, 0, NULL); if (inp == NULL) { inp = in6_pcblookup_hash(pi, &saddr->v6, sport, &daddr->v6, dport, INPLOOKUP_WILDCARD, NULL); if (inp == NULL) { INP_INFO_RUNLOCK(pi); return (0); } } #else inp = in6_pcbhashlookup(tb, &saddr->v6, sport, &daddr->v6, dport); if (inp == NULL) { inp = in6_pcblookup_listen(tb, &daddr->v6, dport, 0); if (inp == NULL) return (0); } #endif break; #endif /* INET6 */ default: return (0); } #ifdef __FreeBSD__ INP_LOCK(inp); if ((inp->inp_socket == NULL) || (inp->inp_socket->so_cred == NULL)) { INP_UNLOCK(inp); INP_INFO_RUNLOCK(pi); return (0); } *uid = inp->inp_socket->so_cred->cr_uid; *gid = inp->inp_socket->so_cred->cr_groups[0]; INP_UNLOCK(inp); INP_INFO_RUNLOCK(pi); #else *uid = inp->inp_socket->so_euid; *gid = inp->inp_socket->so_egid; #endif return (1); } u_int8_t pf_get_wscale(struct mbuf *m, int off, u_int16_t th_off, sa_family_t af) { int hlen; u_int8_t hdr[60]; u_int8_t *opt, optlen; u_int8_t wscale = 0; hlen = th_off << 2; /* hlen <= sizeof(hdr) */ if (hlen <= sizeof(struct tcphdr)) return (0); if (!pf_pull_hdr(m, off, hdr, hlen, NULL, NULL, af)) return (0); opt = hdr + sizeof(struct tcphdr); hlen -= sizeof(struct tcphdr); while (hlen >= 3) { switch (*opt) { case TCPOPT_EOL: case TCPOPT_NOP: ++opt; --hlen; break; case TCPOPT_WINDOW: wscale = opt[2]; if (wscale > TCP_MAX_WINSHIFT) wscale = TCP_MAX_WINSHIFT; wscale |= PF_WSCALE_FLAG; /* FALLTHROUGH */ default: optlen = opt[1]; if (optlen < 2) optlen = 2; hlen -= optlen; opt += optlen; break; } } return (wscale); } u_int16_t pf_get_mss(struct mbuf *m, int off, u_int16_t th_off, sa_family_t af) { int hlen; u_int8_t hdr[60]; u_int8_t *opt, optlen; u_int16_t mss = tcp_mssdflt; hlen = th_off << 2; /* hlen <= sizeof(hdr) */ if (hlen <= sizeof(struct tcphdr)) return (0); if (!pf_pull_hdr(m, off, hdr, hlen, NULL, NULL, af)) return (0); opt = hdr + sizeof(struct tcphdr); hlen -= sizeof(struct tcphdr); while (hlen >= TCPOLEN_MAXSEG) { switch (*opt) { case TCPOPT_EOL: case TCPOPT_NOP: ++opt; --hlen; break; case TCPOPT_MAXSEG: bcopy((caddr_t)(opt + 2), (caddr_t)&mss, 2); NTOHS(mss); /* FALLTHROUGH */ default: optlen = opt[1]; if (optlen < 2) optlen = 2; hlen -= optlen; opt += optlen; break; } } return (mss); } u_int16_t pf_calc_mss(struct pf_addr *addr, sa_family_t af, u_int16_t offer) { #ifdef INET struct sockaddr_in *dst; struct route ro; #endif /* INET */ #ifdef INET6 struct sockaddr_in6 *dst6; struct route_in6 ro6; #endif /* INET6 */ struct rtentry *rt = NULL; int hlen = 0; /* make the compiler happy */ u_int16_t mss = tcp_mssdflt; switch (af) { #ifdef INET case AF_INET: hlen = sizeof(struct ip); bzero(&ro, sizeof(ro)); dst = (struct sockaddr_in *)&ro.ro_dst; dst->sin_family = AF_INET; dst->sin_len = sizeof(*dst); dst->sin_addr = addr->v4; #ifdef __FreeBSD__ #ifdef RTF_PRCLONING rtalloc_ign(&ro, (RTF_CLONING | RTF_PRCLONING)); #else /* !RTF_PRCLONING */ rtalloc_ign(&ro, RTF_CLONING); #endif #else /* ! __FreeBSD__ */ rtalloc_noclone(&ro, NO_CLONING); #endif rt = ro.ro_rt; break; #endif /* INET */ #ifdef INET6 case AF_INET6: hlen = sizeof(struct ip6_hdr); bzero(&ro6, sizeof(ro6)); dst6 = (struct sockaddr_in6 *)&ro6.ro_dst; dst6->sin6_family = AF_INET6; dst6->sin6_len = sizeof(*dst6); dst6->sin6_addr = addr->v6; #ifdef __FreeBSD__ #ifdef RTF_PRCLONING rtalloc_ign((struct route *)&ro6, (RTF_CLONING | RTF_PRCLONING)); #else /* !RTF_PRCLONING */ rtalloc_ign((struct route *)&ro6, RTF_CLONING); #endif #else /* ! __FreeBSD__ */ rtalloc_noclone((struct route *)&ro6, NO_CLONING); #endif rt = ro6.ro_rt; break; #endif /* INET6 */ } if (rt && rt->rt_ifp) { mss = rt->rt_ifp->if_mtu - hlen - sizeof(struct tcphdr); mss = max(tcp_mssdflt, mss); RTFREE(rt); } mss = min(mss, offer); mss = max(mss, 64); /* sanity - at least max opt space */ return (mss); } void pf_set_rt_ifp(struct pf_state *s, struct pf_addr *saddr) { struct pf_rule *r = s->rule.ptr; s->rt_kif = NULL; if (!r->rt || r->rt == PF_FASTROUTE) return; switch (s->af) { #ifdef INET case AF_INET: pf_map_addr(AF_INET, r, saddr, &s->rt_addr, NULL, &s->nat_src_node); s->rt_kif = r->rpool.cur->kif; break; #endif /* INET */ #ifdef INET6 case AF_INET6: pf_map_addr(AF_INET6, r, saddr, &s->rt_addr, NULL, &s->nat_src_node); s->rt_kif = r->rpool.cur->kif; break; #endif /* INET6 */ } } int pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, struct pfi_kif *kif, struct mbuf *m, int off, void *h, #ifdef __FreeBSD__ struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm, struct ifqueue *ifq, struct inpcb *inp) #else struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm, struct ifqueue *ifq) #endif { struct pf_rule *nr = NULL; struct pf_addr *saddr = pd->src, *daddr = pd->dst; struct tcphdr *th = pd->hdr.tcp; u_int16_t bport, nport = 0; sa_family_t af = pd->af; int lookup = -1; uid_t uid; gid_t gid; struct pf_rule *r, *a = NULL; struct pf_ruleset *ruleset = NULL; struct pf_src_node *nsn = NULL; u_short reason; int rewrite = 0; struct pf_tag *pftag = NULL; int tag = -1; u_int16_t mss = tcp_mssdflt; int asd = 0; if (pf_check_congestion(ifq)) { REASON_SET(&reason, PFRES_CONGEST); return (PF_DROP); } r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); if (direction == PF_OUT) { bport = nport = th->th_sport; /* check outgoing packet for BINAT/NAT */ if ((nr = pf_get_translation(pd, m, off, PF_OUT, kif, &nsn, saddr, th->th_sport, daddr, th->th_dport, &pd->naddr, &nport)) != NULL) { PF_ACPY(&pd->baddr, saddr, af); pf_change_ap(saddr, &th->th_sport, pd->ip_sum, &th->th_sum, &pd->naddr, nport, 0, af); rewrite++; if (nr->natpass) r = NULL; pd->nat_rule = nr; } } else { bport = nport = th->th_dport; /* check incoming packet for BINAT/RDR */ if ((nr = pf_get_translation(pd, m, off, PF_IN, kif, &nsn, saddr, th->th_sport, daddr, th->th_dport, &pd->naddr, &nport)) != NULL) { PF_ACPY(&pd->baddr, daddr, af); pf_change_ap(daddr, &th->th_dport, pd->ip_sum, &th->th_sum, &pd->naddr, nport, 0, af); rewrite++; if (nr->natpass) r = NULL; pd->nat_rule = nr; } } while (r != NULL) { r->evaluations++; if (r->kif != NULL && (r->kif != kif && r->kif != kif->pfik_parent) == !r->ifnot) r = r->skip[PF_SKIP_IFP].ptr; else if (r->direction && r->direction != direction) r = r->skip[PF_SKIP_DIR].ptr; else if (r->af && r->af != af) r = r->skip[PF_SKIP_AF].ptr; else if (r->proto && r->proto != IPPROTO_TCP) r = r->skip[PF_SKIP_PROTO].ptr; else if (PF_MISMATCHAW(&r->src.addr, saddr, af, r->src.neg)) r = r->skip[PF_SKIP_SRC_ADDR].ptr; else if (r->src.port_op && !pf_match_port(r->src.port_op, r->src.port[0], r->src.port[1], th->th_sport)) r = r->skip[PF_SKIP_SRC_PORT].ptr; else if (PF_MISMATCHAW(&r->dst.addr, daddr, af, r->dst.neg)) r = r->skip[PF_SKIP_DST_ADDR].ptr; else if (r->dst.port_op && !pf_match_port(r->dst.port_op, r->dst.port[0], r->dst.port[1], th->th_dport)) r = r->skip[PF_SKIP_DST_PORT].ptr; else if (r->tos && !(r->tos & pd->tos)) r = TAILQ_NEXT(r, entries); else if (r->rule_flag & PFRULE_FRAGMENT) r = TAILQ_NEXT(r, entries); else if ((r->flagset & th->th_flags) != r->flags) r = TAILQ_NEXT(r, entries); else if (r->uid.op && (lookup != -1 || (lookup = #ifdef __FreeBSD__ pf_socket_lookup(&uid, &gid, direction, pd, inp), 1)) && #else pf_socket_lookup(&uid, &gid, direction, pd), 1)) && #endif !pf_match_uid(r->uid.op, r->uid.uid[0], r->uid.uid[1], uid)) r = TAILQ_NEXT(r, entries); else if (r->gid.op && (lookup != -1 || (lookup = #ifdef __FreeBSD__ pf_socket_lookup(&uid, &gid, direction, pd, inp), 1)) && #else pf_socket_lookup(&uid, &gid, direction, pd), 1)) && #endif !pf_match_gid(r->gid.op, r->gid.gid[0], r->gid.gid[1], gid)) r = TAILQ_NEXT(r, entries); else if (r->prob && r->prob <= arc4random()) r = TAILQ_NEXT(r, entries); else if (r->match_tag && !pf_match_tag(m, r, &pftag, &tag)) r = TAILQ_NEXT(r, entries); else if (r->os_fingerprint != PF_OSFP_ANY && !pf_osfp_match( pf_osfp_fingerprint(pd, m, off, th), r->os_fingerprint)) r = TAILQ_NEXT(r, entries); else { if (r->tag) tag = r->tag; if (r->anchor == NULL) { *rm = r; *am = a; *rsm = ruleset; if ((*rm)->quick) break; r = TAILQ_NEXT(r, entries); } else pf_step_into_anchor(&asd, &ruleset, PF_RULESET_FILTER, &r, &a); } if (r == NULL) pf_step_out_of_anchor(&asd, &ruleset, PF_RULESET_FILTER, &r, &a); } r = *rm; a = *am; ruleset = *rsm; REASON_SET(&reason, PFRES_MATCH); if (r->log) { if (rewrite) m_copyback(m, off, sizeof(*th), (caddr_t)th); PFLOG_PACKET(kif, h, m, af, direction, reason, r, a, ruleset); } if ((r->action == PF_DROP) && ((r->rule_flag & PFRULE_RETURNRST) || (r->rule_flag & PFRULE_RETURNICMP) || (r->rule_flag & PFRULE_RETURN))) { /* undo NAT changes, if they have taken place */ if (nr != NULL) { if (direction == PF_OUT) { pf_change_ap(saddr, &th->th_sport, pd->ip_sum, &th->th_sum, &pd->baddr, bport, 0, af); rewrite++; } else { pf_change_ap(daddr, &th->th_dport, pd->ip_sum, &th->th_sum, &pd->baddr, bport, 0, af); rewrite++; } } if (((r->rule_flag & PFRULE_RETURNRST) || (r->rule_flag & PFRULE_RETURN)) && !(th->th_flags & TH_RST)) { u_int32_t ack = ntohl(th->th_seq) + pd->p_len; if (th->th_flags & TH_SYN) ack++; if (th->th_flags & TH_FIN) ack++; +#ifdef __FreeBSD__ + pf_send_tcp(m, r, af, pd->dst, +#else pf_send_tcp(r, af, pd->dst, +#endif pd->src, th->th_dport, th->th_sport, ntohl(th->th_ack), ack, TH_RST|TH_ACK, 0, 0, r->return_ttl, 1, pd->eh, kif->pfik_ifp); } else if ((af == AF_INET) && r->return_icmp) pf_send_icmp(m, r->return_icmp >> 8, r->return_icmp & 255, af, r); else if ((af == AF_INET6) && r->return_icmp6) pf_send_icmp(m, r->return_icmp6 >> 8, r->return_icmp6 & 255, af, r); } if (r->action == PF_DROP) return (PF_DROP); if (pf_tag_packet(m, pftag, tag)) { REASON_SET(&reason, PFRES_MEMORY); return (PF_DROP); } if (r->keep_state || nr != NULL || (pd->flags & PFDESC_TCP_NORM)) { /* create new state */ u_int16_t len; struct pf_state *s = NULL; struct pf_src_node *sn = NULL; len = pd->tot_len - off - (th->th_off << 2); /* check maximums */ if (r->max_states && (r->states >= r->max_states)) { pf_status.lcounters[LCNT_STATES]++; REASON_SET(&reason, PFRES_MAXSTATES); goto cleanup; } /* src node for flter rule */ if ((r->rule_flag & PFRULE_SRCTRACK || r->rpool.opts & PF_POOL_STICKYADDR) && pf_insert_src_node(&sn, r, saddr, af) != 0) { REASON_SET(&reason, PFRES_SRCLIMIT); goto cleanup; } /* src node for translation rule */ if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) && ((direction == PF_OUT && pf_insert_src_node(&nsn, nr, &pd->baddr, af) != 0) || (pf_insert_src_node(&nsn, nr, saddr, af) != 0))) { REASON_SET(&reason, PFRES_SRCLIMIT); goto cleanup; } s = pool_get(&pf_state_pl, PR_NOWAIT); if (s == NULL) { REASON_SET(&reason, PFRES_MEMORY); cleanup: if (sn != NULL && sn->states == 0 && sn->expire == 0) { RB_REMOVE(pf_src_tree, &tree_src_tracking, sn); pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; pf_status.src_nodes--; pool_put(&pf_src_tree_pl, sn); } if (nsn != sn && nsn != NULL && nsn->states == 0 && nsn->expire == 0) { RB_REMOVE(pf_src_tree, &tree_src_tracking, nsn); pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; pf_status.src_nodes--; pool_put(&pf_src_tree_pl, nsn); } return (PF_DROP); } bzero(s, sizeof(*s)); s->rule.ptr = r; s->nat_rule.ptr = nr; s->anchor.ptr = a; STATE_INC_COUNTERS(s); s->allow_opts = r->allow_opts; s->log = r->log & 2; s->proto = IPPROTO_TCP; s->direction = direction; s->af = af; if (direction == PF_OUT) { PF_ACPY(&s->gwy.addr, saddr, af); s->gwy.port = th->th_sport; /* sport */ PF_ACPY(&s->ext.addr, daddr, af); s->ext.port = th->th_dport; if (nr != NULL) { PF_ACPY(&s->lan.addr, &pd->baddr, af); s->lan.port = bport; } else { PF_ACPY(&s->lan.addr, &s->gwy.addr, af); s->lan.port = s->gwy.port; } } else { PF_ACPY(&s->lan.addr, daddr, af); s->lan.port = th->th_dport; PF_ACPY(&s->ext.addr, saddr, af); s->ext.port = th->th_sport; if (nr != NULL) { PF_ACPY(&s->gwy.addr, &pd->baddr, af); s->gwy.port = bport; } else { PF_ACPY(&s->gwy.addr, &s->lan.addr, af); s->gwy.port = s->lan.port; } } s->src.seqlo = ntohl(th->th_seq); s->src.seqhi = s->src.seqlo + len + 1; if ((th->th_flags & (TH_SYN|TH_ACK)) == TH_SYN && r->keep_state == PF_STATE_MODULATE) { /* Generate sequence number modulator */ while ((s->src.seqdiff = htonl(arc4random())) == 0) ; pf_change_a(&th->th_seq, &th->th_sum, htonl(s->src.seqlo + s->src.seqdiff), 0); rewrite = 1; } else s->src.seqdiff = 0; if (th->th_flags & TH_SYN) { s->src.seqhi++; s->src.wscale = pf_get_wscale(m, off, th->th_off, af); } s->src.max_win = MAX(ntohs(th->th_win), 1); if (s->src.wscale & PF_WSCALE_MASK) { /* Remove scale factor from initial window */ int win = s->src.max_win; win += 1 << (s->src.wscale & PF_WSCALE_MASK); s->src.max_win = (win - 1) >> (s->src.wscale & PF_WSCALE_MASK); } if (th->th_flags & TH_FIN) s->src.seqhi++; s->dst.seqhi = 1; s->dst.max_win = 1; s->src.state = TCPS_SYN_SENT; s->dst.state = TCPS_CLOSED; s->creation = time_second; s->expire = time_second; s->timeout = PFTM_TCP_FIRST_PACKET; pf_set_rt_ifp(s, saddr); if (sn != NULL) { s->src_node = sn; s->src_node->states++; } if (nsn != NULL) { PF_ACPY(&nsn->raddr, &pd->naddr, af); s->nat_src_node = nsn; s->nat_src_node->states++; } if ((pd->flags & PFDESC_TCP_NORM) && pf_normalize_tcp_init(m, off, pd, th, &s->src, &s->dst)) { REASON_SET(&reason, PFRES_MEMORY); pf_src_tree_remove_state(s); STATE_DEC_COUNTERS(s); pool_put(&pf_state_pl, s); return (PF_DROP); } if ((pd->flags & PFDESC_TCP_NORM) && s->src.scrub && pf_normalize_tcp_stateful(m, off, pd, &reason, th, s, &s->src, &s->dst, &rewrite)) { /* This really shouldn't happen!!! */ DPFPRINTF(PF_DEBUG_URGENT, ("pf_normalize_tcp_stateful failed on first pkt")); pf_normalize_tcp_cleanup(s); pf_src_tree_remove_state(s); STATE_DEC_COUNTERS(s); pool_put(&pf_state_pl, s); return (PF_DROP); } if (pf_insert_state(BOUND_IFACE(r, kif), s)) { pf_normalize_tcp_cleanup(s); REASON_SET(&reason, PFRES_STATEINS); pf_src_tree_remove_state(s); STATE_DEC_COUNTERS(s); pool_put(&pf_state_pl, s); return (PF_DROP); } else *sm = s; if (tag > 0) { pf_tag_ref(tag); s->tag = tag; } if ((th->th_flags & (TH_SYN|TH_ACK)) == TH_SYN && r->keep_state == PF_STATE_SYNPROXY) { s->src.state = PF_TCPS_PROXY_SRC; if (nr != NULL) { if (direction == PF_OUT) { pf_change_ap(saddr, &th->th_sport, pd->ip_sum, &th->th_sum, &pd->baddr, bport, 0, af); } else { pf_change_ap(daddr, &th->th_dport, pd->ip_sum, &th->th_sum, &pd->baddr, bport, 0, af); } } s->src.seqhi = htonl(arc4random()); /* Find mss option */ mss = pf_get_mss(m, off, th->th_off, af); mss = pf_calc_mss(saddr, af, mss); mss = pf_calc_mss(daddr, af, mss); s->src.mss = mss; +#ifdef __FreeBSD__ + pf_send_tcp(NULL, r, af, daddr, saddr, th->th_dport, +#else pf_send_tcp(r, af, daddr, saddr, th->th_dport, +#endif th->th_sport, s->src.seqhi, ntohl(th->th_seq) + 1, TH_SYN|TH_ACK, 0, s->src.mss, 0, 1, NULL, NULL); REASON_SET(&reason, PFRES_SYNPROXY); return (PF_SYNPROXY_DROP); } } /* copy back packet headers if we performed NAT operations */ if (rewrite) m_copyback(m, off, sizeof(*th), (caddr_t)th); return (PF_PASS); } int pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction, struct pfi_kif *kif, struct mbuf *m, int off, void *h, #ifdef __FreeBSD__ struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm, struct ifqueue *ifq, struct inpcb *inp) #else struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm, struct ifqueue *ifq) #endif { struct pf_rule *nr = NULL; struct pf_addr *saddr = pd->src, *daddr = pd->dst; struct udphdr *uh = pd->hdr.udp; u_int16_t bport, nport = 0; sa_family_t af = pd->af; int lookup = -1; uid_t uid; gid_t gid; struct pf_rule *r, *a = NULL; struct pf_ruleset *ruleset = NULL; struct pf_src_node *nsn = NULL; u_short reason; int rewrite = 0; struct pf_tag *pftag = NULL; int tag = -1; int asd = 0; if (pf_check_congestion(ifq)) { REASON_SET(&reason, PFRES_CONGEST); return (PF_DROP); } r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); if (direction == PF_OUT) { bport = nport = uh->uh_sport; /* check outgoing packet for BINAT/NAT */ if ((nr = pf_get_translation(pd, m, off, PF_OUT, kif, &nsn, saddr, uh->uh_sport, daddr, uh->uh_dport, &pd->naddr, &nport)) != NULL) { PF_ACPY(&pd->baddr, saddr, af); pf_change_ap(saddr, &uh->uh_sport, pd->ip_sum, &uh->uh_sum, &pd->naddr, nport, 1, af); rewrite++; if (nr->natpass) r = NULL; pd->nat_rule = nr; } } else { bport = nport = uh->uh_dport; /* check incoming packet for BINAT/RDR */ if ((nr = pf_get_translation(pd, m, off, PF_IN, kif, &nsn, saddr, uh->uh_sport, daddr, uh->uh_dport, &pd->naddr, &nport)) != NULL) { PF_ACPY(&pd->baddr, daddr, af); pf_change_ap(daddr, &uh->uh_dport, pd->ip_sum, &uh->uh_sum, &pd->naddr, nport, 1, af); rewrite++; if (nr->natpass) r = NULL; pd->nat_rule = nr; } } while (r != NULL) { r->evaluations++; if (r->kif != NULL && (r->kif != kif && r->kif != kif->pfik_parent) == !r->ifnot) r = r->skip[PF_SKIP_IFP].ptr; else if (r->direction && r->direction != direction) r = r->skip[PF_SKIP_DIR].ptr; else if (r->af && r->af != af) r = r->skip[PF_SKIP_AF].ptr; else if (r->proto && r->proto != IPPROTO_UDP) r = r->skip[PF_SKIP_PROTO].ptr; else if (PF_MISMATCHAW(&r->src.addr, saddr, af, r->src.neg)) r = r->skip[PF_SKIP_SRC_ADDR].ptr; else if (r->src.port_op && !pf_match_port(r->src.port_op, r->src.port[0], r->src.port[1], uh->uh_sport)) r = r->skip[PF_SKIP_SRC_PORT].ptr; else if (PF_MISMATCHAW(&r->dst.addr, daddr, af, r->dst.neg)) r = r->skip[PF_SKIP_DST_ADDR].ptr; else if (r->dst.port_op && !pf_match_port(r->dst.port_op, r->dst.port[0], r->dst.port[1], uh->uh_dport)) r = r->skip[PF_SKIP_DST_PORT].ptr; else if (r->tos && !(r->tos & pd->tos)) r = TAILQ_NEXT(r, entries); else if (r->rule_flag & PFRULE_FRAGMENT) r = TAILQ_NEXT(r, entries); else if (r->uid.op && (lookup != -1 || (lookup = #ifdef __FreeBSD__ pf_socket_lookup(&uid, &gid, direction, pd, inp), 1)) && #else pf_socket_lookup(&uid, &gid, direction, pd), 1)) && #endif !pf_match_uid(r->uid.op, r->uid.uid[0], r->uid.uid[1], uid)) r = TAILQ_NEXT(r, entries); else if (r->gid.op && (lookup != -1 || (lookup = #ifdef __FreeBSD__ pf_socket_lookup(&uid, &gid, direction, pd, inp), 1)) && #else pf_socket_lookup(&uid, &gid, direction, pd), 1)) && #endif !pf_match_gid(r->gid.op, r->gid.gid[0], r->gid.gid[1], gid)) r = TAILQ_NEXT(r, entries); else if (r->prob && r->prob <= arc4random()) r = TAILQ_NEXT(r, entries); else if (r->match_tag && !pf_match_tag(m, r, &pftag, &tag)) r = TAILQ_NEXT(r, entries); else if (r->os_fingerprint != PF_OSFP_ANY) r = TAILQ_NEXT(r, entries); else { if (r->tag) tag = r->tag; if (r->anchor == NULL) { *rm = r; *am = a; *rsm = ruleset; if ((*rm)->quick) break; r = TAILQ_NEXT(r, entries); } else pf_step_into_anchor(&asd, &ruleset, PF_RULESET_FILTER, &r, &a); } if (r == NULL) pf_step_out_of_anchor(&asd, &ruleset, PF_RULESET_FILTER, &r, &a); } r = *rm; a = *am; ruleset = *rsm; REASON_SET(&reason, PFRES_MATCH); if (r->log) { if (rewrite) m_copyback(m, off, sizeof(*uh), (caddr_t)uh); PFLOG_PACKET(kif, h, m, af, direction, reason, r, a, ruleset); } if ((r->action == PF_DROP) && ((r->rule_flag & PFRULE_RETURNICMP) || (r->rule_flag & PFRULE_RETURN))) { /* undo NAT changes, if they have taken place */ if (nr != NULL) { if (direction == PF_OUT) { pf_change_ap(saddr, &uh->uh_sport, pd->ip_sum, &uh->uh_sum, &pd->baddr, bport, 1, af); rewrite++; } else { pf_change_ap(daddr, &uh->uh_dport, pd->ip_sum, &uh->uh_sum, &pd->baddr, bport, 1, af); rewrite++; } } if ((af == AF_INET) && r->return_icmp) pf_send_icmp(m, r->return_icmp >> 8, r->return_icmp & 255, af, r); else if ((af == AF_INET6) && r->return_icmp6) pf_send_icmp(m, r->return_icmp6 >> 8, r->return_icmp6 & 255, af, r); } if (r->action == PF_DROP) return (PF_DROP); if (pf_tag_packet(m, pftag, tag)) { REASON_SET(&reason, PFRES_MEMORY); return (PF_DROP); } if (r->keep_state || nr != NULL) { /* create new state */ struct pf_state *s = NULL; struct pf_src_node *sn = NULL; /* check maximums */ if (r->max_states && (r->states >= r->max_states)) { pf_status.lcounters[LCNT_STATES]++; REASON_SET(&reason, PFRES_MAXSTATES); goto cleanup; } /* src node for flter rule */ if ((r->rule_flag & PFRULE_SRCTRACK || r->rpool.opts & PF_POOL_STICKYADDR) && pf_insert_src_node(&sn, r, saddr, af) != 0) { REASON_SET(&reason, PFRES_SRCLIMIT); goto cleanup; } /* src node for translation rule */ if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) && ((direction == PF_OUT && pf_insert_src_node(&nsn, nr, &pd->baddr, af) != 0) || (pf_insert_src_node(&nsn, nr, saddr, af) != 0))) { REASON_SET(&reason, PFRES_SRCLIMIT); goto cleanup; } s = pool_get(&pf_state_pl, PR_NOWAIT); if (s == NULL) { REASON_SET(&reason, PFRES_MEMORY); cleanup: if (sn != NULL && sn->states == 0 && sn->expire == 0) { RB_REMOVE(pf_src_tree, &tree_src_tracking, sn); pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; pf_status.src_nodes--; pool_put(&pf_src_tree_pl, sn); } if (nsn != sn && nsn != NULL && nsn->states == 0 && nsn->expire == 0) { RB_REMOVE(pf_src_tree, &tree_src_tracking, nsn); pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; pf_status.src_nodes--; pool_put(&pf_src_tree_pl, nsn); } return (PF_DROP); } bzero(s, sizeof(*s)); s->rule.ptr = r; s->nat_rule.ptr = nr; s->anchor.ptr = a; STATE_INC_COUNTERS(s); s->allow_opts = r->allow_opts; s->log = r->log & 2; s->proto = IPPROTO_UDP; s->direction = direction; s->af = af; if (direction == PF_OUT) { PF_ACPY(&s->gwy.addr, saddr, af); s->gwy.port = uh->uh_sport; PF_ACPY(&s->ext.addr, daddr, af); s->ext.port = uh->uh_dport; if (nr != NULL) { PF_ACPY(&s->lan.addr, &pd->baddr, af); s->lan.port = bport; } else { PF_ACPY(&s->lan.addr, &s->gwy.addr, af); s->lan.port = s->gwy.port; } } else { PF_ACPY(&s->lan.addr, daddr, af); s->lan.port = uh->uh_dport; PF_ACPY(&s->ext.addr, saddr, af); s->ext.port = uh->uh_sport; if (nr != NULL) { PF_ACPY(&s->gwy.addr, &pd->baddr, af); s->gwy.port = bport; } else { PF_ACPY(&s->gwy.addr, &s->lan.addr, af); s->gwy.port = s->lan.port; } } s->src.state = PFUDPS_SINGLE; s->dst.state = PFUDPS_NO_TRAFFIC; s->creation = time_second; s->expire = time_second; s->timeout = PFTM_UDP_FIRST_PACKET; pf_set_rt_ifp(s, saddr); if (sn != NULL) { s->src_node = sn; s->src_node->states++; } if (nsn != NULL) { PF_ACPY(&nsn->raddr, &pd->naddr, af); s->nat_src_node = nsn; s->nat_src_node->states++; } if (pf_insert_state(BOUND_IFACE(r, kif), s)) { REASON_SET(&reason, PFRES_STATEINS); pf_src_tree_remove_state(s); STATE_DEC_COUNTERS(s); pool_put(&pf_state_pl, s); return (PF_DROP); } else *sm = s; if (tag > 0) { pf_tag_ref(tag); s->tag = tag; } } /* copy back packet headers if we performed NAT operations */ if (rewrite) m_copyback(m, off, sizeof(*uh), (caddr_t)uh); return (PF_PASS); } int pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction, struct pfi_kif *kif, struct mbuf *m, int off, void *h, struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm, struct ifqueue *ifq) { struct pf_rule *nr = NULL; struct pf_addr *saddr = pd->src, *daddr = pd->dst; struct pf_rule *r, *a = NULL; struct pf_ruleset *ruleset = NULL; struct pf_src_node *nsn = NULL; u_short reason; u_int16_t icmpid = 0, bport, nport = 0; sa_family_t af = pd->af; u_int8_t icmptype = 0; /* make the compiler happy */ u_int8_t icmpcode = 0; /* make the compiler happy */ int state_icmp = 0; struct pf_tag *pftag = NULL; int tag = -1; #ifdef INET6 int rewrite = 0; #endif /* INET6 */ int asd = 0; if (pf_check_congestion(ifq)) { REASON_SET(&reason, PFRES_CONGEST); return (PF_DROP); } switch (pd->proto) { #ifdef INET case IPPROTO_ICMP: icmptype = pd->hdr.icmp->icmp_type; icmpcode = pd->hdr.icmp->icmp_code; icmpid = pd->hdr.icmp->icmp_id; if (icmptype == ICMP_UNREACH || icmptype == ICMP_SOURCEQUENCH || icmptype == ICMP_REDIRECT || icmptype == ICMP_TIMXCEED || icmptype == ICMP_PARAMPROB) state_icmp++; break; #endif /* INET */ #ifdef INET6 case IPPROTO_ICMPV6: icmptype = pd->hdr.icmp6->icmp6_type; icmpcode = pd->hdr.icmp6->icmp6_code; icmpid = pd->hdr.icmp6->icmp6_id; if (icmptype == ICMP6_DST_UNREACH || icmptype == ICMP6_PACKET_TOO_BIG || icmptype == ICMP6_TIME_EXCEEDED || icmptype == ICMP6_PARAM_PROB) state_icmp++; break; #endif /* INET6 */ } r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); if (direction == PF_OUT) { bport = nport = icmpid; /* check outgoing packet for BINAT/NAT */ if ((nr = pf_get_translation(pd, m, off, PF_OUT, kif, &nsn, saddr, icmpid, daddr, icmpid, &pd->naddr, &nport)) != NULL) { PF_ACPY(&pd->baddr, saddr, af); switch (af) { #ifdef INET case AF_INET: pf_change_a(&saddr->v4.s_addr, pd->ip_sum, pd->naddr.v4.s_addr, 0); pd->hdr.icmp->icmp_cksum = pf_cksum_fixup( pd->hdr.icmp->icmp_cksum, icmpid, nport, 0); pd->hdr.icmp->icmp_id = nport; m_copyback(m, off, ICMP_MINLEN, (caddr_t)pd->hdr.icmp); break; #endif /* INET */ #ifdef INET6 case AF_INET6: pf_change_a6(saddr, &pd->hdr.icmp6->icmp6_cksum, &pd->naddr, 0); rewrite++; break; #endif /* INET6 */ } if (nr->natpass) r = NULL; pd->nat_rule = nr; } } else { bport = nport = icmpid; /* check incoming packet for BINAT/RDR */ if ((nr = pf_get_translation(pd, m, off, PF_IN, kif, &nsn, saddr, icmpid, daddr, icmpid, &pd->naddr, &nport)) != NULL) { PF_ACPY(&pd->baddr, daddr, af); switch (af) { #ifdef INET case AF_INET: pf_change_a(&daddr->v4.s_addr, pd->ip_sum, pd->naddr.v4.s_addr, 0); break; #endif /* INET */ #ifdef INET6 case AF_INET6: pf_change_a6(daddr, &pd->hdr.icmp6->icmp6_cksum, &pd->naddr, 0); rewrite++; break; #endif /* INET6 */ } if (nr->natpass) r = NULL; pd->nat_rule = nr; } } while (r != NULL) { r->evaluations++; if (r->kif != NULL && (r->kif != kif && r->kif != kif->pfik_parent) == !r->ifnot) r = r->skip[PF_SKIP_IFP].ptr; else if (r->direction && r->direction != direction) r = r->skip[PF_SKIP_DIR].ptr; else if (r->af && r->af != af) r = r->skip[PF_SKIP_AF].ptr; else if (r->proto && r->proto != pd->proto) r = r->skip[PF_SKIP_PROTO].ptr; else if (PF_MISMATCHAW(&r->src.addr, saddr, af, r->src.neg)) r = r->skip[PF_SKIP_SRC_ADDR].ptr; else if (PF_MISMATCHAW(&r->dst.addr, daddr, af, r->dst.neg)) r = r->skip[PF_SKIP_DST_ADDR].ptr; else if (r->type && r->type != icmptype + 1) r = TAILQ_NEXT(r, entries); else if (r->code && r->code != icmpcode + 1) r = TAILQ_NEXT(r, entries); else if (r->tos && !(r->tos & pd->tos)) r = TAILQ_NEXT(r, entries); else if (r->rule_flag & PFRULE_FRAGMENT) r = TAILQ_NEXT(r, entries); else if (r->prob && r->prob <= arc4random()) r = TAILQ_NEXT(r, entries); else if (r->match_tag && !pf_match_tag(m, r, &pftag, &tag)) r = TAILQ_NEXT(r, entries); else if (r->os_fingerprint != PF_OSFP_ANY) r = TAILQ_NEXT(r, entries); else { if (r->tag) tag = r->tag; if (r->anchor == NULL) { *rm = r; *am = a; *rsm = ruleset; if ((*rm)->quick) break; r = TAILQ_NEXT(r, entries); } else pf_step_into_anchor(&asd, &ruleset, PF_RULESET_FILTER, &r, &a); } if (r == NULL) pf_step_out_of_anchor(&asd, &ruleset, PF_RULESET_FILTER, &r, &a); } r = *rm; a = *am; ruleset = *rsm; REASON_SET(&reason, PFRES_MATCH); if (r->log) { #ifdef INET6 if (rewrite) m_copyback(m, off, sizeof(struct icmp6_hdr), (caddr_t)pd->hdr.icmp6); #endif /* INET6 */ PFLOG_PACKET(kif, h, m, af, direction, reason, r, a, ruleset); } if (r->action != PF_PASS) return (PF_DROP); if (pf_tag_packet(m, pftag, tag)) { REASON_SET(&reason, PFRES_MEMORY); return (PF_DROP); } if (!state_icmp && (r->keep_state || nr != NULL)) { /* create new state */ struct pf_state *s = NULL; struct pf_src_node *sn = NULL; /* check maximums */ if (r->max_states && (r->states >= r->max_states)) { pf_status.lcounters[LCNT_STATES]++; REASON_SET(&reason, PFRES_MAXSTATES); goto cleanup; } /* src node for flter rule */ if ((r->rule_flag & PFRULE_SRCTRACK || r->rpool.opts & PF_POOL_STICKYADDR) && pf_insert_src_node(&sn, r, saddr, af) != 0) { REASON_SET(&reason, PFRES_SRCLIMIT); goto cleanup; } /* src node for translation rule */ if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) && ((direction == PF_OUT && pf_insert_src_node(&nsn, nr, &pd->baddr, af) != 0) || (pf_insert_src_node(&nsn, nr, saddr, af) != 0))) { REASON_SET(&reason, PFRES_SRCLIMIT); goto cleanup; } s = pool_get(&pf_state_pl, PR_NOWAIT); if (s == NULL) { REASON_SET(&reason, PFRES_MEMORY); cleanup: if (sn != NULL && sn->states == 0 && sn->expire == 0) { RB_REMOVE(pf_src_tree, &tree_src_tracking, sn); pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; pf_status.src_nodes--; pool_put(&pf_src_tree_pl, sn); } if (nsn != sn && nsn != NULL && nsn->states == 0 && nsn->expire == 0) { RB_REMOVE(pf_src_tree, &tree_src_tracking, nsn); pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; pf_status.src_nodes--; pool_put(&pf_src_tree_pl, nsn); } return (PF_DROP); } bzero(s, sizeof(*s)); s->rule.ptr = r; s->nat_rule.ptr = nr; s->anchor.ptr = a; STATE_INC_COUNTERS(s); s->allow_opts = r->allow_opts; s->log = r->log & 2; s->proto = pd->proto; s->direction = direction; s->af = af; if (direction == PF_OUT) { PF_ACPY(&s->gwy.addr, saddr, af); s->gwy.port = nport; PF_ACPY(&s->ext.addr, daddr, af); s->ext.port = 0; if (nr != NULL) { PF_ACPY(&s->lan.addr, &pd->baddr, af); s->lan.port = bport; } else { PF_ACPY(&s->lan.addr, &s->gwy.addr, af); s->lan.port = s->gwy.port; } } else { PF_ACPY(&s->lan.addr, daddr, af); s->lan.port = nport; PF_ACPY(&s->ext.addr, saddr, af); s->ext.port = 0; if (nr != NULL) { PF_ACPY(&s->gwy.addr, &pd->baddr, af); s->gwy.port = bport; } else { PF_ACPY(&s->gwy.addr, &s->lan.addr, af); s->gwy.port = s->lan.port; } } s->creation = time_second; s->expire = time_second; s->timeout = PFTM_ICMP_FIRST_PACKET; pf_set_rt_ifp(s, saddr); if (sn != NULL) { s->src_node = sn; s->src_node->states++; } if (nsn != NULL) { PF_ACPY(&nsn->raddr, &pd->naddr, af); s->nat_src_node = nsn; s->nat_src_node->states++; } if (pf_insert_state(BOUND_IFACE(r, kif), s)) { REASON_SET(&reason, PFRES_STATEINS); pf_src_tree_remove_state(s); STATE_DEC_COUNTERS(s); pool_put(&pf_state_pl, s); return (PF_DROP); } else *sm = s; if (tag > 0) { pf_tag_ref(tag); s->tag = tag; } } #ifdef INET6 /* copy back packet headers if we performed IPv6 NAT operations */ if (rewrite) m_copyback(m, off, sizeof(struct icmp6_hdr), (caddr_t)pd->hdr.icmp6); #endif /* INET6 */ return (PF_PASS); } int pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction, struct pfi_kif *kif, struct mbuf *m, int off, void *h, struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm, struct ifqueue *ifq) { struct pf_rule *nr = NULL; struct pf_rule *r, *a = NULL; struct pf_ruleset *ruleset = NULL; struct pf_src_node *nsn = NULL; struct pf_addr *saddr = pd->src, *daddr = pd->dst; sa_family_t af = pd->af; u_short reason; struct pf_tag *pftag = NULL; int tag = -1; int asd = 0; if (pf_check_congestion(ifq)) { REASON_SET(&reason, PFRES_CONGEST); return (PF_DROP); } r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); if (direction == PF_OUT) { /* check outgoing packet for BINAT/NAT */ if ((nr = pf_get_translation(pd, m, off, PF_OUT, kif, &nsn, saddr, 0, daddr, 0, &pd->naddr, NULL)) != NULL) { PF_ACPY(&pd->baddr, saddr, af); switch (af) { #ifdef INET case AF_INET: pf_change_a(&saddr->v4.s_addr, pd->ip_sum, pd->naddr.v4.s_addr, 0); break; #endif /* INET */ #ifdef INET6 case AF_INET6: PF_ACPY(saddr, &pd->naddr, af); break; #endif /* INET6 */ } if (nr->natpass) r = NULL; pd->nat_rule = nr; } } else { /* check incoming packet for BINAT/RDR */ if ((nr = pf_get_translation(pd, m, off, PF_IN, kif, &nsn, saddr, 0, daddr, 0, &pd->naddr, NULL)) != NULL) { PF_ACPY(&pd->baddr, daddr, af); switch (af) { #ifdef INET case AF_INET: pf_change_a(&daddr->v4.s_addr, pd->ip_sum, pd->naddr.v4.s_addr, 0); break; #endif /* INET */ #ifdef INET6 case AF_INET6: PF_ACPY(daddr, &pd->naddr, af); break; #endif /* INET6 */ } if (nr->natpass) r = NULL; pd->nat_rule = nr; } } while (r != NULL) { r->evaluations++; if (r->kif != NULL && (r->kif != kif && r->kif != kif->pfik_parent) == !r->ifnot) r = r->skip[PF_SKIP_IFP].ptr; else if (r->direction && r->direction != direction) r = r->skip[PF_SKIP_DIR].ptr; else if (r->af && r->af != af) r = r->skip[PF_SKIP_AF].ptr; else if (r->proto && r->proto != pd->proto) r = r->skip[PF_SKIP_PROTO].ptr; else if (PF_MISMATCHAW(&r->src.addr, pd->src, af, r->src.neg)) r = r->skip[PF_SKIP_SRC_ADDR].ptr; else if (PF_MISMATCHAW(&r->dst.addr, pd->dst, af, r->dst.neg)) r = r->skip[PF_SKIP_DST_ADDR].ptr; else if (r->tos && !(r->tos & pd->tos)) r = TAILQ_NEXT(r, entries); else if (r->rule_flag & PFRULE_FRAGMENT) r = TAILQ_NEXT(r, entries); else if (r->prob && r->prob <= arc4random()) r = TAILQ_NEXT(r, entries); else if (r->match_tag && !pf_match_tag(m, r, &pftag, &tag)) r = TAILQ_NEXT(r, entries); else if (r->os_fingerprint != PF_OSFP_ANY) r = TAILQ_NEXT(r, entries); else { if (r->tag) tag = r->tag; if (r->anchor == NULL) { *rm = r; *am = a; *rsm = ruleset; if ((*rm)->quick) break; r = TAILQ_NEXT(r, entries); } else pf_step_into_anchor(&asd, &ruleset, PF_RULESET_FILTER, &r, &a); } if (r == NULL) pf_step_out_of_anchor(&asd, &ruleset, PF_RULESET_FILTER, &r, &a); } r = *rm; a = *am; ruleset = *rsm; REASON_SET(&reason, PFRES_MATCH); if (r->log) PFLOG_PACKET(kif, h, m, af, direction, reason, r, a, ruleset); if ((r->action == PF_DROP) && ((r->rule_flag & PFRULE_RETURNICMP) || (r->rule_flag & PFRULE_RETURN))) { struct pf_addr *a = NULL; if (nr != NULL) { if (direction == PF_OUT) a = saddr; else a = daddr; } if (a != NULL) { switch (af) { #ifdef INET case AF_INET: pf_change_a(&a->v4.s_addr, pd->ip_sum, pd->baddr.v4.s_addr, 0); break; #endif /* INET */ #ifdef INET6 case AF_INET6: PF_ACPY(a, &pd->baddr, af); break; #endif /* INET6 */ } } if ((af == AF_INET) && r->return_icmp) pf_send_icmp(m, r->return_icmp >> 8, r->return_icmp & 255, af, r); else if ((af == AF_INET6) && r->return_icmp6) pf_send_icmp(m, r->return_icmp6 >> 8, r->return_icmp6 & 255, af, r); } if (r->action != PF_PASS) return (PF_DROP); if (pf_tag_packet(m, pftag, tag)) { REASON_SET(&reason, PFRES_MEMORY); return (PF_DROP); } if (r->keep_state || nr != NULL) { /* create new state */ struct pf_state *s = NULL; struct pf_src_node *sn = NULL; /* check maximums */ if (r->max_states && (r->states >= r->max_states)) { pf_status.lcounters[LCNT_STATES]++; REASON_SET(&reason, PFRES_MAXSTATES); goto cleanup; } /* src node for flter rule */ if ((r->rule_flag & PFRULE_SRCTRACK || r->rpool.opts & PF_POOL_STICKYADDR) && pf_insert_src_node(&sn, r, saddr, af) != 0) { REASON_SET(&reason, PFRES_SRCLIMIT); goto cleanup; } /* src node for translation rule */ if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) && ((direction == PF_OUT && pf_insert_src_node(&nsn, nr, &pd->baddr, af) != 0) || (pf_insert_src_node(&nsn, nr, saddr, af) != 0))) { REASON_SET(&reason, PFRES_SRCLIMIT); goto cleanup; } s = pool_get(&pf_state_pl, PR_NOWAIT); if (s == NULL) { REASON_SET(&reason, PFRES_MEMORY); cleanup: if (sn != NULL && sn->states == 0 && sn->expire == 0) { RB_REMOVE(pf_src_tree, &tree_src_tracking, sn); pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; pf_status.src_nodes--; pool_put(&pf_src_tree_pl, sn); } if (nsn != sn && nsn != NULL && nsn->states == 0 && nsn->expire == 0) { RB_REMOVE(pf_src_tree, &tree_src_tracking, nsn); pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; pf_status.src_nodes--; pool_put(&pf_src_tree_pl, nsn); } return (PF_DROP); } bzero(s, sizeof(*s)); s->rule.ptr = r; s->nat_rule.ptr = nr; s->anchor.ptr = a; STATE_INC_COUNTERS(s); s->allow_opts = r->allow_opts; s->log = r->log & 2; s->proto = pd->proto; s->direction = direction; s->af = af; if (direction == PF_OUT) { PF_ACPY(&s->gwy.addr, saddr, af); PF_ACPY(&s->ext.addr, daddr, af); if (nr != NULL) PF_ACPY(&s->lan.addr, &pd->baddr, af); else PF_ACPY(&s->lan.addr, &s->gwy.addr, af); } else { PF_ACPY(&s->lan.addr, daddr, af); PF_ACPY(&s->ext.addr, saddr, af); if (nr != NULL) PF_ACPY(&s->gwy.addr, &pd->baddr, af); else PF_ACPY(&s->gwy.addr, &s->lan.addr, af); } s->src.state = PFOTHERS_SINGLE; s->dst.state = PFOTHERS_NO_TRAFFIC; s->creation = time_second; s->expire = time_second; s->timeout = PFTM_OTHER_FIRST_PACKET; pf_set_rt_ifp(s, saddr); if (sn != NULL) { s->src_node = sn; s->src_node->states++; } if (nsn != NULL) { PF_ACPY(&nsn->raddr, &pd->naddr, af); s->nat_src_node = nsn; s->nat_src_node->states++; } if (pf_insert_state(BOUND_IFACE(r, kif), s)) { REASON_SET(&reason, PFRES_STATEINS); pf_src_tree_remove_state(s); STATE_DEC_COUNTERS(s); pool_put(&pf_state_pl, s); return (PF_DROP); } else *sm = s; if (tag > 0) { pf_tag_ref(tag); s->tag = tag; } } return (PF_PASS); } int pf_test_fragment(struct pf_rule **rm, int direction, struct pfi_kif *kif, struct mbuf *m, void *h, struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm) { struct pf_rule *r, *a = NULL; struct pf_ruleset *ruleset = NULL; sa_family_t af = pd->af; u_short reason; struct pf_tag *pftag = NULL; int tag = -1; int asd = 0; r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); while (r != NULL) { r->evaluations++; if (r->kif != NULL && (r->kif != kif && r->kif != kif->pfik_parent) == !r->ifnot) r = r->skip[PF_SKIP_IFP].ptr; else if (r->direction && r->direction != direction) r = r->skip[PF_SKIP_DIR].ptr; else if (r->af && r->af != af) r = r->skip[PF_SKIP_AF].ptr; else if (r->proto && r->proto != pd->proto) r = r->skip[PF_SKIP_PROTO].ptr; else if (PF_MISMATCHAW(&r->src.addr, pd->src, af, r->src.neg)) r = r->skip[PF_SKIP_SRC_ADDR].ptr; else if (PF_MISMATCHAW(&r->dst.addr, pd->dst, af, r->dst.neg)) r = r->skip[PF_SKIP_DST_ADDR].ptr; else if (r->tos && !(r->tos & pd->tos)) r = TAILQ_NEXT(r, entries); else if (r->src.port_op || r->dst.port_op || r->flagset || r->type || r->code || r->os_fingerprint != PF_OSFP_ANY) r = TAILQ_NEXT(r, entries); else if (r->prob && r->prob <= arc4random()) r = TAILQ_NEXT(r, entries); else if (r->match_tag && !pf_match_tag(m, r, &pftag, &tag)) r = TAILQ_NEXT(r, entries); else { if (r->anchor == NULL) { *rm = r; *am = a; *rsm = ruleset; if ((*rm)->quick) break; r = TAILQ_NEXT(r, entries); } else pf_step_into_anchor(&asd, &ruleset, PF_RULESET_FILTER, &r, &a); } if (r == NULL) pf_step_out_of_anchor(&asd, &ruleset, PF_RULESET_FILTER, &r, &a); } r = *rm; a = *am; ruleset = *rsm; REASON_SET(&reason, PFRES_MATCH); if (r->log) PFLOG_PACKET(kif, h, m, af, direction, reason, r, a, ruleset); if (r->action != PF_PASS) return (PF_DROP); if (pf_tag_packet(m, pftag, tag)) { REASON_SET(&reason, PFRES_MEMORY); return (PF_DROP); } return (PF_PASS); } int pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif, struct mbuf *m, int off, void *h, struct pf_pdesc *pd, u_short *reason) { struct pf_state key; struct tcphdr *th = pd->hdr.tcp; u_int16_t win = ntohs(th->th_win); u_int32_t ack, end, seq, orig_seq; u_int8_t sws, dws; int ackskew; int copyback = 0; struct pf_state_peer *src, *dst; key.af = pd->af; key.proto = IPPROTO_TCP; if (direction == PF_IN) { PF_ACPY(&key.ext.addr, pd->src, key.af); PF_ACPY(&key.gwy.addr, pd->dst, key.af); key.ext.port = th->th_sport; key.gwy.port = th->th_dport; } else { PF_ACPY(&key.lan.addr, pd->src, key.af); PF_ACPY(&key.ext.addr, pd->dst, key.af); key.lan.port = th->th_sport; key.ext.port = th->th_dport; } STATE_LOOKUP(); if (direction == (*state)->direction) { src = &(*state)->src; dst = &(*state)->dst; } else { src = &(*state)->dst; dst = &(*state)->src; } if ((*state)->src.state == PF_TCPS_PROXY_SRC) { if (direction != (*state)->direction) { REASON_SET(reason, PFRES_SYNPROXY); return (PF_SYNPROXY_DROP); } if (th->th_flags & TH_SYN) { if (ntohl(th->th_seq) != (*state)->src.seqlo) { REASON_SET(reason, PFRES_SYNPROXY); return (PF_DROP); } +#ifdef __FreeBSD__ + pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, pd->dst, +#else pf_send_tcp((*state)->rule.ptr, pd->af, pd->dst, +#endif pd->src, th->th_dport, th->th_sport, (*state)->src.seqhi, ntohl(th->th_seq) + 1, TH_SYN|TH_ACK, 0, (*state)->src.mss, 0, 1, NULL, NULL); REASON_SET(reason, PFRES_SYNPROXY); return (PF_SYNPROXY_DROP); } else if (!(th->th_flags & TH_ACK) || (ntohl(th->th_ack) != (*state)->src.seqhi + 1) || (ntohl(th->th_seq) != (*state)->src.seqlo + 1)) { REASON_SET(reason, PFRES_SYNPROXY); return (PF_DROP); } else if ((*state)->src_node != NULL && pf_src_connlimit(state)) { REASON_SET(reason, PFRES_SRCLIMIT); return (PF_DROP); } else (*state)->src.state = PF_TCPS_PROXY_DST; } if ((*state)->src.state == PF_TCPS_PROXY_DST) { struct pf_state_host *src, *dst; if (direction == PF_OUT) { src = &(*state)->gwy; dst = &(*state)->ext; } else { src = &(*state)->ext; dst = &(*state)->lan; } if (direction == (*state)->direction) { if (((th->th_flags & (TH_SYN|TH_ACK)) != TH_ACK) || (ntohl(th->th_ack) != (*state)->src.seqhi + 1) || (ntohl(th->th_seq) != (*state)->src.seqlo + 1)) { REASON_SET(reason, PFRES_SYNPROXY); return (PF_DROP); } (*state)->src.max_win = MAX(ntohs(th->th_win), 1); if ((*state)->dst.seqhi == 1) (*state)->dst.seqhi = htonl(arc4random()); +#ifdef __FreeBSD__ + pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, + &src->addr, +#else pf_send_tcp((*state)->rule.ptr, pd->af, &src->addr, +#endif &dst->addr, src->port, dst->port, (*state)->dst.seqhi, 0, TH_SYN, 0, (*state)->src.mss, 0, 0, NULL, NULL); REASON_SET(reason, PFRES_SYNPROXY); return (PF_SYNPROXY_DROP); } else if (((th->th_flags & (TH_SYN|TH_ACK)) != (TH_SYN|TH_ACK)) || (ntohl(th->th_ack) != (*state)->dst.seqhi + 1)) { REASON_SET(reason, PFRES_SYNPROXY); return (PF_DROP); } else { (*state)->dst.max_win = MAX(ntohs(th->th_win), 1); (*state)->dst.seqlo = ntohl(th->th_seq); +#ifdef __FreeBSD__ + pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, pd->dst, +#else pf_send_tcp((*state)->rule.ptr, pd->af, pd->dst, +#endif pd->src, th->th_dport, th->th_sport, ntohl(th->th_ack), ntohl(th->th_seq) + 1, TH_ACK, (*state)->src.max_win, 0, 0, 0, NULL, NULL); +#ifdef __FreeBSD__ + pf_send_tcp(NULL, (*state)->rule.ptr, pd->af, + &src->addr, +#else pf_send_tcp((*state)->rule.ptr, pd->af, &src->addr, +#endif &dst->addr, src->port, dst->port, (*state)->src.seqhi + 1, (*state)->src.seqlo + 1, TH_ACK, (*state)->dst.max_win, 0, 0, 1, NULL, NULL); (*state)->src.seqdiff = (*state)->dst.seqhi - (*state)->src.seqlo; (*state)->dst.seqdiff = (*state)->src.seqhi - (*state)->dst.seqlo; (*state)->src.seqhi = (*state)->src.seqlo + (*state)->dst.max_win; (*state)->dst.seqhi = (*state)->dst.seqlo + (*state)->src.max_win; (*state)->src.wscale = (*state)->dst.wscale = 0; (*state)->src.state = (*state)->dst.state = TCPS_ESTABLISHED; REASON_SET(reason, PFRES_SYNPROXY); return (PF_SYNPROXY_DROP); } } if (src->wscale && dst->wscale && !(th->th_flags & TH_SYN)) { sws = src->wscale & PF_WSCALE_MASK; dws = dst->wscale & PF_WSCALE_MASK; } else sws = dws = 0; /* * Sequence tracking algorithm from Guido van Rooij's paper: * http://www.madison-gurkha.com/publications/tcp_filtering/ * tcp_filtering.ps */ orig_seq = seq = ntohl(th->th_seq); if (src->seqlo == 0) { /* First packet from this end. Set its state */ if ((pd->flags & PFDESC_TCP_NORM || dst->scrub) && src->scrub == NULL) { if (pf_normalize_tcp_init(m, off, pd, th, src, dst)) { REASON_SET(reason, PFRES_MEMORY); return (PF_DROP); } } /* Deferred generation of sequence number modulator */ if (dst->seqdiff && !src->seqdiff) { while ((src->seqdiff = htonl(arc4random())) == 0) ; ack = ntohl(th->th_ack) - dst->seqdiff; pf_change_a(&th->th_seq, &th->th_sum, htonl(seq + src->seqdiff), 0); pf_change_a(&th->th_ack, &th->th_sum, htonl(ack), 0); copyback = 1; } else { ack = ntohl(th->th_ack); } end = seq + pd->p_len; if (th->th_flags & TH_SYN) { end++; if (dst->wscale & PF_WSCALE_FLAG) { src->wscale = pf_get_wscale(m, off, th->th_off, pd->af); if (src->wscale & PF_WSCALE_FLAG) { /* Remove scale factor from initial * window */ sws = src->wscale & PF_WSCALE_MASK; win = ((u_int32_t)win + (1 << sws) - 1) >> sws; dws = dst->wscale & PF_WSCALE_MASK; } else { /* fixup other window */ dst->max_win <<= dst->wscale & PF_WSCALE_MASK; /* in case of a retrans SYN|ACK */ dst->wscale = 0; } } } if (th->th_flags & TH_FIN) end++; src->seqlo = seq; if (src->state < TCPS_SYN_SENT) src->state = TCPS_SYN_SENT; /* * May need to slide the window (seqhi may have been set by * the crappy stack check or if we picked up the connection * after establishment) */ if (src->seqhi == 1 || SEQ_GEQ(end + MAX(1, dst->max_win << dws), src->seqhi)) src->seqhi = end + MAX(1, dst->max_win << dws); if (win > src->max_win) src->max_win = win; } else { ack = ntohl(th->th_ack) - dst->seqdiff; if (src->seqdiff) { /* Modulate sequence numbers */ pf_change_a(&th->th_seq, &th->th_sum, htonl(seq + src->seqdiff), 0); pf_change_a(&th->th_ack, &th->th_sum, htonl(ack), 0); copyback = 1; } end = seq + pd->p_len; if (th->th_flags & TH_SYN) end++; if (th->th_flags & TH_FIN) end++; } if ((th->th_flags & TH_ACK) == 0) { /* Let it pass through the ack skew check */ ack = dst->seqlo; } else if ((ack == 0 && (th->th_flags & (TH_ACK|TH_RST)) == (TH_ACK|TH_RST)) || /* broken tcp stacks do not set ack */ (dst->state < TCPS_SYN_SENT)) { /* * Many stacks (ours included) will set the ACK number in an * FIN|ACK if the SYN times out -- no sequence to ACK. */ ack = dst->seqlo; } if (seq == end) { /* Ease sequencing restrictions on no data packets */ seq = src->seqlo; end = seq; } ackskew = dst->seqlo - ack; #define MAXACKWINDOW (0xffff + 1500) /* 1500 is an arbitrary fudge factor */ if (SEQ_GEQ(src->seqhi, end) && /* Last octet inside other's window space */ SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws)) && /* Retrans: not more than one window back */ (ackskew >= -MAXACKWINDOW) && /* Acking not more than one reassembled fragment backwards */ (ackskew <= (MAXACKWINDOW << sws)) && /* Acking not more than one window forward */ ((th->th_flags & TH_RST) == 0 || orig_seq == src->seqlo || (pd->flags & PFDESC_IP_REAS) == 0)) { /* Require an exact sequence match on resets when possible */ if (dst->scrub || src->scrub) { if (pf_normalize_tcp_stateful(m, off, pd, reason, th, *state, src, dst, ©back)) return (PF_DROP); } /* update max window */ if (src->max_win < win) src->max_win = win; /* synchronize sequencing */ if (SEQ_GT(end, src->seqlo)) src->seqlo = end; /* slide the window of what the other end can send */ if (SEQ_GEQ(ack + (win << sws), dst->seqhi)) dst->seqhi = ack + MAX((win << sws), 1); /* update states */ if (th->th_flags & TH_SYN) if (src->state < TCPS_SYN_SENT) src->state = TCPS_SYN_SENT; if (th->th_flags & TH_FIN) if (src->state < TCPS_CLOSING) src->state = TCPS_CLOSING; if (th->th_flags & TH_ACK) { if (dst->state == TCPS_SYN_SENT) { dst->state = TCPS_ESTABLISHED; if (src->state == TCPS_ESTABLISHED && (*state)->src_node != NULL && pf_src_connlimit(state)) { REASON_SET(reason, PFRES_SRCLIMIT); return (PF_DROP); } } else if (dst->state == TCPS_CLOSING) dst->state = TCPS_FIN_WAIT_2; } if (th->th_flags & TH_RST) src->state = dst->state = TCPS_TIME_WAIT; /* update expire time */ (*state)->expire = time_second; if (src->state >= TCPS_FIN_WAIT_2 && dst->state >= TCPS_FIN_WAIT_2) (*state)->timeout = PFTM_TCP_CLOSED; else if (src->state >= TCPS_FIN_WAIT_2 || dst->state >= TCPS_FIN_WAIT_2) (*state)->timeout = PFTM_TCP_FIN_WAIT; else if (src->state < TCPS_ESTABLISHED || dst->state < TCPS_ESTABLISHED) (*state)->timeout = PFTM_TCP_OPENING; else if (src->state >= TCPS_CLOSING || dst->state >= TCPS_CLOSING) (*state)->timeout = PFTM_TCP_CLOSING; else (*state)->timeout = PFTM_TCP_ESTABLISHED; /* Fall through to PASS packet */ } else if ((dst->state < TCPS_SYN_SENT || dst->state >= TCPS_FIN_WAIT_2 || src->state >= TCPS_FIN_WAIT_2) && SEQ_GEQ(src->seqhi + MAXACKWINDOW, end) && /* Within a window forward of the originating packet */ SEQ_GEQ(seq, src->seqlo - MAXACKWINDOW)) { /* Within a window backward of the originating packet */ /* * This currently handles three situations: * 1) Stupid stacks will shotgun SYNs before their peer * replies. * 2) When PF catches an already established stream (the * firewall rebooted, the state table was flushed, routes * changed...) * 3) Packets get funky immediately after the connection * closes (this should catch Solaris spurious ACK|FINs * that web servers like to spew after a close) * * This must be a little more careful than the above code * since packet floods will also be caught here. We don't * update the TTL here to mitigate the damage of a packet * flood and so the same code can handle awkward establishment * and a loosened connection close. * In the establishment case, a correct peer response will * validate the connection, go through the normal state code * and keep updating the state TTL. */ if (pf_status.debug >= PF_DEBUG_MISC) { printf("pf: loose state match: "); pf_print_state(*state); pf_print_flags(th->th_flags); printf(" seq=%u ack=%u len=%u ackskew=%d pkts=%d:%d\n", seq, ack, pd->p_len, ackskew, (*state)->packets[0], (*state)->packets[1]); } if (dst->scrub || src->scrub) { if (pf_normalize_tcp_stateful(m, off, pd, reason, th, *state, src, dst, ©back)) return (PF_DROP); } /* update max window */ if (src->max_win < win) src->max_win = win; /* synchronize sequencing */ if (SEQ_GT(end, src->seqlo)) src->seqlo = end; /* slide the window of what the other end can send */ if (SEQ_GEQ(ack + (win << sws), dst->seqhi)) dst->seqhi = ack + MAX((win << sws), 1); /* * Cannot set dst->seqhi here since this could be a shotgunned * SYN and not an already established connection. */ if (th->th_flags & TH_FIN) if (src->state < TCPS_CLOSING) src->state = TCPS_CLOSING; if (th->th_flags & TH_RST) src->state = dst->state = TCPS_TIME_WAIT; /* Fall through to PASS packet */ } else { if ((*state)->dst.state == TCPS_SYN_SENT && (*state)->src.state == TCPS_SYN_SENT) { /* Send RST for state mismatches during handshake */ if (!(th->th_flags & TH_RST)) +#ifdef __FreeBSD__ + pf_send_tcp(m, (*state)->rule.ptr, pd->af, +#else pf_send_tcp((*state)->rule.ptr, pd->af, +#endif pd->dst, pd->src, th->th_dport, th->th_sport, ntohl(th->th_ack), 0, TH_RST, 0, 0, (*state)->rule.ptr->return_ttl, 1, pd->eh, kif->pfik_ifp); src->seqlo = 0; src->seqhi = 1; src->max_win = 1; } else if (pf_status.debug >= PF_DEBUG_MISC) { printf("pf: BAD state: "); pf_print_state(*state); pf_print_flags(th->th_flags); printf(" seq=%u ack=%u len=%u ackskew=%d pkts=%d:%d " "dir=%s,%s\n", seq, ack, pd->p_len, ackskew, (*state)->packets[0], (*state)->packets[1], direction == PF_IN ? "in" : "out", direction == (*state)->direction ? "fwd" : "rev"); printf("pf: State failure on: %c %c %c %c | %c %c\n", SEQ_GEQ(src->seqhi, end) ? ' ' : '1', SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws)) ? ' ': '2', (ackskew >= -MAXACKWINDOW) ? ' ' : '3', (ackskew <= (MAXACKWINDOW << sws)) ? ' ' : '4', SEQ_GEQ(src->seqhi + MAXACKWINDOW, end) ?' ' :'5', SEQ_GEQ(seq, src->seqlo - MAXACKWINDOW) ?' ' :'6'); } REASON_SET(reason, PFRES_BADSTATE); return (PF_DROP); } /* Any packets which have gotten here are to be passed */ /* translate source/destination address, if necessary */ if (STATE_TRANSLATE(*state)) { if (direction == PF_OUT) pf_change_ap(pd->src, &th->th_sport, pd->ip_sum, &th->th_sum, &(*state)->gwy.addr, (*state)->gwy.port, 0, pd->af); else pf_change_ap(pd->dst, &th->th_dport, pd->ip_sum, &th->th_sum, &(*state)->lan.addr, (*state)->lan.port, 0, pd->af); m_copyback(m, off, sizeof(*th), (caddr_t)th); } else if (copyback) { /* Copyback sequence modulation or stateful scrub changes */ m_copyback(m, off, sizeof(*th), (caddr_t)th); } return (PF_PASS); } int pf_test_state_udp(struct pf_state **state, int direction, struct pfi_kif *kif, struct mbuf *m, int off, void *h, struct pf_pdesc *pd) { struct pf_state_peer *src, *dst; struct pf_state key; struct udphdr *uh = pd->hdr.udp; key.af = pd->af; key.proto = IPPROTO_UDP; if (direction == PF_IN) { PF_ACPY(&key.ext.addr, pd->src, key.af); PF_ACPY(&key.gwy.addr, pd->dst, key.af); key.ext.port = uh->uh_sport; key.gwy.port = uh->uh_dport; } else { PF_ACPY(&key.lan.addr, pd->src, key.af); PF_ACPY(&key.ext.addr, pd->dst, key.af); key.lan.port = uh->uh_sport; key.ext.port = uh->uh_dport; } STATE_LOOKUP(); if (direction == (*state)->direction) { src = &(*state)->src; dst = &(*state)->dst; } else { src = &(*state)->dst; dst = &(*state)->src; } /* update states */ if (src->state < PFUDPS_SINGLE) src->state = PFUDPS_SINGLE; if (dst->state == PFUDPS_SINGLE) dst->state = PFUDPS_MULTIPLE; /* update expire time */ (*state)->expire = time_second; if (src->state == PFUDPS_MULTIPLE && dst->state == PFUDPS_MULTIPLE) (*state)->timeout = PFTM_UDP_MULTIPLE; else (*state)->timeout = PFTM_UDP_SINGLE; /* translate source/destination address, if necessary */ if (STATE_TRANSLATE(*state)) { if (direction == PF_OUT) pf_change_ap(pd->src, &uh->uh_sport, pd->ip_sum, &uh->uh_sum, &(*state)->gwy.addr, (*state)->gwy.port, 1, pd->af); else pf_change_ap(pd->dst, &uh->uh_dport, pd->ip_sum, &uh->uh_sum, &(*state)->lan.addr, (*state)->lan.port, 1, pd->af); m_copyback(m, off, sizeof(*uh), (caddr_t)uh); } return (PF_PASS); } int pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, struct mbuf *m, int off, void *h, struct pf_pdesc *pd, u_short *reason) { struct pf_addr *saddr = pd->src, *daddr = pd->dst; u_int16_t icmpid = 0; /* make the compiler happy */ u_int16_t *icmpsum = NULL; /* make the compiler happy */ u_int8_t icmptype = 0; /* make the compiler happy */ int state_icmp = 0; switch (pd->proto) { #ifdef INET case IPPROTO_ICMP: icmptype = pd->hdr.icmp->icmp_type; icmpid = pd->hdr.icmp->icmp_id; icmpsum = &pd->hdr.icmp->icmp_cksum; if (icmptype == ICMP_UNREACH || icmptype == ICMP_SOURCEQUENCH || icmptype == ICMP_REDIRECT || icmptype == ICMP_TIMXCEED || icmptype == ICMP_PARAMPROB) state_icmp++; break; #endif /* INET */ #ifdef INET6 case IPPROTO_ICMPV6: icmptype = pd->hdr.icmp6->icmp6_type; icmpid = pd->hdr.icmp6->icmp6_id; icmpsum = &pd->hdr.icmp6->icmp6_cksum; if (icmptype == ICMP6_DST_UNREACH || icmptype == ICMP6_PACKET_TOO_BIG || icmptype == ICMP6_TIME_EXCEEDED || icmptype == ICMP6_PARAM_PROB) state_icmp++; break; #endif /* INET6 */ } if (!state_icmp) { /* * ICMP query/reply message not related to a TCP/UDP packet. * Search for an ICMP state. */ struct pf_state key; key.af = pd->af; key.proto = pd->proto; if (direction == PF_IN) { PF_ACPY(&key.ext.addr, pd->src, key.af); PF_ACPY(&key.gwy.addr, pd->dst, key.af); key.ext.port = 0; key.gwy.port = icmpid; } else { PF_ACPY(&key.lan.addr, pd->src, key.af); PF_ACPY(&key.ext.addr, pd->dst, key.af); key.lan.port = icmpid; key.ext.port = 0; } STATE_LOOKUP(); (*state)->expire = time_second; (*state)->timeout = PFTM_ICMP_ERROR_REPLY; /* translate source/destination address, if necessary */ if (STATE_TRANSLATE(*state)) { if (direction == PF_OUT) { switch (pd->af) { #ifdef INET case AF_INET: pf_change_a(&saddr->v4.s_addr, pd->ip_sum, (*state)->gwy.addr.v4.s_addr, 0); pd->hdr.icmp->icmp_cksum = pf_cksum_fixup( pd->hdr.icmp->icmp_cksum, icmpid, (*state)->gwy.port, 0); pd->hdr.icmp->icmp_id = (*state)->gwy.port; m_copyback(m, off, ICMP_MINLEN, (caddr_t)pd->hdr.icmp); break; #endif /* INET */ #ifdef INET6 case AF_INET6: pf_change_a6(saddr, &pd->hdr.icmp6->icmp6_cksum, &(*state)->gwy.addr, 0); m_copyback(m, off, sizeof(struct icmp6_hdr), (caddr_t)pd->hdr.icmp6); break; #endif /* INET6 */ } } else { switch (pd->af) { #ifdef INET case AF_INET: pf_change_a(&daddr->v4.s_addr, pd->ip_sum, (*state)->lan.addr.v4.s_addr, 0); pd->hdr.icmp->icmp_cksum = pf_cksum_fixup( pd->hdr.icmp->icmp_cksum, icmpid, (*state)->lan.port, 0); pd->hdr.icmp->icmp_id = (*state)->lan.port; m_copyback(m, off, ICMP_MINLEN, (caddr_t)pd->hdr.icmp); break; #endif /* INET */ #ifdef INET6 case AF_INET6: pf_change_a6(daddr, &pd->hdr.icmp6->icmp6_cksum, &(*state)->lan.addr, 0); m_copyback(m, off, sizeof(struct icmp6_hdr), (caddr_t)pd->hdr.icmp6); break; #endif /* INET6 */ } } } return (PF_PASS); } else { /* * ICMP error message in response to a TCP/UDP packet. * Extract the inner TCP/UDP header and search for that state. */ struct pf_pdesc pd2; #ifdef INET struct ip h2; #endif /* INET */ #ifdef INET6 struct ip6_hdr h2_6; int terminal = 0; #endif /* INET6 */ int ipoff2 = 0; /* make the compiler happy */ int off2 = 0; /* make the compiler happy */ pd2.af = pd->af; switch (pd->af) { #ifdef INET case AF_INET: /* offset of h2 in mbuf chain */ ipoff2 = off + ICMP_MINLEN; if (!pf_pull_hdr(m, ipoff2, &h2, sizeof(h2), NULL, reason, pd2.af)) { DPFPRINTF(PF_DEBUG_MISC, ("pf: ICMP error message too short " "(ip)\n")); return (PF_DROP); } /* * ICMP error messages don't refer to non-first * fragments */ if (h2.ip_off & htons(IP_OFFMASK)) { REASON_SET(reason, PFRES_FRAG); return (PF_DROP); } /* offset of protocol header that follows h2 */ off2 = ipoff2 + (h2.ip_hl << 2); pd2.proto = h2.ip_p; pd2.src = (struct pf_addr *)&h2.ip_src; pd2.dst = (struct pf_addr *)&h2.ip_dst; pd2.ip_sum = &h2.ip_sum; break; #endif /* INET */ #ifdef INET6 case AF_INET6: ipoff2 = off + sizeof(struct icmp6_hdr); if (!pf_pull_hdr(m, ipoff2, &h2_6, sizeof(h2_6), NULL, reason, pd2.af)) { DPFPRINTF(PF_DEBUG_MISC, ("pf: ICMP error message too short " "(ip6)\n")); return (PF_DROP); } pd2.proto = h2_6.ip6_nxt; pd2.src = (struct pf_addr *)&h2_6.ip6_src; pd2.dst = (struct pf_addr *)&h2_6.ip6_dst; pd2.ip_sum = NULL; off2 = ipoff2 + sizeof(h2_6); do { switch (pd2.proto) { case IPPROTO_FRAGMENT: /* * ICMPv6 error messages for * non-first fragments */ REASON_SET(reason, PFRES_FRAG); return (PF_DROP); case IPPROTO_AH: case IPPROTO_HOPOPTS: case IPPROTO_ROUTING: case IPPROTO_DSTOPTS: { /* get next header and header length */ struct ip6_ext opt6; if (!pf_pull_hdr(m, off2, &opt6, sizeof(opt6), NULL, reason, pd2.af)) { DPFPRINTF(PF_DEBUG_MISC, ("pf: ICMPv6 short opt\n")); return (PF_DROP); } if (pd2.proto == IPPROTO_AH) off2 += (opt6.ip6e_len + 2) * 4; else off2 += (opt6.ip6e_len + 1) * 8; pd2.proto = opt6.ip6e_nxt; /* goto the next header */ break; } default: terminal++; break; } } while (!terminal); break; #endif /* INET6 */ } switch (pd2.proto) { case IPPROTO_TCP: { struct tcphdr th; u_int32_t seq; struct pf_state key; struct pf_state_peer *src, *dst; u_int8_t dws; int copyback = 0; /* * Only the first 8 bytes of the TCP header can be * expected. Don't access any TCP header fields after * th_seq, an ackskew test is not possible. */ if (!pf_pull_hdr(m, off2, &th, 8, NULL, reason, pd2.af)) { DPFPRINTF(PF_DEBUG_MISC, ("pf: ICMP error message too short " "(tcp)\n")); return (PF_DROP); } key.af = pd2.af; key.proto = IPPROTO_TCP; if (direction == PF_IN) { PF_ACPY(&key.ext.addr, pd2.dst, key.af); PF_ACPY(&key.gwy.addr, pd2.src, key.af); key.ext.port = th.th_dport; key.gwy.port = th.th_sport; } else { PF_ACPY(&key.lan.addr, pd2.dst, key.af); PF_ACPY(&key.ext.addr, pd2.src, key.af); key.lan.port = th.th_dport; key.ext.port = th.th_sport; } STATE_LOOKUP(); if (direction == (*state)->direction) { src = &(*state)->dst; dst = &(*state)->src; } else { src = &(*state)->src; dst = &(*state)->dst; } if (src->wscale && dst->wscale && !(th.th_flags & TH_SYN)) dws = dst->wscale & PF_WSCALE_MASK; else dws = 0; /* Demodulate sequence number */ seq = ntohl(th.th_seq) - src->seqdiff; if (src->seqdiff) { pf_change_a(&th.th_seq, icmpsum, htonl(seq), 0); copyback = 1; } if (!SEQ_GEQ(src->seqhi, seq) || !SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws))) { if (pf_status.debug >= PF_DEBUG_MISC) { printf("pf: BAD ICMP %d:%d ", icmptype, pd->hdr.icmp->icmp_code); pf_print_host(pd->src, 0, pd->af); printf(" -> "); pf_print_host(pd->dst, 0, pd->af); printf(" state: "); pf_print_state(*state); printf(" seq=%u\n", seq); } REASON_SET(reason, PFRES_BADSTATE); return (PF_DROP); } if (STATE_TRANSLATE(*state)) { if (direction == PF_IN) { pf_change_icmp(pd2.src, &th.th_sport, daddr, &(*state)->lan.addr, (*state)->lan.port, NULL, pd2.ip_sum, icmpsum, pd->ip_sum, 0, pd2.af); } else { pf_change_icmp(pd2.dst, &th.th_dport, saddr, &(*state)->gwy.addr, (*state)->gwy.port, NULL, pd2.ip_sum, icmpsum, pd->ip_sum, 0, pd2.af); } copyback = 1; } if (copyback) { switch (pd2.af) { #ifdef INET case AF_INET: m_copyback(m, off, ICMP_MINLEN, (caddr_t)pd->hdr.icmp); m_copyback(m, ipoff2, sizeof(h2), (caddr_t)&h2); break; #endif /* INET */ #ifdef INET6 case AF_INET6: m_copyback(m, off, sizeof(struct icmp6_hdr), (caddr_t)pd->hdr.icmp6); m_copyback(m, ipoff2, sizeof(h2_6), (caddr_t)&h2_6); break; #endif /* INET6 */ } m_copyback(m, off2, 8, (caddr_t)&th); } return (PF_PASS); break; } case IPPROTO_UDP: { struct udphdr uh; struct pf_state key; if (!pf_pull_hdr(m, off2, &uh, sizeof(uh), NULL, reason, pd2.af)) { DPFPRINTF(PF_DEBUG_MISC, ("pf: ICMP error message too short " "(udp)\n")); return (PF_DROP); } key.af = pd2.af; key.proto = IPPROTO_UDP; if (direction == PF_IN) { PF_ACPY(&key.ext.addr, pd2.dst, key.af); PF_ACPY(&key.gwy.addr, pd2.src, key.af); key.ext.port = uh.uh_dport; key.gwy.port = uh.uh_sport; } else { PF_ACPY(&key.lan.addr, pd2.dst, key.af); PF_ACPY(&key.ext.addr, pd2.src, key.af); key.lan.port = uh.uh_dport; key.ext.port = uh.uh_sport; } STATE_LOOKUP(); if (STATE_TRANSLATE(*state)) { if (direction == PF_IN) { pf_change_icmp(pd2.src, &uh.uh_sport, daddr, &(*state)->lan.addr, (*state)->lan.port, &uh.uh_sum, pd2.ip_sum, icmpsum, pd->ip_sum, 1, pd2.af); } else { pf_change_icmp(pd2.dst, &uh.uh_dport, saddr, &(*state)->gwy.addr, (*state)->gwy.port, &uh.uh_sum, pd2.ip_sum, icmpsum, pd->ip_sum, 1, pd2.af); } switch (pd2.af) { #ifdef INET case AF_INET: m_copyback(m, off, ICMP_MINLEN, (caddr_t)pd->hdr.icmp); m_copyback(m, ipoff2, sizeof(h2), (caddr_t)&h2); break; #endif /* INET */ #ifdef INET6 case AF_INET6: m_copyback(m, off, sizeof(struct icmp6_hdr), (caddr_t)pd->hdr.icmp6); m_copyback(m, ipoff2, sizeof(h2_6), (caddr_t)&h2_6); break; #endif /* INET6 */ } m_copyback(m, off2, sizeof(uh), (caddr_t)&uh); } return (PF_PASS); break; } #ifdef INET case IPPROTO_ICMP: { struct icmp iih; struct pf_state key; if (!pf_pull_hdr(m, off2, &iih, ICMP_MINLEN, NULL, reason, pd2.af)) { DPFPRINTF(PF_DEBUG_MISC, ("pf: ICMP error message too short i" "(icmp)\n")); return (PF_DROP); } key.af = pd2.af; key.proto = IPPROTO_ICMP; if (direction == PF_IN) { PF_ACPY(&key.ext.addr, pd2.dst, key.af); PF_ACPY(&key.gwy.addr, pd2.src, key.af); key.ext.port = 0; key.gwy.port = iih.icmp_id; } else { PF_ACPY(&key.lan.addr, pd2.dst, key.af); PF_ACPY(&key.ext.addr, pd2.src, key.af); key.lan.port = iih.icmp_id; key.ext.port = 0; } STATE_LOOKUP(); if (STATE_TRANSLATE(*state)) { if (direction == PF_IN) { pf_change_icmp(pd2.src, &iih.icmp_id, daddr, &(*state)->lan.addr, (*state)->lan.port, NULL, pd2.ip_sum, icmpsum, pd->ip_sum, 0, AF_INET); } else { pf_change_icmp(pd2.dst, &iih.icmp_id, saddr, &(*state)->gwy.addr, (*state)->gwy.port, NULL, pd2.ip_sum, icmpsum, pd->ip_sum, 0, AF_INET); } m_copyback(m, off, ICMP_MINLEN, (caddr_t)pd->hdr.icmp); m_copyback(m, ipoff2, sizeof(h2), (caddr_t)&h2); m_copyback(m, off2, ICMP_MINLEN, (caddr_t)&iih); } return (PF_PASS); break; } #endif /* INET */ #ifdef INET6 case IPPROTO_ICMPV6: { struct icmp6_hdr iih; struct pf_state key; if (!pf_pull_hdr(m, off2, &iih, sizeof(struct icmp6_hdr), NULL, reason, pd2.af)) { DPFPRINTF(PF_DEBUG_MISC, ("pf: ICMP error message too short " "(icmp6)\n")); return (PF_DROP); } key.af = pd2.af; key.proto = IPPROTO_ICMPV6; if (direction == PF_IN) { PF_ACPY(&key.ext.addr, pd2.dst, key.af); PF_ACPY(&key.gwy.addr, pd2.src, key.af); key.ext.port = 0; key.gwy.port = iih.icmp6_id; } else { PF_ACPY(&key.lan.addr, pd2.dst, key.af); PF_ACPY(&key.ext.addr, pd2.src, key.af); key.lan.port = iih.icmp6_id; key.ext.port = 0; } STATE_LOOKUP(); if (STATE_TRANSLATE(*state)) { if (direction == PF_IN) { pf_change_icmp(pd2.src, &iih.icmp6_id, daddr, &(*state)->lan.addr, (*state)->lan.port, NULL, pd2.ip_sum, icmpsum, pd->ip_sum, 0, AF_INET6); } else { pf_change_icmp(pd2.dst, &iih.icmp6_id, saddr, &(*state)->gwy.addr, (*state)->gwy.port, NULL, pd2.ip_sum, icmpsum, pd->ip_sum, 0, AF_INET6); } m_copyback(m, off, sizeof(struct icmp6_hdr), (caddr_t)pd->hdr.icmp6); m_copyback(m, ipoff2, sizeof(h2_6), (caddr_t)&h2_6); m_copyback(m, off2, sizeof(struct icmp6_hdr), (caddr_t)&iih); } return (PF_PASS); break; } #endif /* INET6 */ default: { struct pf_state key; key.af = pd2.af; key.proto = pd2.proto; if (direction == PF_IN) { PF_ACPY(&key.ext.addr, pd2.dst, key.af); PF_ACPY(&key.gwy.addr, pd2.src, key.af); key.ext.port = 0; key.gwy.port = 0; } else { PF_ACPY(&key.lan.addr, pd2.dst, key.af); PF_ACPY(&key.ext.addr, pd2.src, key.af); key.lan.port = 0; key.ext.port = 0; } STATE_LOOKUP(); if (STATE_TRANSLATE(*state)) { if (direction == PF_IN) { pf_change_icmp(pd2.src, NULL, daddr, &(*state)->lan.addr, 0, NULL, pd2.ip_sum, icmpsum, pd->ip_sum, 0, pd2.af); } else { pf_change_icmp(pd2.dst, NULL, saddr, &(*state)->gwy.addr, 0, NULL, pd2.ip_sum, icmpsum, pd->ip_sum, 0, pd2.af); } switch (pd2.af) { #ifdef INET case AF_INET: m_copyback(m, off, ICMP_MINLEN, (caddr_t)pd->hdr.icmp); m_copyback(m, ipoff2, sizeof(h2), (caddr_t)&h2); break; #endif /* INET */ #ifdef INET6 case AF_INET6: m_copyback(m, off, sizeof(struct icmp6_hdr), (caddr_t)pd->hdr.icmp6); m_copyback(m, ipoff2, sizeof(h2_6), (caddr_t)&h2_6); break; #endif /* INET6 */ } } return (PF_PASS); break; } } } } int pf_test_state_other(struct pf_state **state, int direction, struct pfi_kif *kif, struct pf_pdesc *pd) { struct pf_state_peer *src, *dst; struct pf_state key; key.af = pd->af; key.proto = pd->proto; if (direction == PF_IN) { PF_ACPY(&key.ext.addr, pd->src, key.af); PF_ACPY(&key.gwy.addr, pd->dst, key.af); key.ext.port = 0; key.gwy.port = 0; } else { PF_ACPY(&key.lan.addr, pd->src, key.af); PF_ACPY(&key.ext.addr, pd->dst, key.af); key.lan.port = 0; key.ext.port = 0; } STATE_LOOKUP(); if (direction == (*state)->direction) { src = &(*state)->src; dst = &(*state)->dst; } else { src = &(*state)->dst; dst = &(*state)->src; } /* update states */ if (src->state < PFOTHERS_SINGLE) src->state = PFOTHERS_SINGLE; if (dst->state == PFOTHERS_SINGLE) dst->state = PFOTHERS_MULTIPLE; /* update expire time */ (*state)->expire = time_second; if (src->state == PFOTHERS_MULTIPLE && dst->state == PFOTHERS_MULTIPLE) (*state)->timeout = PFTM_OTHER_MULTIPLE; else (*state)->timeout = PFTM_OTHER_SINGLE; /* translate source/destination address, if necessary */ if (STATE_TRANSLATE(*state)) { if (direction == PF_OUT) switch (pd->af) { #ifdef INET case AF_INET: pf_change_a(&pd->src->v4.s_addr, pd->ip_sum, (*state)->gwy.addr.v4.s_addr, 0); break; #endif /* INET */ #ifdef INET6 case AF_INET6: PF_ACPY(pd->src, &(*state)->gwy.addr, pd->af); break; #endif /* INET6 */ } else switch (pd->af) { #ifdef INET case AF_INET: pf_change_a(&pd->dst->v4.s_addr, pd->ip_sum, (*state)->lan.addr.v4.s_addr, 0); break; #endif /* INET */ #ifdef INET6 case AF_INET6: PF_ACPY(pd->dst, &(*state)->lan.addr, pd->af); break; #endif /* INET6 */ } } return (PF_PASS); } /* * ipoff and off are measured from the start of the mbuf chain. * h must be at "ipoff" on the mbuf chain. */ void * pf_pull_hdr(struct mbuf *m, int off, void *p, int len, u_short *actionp, u_short *reasonp, sa_family_t af) { switch (af) { #ifdef INET case AF_INET: { struct ip *h = mtod(m, struct ip *); u_int16_t fragoff = (ntohs(h->ip_off) & IP_OFFMASK) << 3; if (fragoff) { if (fragoff >= len) ACTION_SET(actionp, PF_PASS); else { ACTION_SET(actionp, PF_DROP); REASON_SET(reasonp, PFRES_FRAG); } return (NULL); } if (m->m_pkthdr.len < off + len || ntohs(h->ip_len) < off + len) { ACTION_SET(actionp, PF_DROP); REASON_SET(reasonp, PFRES_SHORT); return (NULL); } break; } #endif /* INET */ #ifdef INET6 case AF_INET6: { struct ip6_hdr *h = mtod(m, struct ip6_hdr *); if (m->m_pkthdr.len < off + len || (ntohs(h->ip6_plen) + sizeof(struct ip6_hdr)) < (unsigned)(off + len)) { ACTION_SET(actionp, PF_DROP); REASON_SET(reasonp, PFRES_SHORT); return (NULL); } break; } #endif /* INET6 */ } m_copydata(m, off, len, p); return (p); } int pf_routable(struct pf_addr *addr, sa_family_t af) { struct sockaddr_in *dst; #ifdef INET6 struct sockaddr_in6 *dst6; struct route_in6 ro; #else struct route ro; #endif bzero(&ro, sizeof(ro)); switch (af) { case AF_INET: dst = satosin(&ro.ro_dst); dst->sin_family = AF_INET; dst->sin_len = sizeof(*dst); dst->sin_addr = addr->v4; break; #ifdef INET6 case AF_INET6: dst6 = (struct sockaddr_in6 *)&ro.ro_dst; dst6->sin6_family = AF_INET6; dst6->sin6_len = sizeof(*dst6); dst6->sin6_addr = addr->v6; break; #endif /* INET6 */ default: return (0); } #ifdef __FreeBSD__ #ifdef RTF_PRCLONING rtalloc_ign((struct route *)&ro, (RTF_CLONING | RTF_PRCLONING)); #else /* !RTF_PRCLONING */ rtalloc_ign((struct route *)&ro, RTF_CLONING); #endif #else /* ! __FreeBSD__ */ rtalloc_noclone((struct route *)&ro, NO_CLONING); #endif if (ro.ro_rt != NULL) { RTFREE(ro.ro_rt); return (1); } return (0); } int pf_rtlabel_match(struct pf_addr *addr, sa_family_t af, struct pf_addr_wrap *aw) { struct sockaddr_in *dst; #ifdef INET6 struct sockaddr_in6 *dst6; struct route_in6 ro; #else struct route ro; #endif int ret = 0; bzero(&ro, sizeof(ro)); switch (af) { case AF_INET: dst = satosin(&ro.ro_dst); dst->sin_family = AF_INET; dst->sin_len = sizeof(*dst); dst->sin_addr = addr->v4; break; #ifdef INET6 case AF_INET6: dst6 = (struct sockaddr_in6 *)&ro.ro_dst; dst6->sin6_family = AF_INET6; dst6->sin6_len = sizeof(*dst6); dst6->sin6_addr = addr->v6; break; #endif /* INET6 */ default: return (0); } #ifdef __FreeBSD__ # ifdef RTF_PRCLONING rtalloc_ign((struct route *)&ro, (RTF_CLONING|RTF_PRCLONING)); # else /* !RTF_PRCLONING */ rtalloc_ign((struct route *)&ro, RTF_CLONING); # endif #else /* ! __FreeBSD__ */ rtalloc_noclone((struct route *)&ro, NO_CLONING); #endif if (ro.ro_rt != NULL) { #ifdef __FreeBSD__ /* XXX_IMPORT: later */ #else if (ro.ro_rt->rt_labelid == aw->v.rtlabel) ret = 1; #endif RTFREE(ro.ro_rt); } return (ret); } #ifdef INET void pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, struct pf_state *s) { struct mbuf *m0, *m1; struct m_tag *mtag; struct route iproute; struct route *ro = NULL; /* XXX: was uninitialized */ struct sockaddr_in *dst; struct ip *ip; struct ifnet *ifp = NULL; struct pf_addr naddr; struct pf_src_node *sn = NULL; int error = 0; #ifdef __FreeBSD__ int sw_csum; #endif if (m == NULL || *m == NULL || r == NULL || (dir != PF_IN && dir != PF_OUT) || oifp == NULL) panic("pf_route: invalid parameters"); if ((mtag = m_tag_find(*m, PACKET_TAG_PF_ROUTED, NULL)) == NULL) { if ((mtag = m_tag_get(PACKET_TAG_PF_ROUTED, 1, M_NOWAIT)) == NULL) { m0 = *m; *m = NULL; goto bad; } *(char *)(mtag + 1) = 1; m_tag_prepend(*m, mtag); } else { if (*(char *)(mtag + 1) > 3) { m0 = *m; *m = NULL; goto bad; } (*(char *)(mtag + 1))++; } if (r->rt == PF_DUPTO) { #ifdef __FreeBSD__ if ((m0 = m_dup(*m, M_DONTWAIT)) == NULL) #else if ((m0 = m_copym2(*m, 0, M_COPYALL, M_NOWAIT)) == NULL) #endif return; } else { if ((r->rt == PF_REPLYTO) == (r->direction == dir)) return; m0 = *m; } if (m0->m_len < sizeof(struct ip)) { DPFPRINTF(PF_DEBUG_URGENT, ("pf_route: m0->m_len < sizeof(struct ip)\n")); goto bad; } ip = mtod(m0, struct ip *); ro = &iproute; bzero((caddr_t)ro, sizeof(*ro)); dst = satosin(&ro->ro_dst); dst->sin_family = AF_INET; dst->sin_len = sizeof(*dst); dst->sin_addr = ip->ip_dst; if (r->rt == PF_FASTROUTE) { rtalloc(ro); if (ro->ro_rt == 0) { ipstat.ips_noroute++; goto bad; } ifp = ro->ro_rt->rt_ifp; ro->ro_rt->rt_use++; if (ro->ro_rt->rt_flags & RTF_GATEWAY) dst = satosin(ro->ro_rt->rt_gateway); } else { if (TAILQ_EMPTY(&r->rpool.list)) { DPFPRINTF(PF_DEBUG_URGENT, ("pf_route: TAILQ_EMPTY(&r->rpool.list)\n")); goto bad; } if (s == NULL) { pf_map_addr(AF_INET, r, (struct pf_addr *)&ip->ip_src, &naddr, NULL, &sn); if (!PF_AZERO(&naddr, AF_INET)) dst->sin_addr.s_addr = naddr.v4.s_addr; ifp = r->rpool.cur->kif ? r->rpool.cur->kif->pfik_ifp : NULL; } else { if (!PF_AZERO(&s->rt_addr, AF_INET)) dst->sin_addr.s_addr = s->rt_addr.v4.s_addr; ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL; } } if (ifp == NULL) goto bad; if (oifp != ifp) { #ifdef __FreeBSD__ PF_UNLOCK(); if (pf_test(PF_OUT, ifp, &m0, NULL, NULL) != PF_PASS) { PF_LOCK(); goto bad; } else if (m0 == NULL) { PF_LOCK(); goto done; } PF_LOCK(); #else if (pf_test(PF_OUT, ifp, &m0, NULL) != PF_PASS) goto bad; else if (m0 == NULL) goto done; #endif if (m0->m_len < sizeof(struct ip)) { DPFPRINTF(PF_DEBUG_URGENT, ("pf_route: m0->m_len < sizeof(struct ip)\n")); goto bad; } ip = mtod(m0, struct ip *); } #ifdef __FreeBSD__ /* Copied from FreeBSD 5.1-CURRENT ip_output. */ m0->m_pkthdr.csum_flags |= CSUM_IP; sw_csum = m0->m_pkthdr.csum_flags & ~ifp->if_hwassist; if (sw_csum & CSUM_DELAY_DATA) { /* * XXX: in_delayed_cksum assumes HBO for ip->ip_len (at least) */ NTOHS(ip->ip_len); NTOHS(ip->ip_off); /* XXX: needed? */ in_delayed_cksum(m0); HTONS(ip->ip_len); HTONS(ip->ip_off); sw_csum &= ~CSUM_DELAY_DATA; } m0->m_pkthdr.csum_flags &= ifp->if_hwassist; if (ntohs(ip->ip_len) <= ifp->if_mtu || (ifp->if_hwassist & CSUM_FRAGMENT && ((ip->ip_off & htons(IP_DF)) == 0))) { /* * ip->ip_len = htons(ip->ip_len); * ip->ip_off = htons(ip->ip_off); */ ip->ip_sum = 0; if (sw_csum & CSUM_DELAY_IP) { /* From KAME */ if (ip->ip_v == IPVERSION && (ip->ip_hl << 2) == sizeof(*ip)) { ip->ip_sum = in_cksum_hdr(ip); } else { ip->ip_sum = in_cksum(m0, ip->ip_hl << 2); } } PF_UNLOCK(); error = (*ifp->if_output)(ifp, m0, sintosa(dst), ro->ro_rt); PF_LOCK(); goto done; } #else /* Copied from ip_output. */ #ifdef IPSEC /* * If deferred crypto processing is needed, check that the * interface supports it. */ if ((mtag = m_tag_find(m0, PACKET_TAG_IPSEC_OUT_CRYPTO_NEEDED, NULL)) != NULL && (ifp->if_capabilities & IFCAP_IPSEC) == 0) { /* Notify IPsec to do its own crypto. */ ipsp_skipcrypto_unmark((struct tdb_ident *)(mtag + 1)); goto bad; } #endif /* IPSEC */ /* Catch routing changes wrt. hardware checksumming for TCP or UDP. */ if (m0->m_pkthdr.csum & M_TCPV4_CSUM_OUT) { if (!(ifp->if_capabilities & IFCAP_CSUM_TCPv4) || ifp->if_bridge != NULL) { in_delayed_cksum(m0); m0->m_pkthdr.csum &= ~M_TCPV4_CSUM_OUT; /* Clear */ } } else if (m0->m_pkthdr.csum & M_UDPV4_CSUM_OUT) { if (!(ifp->if_capabilities & IFCAP_CSUM_UDPv4) || ifp->if_bridge != NULL) { in_delayed_cksum(m0); m0->m_pkthdr.csum &= ~M_UDPV4_CSUM_OUT; /* Clear */ } } if (ntohs(ip->ip_len) <= ifp->if_mtu) { if ((ifp->if_capabilities & IFCAP_CSUM_IPv4) && ifp->if_bridge == NULL) { m0->m_pkthdr.csum |= M_IPV4_CSUM_OUT; ipstat.ips_outhwcsum++; } else { ip->ip_sum = 0; ip->ip_sum = in_cksum(m0, ip->ip_hl << 2); } /* Update relevant hardware checksum stats for TCP/UDP */ if (m0->m_pkthdr.csum & M_TCPV4_CSUM_OUT) tcpstat.tcps_outhwcsum++; else if (m0->m_pkthdr.csum & M_UDPV4_CSUM_OUT) udpstat.udps_outhwcsum++; error = (*ifp->if_output)(ifp, m0, sintosa(dst), NULL); goto done; } #endif /* * Too large for interface; fragment if possible. * Must be able to put at least 8 bytes per fragment. */ if (ip->ip_off & htons(IP_DF)) { ipstat.ips_cantfrag++; if (r->rt != PF_DUPTO) { #ifdef __FreeBSD__ /* icmp_error() expects host byte ordering */ NTOHS(ip->ip_len); NTOHS(ip->ip_off); PF_UNLOCK(); icmp_error(m0, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG, 0, ifp->if_mtu); PF_LOCK(); #else icmp_error(m0, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG, 0, ifp); #endif goto done; } else goto bad; } m1 = m0; #ifdef __FreeBSD__ /* * XXX: is cheaper + less error prone than own function */ NTOHS(ip->ip_len); NTOHS(ip->ip_off); error = ip_fragment(ip, &m0, ifp->if_mtu, ifp->if_hwassist, sw_csum); #else error = ip_fragment(m0, ifp, ifp->if_mtu); #endif if (error) { #ifndef __FreeBSD__ /* ip_fragment does not do m_freem() on FreeBSD */ m0 = NULL; #endif goto bad; } for (m0 = m1; m0; m0 = m1) { m1 = m0->m_nextpkt; m0->m_nextpkt = 0; #ifdef __FreeBSD__ if (error == 0) { PF_UNLOCK(); error = (*ifp->if_output)(ifp, m0, sintosa(dst), NULL); PF_LOCK(); } else #else if (error == 0) error = (*ifp->if_output)(ifp, m0, sintosa(dst), NULL); else #endif m_freem(m0); } if (error == 0) ipstat.ips_fragmented++; done: if (r->rt != PF_DUPTO) *m = NULL; if (ro == &iproute && ro->ro_rt) RTFREE(ro->ro_rt); return; bad: m_freem(m0); goto done; } #endif /* INET */ #ifdef INET6 void pf_route6(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, struct pf_state *s) { struct mbuf *m0; struct m_tag *mtag; struct route_in6 ip6route; struct route_in6 *ro; struct sockaddr_in6 *dst; struct ip6_hdr *ip6; struct ifnet *ifp = NULL; struct pf_addr naddr; struct pf_src_node *sn = NULL; int error = 0; if (m == NULL || *m == NULL || r == NULL || (dir != PF_IN && dir != PF_OUT) || oifp == NULL) panic("pf_route6: invalid parameters"); if ((mtag = m_tag_find(*m, PACKET_TAG_PF_ROUTED, NULL)) == NULL) { if ((mtag = m_tag_get(PACKET_TAG_PF_ROUTED, 1, M_NOWAIT)) == NULL) { m0 = *m; *m = NULL; goto bad; } *(char *)(mtag + 1) = 1; m_tag_prepend(*m, mtag); } else { if (*(char *)(mtag + 1) > 3) { m0 = *m; *m = NULL; goto bad; } (*(char *)(mtag + 1))++; } if (r->rt == PF_DUPTO) { #ifdef __FreeBSD__ if ((m0 = m_dup(*m, M_DONTWAIT)) == NULL) #else if ((m0 = m_copym2(*m, 0, M_COPYALL, M_NOWAIT)) == NULL) #endif return; } else { if ((r->rt == PF_REPLYTO) == (r->direction == dir)) return; m0 = *m; } if (m0->m_len < sizeof(struct ip6_hdr)) { DPFPRINTF(PF_DEBUG_URGENT, ("pf_route6: m0->m_len < sizeof(struct ip6_hdr)\n")); goto bad; } ip6 = mtod(m0, struct ip6_hdr *); ro = &ip6route; bzero((caddr_t)ro, sizeof(*ro)); dst = (struct sockaddr_in6 *)&ro->ro_dst; dst->sin6_family = AF_INET6; dst->sin6_len = sizeof(*dst); dst->sin6_addr = ip6->ip6_dst; /* Cheat. */ if (r->rt == PF_FASTROUTE) { #ifdef __FreeBSD__ m0->m_flags |= M_SKIP_FIREWALL; PF_UNLOCK(); ip6_output(m0, NULL, NULL, 0, NULL, NULL, NULL); PF_LOCK(); #else mtag = m_tag_get(PACKET_TAG_PF_GENERATED, 0, M_NOWAIT); if (mtag == NULL) goto bad; m_tag_prepend(m0, mtag); ip6_output(m0, NULL, NULL, 0, NULL, NULL); #endif return; } if (TAILQ_EMPTY(&r->rpool.list)) { DPFPRINTF(PF_DEBUG_URGENT, ("pf_route6: TAILQ_EMPTY(&r->rpool.list)\n")); goto bad; } if (s == NULL) { pf_map_addr(AF_INET6, r, (struct pf_addr *)&ip6->ip6_src, &naddr, NULL, &sn); if (!PF_AZERO(&naddr, AF_INET6)) PF_ACPY((struct pf_addr *)&dst->sin6_addr, &naddr, AF_INET6); ifp = r->rpool.cur->kif ? r->rpool.cur->kif->pfik_ifp : NULL; } else { if (!PF_AZERO(&s->rt_addr, AF_INET6)) PF_ACPY((struct pf_addr *)&dst->sin6_addr, &s->rt_addr, AF_INET6); ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL; } if (ifp == NULL) goto bad; if (oifp != ifp) { #ifdef __FreeBSD__ PF_UNLOCK(); if (pf_test6(PF_OUT, ifp, &m0, NULL, NULL) != PF_PASS) { PF_LOCK(); goto bad; } else if (m0 == NULL) { PF_LOCK(); goto done; } PF_LOCK(); #else if (pf_test6(PF_OUT, ifp, &m0, NULL) != PF_PASS) goto bad; else if (m0 == NULL) goto done; #endif if (m0->m_len < sizeof(struct ip6_hdr)) { DPFPRINTF(PF_DEBUG_URGENT, ("pf_route6: m0->m_len < sizeof(struct ip6_hdr)\n")); goto bad; } ip6 = mtod(m0, struct ip6_hdr *); } /* * If the packet is too large for the outgoing interface, * send back an icmp6 error. */ if (IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr)) dst->sin6_addr.s6_addr16[1] = htons(ifp->if_index); if ((u_long)m0->m_pkthdr.len <= ifp->if_mtu) { #ifdef __FreeBSD__ PF_UNLOCK(); #endif error = nd6_output(ifp, ifp, m0, dst, NULL); #ifdef __FreeBSD__ PF_LOCK(); #endif } else { in6_ifstat_inc(ifp, ifs6_in_toobig); #ifdef __FreeBSD__ if (r->rt != PF_DUPTO) { PF_UNLOCK(); icmp6_error(m0, ICMP6_PACKET_TOO_BIG, 0, ifp->if_mtu); PF_LOCK(); } else #else if (r->rt != PF_DUPTO) icmp6_error(m0, ICMP6_PACKET_TOO_BIG, 0, ifp->if_mtu); else #endif goto bad; } done: if (r->rt != PF_DUPTO) *m = NULL; return; bad: m_freem(m0); goto done; } #endif /* INET6 */ #ifdef __FreeBSD__ /* * FreeBSD supports cksum offloads for the following drivers. * em(4), fxp(4), ixgb(4), lge(4), ndis(4), nge(4), re(4), * ti(4), txp(4), xl(4) * * CSUM_DATA_VALID | CSUM_PSEUDO_HDR : * network driver performed cksum including pseudo header, need to verify * csum_data * CSUM_DATA_VALID : * network driver performed cksum, needs to additional pseudo header * cksum computation with partial csum_data(i.e. lack of H/W support for * pseudo header, for instance hme(4), sk(4) and possibly gem(4)) * * After validating the cksum of packet, set both flag CSUM_DATA_VALID and * CSUM_PSEUDO_HDR in order to avoid recomputation of the cksum in upper * TCP/UDP layer. * Also, set csum_data to 0xffff to force cksum validation. */ int pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p, sa_family_t af) { u_int16_t sum = 0; int hw_assist = 0; struct ip *ip; if (off < sizeof(struct ip) || len < sizeof(struct udphdr)) return (1); if (m->m_pkthdr.len < off + len) return (1); switch (p) { case IPPROTO_TCP: if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) { if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) { sum = m->m_pkthdr.csum_data; } else { ip = mtod(m, struct ip *); sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htonl((u_short)len + m->m_pkthdr.csum_data + IPPROTO_TCP)); } sum ^= 0xffff; ++hw_assist; } break; case IPPROTO_UDP: if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) { if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) { sum = m->m_pkthdr.csum_data; } else { ip = mtod(m, struct ip *); sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htonl((u_short)len + m->m_pkthdr.csum_data + IPPROTO_UDP)); } sum ^= 0xffff; ++hw_assist; } break; case IPPROTO_ICMP: #ifdef INET6 case IPPROTO_ICMPV6: #endif /* INET6 */ break; default: return (1); } if (!hw_assist) { switch (af) { case AF_INET: if (p == IPPROTO_ICMP) { if (m->m_len < off) return (1); m->m_data += off; m->m_len -= off; sum = in_cksum(m, len); m->m_data -= off; m->m_len += off; } else { if (m->m_len < sizeof(struct ip)) return (1); sum = in4_cksum(m, p, off, len); } break; #ifdef INET6 case AF_INET6: if (m->m_len < sizeof(struct ip6_hdr)) return (1); sum = in6_cksum(m, p, off, len); break; #endif /* INET6 */ default: return (1); } } if (sum) { switch (p) { case IPPROTO_TCP: tcpstat.tcps_rcvbadsum++; break; case IPPROTO_UDP: udpstat.udps_badsum++; break; case IPPROTO_ICMP: icmpstat.icps_checksum++; break; #ifdef INET6 case IPPROTO_ICMPV6: icmp6stat.icp6s_checksum++; break; #endif /* INET6 */ } return (1); } else { if (p == IPPROTO_TCP || p == IPPROTO_UDP) { m->m_pkthdr.csum_flags |= (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); m->m_pkthdr.csum_data = 0xffff; } } return (0); } #else /* * check protocol (tcp/udp/icmp/icmp6) checksum and set mbuf flag * off is the offset where the protocol header starts * len is the total length of protocol header plus payload * returns 0 when the checksum is valid, otherwise returns 1. */ int pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p, sa_family_t af) { u_int16_t flag_ok, flag_bad; u_int16_t sum; switch (p) { case IPPROTO_TCP: flag_ok = M_TCP_CSUM_IN_OK; flag_bad = M_TCP_CSUM_IN_BAD; break; case IPPROTO_UDP: flag_ok = M_UDP_CSUM_IN_OK; flag_bad = M_UDP_CSUM_IN_BAD; break; case IPPROTO_ICMP: #ifdef INET6 case IPPROTO_ICMPV6: #endif /* INET6 */ flag_ok = flag_bad = 0; break; default: return (1); } if (m->m_pkthdr.csum & flag_ok) return (0); if (m->m_pkthdr.csum & flag_bad) return (1); if (off < sizeof(struct ip) || len < sizeof(struct udphdr)) return (1); if (m->m_pkthdr.len < off + len) return (1); switch (af) { #ifdef INET case AF_INET: if (p == IPPROTO_ICMP) { if (m->m_len < off) return (1); m->m_data += off; m->m_len -= off; sum = in_cksum(m, len); m->m_data -= off; m->m_len += off; } else { if (m->m_len < sizeof(struct ip)) return (1); sum = in4_cksum(m, p, off, len); } break; #endif /* INET */ #ifdef INET6 case AF_INET6: if (m->m_len < sizeof(struct ip6_hdr)) return (1); sum = in6_cksum(m, p, off, len); break; #endif /* INET6 */ default: return (1); } if (sum) { m->m_pkthdr.csum |= flag_bad; switch (p) { case IPPROTO_TCP: tcpstat.tcps_rcvbadsum++; break; case IPPROTO_UDP: udpstat.udps_badsum++; break; case IPPROTO_ICMP: icmpstat.icps_checksum++; break; #ifdef INET6 case IPPROTO_ICMPV6: icmp6stat.icp6s_checksum++; break; #endif /* INET6 */ } return (1); } m->m_pkthdr.csum |= flag_ok; return (0); } #endif static int pf_add_mbuf_tag(struct mbuf *m, u_int tag) { struct m_tag *mtag; if (m_tag_find(m, tag, NULL) != NULL) return (0); mtag = m_tag_get(tag, 0, M_NOWAIT); if (mtag == NULL) return (1); m_tag_prepend(m, mtag); return (0); } #ifdef INET int #ifdef __FreeBSD__ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, struct ether_header *eh, struct inpcb *inp) #else pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, struct ether_header *eh) #endif { struct pfi_kif *kif; u_short action, reason = 0, log = 0; struct mbuf *m = *m0; struct ip *h = NULL; /* make the compiler happy */ struct pf_rule *a = NULL, *r = &pf_default_rule, *tr, *nr; struct pf_state *s = NULL; struct pf_ruleset *ruleset = NULL; struct pf_pdesc pd; int off, dirndx, pqid = 0; #ifdef __FreeBSD__ PF_LOCK(); #endif if (!pf_status.running || #ifdef __FreeBSD__ (m->m_flags & M_SKIP_FIREWALL)) { PF_UNLOCK(); #else (m_tag_find(m, PACKET_TAG_PF_GENERATED, NULL) != NULL)) { #endif return (PF_PASS); } #ifdef __FreeBSD__ /* XXX_IMPORT: later */ #else if (ifp->if_type == IFT_CARP && ifp->if_carpdev) ifp = ifp->if_carpdev; #endif kif = pfi_index2kif[ifp->if_index]; if (kif == NULL) { #ifdef __FreeBSD__ PF_UNLOCK(); #endif DPFPRINTF(PF_DEBUG_URGENT, ("pf_test: kif == NULL, if_xname %s\n", ifp->if_xname)); return (PF_DROP); } if (kif->pfik_flags & PFI_IFLAG_SKIP) { #ifdef __FreeBSD__ PF_UNLOCK(); #endif return (PF_PASS); } #ifdef __FreeBSD__ M_ASSERTPKTHDR(m); #else #ifdef DIAGNOSTIC if ((m->m_flags & M_PKTHDR) == 0) panic("non-M_PKTHDR is passed to pf_test"); #endif /* DIAGNOSTIC */ #endif /* __FreeBSD__ */ memset(&pd, 0, sizeof(pd)); if (m->m_pkthdr.len < (int)sizeof(*h)) { action = PF_DROP; REASON_SET(&reason, PFRES_SHORT); log = 1; goto done; } /* We do IP header normalization and packet reassembly here */ if (pf_normalize_ip(m0, dir, kif, &reason, &pd) != PF_PASS) { action = PF_DROP; goto done; } m = *m0; h = mtod(m, struct ip *); off = h->ip_hl << 2; if (off < (int)sizeof(*h)) { action = PF_DROP; REASON_SET(&reason, PFRES_SHORT); log = 1; goto done; } pd.src = (struct pf_addr *)&h->ip_src; pd.dst = (struct pf_addr *)&h->ip_dst; PF_ACPY(&pd.baddr, dir == PF_OUT ? pd.src : pd.dst, AF_INET); pd.ip_sum = &h->ip_sum; pd.proto = h->ip_p; pd.af = AF_INET; pd.tos = h->ip_tos; pd.tot_len = ntohs(h->ip_len); pd.eh = eh; /* handle fragments that didn't get reassembled by normalization */ if (h->ip_off & htons(IP_MF | IP_OFFMASK)) { action = pf_test_fragment(&r, dir, kif, m, h, &pd, &a, &ruleset); goto done; } switch (h->ip_p) { case IPPROTO_TCP: { struct tcphdr th; pd.hdr.tcp = &th; if (!pf_pull_hdr(m, off, &th, sizeof(th), &action, &reason, AF_INET)) { log = action != PF_PASS; goto done; } if (dir == PF_IN && pf_check_proto_cksum(m, off, ntohs(h->ip_len) - off, IPPROTO_TCP, AF_INET)) { action = PF_DROP; goto done; } pd.p_len = pd.tot_len - off - (th.th_off << 2); if ((th.th_flags & TH_ACK) && pd.p_len == 0) pqid = 1; action = pf_normalize_tcp(dir, kif, m, 0, off, h, &pd); if (action == PF_DROP) goto done; action = pf_test_state_tcp(&s, dir, kif, m, off, h, &pd, &reason); if (action == PF_PASS) { #if NPFSYNC pfsync_update_state(s); #endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) #ifdef __FreeBSD__ action = pf_test_tcp(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, NULL, inp); #else action = pf_test_tcp(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, &ipintrq); #endif break; } case IPPROTO_UDP: { struct udphdr uh; pd.hdr.udp = &uh; if (!pf_pull_hdr(m, off, &uh, sizeof(uh), &action, &reason, AF_INET)) { log = action != PF_PASS; goto done; } if (dir == PF_IN && uh.uh_sum && pf_check_proto_cksum(m, off, ntohs(h->ip_len) - off, IPPROTO_UDP, AF_INET)) { action = PF_DROP; goto done; } if (uh.uh_dport == 0 || ntohs(uh.uh_ulen) > m->m_pkthdr.len - off || ntohs(uh.uh_ulen) < sizeof(struct udphdr)) { action = PF_DROP; goto done; } action = pf_test_state_udp(&s, dir, kif, m, off, h, &pd); if (action == PF_PASS) { #if NPFSYNC pfsync_update_state(s); #endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) #ifdef __FreeBSD__ action = pf_test_udp(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, NULL, inp); #else action = pf_test_udp(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, &ipintrq); #endif break; } case IPPROTO_ICMP: { struct icmp ih; pd.hdr.icmp = &ih; if (!pf_pull_hdr(m, off, &ih, ICMP_MINLEN, &action, &reason, AF_INET)) { log = action != PF_PASS; goto done; } if (dir == PF_IN && pf_check_proto_cksum(m, off, ntohs(h->ip_len) - off, IPPROTO_ICMP, AF_INET)) { action = PF_DROP; goto done; } action = pf_test_state_icmp(&s, dir, kif, m, off, h, &pd, &reason); if (action == PF_PASS) { #if NPFSYNC pfsync_update_state(s); #endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) #ifdef __FreeBSD__ action = pf_test_icmp(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, NULL); #else action = pf_test_icmp(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, &ipintrq); #endif break; } default: action = pf_test_state_other(&s, dir, kif, &pd); if (action == PF_PASS) { #if NPFSYNC pfsync_update_state(s); #endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) #ifdef __FreeBSD__ action = pf_test_other(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, NULL); #else action = pf_test_other(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, &ipintrq); #endif break; } done: if (action == PF_PASS && h->ip_hl > 5 && !((s && s->allow_opts) || r->allow_opts)) { action = PF_DROP; REASON_SET(&reason, PFRES_IPOPTIONS); log = 1; DPFPRINTF(PF_DEBUG_MISC, ("pf: dropping packet with ip options\n")); } if (s && s->tag) pf_tag_packet(m, pf_get_tag(m), s->tag); #ifdef ALTQ if (action == PF_PASS && r->qid) { struct m_tag *mtag; struct altq_tag *atag; mtag = m_tag_get(PACKET_TAG_PF_QID, sizeof(*atag), M_NOWAIT); if (mtag != NULL) { atag = (struct altq_tag *)(mtag + 1); if (pqid || pd.tos == IPTOS_LOWDELAY) atag->qid = r->pqid; else atag->qid = r->qid; /* add hints for ecn */ atag->af = AF_INET; atag->hdr = h; m_tag_prepend(m, mtag); } } #endif /* ALTQ */ /* * connections redirected to loopback should not match sockets * bound specifically to loopback due to security implications, * see tcp_input() and in_pcblookup_listen(). */ if (dir == PF_IN && action == PF_PASS && (pd.proto == IPPROTO_TCP || pd.proto == IPPROTO_UDP) && s != NULL && s->nat_rule.ptr != NULL && (s->nat_rule.ptr->action == PF_RDR || s->nat_rule.ptr->action == PF_BINAT) && (ntohl(pd.dst->v4.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET && pf_add_mbuf_tag(m, PACKET_TAG_PF_TRANSLATE_LOCALHOST)) { action = PF_DROP; REASON_SET(&reason, PFRES_MEMORY); } if (log) PFLOG_PACKET(kif, h, m, AF_INET, dir, reason, r, a, ruleset); kif->pfik_bytes[0][dir == PF_OUT][action != PF_PASS] += pd.tot_len; kif->pfik_packets[0][dir == PF_OUT][action != PF_PASS]++; if (action == PF_PASS || r->action == PF_DROP) { r->packets++; r->bytes += pd.tot_len; if (a != NULL) { a->packets++; a->bytes += pd.tot_len; } if (s != NULL) { dirndx = (dir == s->direction) ? 0 : 1; s->packets[dirndx]++; s->bytes[dirndx] += pd.tot_len; if (s->nat_rule.ptr != NULL) { s->nat_rule.ptr->packets++; s->nat_rule.ptr->bytes += pd.tot_len; } if (s->src_node != NULL) { s->src_node->packets++; s->src_node->bytes += pd.tot_len; } if (s->nat_src_node != NULL) { s->nat_src_node->packets++; s->nat_src_node->bytes += pd.tot_len; } } tr = r; nr = (s != NULL) ? s->nat_rule.ptr : pd.nat_rule; if (nr != NULL) { struct pf_addr *x; /* * XXX: we need to make sure that the addresses * passed to pfr_update_stats() are the same than * the addresses used during matching (pfr_match) */ if (r == &pf_default_rule) { tr = nr; x = (s == NULL || s->direction == dir) ? &pd.baddr : &pd.naddr; } else x = (s == NULL || s->direction == dir) ? &pd.naddr : &pd.baddr; if (x == &pd.baddr || s == NULL) { /* we need to change the address */ if (dir == PF_OUT) pd.src = x; else pd.dst = x; } } if (tr->src.addr.type == PF_ADDR_TABLE) pfr_update_stats(tr->src.addr.p.tbl, (s == NULL || s->direction == dir) ? pd.src : pd.dst, pd.af, pd.tot_len, dir == PF_OUT, r->action == PF_PASS, tr->src.neg); if (tr->dst.addr.type == PF_ADDR_TABLE) pfr_update_stats(tr->dst.addr.p.tbl, (s == NULL || s->direction == dir) ? pd.dst : pd.src, pd.af, pd.tot_len, dir == PF_OUT, r->action == PF_PASS, tr->dst.neg); } if (action == PF_SYNPROXY_DROP) { m_freem(*m0); *m0 = NULL; action = PF_PASS; } else if (r->rt) /* pf_route can free the mbuf causing *m0 to become NULL */ pf_route(m0, r, dir, ifp, s); #ifdef __FreeBSD__ PF_UNLOCK(); #endif return (action); } #endif /* INET */ #ifdef INET6 int #ifdef __FreeBSD__ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct ether_header *eh, struct inpcb *inp) #else pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct ether_header *eh) #endif { struct pfi_kif *kif; u_short action, reason = 0, log = 0; struct mbuf *m = *m0; struct ip6_hdr *h = NULL; /* make the compiler happy */ struct pf_rule *a = NULL, *r = &pf_default_rule, *tr, *nr; struct pf_state *s = NULL; struct pf_ruleset *ruleset = NULL; struct pf_pdesc pd; int off, terminal = 0, dirndx; #ifdef __FreeBSD__ PF_LOCK(); #endif if (!pf_status.running || #ifdef __FreeBSD__ (m->m_flags & M_SKIP_FIREWALL)) { PF_UNLOCK(); #else (m_tag_find(m, PACKET_TAG_PF_GENERATED, NULL) != NULL)) { #endif return (PF_PASS); } #ifdef __FreeBSD__ /* XXX_IMPORT: later */ #else if (ifp->if_type == IFT_CARP && ifp->if_carpdev) ifp = ifp->if_carpdev; #endif kif = pfi_index2kif[ifp->if_index]; if (kif == NULL) { #ifdef __FreeBSD__ PF_UNLOCK(); #endif DPFPRINTF(PF_DEBUG_URGENT, ("pf_test6: kif == NULL, if_xname %s\n", ifp->if_xname)); return (PF_DROP); } if (kif->pfik_flags & PFI_IFLAG_SKIP) { #ifdef __FreeBSD__ PF_UNLOCK(); #endif return (PF_PASS); } #ifdef __FreeBSD__ M_ASSERTPKTHDR(m); #else #ifdef DIAGNOSTIC if ((m->m_flags & M_PKTHDR) == 0) panic("non-M_PKTHDR is passed to pf_test6"); #endif /* DIAGNOSTIC */ #endif memset(&pd, 0, sizeof(pd)); if (m->m_pkthdr.len < (int)sizeof(*h)) { action = PF_DROP; REASON_SET(&reason, PFRES_SHORT); log = 1; goto done; } /* We do IP header normalization and packet reassembly here */ if (pf_normalize_ip6(m0, dir, kif, &reason, &pd) != PF_PASS) { action = PF_DROP; goto done; } m = *m0; h = mtod(m, struct ip6_hdr *); pd.src = (struct pf_addr *)&h->ip6_src; pd.dst = (struct pf_addr *)&h->ip6_dst; PF_ACPY(&pd.baddr, dir == PF_OUT ? pd.src : pd.dst, AF_INET6); pd.ip_sum = NULL; pd.af = AF_INET6; pd.tos = 0; pd.tot_len = ntohs(h->ip6_plen) + sizeof(struct ip6_hdr); pd.eh = eh; off = ((caddr_t)h - m->m_data) + sizeof(struct ip6_hdr); pd.proto = h->ip6_nxt; do { switch (pd.proto) { case IPPROTO_FRAGMENT: action = pf_test_fragment(&r, dir, kif, m, h, &pd, &a, &ruleset); if (action == PF_DROP) REASON_SET(&reason, PFRES_FRAG); goto done; case IPPROTO_AH: case IPPROTO_HOPOPTS: case IPPROTO_ROUTING: case IPPROTO_DSTOPTS: { /* get next header and header length */ struct ip6_ext opt6; if (!pf_pull_hdr(m, off, &opt6, sizeof(opt6), NULL, &reason, pd.af)) { DPFPRINTF(PF_DEBUG_MISC, ("pf: IPv6 short opt\n")); action = PF_DROP; log = 1; goto done; } if (pd.proto == IPPROTO_AH) off += (opt6.ip6e_len + 2) * 4; else off += (opt6.ip6e_len + 1) * 8; pd.proto = opt6.ip6e_nxt; /* goto the next header */ break; } default: terminal++; break; } } while (!terminal); switch (pd.proto) { case IPPROTO_TCP: { struct tcphdr th; pd.hdr.tcp = &th; if (!pf_pull_hdr(m, off, &th, sizeof(th), &action, &reason, AF_INET6)) { log = action != PF_PASS; goto done; } if (dir == PF_IN && pf_check_proto_cksum(m, off, ntohs(h->ip6_plen) - (off - sizeof(struct ip6_hdr)), IPPROTO_TCP, AF_INET6)) { action = PF_DROP; REASON_SET(&reason, PFRES_PROTCKSUM); goto done; } pd.p_len = pd.tot_len - off - (th.th_off << 2); action = pf_normalize_tcp(dir, kif, m, 0, off, h, &pd); if (action == PF_DROP) goto done; action = pf_test_state_tcp(&s, dir, kif, m, off, h, &pd, &reason); if (action == PF_PASS) { #if NPFSYNC pfsync_update_state(s); #endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) #ifdef __FreeBSD__ action = pf_test_tcp(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, NULL, inp); #else action = pf_test_tcp(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, &ip6intrq); #endif break; } case IPPROTO_UDP: { struct udphdr uh; pd.hdr.udp = &uh; if (!pf_pull_hdr(m, off, &uh, sizeof(uh), &action, &reason, AF_INET6)) { log = action != PF_PASS; goto done; } if (dir == PF_IN && uh.uh_sum && pf_check_proto_cksum(m, off, ntohs(h->ip6_plen) - (off - sizeof(struct ip6_hdr)), IPPROTO_UDP, AF_INET6)) { action = PF_DROP; REASON_SET(&reason, PFRES_PROTCKSUM); goto done; } if (uh.uh_dport == 0 || ntohs(uh.uh_ulen) > m->m_pkthdr.len - off || ntohs(uh.uh_ulen) < sizeof(struct udphdr)) { action = PF_DROP; goto done; } action = pf_test_state_udp(&s, dir, kif, m, off, h, &pd); if (action == PF_PASS) { #if NPFSYNC pfsync_update_state(s); #endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) #ifdef __FreeBSD__ action = pf_test_udp(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, NULL, inp); #else action = pf_test_udp(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, &ip6intrq); #endif break; } case IPPROTO_ICMPV6: { struct icmp6_hdr ih; pd.hdr.icmp6 = &ih; if (!pf_pull_hdr(m, off, &ih, sizeof(ih), &action, &reason, AF_INET6)) { log = action != PF_PASS; goto done; } if (dir == PF_IN && pf_check_proto_cksum(m, off, ntohs(h->ip6_plen) - (off - sizeof(struct ip6_hdr)), IPPROTO_ICMPV6, AF_INET6)) { action = PF_DROP; REASON_SET(&reason, PFRES_PROTCKSUM); goto done; } action = pf_test_state_icmp(&s, dir, kif, m, off, h, &pd, &reason); if (action == PF_PASS) { #if NPFSYNC pfsync_update_state(s); #endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) #ifdef __FreeBSD__ action = pf_test_icmp(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, NULL); #else action = pf_test_icmp(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, &ip6intrq); #endif break; } default: action = pf_test_state_other(&s, dir, kif, &pd); if (action == PF_PASS) { #if NPFSYNC pfsync_update_state(s); #endif /* NPFSYNC */ r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) #ifdef __FreeBSD__ action = pf_test_other(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, NULL); #else action = pf_test_other(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset, &ip6intrq); #endif break; } done: /* XXX handle IPv6 options, if not allowed. not implemented. */ if (s && s->tag) pf_tag_packet(m, pf_get_tag(m), s->tag); #ifdef ALTQ if (action == PF_PASS && r->qid) { struct m_tag *mtag; struct altq_tag *atag; mtag = m_tag_get(PACKET_TAG_PF_QID, sizeof(*atag), M_NOWAIT); if (mtag != NULL) { atag = (struct altq_tag *)(mtag + 1); if (pd.tos == IPTOS_LOWDELAY) atag->qid = r->pqid; else atag->qid = r->qid; /* add hints for ecn */ atag->af = AF_INET6; atag->hdr = h; m_tag_prepend(m, mtag); } } #endif /* ALTQ */ if (dir == PF_IN && action == PF_PASS && (pd.proto == IPPROTO_TCP || pd.proto == IPPROTO_UDP) && s != NULL && s->nat_rule.ptr != NULL && (s->nat_rule.ptr->action == PF_RDR || s->nat_rule.ptr->action == PF_BINAT) && IN6_IS_ADDR_LOOPBACK(&pd.dst->v6) && pf_add_mbuf_tag(m, PACKET_TAG_PF_TRANSLATE_LOCALHOST)) { action = PF_DROP; REASON_SET(&reason, PFRES_MEMORY); } if (log) PFLOG_PACKET(kif, h, m, AF_INET6, dir, reason, r, a, ruleset); kif->pfik_bytes[1][dir == PF_OUT][action != PF_PASS] += pd.tot_len; kif->pfik_packets[1][dir == PF_OUT][action != PF_PASS]++; if (action == PF_PASS || r->action == PF_DROP) { r->packets++; r->bytes += pd.tot_len; if (a != NULL) { a->packets++; a->bytes += pd.tot_len; } if (s != NULL) { dirndx = (dir == s->direction) ? 0 : 1; s->packets[dirndx]++; s->bytes[dirndx] += pd.tot_len; if (s->nat_rule.ptr != NULL) { s->nat_rule.ptr->packets++; s->nat_rule.ptr->bytes += pd.tot_len; } if (s->src_node != NULL) { s->src_node->packets++; s->src_node->bytes += pd.tot_len; } if (s->nat_src_node != NULL) { s->nat_src_node->packets++; s->nat_src_node->bytes += pd.tot_len; } } tr = r; nr = (s != NULL) ? s->nat_rule.ptr : pd.nat_rule; if (nr != NULL) { struct pf_addr *x; /* * XXX: we need to make sure that the addresses * passed to pfr_update_stats() are the same than * the addresses used during matching (pfr_match) */ if (r == &pf_default_rule) { tr = nr; x = (s == NULL || s->direction == dir) ? &pd.baddr : &pd.naddr; } else { x = (s == NULL || s->direction == dir) ? &pd.naddr : &pd.baddr; } if (x == &pd.baddr || s == NULL) { if (dir == PF_OUT) pd.src = x; else pd.dst = x; } } if (tr->src.addr.type == PF_ADDR_TABLE) pfr_update_stats(tr->src.addr.p.tbl, (s == NULL || s->direction == dir) ? pd.src : pd.dst, pd.af, pd.tot_len, dir == PF_OUT, r->action == PF_PASS, tr->src.neg); if (tr->dst.addr.type == PF_ADDR_TABLE) pfr_update_stats(tr->dst.addr.p.tbl, (s == NULL || s->direction == dir) ? pd.dst : pd.src, pd.af, pd.tot_len, dir == PF_OUT, r->action == PF_PASS, tr->dst.neg); } if (action == PF_SYNPROXY_DROP) { m_freem(*m0); *m0 = NULL; action = PF_PASS; } else if (r->rt) /* pf_route6 can free the mbuf causing *m0 to become NULL */ pf_route6(m0, r, dir, ifp, s); #ifdef __FreeBSD__ PF_UNLOCK(); #endif return (action); } #endif /* INET6 */ int pf_check_congestion(struct ifqueue *ifq) { #ifdef __FreeBSD__ /* XXX_IMPORT: later */ return (0); #else if (ifq->ifq_congestion) return (1); else return (0); #endif } Index: stable/6/sys/modules/ipfw/Makefile =================================================================== --- stable/6/sys/modules/ipfw/Makefile (revision 162447) +++ stable/6/sys/modules/ipfw/Makefile (revision 162448) @@ -1,26 +1,26 @@ # $FreeBSD$ .PATH: ${.CURDIR}/../../netinet KMOD= ipfw SRCS= ip_fw2.c ip_fw_pfil.c -SRCS+= opt_inet6.h opt_ipsec.h +SRCS+= opt_inet6.h opt_ipsec.h opt_mac.h CFLAGS+= -DIPFIREWALL # #If you want it verbose #CFLAGS+= -DIPFIREWALL_VERBOSE #CFLAGS+= -DIPFIREWALL_VERBOSE_LIMIT=100 # #If you want it to pass all packets by default #CFLAGS+= -DIPFIREWALL_DEFAULT_TO_ACCEPT # .if !defined(KERNBUILDDIR) .if !defined(NO_INET6) opt_inet6.h: echo "#define INET6 1" > ${.TARGET} .endif .endif .include Index: stable/6/sys/modules/pf/Makefile =================================================================== --- stable/6/sys/modules/pf/Makefile (revision 162447) +++ stable/6/sys/modules/pf/Makefile (revision 162448) @@ -1,26 +1,26 @@ # $FreeBSD$ .PATH: ${.CURDIR}/../../contrib/pf/net .PATH: ${.CURDIR}/../../contrib/pf/netinet KMOD= pf SRCS = pf.c pf_if.c pf_subr.c pf_osfp.c pf_ioctl.c pf_norm.c pf_table.c \ in4_cksum.c \ - opt_pf.h opt_inet.h opt_inet6.h opt_bpf.h + opt_pf.h opt_inet.h opt_inet6.h opt_bpf.h opt_mac.h CFLAGS+= -I${.CURDIR}/../../contrib/pf .if !defined(KERNBUILDDIR) opt_inet.h: echo "#define INET 1" > opt_inet.h .if !defined(NO_INET6) opt_inet6.h: echo "#define INET6 1" > opt_inet6.h .endif opt_bpf.h: echo "#define DEV_BPF 1" > opt_bpf.h .endif .include Index: stable/6/sys/netinet/ip_fw2.c =================================================================== --- stable/6/sys/netinet/ip_fw2.c (revision 162447) +++ stable/6/sys/netinet/ip_fw2.c (revision 162448) @@ -1,4383 +1,4398 @@ /*- * Copyright (c) 2002 Luigi Rizzo, Universita` di Pisa * * 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$ */ #define DEB(x) #define DDB(x) x /* * Implement IP packet firewall (new version) */ #if !defined(KLD_MODULE) #include "opt_ipfw.h" #include "opt_ip6fw.h" #include "opt_ipdn.h" #include "opt_inet.h" #ifndef INET #error IPFIREWALL requires INET. #endif /* INET */ #endif #include "opt_inet6.h" #include "opt_ipsec.h" +#include "opt_mac.h" #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef IPSEC #include #endif #include #include #ifdef INET6 #include #endif #include /* XXX for ETHERTYPE_IP */ #include /* XXX for in_cksum */ /* * set_disable contains one bit per set value (0..31). * If the bit is set, all rules with the corresponding set * are disabled. Set RESVD_SET(31) is reserved for the default rule * and rules that are not deleted by the flush command, * and CANNOT be disabled. * Rules in set RESVD_SET can only be deleted explicitly. */ static u_int32_t set_disable; static int fw_verbose; static int verbose_limit; static struct callout ipfw_timeout; static uma_zone_t ipfw_dyn_rule_zone; #define IPFW_DEFAULT_RULE 65535 /* * Data structure to cache our ucred related * information. This structure only gets used if * the user specified UID/GID based constraints in * a firewall rule. */ struct ip_fw_ugid { gid_t fw_groups[NGROUPS]; int fw_ngroups; uid_t fw_uid; int fw_prid; }; #define IPFW_TABLES_MAX 128 struct ip_fw_chain { struct ip_fw *rules; /* list of rules */ struct ip_fw *reap; /* list of rules to reap */ struct radix_node_head *tables[IPFW_TABLES_MAX]; struct mtx mtx; /* lock guarding rule list */ int busy_count; /* busy count for rw locks */ int want_write; struct cv cv; }; #define IPFW_LOCK_INIT(_chain) \ mtx_init(&(_chain)->mtx, "IPFW static rules", NULL, \ MTX_DEF | MTX_RECURSE) #define IPFW_LOCK_DESTROY(_chain) mtx_destroy(&(_chain)->mtx) #define IPFW_WLOCK_ASSERT(_chain) do { \ mtx_assert(&(_chain)->mtx, MA_OWNED); \ NET_ASSERT_GIANT(); \ } while (0) static __inline void IPFW_RLOCK(struct ip_fw_chain *chain) { mtx_lock(&chain->mtx); chain->busy_count++; mtx_unlock(&chain->mtx); } static __inline void IPFW_RUNLOCK(struct ip_fw_chain *chain) { mtx_lock(&chain->mtx); chain->busy_count--; if (chain->busy_count == 0 && chain->want_write) cv_signal(&chain->cv); mtx_unlock(&chain->mtx); } static __inline void IPFW_WLOCK(struct ip_fw_chain *chain) { mtx_lock(&chain->mtx); chain->want_write++; while (chain->busy_count > 0) cv_wait(&chain->cv, &chain->mtx); } static __inline void IPFW_WUNLOCK(struct ip_fw_chain *chain) { chain->want_write--; cv_signal(&chain->cv); mtx_unlock(&chain->mtx); } /* * list of rules for layer 3 */ static struct ip_fw_chain layer3_chain; MALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's"); MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables"); struct table_entry { struct radix_node rn[2]; struct sockaddr_in addr, mask; u_int32_t value; }; static int fw_debug = 1; static int autoinc_step = 100; /* bounded to 1..1000 in add_rule() */ #ifdef SYSCTL_NODE SYSCTL_NODE(_net_inet_ip, OID_AUTO, fw, CTLFLAG_RW, 0, "Firewall"); SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, enable, CTLFLAG_RW | CTLFLAG_SECURE3, &fw_enable, 0, "Enable ipfw"); SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, autoinc_step, CTLFLAG_RW, &autoinc_step, 0, "Rule number autincrement step"); SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, one_pass, CTLFLAG_RW | CTLFLAG_SECURE3, &fw_one_pass, 0, "Only do a single pass through ipfw when using dummynet(4)"); SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, debug, CTLFLAG_RW, &fw_debug, 0, "Enable printing of debug ip_fw statements"); SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, verbose, CTLFLAG_RW | CTLFLAG_SECURE3, &fw_verbose, 0, "Log matches to ipfw rules"); SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, verbose_limit, CTLFLAG_RW, &verbose_limit, 0, "Set upper limit of matches of ipfw rules logged"); /* * Description of dynamic rules. * * Dynamic rules are stored in lists accessed through a hash table * (ipfw_dyn_v) whose size is curr_dyn_buckets. This value can * be modified through the sysctl variable dyn_buckets which is * updated when the table becomes empty. * * XXX currently there is only one list, ipfw_dyn. * * When a packet is received, its address fields are first masked * with the mask defined for the rule, then hashed, then matched * against the entries in the corresponding list. * Dynamic rules can be used for different purposes: * + stateful rules; * + enforcing limits on the number of sessions; * + in-kernel NAT (not implemented yet) * * The lifetime of dynamic rules is regulated by dyn_*_lifetime, * measured in seconds and depending on the flags. * * The total number of dynamic rules is stored in dyn_count. * The max number of dynamic rules is dyn_max. When we reach * the maximum number of rules we do not create anymore. This is * done to avoid consuming too much memory, but also too much * time when searching on each packet (ideally, we should try instead * to put a limit on the length of the list on each bucket...). * * Each dynamic rule holds a pointer to the parent ipfw rule so * we know what action to perform. Dynamic rules are removed when * the parent rule is deleted. XXX we should make them survive. * * There are some limitations with dynamic rules -- we do not * obey the 'randomized match', and we do not do multiple * passes through the firewall. XXX check the latter!!! */ static ipfw_dyn_rule **ipfw_dyn_v = NULL; static u_int32_t dyn_buckets = 256; /* must be power of 2 */ static u_int32_t curr_dyn_buckets = 256; /* must be power of 2 */ static struct mtx ipfw_dyn_mtx; /* mutex guarding dynamic rules */ #define IPFW_DYN_LOCK_INIT() \ mtx_init(&ipfw_dyn_mtx, "IPFW dynamic rules", NULL, MTX_DEF) #define IPFW_DYN_LOCK_DESTROY() mtx_destroy(&ipfw_dyn_mtx) #define IPFW_DYN_LOCK() mtx_lock(&ipfw_dyn_mtx) #define IPFW_DYN_UNLOCK() mtx_unlock(&ipfw_dyn_mtx) #define IPFW_DYN_LOCK_ASSERT() mtx_assert(&ipfw_dyn_mtx, MA_OWNED) /* * Timeouts for various events in handing dynamic rules. */ static u_int32_t dyn_ack_lifetime = 300; static u_int32_t dyn_syn_lifetime = 20; static u_int32_t dyn_fin_lifetime = 1; static u_int32_t dyn_rst_lifetime = 1; static u_int32_t dyn_udp_lifetime = 10; static u_int32_t dyn_short_lifetime = 5; /* * Keepalives are sent if dyn_keepalive is set. They are sent every * dyn_keepalive_period seconds, in the last dyn_keepalive_interval * seconds of lifetime of a rule. * dyn_rst_lifetime and dyn_fin_lifetime should be strictly lower * than dyn_keepalive_period. */ static u_int32_t dyn_keepalive_interval = 20; static u_int32_t dyn_keepalive_period = 5; static u_int32_t dyn_keepalive = 1; /* do send keepalives */ static u_int32_t static_count; /* # of static rules */ static u_int32_t static_len; /* size in bytes of static rules */ static u_int32_t dyn_count; /* # of dynamic rules */ static u_int32_t dyn_max = 4096; /* max # of dynamic rules */ SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_buckets, CTLFLAG_RW, &dyn_buckets, 0, "Number of dyn. buckets"); SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, curr_dyn_buckets, CTLFLAG_RD, &curr_dyn_buckets, 0, "Current Number of dyn. buckets"); SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_count, CTLFLAG_RD, &dyn_count, 0, "Number of dyn. rules"); SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_max, CTLFLAG_RW, &dyn_max, 0, "Max number of dyn. rules"); SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, static_count, CTLFLAG_RD, &static_count, 0, "Number of static rules"); SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_ack_lifetime, CTLFLAG_RW, &dyn_ack_lifetime, 0, "Lifetime of dyn. rules for acks"); SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_syn_lifetime, CTLFLAG_RW, &dyn_syn_lifetime, 0, "Lifetime of dyn. rules for syn"); SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_fin_lifetime, CTLFLAG_RW, &dyn_fin_lifetime, 0, "Lifetime of dyn. rules for fin"); SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_rst_lifetime, CTLFLAG_RW, &dyn_rst_lifetime, 0, "Lifetime of dyn. rules for rst"); SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_udp_lifetime, CTLFLAG_RW, &dyn_udp_lifetime, 0, "Lifetime of dyn. rules for UDP"); SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_short_lifetime, CTLFLAG_RW, &dyn_short_lifetime, 0, "Lifetime of dyn. rules for other situations"); SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_keepalive, CTLFLAG_RW, &dyn_keepalive, 0, "Enable keepalives for dyn. rules"); #ifdef INET6 /* * IPv6 specific variables */ SYSCTL_DECL(_net_inet6_ip6); static struct sysctl_ctx_list ip6_fw_sysctl_ctx; static struct sysctl_oid *ip6_fw_sysctl_tree; #endif /* INET6 */ #endif /* SYSCTL_NODE */ static int fw_deny_unknown_exthdrs = 1; /* * L3HDR maps an ipv4 pointer into a layer3 header pointer of type T * Other macros just cast void * into the appropriate type */ #define L3HDR(T, ip) ((T *)((u_int32_t *)(ip) + (ip)->ip_hl)) #define TCP(p) ((struct tcphdr *)(p)) #define UDP(p) ((struct udphdr *)(p)) #define ICMP(p) ((struct icmphdr *)(p)) #define ICMP6(p) ((struct icmp6_hdr *)(p)) static __inline int icmptype_match(struct icmphdr *icmp, ipfw_insn_u32 *cmd) { int type = icmp->icmp_type; return (type <= ICMP_MAXTYPE && (cmd->d[0] & (1<icmp_type; return (type <= ICMP_MAXTYPE && (TT & (1<arg1 or cmd->d[0]. * * We scan options and store the bits we find set. We succeed if * * (want_set & ~bits) == 0 && (want_clear & ~bits) == want_clear * * The code is sometimes optimized not to store additional variables. */ static int flags_match(ipfw_insn *cmd, u_int8_t bits) { u_char want_clear; bits = ~bits; if ( ((cmd->arg1 & 0xff) & bits) != 0) return 0; /* some bits we want set were clear */ want_clear = (cmd->arg1 >> 8) & 0xff; if ( (want_clear & bits) != want_clear) return 0; /* some bits we want clear were set */ return 1; } static int ipopts_match(struct ip *ip, ipfw_insn *cmd) { int optlen, bits = 0; u_char *cp = (u_char *)(ip + 1); int x = (ip->ip_hl << 2) - sizeof (struct ip); for (; x > 0; x -= optlen, cp += optlen) { int opt = cp[IPOPT_OPTVAL]; if (opt == IPOPT_EOL) break; if (opt == IPOPT_NOP) optlen = 1; else { optlen = cp[IPOPT_OLEN]; if (optlen <= 0 || optlen > x) return 0; /* invalid or truncated */ } switch (opt) { default: break; case IPOPT_LSRR: bits |= IP_FW_IPOPT_LSRR; break; case IPOPT_SSRR: bits |= IP_FW_IPOPT_SSRR; break; case IPOPT_RR: bits |= IP_FW_IPOPT_RR; break; case IPOPT_TS: bits |= IP_FW_IPOPT_TS; break; } } return (flags_match(cmd, bits)); } static int tcpopts_match(struct tcphdr *tcp, ipfw_insn *cmd) { int optlen, bits = 0; u_char *cp = (u_char *)(tcp + 1); int x = (tcp->th_off << 2) - sizeof(struct tcphdr); for (; x > 0; x -= optlen, cp += optlen) { int opt = cp[0]; if (opt == TCPOPT_EOL) break; if (opt == TCPOPT_NOP) optlen = 1; else { optlen = cp[1]; if (optlen <= 0) break; } switch (opt) { default: break; case TCPOPT_MAXSEG: bits |= IP_FW_TCPOPT_MSS; break; case TCPOPT_WINDOW: bits |= IP_FW_TCPOPT_WINDOW; break; case TCPOPT_SACK_PERMITTED: case TCPOPT_SACK: bits |= IP_FW_TCPOPT_SACK; break; case TCPOPT_TIMESTAMP: bits |= IP_FW_TCPOPT_TS; break; } } return (flags_match(cmd, bits)); } static int iface_match(struct ifnet *ifp, ipfw_insn_if *cmd) { if (ifp == NULL) /* no iface with this packet, match fails */ return 0; /* Check by name or by IP address */ if (cmd->name[0] != '\0') { /* match by name */ /* Check name */ if (cmd->p.glob) { if (fnmatch(cmd->name, ifp->if_xname, 0) == 0) return(1); } else { if (strncmp(ifp->if_xname, cmd->name, IFNAMSIZ) == 0) return(1); } } else { struct ifaddr *ia; /* XXX lock? */ TAILQ_FOREACH(ia, &ifp->if_addrhead, ifa_link) { if (ia->ifa_addr == NULL) continue; if (ia->ifa_addr->sa_family != AF_INET) continue; if (cmd->p.ip.s_addr == ((struct sockaddr_in *) (ia->ifa_addr))->sin_addr.s_addr) return(1); /* match */ } } return(0); /* no match, fail ... */ } /* * The verify_path function checks if a route to the src exists and * if it is reachable via ifp (when provided). * * The 'verrevpath' option checks that the interface that an IP packet * arrives on is the same interface that traffic destined for the * packet's source address would be routed out of. The 'versrcreach' * option just checks that the source address is reachable via any route * (except default) in the routing table. These two are a measure to block * forged packets. This is also commonly known as "anti-spoofing" or Unicast * Reverse Path Forwarding (Unicast RFP) in Cisco-ese. The name of the knobs * is purposely reminiscent of the Cisco IOS command, * * ip verify unicast reverse-path * ip verify unicast source reachable-via any * * which implements the same functionality. But note that syntax is * misleading. The check may be performed on all IP packets whether unicast, * multicast, or broadcast. */ static int verify_path(struct in_addr src, struct ifnet *ifp) { struct route ro; struct sockaddr_in *dst; bzero(&ro, sizeof(ro)); dst = (struct sockaddr_in *)&(ro.ro_dst); dst->sin_family = AF_INET; dst->sin_len = sizeof(*dst); dst->sin_addr = src; rtalloc_ign(&ro, RTF_CLONING); if (ro.ro_rt == NULL) return 0; /* * If ifp is provided, check for equality with rtentry. * We should use rt->rt_ifa->ifa_ifp, instead of rt->rt_ifp, * in order to pass packets injected back by if_simloop(): * if useloopback == 1 routing entry (via lo0) for our own address * may exist, so we need to handle routing assymetry. */ if (ifp != NULL && ro.ro_rt->rt_ifa->ifa_ifp != ifp) { RTFREE(ro.ro_rt); return 0; } /* if no ifp provided, check if rtentry is not default route */ if (ifp == NULL && satosin(rt_key(ro.ro_rt))->sin_addr.s_addr == INADDR_ANY) { RTFREE(ro.ro_rt); return 0; } /* or if this is a blackhole/reject route */ if (ifp == NULL && ro.ro_rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) { RTFREE(ro.ro_rt); return 0; } /* found valid route */ RTFREE(ro.ro_rt); return 1; } #ifdef INET6 /* * ipv6 specific rules here... */ static __inline int icmp6type_match (int type, ipfw_insn_u32 *cmd) { return (type <= ICMP6_MAXTYPE && (cmd->d[type/32] & (1<<(type%32)) ) ); } static int flow6id_match( int curr_flow, ipfw_insn_u32 *cmd ) { int i; for (i=0; i <= cmd->o.arg1; ++i ) if (curr_flow == cmd->d[i] ) return 1; return 0; } /* support for IP6_*_ME opcodes */ static int search_ip6_addr_net (struct in6_addr * ip6_addr) { struct ifnet *mdc; struct ifaddr *mdc2; struct in6_ifaddr *fdm; struct in6_addr copia; TAILQ_FOREACH(mdc, &ifnet, if_link) for (mdc2 = mdc->if_addrlist.tqh_first; mdc2; mdc2 = mdc2->ifa_list.tqe_next) { if (!mdc2->ifa_addr) continue; if (mdc2->ifa_addr->sa_family == AF_INET6) { fdm = (struct in6_ifaddr *)mdc2; copia = fdm->ia_addr.sin6_addr; /* need for leaving scope_id in the sock_addr */ in6_clearscope(&copia); if (IN6_ARE_ADDR_EQUAL(ip6_addr, &copia)) return 1; } } return 0; } static int verify_path6(struct in6_addr *src, struct ifnet *ifp) { struct route_in6 ro; struct sockaddr_in6 *dst; bzero(&ro, sizeof(ro)); dst = (struct sockaddr_in6 * )&(ro.ro_dst); dst->sin6_family = AF_INET6; dst->sin6_len = sizeof(*dst); dst->sin6_addr = *src; rtalloc_ign((struct route *)&ro, RTF_CLONING); if (ro.ro_rt == NULL) return 0; /* * if ifp is provided, check for equality with rtentry * We should use rt->rt_ifa->ifa_ifp, instead of rt->rt_ifp, * to support the case of sending packets to an address of our own. * (where the former interface is the first argument of if_simloop() * (=ifp), the latter is lo0) */ if (ifp != NULL && ro.ro_rt->rt_ifa->ifa_ifp != ifp) { RTFREE(ro.ro_rt); return 0; } /* if no ifp provided, check if rtentry is not default route */ if (ifp == NULL && IN6_IS_ADDR_UNSPECIFIED(&satosin6(rt_key(ro.ro_rt))->sin6_addr)) { RTFREE(ro.ro_rt); return 0; } /* or if this is a blackhole/reject route */ if (ifp == NULL && ro.ro_rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) { RTFREE(ro.ro_rt); return 0; } /* found valid route */ RTFREE(ro.ro_rt); return 1; } static __inline int hash_packet6(struct ipfw_flow_id *id) { u_int32_t i; i = (id->dst_ip6.__u6_addr.__u6_addr32[2]) ^ (id->dst_ip6.__u6_addr.__u6_addr32[3]) ^ (id->src_ip6.__u6_addr.__u6_addr32[2]) ^ (id->src_ip6.__u6_addr.__u6_addr32[3]) ^ (id->dst_port) ^ (id->src_port); return i; } static int is_icmp6_query(int icmp6_type) { if ((icmp6_type <= ICMP6_MAXTYPE) && (icmp6_type == ICMP6_ECHO_REQUEST || icmp6_type == ICMP6_MEMBERSHIP_QUERY || icmp6_type == ICMP6_WRUREQUEST || icmp6_type == ICMP6_FQDN_QUERY || icmp6_type == ICMP6_NI_QUERY)) return (1); return (0); } static void send_reject6(struct ip_fw_args *args, int code, u_short offset, u_int hlen) { if (code == ICMP6_UNREACH_RST && offset == 0 && args->f_id.proto == IPPROTO_TCP) { struct ip6_hdr *ip6; struct tcphdr *tcp; tcp_seq ack, seq; int flags; struct { struct ip6_hdr ip6; struct tcphdr th; } ti; if (args->m->m_len < (hlen+sizeof(struct tcphdr))) { args->m = m_pullup(args->m, hlen+sizeof(struct tcphdr)); if (args->m == NULL) return; } ip6 = mtod(args->m, struct ip6_hdr *); tcp = (struct tcphdr *)(mtod(args->m, char *) + hlen); if ((tcp->th_flags & TH_RST) != 0) { m_freem(args->m); return; } ti.ip6 = *ip6; ti.th = *tcp; ti.th.th_seq = ntohl(ti.th.th_seq); ti.th.th_ack = ntohl(ti.th.th_ack); ti.ip6.ip6_nxt = IPPROTO_TCP; if (ti.th.th_flags & TH_ACK) { ack = 0; seq = ti.th.th_ack; flags = TH_RST; } else { ack = ti.th.th_seq; if (((args->m)->m_flags & M_PKTHDR) != 0) { ack += (args->m)->m_pkthdr.len - hlen - (ti.th.th_off << 2); } else if (ip6->ip6_plen) { ack += ntohs(ip6->ip6_plen) + sizeof(*ip6) - hlen - (ti.th.th_off << 2); } else { m_freem(args->m); return; } if (tcp->th_flags & TH_SYN) ack++; seq = 0; flags = TH_RST|TH_ACK; } bcopy(&ti, ip6, sizeof(ti)); tcp_respond(NULL, ip6, (struct tcphdr *)(ip6 + 1), args->m, ack, seq, flags); } else if (code != ICMP6_UNREACH_RST) { /* Send an ICMPv6 unreach. */ icmp6_error(args->m, ICMP6_DST_UNREACH, code, 0); } else m_freem(args->m); args->m = NULL; } #endif /* INET6 */ static u_int64_t norule_counter; /* counter for ipfw_log(NULL...) */ #define SNPARGS(buf, len) buf + len, sizeof(buf) > len ? sizeof(buf) - len : 0 #define SNP(buf) buf, sizeof(buf) /* * We enter here when we have a rule with O_LOG. * XXX this function alone takes about 2Kbytes of code! */ static void ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args, struct mbuf *m, struct ifnet *oif, u_short offset) { struct ether_header *eh = args->eh; char *action; int limit_reached = 0; char action2[40], proto[128], fragment[32]; fragment[0] = '\0'; proto[0] = '\0'; if (f == NULL) { /* bogus pkt */ if (verbose_limit != 0 && norule_counter >= verbose_limit) return; norule_counter++; if (norule_counter == verbose_limit) limit_reached = verbose_limit; action = "Refuse"; } else { /* O_LOG is the first action, find the real one */ ipfw_insn *cmd = ACTION_PTR(f); ipfw_insn_log *l = (ipfw_insn_log *)cmd; if (l->max_log != 0 && l->log_left == 0) return; l->log_left--; if (l->log_left == 0) limit_reached = l->max_log; cmd += F_LEN(cmd); /* point to first action */ if (cmd->opcode == O_ALTQ) { ipfw_insn_altq *altq = (ipfw_insn_altq *)cmd; snprintf(SNPARGS(action2, 0), "Altq %d", altq->qid); cmd += F_LEN(cmd); } if (cmd->opcode == O_PROB) cmd += F_LEN(cmd); if (cmd->opcode == O_TAG) cmd += F_LEN(cmd); action = action2; switch (cmd->opcode) { case O_DENY: action = "Deny"; break; case O_REJECT: if (cmd->arg1==ICMP_REJECT_RST) action = "Reset"; else if (cmd->arg1==ICMP_UNREACH_HOST) action = "Reject"; else snprintf(SNPARGS(action2, 0), "Unreach %d", cmd->arg1); break; case O_UNREACH6: if (cmd->arg1==ICMP6_UNREACH_RST) action = "Reset"; else snprintf(SNPARGS(action2, 0), "Unreach %d", cmd->arg1); break; case O_ACCEPT: action = "Accept"; break; case O_COUNT: action = "Count"; break; case O_DIVERT: snprintf(SNPARGS(action2, 0), "Divert %d", cmd->arg1); break; case O_TEE: snprintf(SNPARGS(action2, 0), "Tee %d", cmd->arg1); break; case O_SKIPTO: snprintf(SNPARGS(action2, 0), "SkipTo %d", cmd->arg1); break; case O_PIPE: snprintf(SNPARGS(action2, 0), "Pipe %d", cmd->arg1); break; case O_QUEUE: snprintf(SNPARGS(action2, 0), "Queue %d", cmd->arg1); break; case O_FORWARD_IP: { ipfw_insn_sa *sa = (ipfw_insn_sa *)cmd; int len; len = snprintf(SNPARGS(action2, 0), "Forward to %s", inet_ntoa(sa->sa.sin_addr)); if (sa->sa.sin_port) snprintf(SNPARGS(action2, len), ":%d", sa->sa.sin_port); } break; case O_NETGRAPH: snprintf(SNPARGS(action2, 0), "Netgraph %d", cmd->arg1); break; case O_NGTEE: snprintf(SNPARGS(action2, 0), "Ngtee %d", cmd->arg1); break; default: action = "UNKNOWN"; break; } } if (hlen == 0) { /* non-ip */ snprintf(SNPARGS(proto, 0), "MAC"); } else { int len; char src[48], dst[48]; struct icmphdr *icmp; struct tcphdr *tcp; struct udphdr *udp; /* Initialize to make compiler happy. */ struct ip *ip = NULL; #ifdef INET6 struct ip6_hdr *ip6 = NULL; struct icmp6_hdr *icmp6; #endif src[0] = '\0'; dst[0] = '\0'; #ifdef INET6 if (args->f_id.addr_type == 6) { snprintf(src, sizeof(src), "[%s]", ip6_sprintf(&args->f_id.src_ip6)); snprintf(dst, sizeof(dst), "[%s]", ip6_sprintf(&args->f_id.dst_ip6)); ip6 = (struct ip6_hdr *)mtod(m, struct ip6_hdr *); tcp = (struct tcphdr *)(mtod(args->m, char *) + hlen); udp = (struct udphdr *)(mtod(args->m, char *) + hlen); } else #endif { ip = mtod(m, struct ip *); tcp = L3HDR(struct tcphdr, ip); udp = L3HDR(struct udphdr, ip); inet_ntoa_r(ip->ip_src, src); inet_ntoa_r(ip->ip_dst, dst); } switch (args->f_id.proto) { case IPPROTO_TCP: len = snprintf(SNPARGS(proto, 0), "TCP %s", src); if (offset == 0) snprintf(SNPARGS(proto, len), ":%d %s:%d", ntohs(tcp->th_sport), dst, ntohs(tcp->th_dport)); else snprintf(SNPARGS(proto, len), " %s", dst); break; case IPPROTO_UDP: len = snprintf(SNPARGS(proto, 0), "UDP %s", src); if (offset == 0) snprintf(SNPARGS(proto, len), ":%d %s:%d", ntohs(udp->uh_sport), dst, ntohs(udp->uh_dport)); else snprintf(SNPARGS(proto, len), " %s", dst); break; case IPPROTO_ICMP: icmp = L3HDR(struct icmphdr, ip); if (offset == 0) len = snprintf(SNPARGS(proto, 0), "ICMP:%u.%u ", icmp->icmp_type, icmp->icmp_code); else len = snprintf(SNPARGS(proto, 0), "ICMP "); len += snprintf(SNPARGS(proto, len), "%s", src); snprintf(SNPARGS(proto, len), " %s", dst); break; #ifdef INET6 case IPPROTO_ICMPV6: icmp6 = (struct icmp6_hdr *)(mtod(args->m, char *) + hlen); if (offset == 0) len = snprintf(SNPARGS(proto, 0), "ICMPv6:%u.%u ", icmp6->icmp6_type, icmp6->icmp6_code); else len = snprintf(SNPARGS(proto, 0), "ICMPv6 "); len += snprintf(SNPARGS(proto, len), "%s", src); snprintf(SNPARGS(proto, len), " %s", dst); break; #endif default: len = snprintf(SNPARGS(proto, 0), "P:%d %s", args->f_id.proto, src); snprintf(SNPARGS(proto, len), " %s", dst); break; } #ifdef INET6 if (args->f_id.addr_type == 6) { if (offset & (IP6F_OFF_MASK | IP6F_MORE_FRAG)) snprintf(SNPARGS(fragment, 0), " (frag %08x:%d@%d%s)", args->f_id.frag_id6, ntohs(ip6->ip6_plen) - hlen, ntohs(offset & IP6F_OFF_MASK) << 3, (offset & IP6F_MORE_FRAG) ? "+" : ""); } else #endif { int ip_off, ip_len; if (eh != NULL) { /* layer 2 packets are as on the wire */ ip_off = ntohs(ip->ip_off); ip_len = ntohs(ip->ip_len); } else { ip_off = ip->ip_off; ip_len = ip->ip_len; } if (ip_off & (IP_MF | IP_OFFMASK)) snprintf(SNPARGS(fragment, 0), " (frag %d:%d@%d%s)", ntohs(ip->ip_id), ip_len - (ip->ip_hl << 2), offset << 3, (ip_off & IP_MF) ? "+" : ""); } } if (oif || m->m_pkthdr.rcvif) log(LOG_SECURITY | LOG_INFO, "ipfw: %d %s %s %s via %s%s\n", f ? f->rulenum : -1, action, proto, oif ? "out" : "in", oif ? oif->if_xname : m->m_pkthdr.rcvif->if_xname, fragment); else log(LOG_SECURITY | LOG_INFO, "ipfw: %d %s %s [no if info]%s\n", f ? f->rulenum : -1, action, proto, fragment); if (limit_reached) log(LOG_SECURITY | LOG_NOTICE, "ipfw: limit %d reached on entry %d\n", limit_reached, f ? f->rulenum : -1); } /* * IMPORTANT: the hash function for dynamic rules must be commutative * in source and destination (ip,port), because rules are bidirectional * and we want to find both in the same bucket. */ static __inline int hash_packet(struct ipfw_flow_id *id) { u_int32_t i; #ifdef INET6 if (IS_IP6_FLOW_ID(id)) i = hash_packet6(id); else #endif /* INET6 */ i = (id->dst_ip) ^ (id->src_ip) ^ (id->dst_port) ^ (id->src_port); i &= (curr_dyn_buckets - 1); return i; } /** * unlink a dynamic rule from a chain. prev is a pointer to * the previous one, q is a pointer to the rule to delete, * head is a pointer to the head of the queue. * Modifies q and potentially also head. */ #define UNLINK_DYN_RULE(prev, head, q) { \ ipfw_dyn_rule *old_q = q; \ \ /* remove a refcount to the parent */ \ if (q->dyn_type == O_LIMIT) \ q->parent->count--; \ DEB(printf("ipfw: unlink entry 0x%08x %d -> 0x%08x %d, %d left\n",\ (q->id.src_ip), (q->id.src_port), \ (q->id.dst_ip), (q->id.dst_port), dyn_count-1 ); ) \ if (prev != NULL) \ prev->next = q = q->next; \ else \ head = q = q->next; \ dyn_count--; \ uma_zfree(ipfw_dyn_rule_zone, old_q); } #define TIME_LEQ(a,b) ((int)((a)-(b)) <= 0) /** * Remove dynamic rules pointing to "rule", or all of them if rule == NULL. * * If keep_me == NULL, rules are deleted even if not expired, * otherwise only expired rules are removed. * * The value of the second parameter is also used to point to identify * a rule we absolutely do not want to remove (e.g. because we are * holding a reference to it -- this is the case with O_LIMIT_PARENT * rules). The pointer is only used for comparison, so any non-null * value will do. */ static void remove_dyn_rule(struct ip_fw *rule, ipfw_dyn_rule *keep_me) { static u_int32_t last_remove = 0; #define FORCE (keep_me == NULL) ipfw_dyn_rule *prev, *q; int i, pass = 0, max_pass = 0; IPFW_DYN_LOCK_ASSERT(); if (ipfw_dyn_v == NULL || dyn_count == 0) return; /* do not expire more than once per second, it is useless */ if (!FORCE && last_remove == time_second) return; last_remove = time_second; /* * because O_LIMIT refer to parent rules, during the first pass only * remove child and mark any pending LIMIT_PARENT, and remove * them in a second pass. */ next_pass: for (i = 0 ; i < curr_dyn_buckets ; i++) { for (prev=NULL, q = ipfw_dyn_v[i] ; q ; ) { /* * Logic can become complex here, so we split tests. */ if (q == keep_me) goto next; if (rule != NULL && rule != q->rule) goto next; /* not the one we are looking for */ if (q->dyn_type == O_LIMIT_PARENT) { /* * handle parent in the second pass, * record we need one. */ max_pass = 1; if (pass == 0) goto next; if (FORCE && q->count != 0 ) { /* XXX should not happen! */ printf("ipfw: OUCH! cannot remove rule," " count %d\n", q->count); } } else { if (!FORCE && !TIME_LEQ( q->expire, time_second )) goto next; } if (q->dyn_type != O_LIMIT_PARENT || !q->count) { UNLINK_DYN_RULE(prev, ipfw_dyn_v[i], q); continue; } next: prev=q; q=q->next; } } if (pass++ < max_pass) goto next_pass; } /** * lookup a dynamic rule. */ static ipfw_dyn_rule * lookup_dyn_rule_locked(struct ipfw_flow_id *pkt, int *match_direction, struct tcphdr *tcp) { /* * stateful ipfw extensions. * Lookup into dynamic session queue */ #define MATCH_REVERSE 0 #define MATCH_FORWARD 1 #define MATCH_NONE 2 #define MATCH_UNKNOWN 3 int i, dir = MATCH_NONE; ipfw_dyn_rule *prev, *q=NULL; IPFW_DYN_LOCK_ASSERT(); if (ipfw_dyn_v == NULL) goto done; /* not found */ i = hash_packet( pkt ); for (prev=NULL, q = ipfw_dyn_v[i] ; q != NULL ; ) { if (q->dyn_type == O_LIMIT_PARENT && q->count) goto next; if (TIME_LEQ( q->expire, time_second)) { /* expire entry */ UNLINK_DYN_RULE(prev, ipfw_dyn_v[i], q); continue; } if (pkt->proto == q->id.proto && q->dyn_type != O_LIMIT_PARENT) { if (IS_IP6_FLOW_ID(pkt)) { if (IN6_ARE_ADDR_EQUAL(&(pkt->src_ip6), &(q->id.src_ip6)) && IN6_ARE_ADDR_EQUAL(&(pkt->dst_ip6), &(q->id.dst_ip6)) && pkt->src_port == q->id.src_port && pkt->dst_port == q->id.dst_port ) { dir = MATCH_FORWARD; break; } if (IN6_ARE_ADDR_EQUAL(&(pkt->src_ip6), &(q->id.dst_ip6)) && IN6_ARE_ADDR_EQUAL(&(pkt->dst_ip6), &(q->id.src_ip6)) && pkt->src_port == q->id.dst_port && pkt->dst_port == q->id.src_port ) { dir = MATCH_REVERSE; break; } } else { if (pkt->src_ip == q->id.src_ip && pkt->dst_ip == q->id.dst_ip && pkt->src_port == q->id.src_port && pkt->dst_port == q->id.dst_port ) { dir = MATCH_FORWARD; break; } if (pkt->src_ip == q->id.dst_ip && pkt->dst_ip == q->id.src_ip && pkt->src_port == q->id.dst_port && pkt->dst_port == q->id.src_port ) { dir = MATCH_REVERSE; break; } } } next: prev = q; q = q->next; } if (q == NULL) goto done; /* q = NULL, not found */ if ( prev != NULL) { /* found and not in front */ prev->next = q->next; q->next = ipfw_dyn_v[i]; ipfw_dyn_v[i] = q; } if (pkt->proto == IPPROTO_TCP) { /* update state according to flags */ u_char flags = pkt->flags & (TH_FIN|TH_SYN|TH_RST); #define BOTH_SYN (TH_SYN | (TH_SYN << 8)) #define BOTH_FIN (TH_FIN | (TH_FIN << 8)) q->state |= (dir == MATCH_FORWARD ) ? flags : (flags << 8); switch (q->state) { case TH_SYN: /* opening */ q->expire = time_second + dyn_syn_lifetime; break; case BOTH_SYN: /* move to established */ case BOTH_SYN | TH_FIN : /* one side tries to close */ case BOTH_SYN | (TH_FIN << 8) : if (tcp) { #define _SEQ_GE(a,b) ((int)(a) - (int)(b) >= 0) u_int32_t ack = ntohl(tcp->th_ack); if (dir == MATCH_FORWARD) { if (q->ack_fwd == 0 || _SEQ_GE(ack, q->ack_fwd)) q->ack_fwd = ack; else { /* ignore out-of-sequence */ break; } } else { if (q->ack_rev == 0 || _SEQ_GE(ack, q->ack_rev)) q->ack_rev = ack; else { /* ignore out-of-sequence */ break; } } } q->expire = time_second + dyn_ack_lifetime; break; case BOTH_SYN | BOTH_FIN: /* both sides closed */ if (dyn_fin_lifetime >= dyn_keepalive_period) dyn_fin_lifetime = dyn_keepalive_period - 1; q->expire = time_second + dyn_fin_lifetime; break; default: #if 0 /* * reset or some invalid combination, but can also * occur if we use keep-state the wrong way. */ if ( (q->state & ((TH_RST << 8)|TH_RST)) == 0) printf("invalid state: 0x%x\n", q->state); #endif if (dyn_rst_lifetime >= dyn_keepalive_period) dyn_rst_lifetime = dyn_keepalive_period - 1; q->expire = time_second + dyn_rst_lifetime; break; } } else if (pkt->proto == IPPROTO_UDP) { q->expire = time_second + dyn_udp_lifetime; } else { /* other protocols */ q->expire = time_second + dyn_short_lifetime; } done: if (match_direction) *match_direction = dir; return q; } static ipfw_dyn_rule * lookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction, struct tcphdr *tcp) { ipfw_dyn_rule *q; IPFW_DYN_LOCK(); q = lookup_dyn_rule_locked(pkt, match_direction, tcp); if (q == NULL) IPFW_DYN_UNLOCK(); /* NB: return table locked when q is not NULL */ return q; } static void realloc_dynamic_table(void) { IPFW_DYN_LOCK_ASSERT(); /* * Try reallocation, make sure we have a power of 2 and do * not allow more than 64k entries. In case of overflow, * default to 1024. */ if (dyn_buckets > 65536) dyn_buckets = 1024; if ((dyn_buckets & (dyn_buckets-1)) != 0) { /* not a power of 2 */ dyn_buckets = curr_dyn_buckets; /* reset */ return; } curr_dyn_buckets = dyn_buckets; if (ipfw_dyn_v != NULL) free(ipfw_dyn_v, M_IPFW); for (;;) { ipfw_dyn_v = malloc(curr_dyn_buckets * sizeof(ipfw_dyn_rule *), M_IPFW, M_NOWAIT | M_ZERO); if (ipfw_dyn_v != NULL || curr_dyn_buckets <= 2) break; curr_dyn_buckets /= 2; } } /** * Install state of type 'type' for a dynamic session. * The hash table contains two type of rules: * - regular rules (O_KEEP_STATE) * - rules for sessions with limited number of sess per user * (O_LIMIT). When they are created, the parent is * increased by 1, and decreased on delete. In this case, * the third parameter is the parent rule and not the chain. * - "parent" rules for the above (O_LIMIT_PARENT). */ static ipfw_dyn_rule * add_dyn_rule(struct ipfw_flow_id *id, u_int8_t dyn_type, struct ip_fw *rule) { ipfw_dyn_rule *r; int i; IPFW_DYN_LOCK_ASSERT(); if (ipfw_dyn_v == NULL || (dyn_count == 0 && dyn_buckets != curr_dyn_buckets)) { realloc_dynamic_table(); if (ipfw_dyn_v == NULL) return NULL; /* failed ! */ } i = hash_packet(id); r = uma_zalloc(ipfw_dyn_rule_zone, M_NOWAIT | M_ZERO); if (r == NULL) { printf ("ipfw: sorry cannot allocate state\n"); return NULL; } /* increase refcount on parent, and set pointer */ if (dyn_type == O_LIMIT) { ipfw_dyn_rule *parent = (ipfw_dyn_rule *)rule; if ( parent->dyn_type != O_LIMIT_PARENT) panic("invalid parent"); parent->count++; r->parent = parent; rule = parent->rule; } r->id = *id; r->expire = time_second + dyn_syn_lifetime; r->rule = rule; r->dyn_type = dyn_type; r->pcnt = r->bcnt = 0; r->count = 0; r->bucket = i; r->next = ipfw_dyn_v[i]; ipfw_dyn_v[i] = r; dyn_count++; DEB(printf("ipfw: add dyn entry ty %d 0x%08x %d -> 0x%08x %d, total %d\n", dyn_type, (r->id.src_ip), (r->id.src_port), (r->id.dst_ip), (r->id.dst_port), dyn_count ); ) return r; } /** * lookup dynamic parent rule using pkt and rule as search keys. * If the lookup fails, then install one. */ static ipfw_dyn_rule * lookup_dyn_parent(struct ipfw_flow_id *pkt, struct ip_fw *rule) { ipfw_dyn_rule *q; int i; IPFW_DYN_LOCK_ASSERT(); if (ipfw_dyn_v) { int is_v6 = IS_IP6_FLOW_ID(pkt); i = hash_packet( pkt ); for (q = ipfw_dyn_v[i] ; q != NULL ; q=q->next) if (q->dyn_type == O_LIMIT_PARENT && rule== q->rule && pkt->proto == q->id.proto && pkt->src_port == q->id.src_port && pkt->dst_port == q->id.dst_port && ( (is_v6 && IN6_ARE_ADDR_EQUAL(&(pkt->src_ip6), &(q->id.src_ip6)) && IN6_ARE_ADDR_EQUAL(&(pkt->dst_ip6), &(q->id.dst_ip6))) || (!is_v6 && pkt->src_ip == q->id.src_ip && pkt->dst_ip == q->id.dst_ip) ) ) { q->expire = time_second + dyn_short_lifetime; DEB(printf("ipfw: lookup_dyn_parent found 0x%p\n",q);) return q; } } return add_dyn_rule(pkt, O_LIMIT_PARENT, rule); } /** * Install dynamic state for rule type cmd->o.opcode * * Returns 1 (failure) if state is not installed because of errors or because * session limitations are enforced. */ static int install_state(struct ip_fw *rule, ipfw_insn_limit *cmd, struct ip_fw_args *args, uint32_t tablearg) { static int last_log; ipfw_dyn_rule *q; DEB( printf("ipfw: %s: type %d 0x%08x %u -> 0x%08x %u\n", __func__, cmd->o.opcode, (args->f_id.src_ip), (args->f_id.src_port), (args->f_id.dst_ip), (args->f_id.dst_port)); ) IPFW_DYN_LOCK(); q = lookup_dyn_rule_locked(&args->f_id, NULL, NULL); if (q != NULL) { /* should never occur */ if (last_log != time_second) { last_log = time_second; printf("ipfw: %s: entry already present, done\n", __func__); } IPFW_DYN_UNLOCK(); return (0); } if (dyn_count >= dyn_max) /* Run out of slots, try to remove any expired rule. */ remove_dyn_rule(NULL, (ipfw_dyn_rule *)1); if (dyn_count >= dyn_max) { if (last_log != time_second) { last_log = time_second; printf("ipfw: %s: Too many dynamic rules\n", __func__); } IPFW_DYN_UNLOCK(); return (1); /* cannot install, notify caller */ } switch (cmd->o.opcode) { case O_KEEP_STATE: /* bidir rule */ add_dyn_rule(&args->f_id, O_KEEP_STATE, rule); break; case O_LIMIT: { /* limit number of sessions */ struct ipfw_flow_id id; ipfw_dyn_rule *parent; uint32_t conn_limit; uint16_t limit_mask = cmd->limit_mask; conn_limit = (cmd->conn_limit == IP_FW_TABLEARG) ? tablearg : cmd->conn_limit; DEB( if (cmd->conn_limit == IP_FW_TABLEARG) printf("ipfw: %s: O_LIMIT rule, conn_limit: %u " "(tablearg)\n", __func__, conn_limit); else printf("ipfw: %s: O_LIMIT rule, conn_limit: %u\n", __func__, conn_limit); ) id.dst_ip = id.src_ip = id.dst_port = id.src_port = 0; id.proto = args->f_id.proto; id.addr_type = args->f_id.addr_type; if (IS_IP6_FLOW_ID (&(args->f_id))) { if (limit_mask & DYN_SRC_ADDR) id.src_ip6 = args->f_id.src_ip6; if (limit_mask & DYN_DST_ADDR) id.dst_ip6 = args->f_id.dst_ip6; } else { if (limit_mask & DYN_SRC_ADDR) id.src_ip = args->f_id.src_ip; if (limit_mask & DYN_DST_ADDR) id.dst_ip = args->f_id.dst_ip; } if (limit_mask & DYN_SRC_PORT) id.src_port = args->f_id.src_port; if (limit_mask & DYN_DST_PORT) id.dst_port = args->f_id.dst_port; if ((parent = lookup_dyn_parent(&id, rule)) == NULL) { printf("ipfw: %s: add parent failed\n", __func__); IPFW_DYN_UNLOCK(); return (1); } if (parent->count >= conn_limit) { /* See if we can remove some expired rule. */ remove_dyn_rule(rule, parent); if (parent->count >= conn_limit) { if (fw_verbose && last_log != time_second) { last_log = time_second; log(LOG_SECURITY | LOG_DEBUG, "drop session, too many entries\n"); } IPFW_DYN_UNLOCK(); return (1); } } add_dyn_rule(&args->f_id, O_LIMIT, (struct ip_fw *)parent); break; } default: printf("ipfw: %s: unknown dynamic rule type %u\n", __func__, cmd->o.opcode); IPFW_DYN_UNLOCK(); return (1); } /* XXX just set lifetime */ lookup_dyn_rule_locked(&args->f_id, NULL, NULL); IPFW_DYN_UNLOCK(); return (0); } /* * Generate a TCP packet, containing either a RST or a keepalive. * When flags & TH_RST, we are sending a RST packet, because of a * "reset" action matched the packet. * Otherwise we are sending a keepalive, and flags & TH_ + * The 'replyto' mbuf is the mbuf being replied to, if any, and is required + * so that MAC can label the reply appropriately. */ static struct mbuf * -send_pkt(struct ipfw_flow_id *id, u_int32_t seq, u_int32_t ack, int flags) +send_pkt(struct mbuf *replyto, struct ipfw_flow_id *id, u_int32_t seq, + u_int32_t ack, int flags) { struct mbuf *m; struct ip *ip; struct tcphdr *tcp; MGETHDR(m, M_DONTWAIT, MT_HEADER); if (m == 0) return (NULL); m->m_pkthdr.rcvif = (struct ifnet *)0; + +#ifdef MAC + if (replyto != NULL) + mac_create_mbuf_netlayer(replyto, m); + else + mac_create_mbuf_from_firewall(m); +#else + (void)replyto; /* don't warn about unused arg */ +#endif + m->m_pkthdr.len = m->m_len = sizeof(struct ip) + sizeof(struct tcphdr); m->m_data += max_linkhdr; ip = mtod(m, struct ip *); bzero(ip, m->m_len); tcp = (struct tcphdr *)(ip + 1); /* no IP options */ ip->ip_p = IPPROTO_TCP; tcp->th_off = 5; /* * Assume we are sending a RST (or a keepalive in the reverse * direction), swap src and destination addresses and ports. */ ip->ip_src.s_addr = htonl(id->dst_ip); ip->ip_dst.s_addr = htonl(id->src_ip); tcp->th_sport = htons(id->dst_port); tcp->th_dport = htons(id->src_port); if (flags & TH_RST) { /* we are sending a RST */ if (flags & TH_ACK) { tcp->th_seq = htonl(ack); tcp->th_ack = htonl(0); tcp->th_flags = TH_RST; } else { if (flags & TH_SYN) seq++; tcp->th_seq = htonl(0); tcp->th_ack = htonl(seq); tcp->th_flags = TH_RST | TH_ACK; } } else { /* * We are sending a keepalive. flags & TH_SYN determines * the direction, forward if set, reverse if clear. * NOTE: seq and ack are always assumed to be correct * as set by the caller. This may be confusing... */ if (flags & TH_SYN) { /* * we have to rewrite the correct addresses! */ ip->ip_dst.s_addr = htonl(id->dst_ip); ip->ip_src.s_addr = htonl(id->src_ip); tcp->th_dport = htons(id->dst_port); tcp->th_sport = htons(id->src_port); } tcp->th_seq = htonl(seq); tcp->th_ack = htonl(ack); tcp->th_flags = TH_ACK; } /* * set ip_len to the payload size so we can compute * the tcp checksum on the pseudoheader * XXX check this, could save a couple of words ? */ ip->ip_len = htons(sizeof(struct tcphdr)); tcp->th_sum = in_cksum(m, m->m_pkthdr.len); /* * now fill fields left out earlier */ ip->ip_ttl = ip_defttl; ip->ip_len = m->m_pkthdr.len; m->m_flags |= M_SKIP_FIREWALL; return (m); } /* * sends a reject message, consuming the mbuf passed as an argument. */ static void send_reject(struct ip_fw_args *args, int code, u_short offset, int ip_len) { if (code != ICMP_REJECT_RST) { /* Send an ICMP unreach */ /* We need the IP header in host order for icmp_error(). */ if (args->eh != NULL) { struct ip *ip = mtod(args->m, struct ip *); ip->ip_len = ntohs(ip->ip_len); ip->ip_off = ntohs(ip->ip_off); } icmp_error(args->m, ICMP_UNREACH, code, 0L, 0); } else if (offset == 0 && args->f_id.proto == IPPROTO_TCP) { struct tcphdr *const tcp = L3HDR(struct tcphdr, mtod(args->m, struct ip *)); if ( (tcp->th_flags & TH_RST) == 0) { struct mbuf *m; - m = send_pkt(&(args->f_id), ntohl(tcp->th_seq), - ntohl(tcp->th_ack), + m = send_pkt(args->m, &(args->f_id), + ntohl(tcp->th_seq), ntohl(tcp->th_ack), tcp->th_flags | TH_RST); if (m != NULL) ip_output(m, NULL, NULL, 0, NULL, NULL); } m_freem(args->m); } else m_freem(args->m); args->m = NULL; } /** * * Given an ip_fw *, lookup_next_rule will return a pointer * to the next rule, which can be either the jump * target (for skipto instructions) or the next one in the list (in * all other cases including a missing jump target). * The result is also written in the "next_rule" field of the rule. * Backward jumps are not allowed, so start looking from the next * rule... * * This never returns NULL -- in case we do not have an exact match, * the next rule is returned. When the ruleset is changed, * pointers are flushed so we are always correct. */ static struct ip_fw * lookup_next_rule(struct ip_fw *me) { struct ip_fw *rule = NULL; ipfw_insn *cmd; /* look for action, in case it is a skipto */ cmd = ACTION_PTR(me); if (cmd->opcode == O_LOG) cmd += F_LEN(cmd); if (cmd->opcode == O_ALTQ) cmd += F_LEN(cmd); if (cmd->opcode == O_TAG) cmd += F_LEN(cmd); if ( cmd->opcode == O_SKIPTO ) for (rule = me->next; rule ; rule = rule->next) if (rule->rulenum >= cmd->arg1) break; if (rule == NULL) /* failure or not a skipto */ rule = me->next; me->next_rule = rule; return rule; } static int add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, uint8_t mlen, uint32_t value) { struct radix_node_head *rnh; struct table_entry *ent; if (tbl >= IPFW_TABLES_MAX) return (EINVAL); rnh = ch->tables[tbl]; ent = malloc(sizeof(*ent), M_IPFW_TBL, M_NOWAIT | M_ZERO); if (ent == NULL) return (ENOMEM); ent->value = value; ent->addr.sin_len = ent->mask.sin_len = 8; ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr; IPFW_WLOCK(&layer3_chain); if (rnh->rnh_addaddr(&ent->addr, &ent->mask, rnh, (void *)ent) == NULL) { IPFW_WUNLOCK(&layer3_chain); free(ent, M_IPFW_TBL); return (EEXIST); } IPFW_WUNLOCK(&layer3_chain); return (0); } static int del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, uint8_t mlen) { struct radix_node_head *rnh; struct table_entry *ent; struct sockaddr_in sa, mask; if (tbl >= IPFW_TABLES_MAX) return (EINVAL); rnh = ch->tables[tbl]; sa.sin_len = mask.sin_len = 8; mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr; IPFW_WLOCK(ch); ent = (struct table_entry *)rnh->rnh_deladdr(&sa, &mask, rnh); if (ent == NULL) { IPFW_WUNLOCK(ch); return (ESRCH); } IPFW_WUNLOCK(ch); free(ent, M_IPFW_TBL); return (0); } static int flush_table_entry(struct radix_node *rn, void *arg) { struct radix_node_head * const rnh = arg; struct table_entry *ent; ent = (struct table_entry *) rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh); if (ent != NULL) free(ent, M_IPFW_TBL); return (0); } static int flush_table(struct ip_fw_chain *ch, uint16_t tbl) { struct radix_node_head *rnh; IPFW_WLOCK_ASSERT(ch); if (tbl >= IPFW_TABLES_MAX) return (EINVAL); rnh = ch->tables[tbl]; KASSERT(rnh != NULL, ("NULL IPFW table")); rnh->rnh_walktree(rnh, flush_table_entry, rnh); return (0); } static void flush_tables(struct ip_fw_chain *ch) { uint16_t tbl; IPFW_WLOCK_ASSERT(ch); for (tbl = 0; tbl < IPFW_TABLES_MAX; tbl++) flush_table(ch, tbl); } static int init_tables(struct ip_fw_chain *ch) { int i; uint16_t j; for (i = 0; i < IPFW_TABLES_MAX; i++) { if (!rn_inithead((void **)&ch->tables[i], 32)) { for (j = 0; j < i; j++) { (void) flush_table(ch, j); } return (ENOMEM); } } return (0); } static int lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, uint32_t *val) { struct radix_node_head *rnh; struct table_entry *ent; struct sockaddr_in sa; if (tbl >= IPFW_TABLES_MAX) return (0); rnh = ch->tables[tbl]; sa.sin_len = 8; sa.sin_addr.s_addr = addr; ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh)); if (ent != NULL) { *val = ent->value; return (1); } return (0); } static int count_table_entry(struct radix_node *rn, void *arg) { u_int32_t * const cnt = arg; (*cnt)++; return (0); } static int count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt) { struct radix_node_head *rnh; if (tbl >= IPFW_TABLES_MAX) return (EINVAL); rnh = ch->tables[tbl]; *cnt = 0; rnh->rnh_walktree(rnh, count_table_entry, cnt); return (0); } static int dump_table_entry(struct radix_node *rn, void *arg) { struct table_entry * const n = (struct table_entry *)rn; ipfw_table * const tbl = arg; ipfw_table_entry *ent; if (tbl->cnt == tbl->size) return (1); ent = &tbl->ent[tbl->cnt]; ent->tbl = tbl->tbl; if (in_nullhost(n->mask.sin_addr)) ent->masklen = 0; else ent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr)); ent->addr = n->addr.sin_addr.s_addr; ent->value = n->value; tbl->cnt++; return (0); } static int dump_table(struct ip_fw_chain *ch, ipfw_table *tbl) { struct radix_node_head *rnh; if (tbl->tbl >= IPFW_TABLES_MAX) return (EINVAL); rnh = ch->tables[tbl->tbl]; tbl->cnt = 0; rnh->rnh_walktree(rnh, dump_table_entry, tbl); return (0); } static void fill_ugid_cache(struct inpcb *inp, struct ip_fw_ugid *ugp) { struct ucred *cr; if (inp->inp_socket != NULL) { cr = inp->inp_socket->so_cred; ugp->fw_prid = jailed(cr) ? cr->cr_prison->pr_id : -1; ugp->fw_uid = cr->cr_uid; ugp->fw_ngroups = cr->cr_ngroups; bcopy(cr->cr_groups, ugp->fw_groups, sizeof(ugp->fw_groups)); } } static int check_uidgid(ipfw_insn_u32 *insn, int proto, struct ifnet *oif, struct in_addr dst_ip, u_int16_t dst_port, struct in_addr src_ip, u_int16_t src_port, struct ip_fw_ugid *ugp, int *lookup, struct inpcb *inp) { struct inpcbinfo *pi; int wildcard; struct inpcb *pcb; int match; gid_t *gp; /* * Check to see if the UDP or TCP stack supplied us with * the PCB. If so, rather then holding a lock and looking * up the PCB, we can use the one that was supplied. */ if (inp && *lookup == 0) { INP_LOCK_ASSERT(inp); if (inp->inp_socket != NULL) { fill_ugid_cache(inp, ugp); *lookup = 1; } } /* * If we have already been here and the packet has no * PCB entry associated with it, then we can safely * assume that this is a no match. */ if (*lookup == -1) return (0); if (proto == IPPROTO_TCP) { wildcard = 0; pi = &tcbinfo; } else if (proto == IPPROTO_UDP) { wildcard = 1; pi = &udbinfo; } else return 0; match = 0; if (*lookup == 0) { INP_INFO_RLOCK(pi); pcb = (oif) ? in_pcblookup_hash(pi, dst_ip, htons(dst_port), src_ip, htons(src_port), wildcard, oif) : in_pcblookup_hash(pi, src_ip, htons(src_port), dst_ip, htons(dst_port), wildcard, NULL); if (pcb != NULL) { INP_LOCK(pcb); if (pcb->inp_socket != NULL) { fill_ugid_cache(pcb, ugp); *lookup = 1; } INP_UNLOCK(pcb); } INP_INFO_RUNLOCK(pi); if (*lookup == 0) { /* * If the lookup did not yield any results, there * is no sense in coming back and trying again. So * we can set lookup to -1 and ensure that we wont * bother the pcb system again. */ *lookup = -1; return (0); } } if (insn->o.opcode == O_UID) match = (ugp->fw_uid == (uid_t)insn->d[0]); else if (insn->o.opcode == O_GID) { for (gp = ugp->fw_groups; gp < &ugp->fw_groups[ugp->fw_ngroups]; gp++) if (*gp == (gid_t)insn->d[0]) { match = 1; break; } } else if (insn->o.opcode == O_JAIL) match = (ugp->fw_prid == (int)insn->d[0]); return match; } /* * The main check routine for the firewall. * * All arguments are in args so we can modify them and return them * back to the caller. * * Parameters: * * args->m (in/out) The packet; we set to NULL when/if we nuke it. * Starts with the IP header. * args->eh (in) Mac header if present, or NULL for layer3 packet. * args->oif Outgoing interface, or NULL if packet is incoming. * The incoming interface is in the mbuf. (in) * args->divert_rule (in/out) * Skip up to the first rule past this rule number; * upon return, non-zero port number for divert or tee. * * args->rule Pointer to the last matching rule (in/out) * args->next_hop Socket we are forwarding to (out). * args->f_id Addresses grabbed from the packet (out) * args->cookie a cookie depending on rule action * * Return value: * * IP_FW_PASS the packet must be accepted * IP_FW_DENY the packet must be dropped * IP_FW_DIVERT divert packet, port in m_tag * IP_FW_TEE tee packet, port in m_tag * IP_FW_DUMMYNET to dummynet, pipe in args->cookie * IP_FW_NETGRAPH into netgraph, cookie args->cookie * */ int ipfw_chk(struct ip_fw_args *args) { /* * Local variables hold state during the processing of a packet. * * IMPORTANT NOTE: to speed up the processing of rules, there * are some assumption on the values of the variables, which * are documented here. Should you change them, please check * the implementation of the various instructions to make sure * that they still work. * * args->eh The MAC header. It is non-null for a layer2 * packet, it is NULL for a layer-3 packet. * * m | args->m Pointer to the mbuf, as received from the caller. * It may change if ipfw_chk() does an m_pullup, or if it * consumes the packet because it calls send_reject(). * XXX This has to change, so that ipfw_chk() never modifies * or consumes the buffer. * ip is simply an alias of the value of m, and it is kept * in sync with it (the packet is supposed to start with * the ip header). */ struct mbuf *m = args->m; struct ip *ip = mtod(m, struct ip *); /* * For rules which contain uid/gid or jail constraints, cache * a copy of the users credentials after the pcb lookup has been * executed. This will speed up the processing of rules with * these types of constraints, as well as decrease contention * on pcb related locks. */ struct ip_fw_ugid fw_ugid_cache; int ugid_lookup = 0; /* * divinput_flags If non-zero, set to the IP_FW_DIVERT_*_FLAG * associated with a packet input on a divert socket. This * will allow to distinguish traffic and its direction when * it originates from a divert socket. */ u_int divinput_flags = 0; /* * oif | args->oif If NULL, ipfw_chk has been called on the * inbound path (ether_input, bdg_forward, ip_input). * If non-NULL, ipfw_chk has been called on the outbound path * (ether_output, ip_output). */ struct ifnet *oif = args->oif; struct ip_fw *f = NULL; /* matching rule */ int retval = 0; /* * hlen The length of the IP header. */ u_int hlen = 0; /* hlen >0 means we have an IP pkt */ /* * offset The offset of a fragment. offset != 0 means that * we have a fragment at this offset of an IPv4 packet. * offset == 0 means that (if this is an IPv4 packet) * this is the first or only fragment. * For IPv6 offset == 0 means there is no Fragment Header. * If offset != 0 for IPv6 always use correct mask to * get the correct offset because we add IP6F_MORE_FRAG * to be able to dectect the first fragment which would * otherwise have offset = 0. */ u_short offset = 0; /* * Local copies of addresses. They are only valid if we have * an IP packet. * * proto The protocol. Set to 0 for non-ip packets, * or to the protocol read from the packet otherwise. * proto != 0 means that we have an IPv4 packet. * * src_port, dst_port port numbers, in HOST format. Only * valid for TCP and UDP packets. * * src_ip, dst_ip ip addresses, in NETWORK format. * Only valid for IPv4 packets. */ u_int8_t proto; u_int16_t src_port = 0, dst_port = 0; /* NOTE: host format */ struct in_addr src_ip, dst_ip; /* NOTE: network format */ u_int16_t ip_len=0; int pktlen; /* * dyn_dir = MATCH_UNKNOWN when rules unchecked, * MATCH_NONE when checked and not matched (q = NULL), * MATCH_FORWARD or MATCH_REVERSE otherwise (q != NULL) */ int dyn_dir = MATCH_UNKNOWN; ipfw_dyn_rule *q = NULL; struct ip_fw_chain *chain = &layer3_chain; struct m_tag *mtag; /* * We store in ulp a pointer to the upper layer protocol header. * In the ipv4 case this is easy to determine from the header, * but for ipv6 we might have some additional headers in the middle. * ulp is NULL if not found. */ void *ulp = NULL; /* upper layer protocol pointer. */ /* XXX ipv6 variables */ int is_ipv6 = 0; u_int16_t ext_hd = 0; /* bits vector for extension header filtering */ /* end of ipv6 variables */ int is_ipv4 = 0; if (m->m_flags & M_SKIP_FIREWALL) return (IP_FW_PASS); /* accept */ pktlen = m->m_pkthdr.len; proto = args->f_id.proto = 0; /* mark f_id invalid */ /* XXX 0 is a valid proto: IP/IPv6 Hop-by-Hop Option */ /* * PULLUP_TO(len, p, T) makes sure that len + sizeof(T) is contiguous, * then it sets p to point at the offset "len" in the mbuf. WARNING: the * pointer might become stale after other pullups (but we never use it * this way). */ #define PULLUP_TO(len, p, T) \ do { \ int x = (len) + sizeof(T); \ if ((m)->m_len < x) { \ args->m = m = m_pullup(m, x); \ if (m == NULL) \ goto pullup_failed; \ } \ p = (mtod(m, char *) + (len)); \ } while (0) /* Identify IP packets and fill up variables. */ if (pktlen >= sizeof(struct ip6_hdr) && (args->eh == NULL || ntohs(args->eh->ether_type)==ETHERTYPE_IPV6) && mtod(m, struct ip *)->ip_v == 6) { is_ipv6 = 1; args->f_id.addr_type = 6; hlen = sizeof(struct ip6_hdr); proto = mtod(m, struct ip6_hdr *)->ip6_nxt; /* Search extension headers to find upper layer protocols */ while (ulp == NULL) { switch (proto) { case IPPROTO_ICMPV6: PULLUP_TO(hlen, ulp, struct icmp6_hdr); args->f_id.flags = ICMP6(ulp)->icmp6_type; break; case IPPROTO_TCP: PULLUP_TO(hlen, ulp, struct tcphdr); dst_port = TCP(ulp)->th_dport; src_port = TCP(ulp)->th_sport; args->f_id.flags = TCP(ulp)->th_flags; break; case IPPROTO_UDP: PULLUP_TO(hlen, ulp, struct udphdr); dst_port = UDP(ulp)->uh_dport; src_port = UDP(ulp)->uh_sport; break; case IPPROTO_HOPOPTS: /* RFC 2460 */ PULLUP_TO(hlen, ulp, struct ip6_hbh); ext_hd |= EXT_HOPOPTS; hlen += (((struct ip6_hbh *)ulp)->ip6h_len + 1) << 3; proto = ((struct ip6_hbh *)ulp)->ip6h_nxt; ulp = NULL; break; case IPPROTO_ROUTING: /* RFC 2460 */ PULLUP_TO(hlen, ulp, struct ip6_rthdr); switch (((struct ip6_rthdr *)ulp)->ip6r_type) { case 0: break; default: printf("IPFW2: IPV6 - Unknown Routing " "Header type(%d)\n", ((struct ip6_rthdr *)ulp)->ip6r_type); if (fw_deny_unknown_exthdrs) return (IP_FW_DENY); break; } ext_hd |= EXT_ROUTING; hlen += (((struct ip6_rthdr *)ulp)->ip6r_len + 1) << 3; proto = ((struct ip6_rthdr *)ulp)->ip6r_nxt; ulp = NULL; break; case IPPROTO_FRAGMENT: /* RFC 2460 */ PULLUP_TO(hlen, ulp, struct ip6_frag); ext_hd |= EXT_FRAGMENT; hlen += sizeof (struct ip6_frag); proto = ((struct ip6_frag *)ulp)->ip6f_nxt; offset = ((struct ip6_frag *)ulp)->ip6f_offlg & IP6F_OFF_MASK; /* Add IP6F_MORE_FRAG for offset of first * fragment to be != 0. */ offset |= ((struct ip6_frag *)ulp)->ip6f_offlg & IP6F_MORE_FRAG; if (offset == 0) { printf("IPFW2: IPV6 - Invalid Fragment " "Header\n"); if (fw_deny_unknown_exthdrs) return (IP_FW_DENY); break; } args->f_id.frag_id6 = ntohl(((struct ip6_frag *)ulp)->ip6f_ident); ulp = NULL; break; case IPPROTO_DSTOPTS: /* RFC 2460 */ PULLUP_TO(hlen, ulp, struct ip6_hbh); ext_hd |= EXT_DSTOPTS; hlen += (((struct ip6_hbh *)ulp)->ip6h_len + 1) << 3; proto = ((struct ip6_hbh *)ulp)->ip6h_nxt; ulp = NULL; break; case IPPROTO_AH: /* RFC 2402 */ PULLUP_TO(hlen, ulp, struct ip6_ext); ext_hd |= EXT_AH; hlen += (((struct ip6_ext *)ulp)->ip6e_len + 2) << 2; proto = ((struct ip6_ext *)ulp)->ip6e_nxt; ulp = NULL; break; case IPPROTO_ESP: /* RFC 2406 */ PULLUP_TO(hlen, ulp, uint32_t); /* SPI, Seq# */ /* Anything past Seq# is variable length and * data past this ext. header is encrypted. */ ext_hd |= EXT_ESP; break; case IPPROTO_NONE: /* RFC 2460 */ PULLUP_TO(hlen, ulp, struct ip6_ext); /* Packet ends here. if ip6e_len!=0 octets * must be ignored. */ break; case IPPROTO_OSPFIGP: /* XXX OSPF header check? */ PULLUP_TO(hlen, ulp, struct ip6_ext); break; case IPPROTO_PIM: /* XXX PIM header check? */ PULLUP_TO(hlen, ulp, struct pim); break; case IPPROTO_IPV6: /* RFC 2893 */ PULLUP_TO(hlen, ulp, struct ip6_hdr); break; case IPPROTO_IPV4: /* RFC 2893 */ PULLUP_TO(hlen, ulp, struct ip); break; default: printf("IPFW2: IPV6 - Unknown Extension " "Header(%d), ext_hd=%x\n", proto, ext_hd); if (fw_deny_unknown_exthdrs) return (IP_FW_DENY); PULLUP_TO(hlen, ulp, struct ip6_ext); break; } /*switch */ } args->f_id.src_ip6 = mtod(m,struct ip6_hdr *)->ip6_src; args->f_id.dst_ip6 = mtod(m,struct ip6_hdr *)->ip6_dst; args->f_id.src_ip = 0; args->f_id.dst_ip = 0; args->f_id.flow_id6 = ntohl(mtod(m, struct ip6_hdr *)->ip6_flow); } else if (pktlen >= sizeof(struct ip) && (args->eh == NULL || ntohs(args->eh->ether_type) == ETHERTYPE_IP) && mtod(m, struct ip *)->ip_v == 4) { is_ipv4 = 1; ip = mtod(m, struct ip *); hlen = ip->ip_hl << 2; args->f_id.addr_type = 4; /* * Collect parameters into local variables for faster matching. */ proto = ip->ip_p; src_ip = ip->ip_src; dst_ip = ip->ip_dst; if (args->eh != NULL) { /* layer 2 packets are as on the wire */ offset = ntohs(ip->ip_off) & IP_OFFMASK; ip_len = ntohs(ip->ip_len); } else { offset = ip->ip_off & IP_OFFMASK; ip_len = ip->ip_len; } pktlen = ip_len < pktlen ? ip_len : pktlen; if (offset == 0) { switch (proto) { case IPPROTO_TCP: PULLUP_TO(hlen, ulp, struct tcphdr); dst_port = TCP(ulp)->th_dport; src_port = TCP(ulp)->th_sport; args->f_id.flags = TCP(ulp)->th_flags; break; case IPPROTO_UDP: PULLUP_TO(hlen, ulp, struct udphdr); dst_port = UDP(ulp)->uh_dport; src_port = UDP(ulp)->uh_sport; break; case IPPROTO_ICMP: PULLUP_TO(hlen, ulp, struct icmphdr); args->f_id.flags = ICMP(ulp)->icmp_type; break; default: break; } } args->f_id.src_ip = ntohl(src_ip.s_addr); args->f_id.dst_ip = ntohl(dst_ip.s_addr); } #undef PULLUP_TO if (proto) { /* we may have port numbers, store them */ args->f_id.proto = proto; args->f_id.src_port = src_port = ntohs(src_port); args->f_id.dst_port = dst_port = ntohs(dst_port); } IPFW_RLOCK(chain); mtag = m_tag_find(m, PACKET_TAG_DIVERT, NULL); if (args->rule) { /* * Packet has already been tagged. Look for the next rule * to restart processing. * * If fw_one_pass != 0 then just accept it. * XXX should not happen here, but optimized out in * the caller. */ if (fw_one_pass) { IPFW_RUNLOCK(chain); return (IP_FW_PASS); } f = args->rule->next_rule; if (f == NULL) f = lookup_next_rule(args->rule); } else { /* * Find the starting rule. It can be either the first * one, or the one after divert_rule if asked so. */ int skipto = mtag ? divert_cookie(mtag) : 0; f = chain->rules; if (args->eh == NULL && skipto != 0) { if (skipto >= IPFW_DEFAULT_RULE) { IPFW_RUNLOCK(chain); return (IP_FW_DENY); /* invalid */ } while (f && f->rulenum <= skipto) f = f->next; if (f == NULL) { /* drop packet */ IPFW_RUNLOCK(chain); return (IP_FW_DENY); } } } /* reset divert rule to avoid confusion later */ if (mtag) { divinput_flags = divert_info(mtag) & (IP_FW_DIVERT_OUTPUT_FLAG | IP_FW_DIVERT_LOOPBACK_FLAG); m_tag_delete(m, mtag); } /* * Now scan the rules, and parse microinstructions for each rule. */ for (; f; f = f->next) { ipfw_insn *cmd; uint32_t tablearg = 0; int l, cmdlen, skip_or; /* skip rest of OR block */ again: if (set_disable & (1 << f->set) ) continue; skip_or = 0; for (l = f->cmd_len, cmd = f->cmd ; l > 0 ; l -= cmdlen, cmd += cmdlen) { int match; /* * check_body is a jump target used when we find a * CHECK_STATE, and need to jump to the body of * the target rule. */ check_body: cmdlen = F_LEN(cmd); /* * An OR block (insn_1 || .. || insn_n) has the * F_OR bit set in all but the last instruction. * The first match will set "skip_or", and cause * the following instructions to be skipped until * past the one with the F_OR bit clear. */ if (skip_or) { /* skip this instruction */ if ((cmd->len & F_OR) == 0) skip_or = 0; /* next one is good */ continue; } match = 0; /* set to 1 if we succeed */ switch (cmd->opcode) { /* * The first set of opcodes compares the packet's * fields with some pattern, setting 'match' if a * match is found. At the end of the loop there is * logic to deal with F_NOT and F_OR flags associated * with the opcode. */ case O_NOP: match = 1; break; case O_FORWARD_MAC: printf("ipfw: opcode %d unimplemented\n", cmd->opcode); break; case O_GID: case O_UID: case O_JAIL: /* * We only check offset == 0 && proto != 0, * as this ensures that we have a * packet with the ports info. */ if (offset!=0) break; if (is_ipv6) /* XXX to be fixed later */ break; if (proto == IPPROTO_TCP || proto == IPPROTO_UDP) match = check_uidgid( (ipfw_insn_u32 *)cmd, proto, oif, dst_ip, dst_port, src_ip, src_port, &fw_ugid_cache, &ugid_lookup, args->inp); break; case O_RECV: match = iface_match(m->m_pkthdr.rcvif, (ipfw_insn_if *)cmd); break; case O_XMIT: match = iface_match(oif, (ipfw_insn_if *)cmd); break; case O_VIA: match = iface_match(oif ? oif : m->m_pkthdr.rcvif, (ipfw_insn_if *)cmd); break; case O_MACADDR2: if (args->eh != NULL) { /* have MAC header */ u_int32_t *want = (u_int32_t *) ((ipfw_insn_mac *)cmd)->addr; u_int32_t *mask = (u_int32_t *) ((ipfw_insn_mac *)cmd)->mask; u_int32_t *hdr = (u_int32_t *)args->eh; match = ( want[0] == (hdr[0] & mask[0]) && want[1] == (hdr[1] & mask[1]) && want[2] == (hdr[2] & mask[2]) ); } break; case O_MAC_TYPE: if (args->eh != NULL) { u_int16_t t = ntohs(args->eh->ether_type); u_int16_t *p = ((ipfw_insn_u16 *)cmd)->ports; int i; for (i = cmdlen - 1; !match && i>0; i--, p += 2) match = (t>=p[0] && t<=p[1]); } break; case O_FRAG: match = (offset != 0); break; case O_IN: /* "out" is "not in" */ match = (oif == NULL); break; case O_LAYER2: match = (args->eh != NULL); break; case O_DIVERTED: match = (cmd->arg1 & 1 && divinput_flags & IP_FW_DIVERT_LOOPBACK_FLAG) || (cmd->arg1 & 2 && divinput_flags & IP_FW_DIVERT_OUTPUT_FLAG); break; case O_PROTO: /* * We do not allow an arg of 0 so the * check of "proto" only suffices. */ match = (proto == cmd->arg1); break; case O_IP_SRC: match = is_ipv4 && (((ipfw_insn_ip *)cmd)->addr.s_addr == src_ip.s_addr); break; case O_IP_SRC_LOOKUP: case O_IP_DST_LOOKUP: if (is_ipv4) { uint32_t a = (cmd->opcode == O_IP_DST_LOOKUP) ? dst_ip.s_addr : src_ip.s_addr; uint32_t v; match = lookup_table(chain, cmd->arg1, a, &v); if (!match) break; if (cmdlen == F_INSN_SIZE(ipfw_insn_u32)) match = ((ipfw_insn_u32 *)cmd)->d[0] == v; else tablearg = v; } break; case O_IP_SRC_MASK: case O_IP_DST_MASK: if (is_ipv4) { uint32_t a = (cmd->opcode == O_IP_DST_MASK) ? dst_ip.s_addr : src_ip.s_addr; uint32_t *p = ((ipfw_insn_u32 *)cmd)->d; int i = cmdlen-1; for (; !match && i>0; i-= 2, p+= 2) match = (p[0] == (a & p[1])); } break; case O_IP_SRC_ME: if (is_ipv4) { struct ifnet *tif; INADDR_TO_IFP(src_ip, tif); match = (tif != NULL); } break; case O_IP_DST_SET: case O_IP_SRC_SET: if (is_ipv4) { u_int32_t *d = (u_int32_t *)(cmd+1); u_int32_t addr = cmd->opcode == O_IP_DST_SET ? args->f_id.dst_ip : args->f_id.src_ip; if (addr < d[0]) break; addr -= d[0]; /* subtract base */ match = (addr < cmd->arg1) && ( d[ 1 + (addr>>5)] & (1<<(addr & 0x1f)) ); } break; case O_IP_DST: match = is_ipv4 && (((ipfw_insn_ip *)cmd)->addr.s_addr == dst_ip.s_addr); break; case O_IP_DST_ME: if (is_ipv4) { struct ifnet *tif; INADDR_TO_IFP(dst_ip, tif); match = (tif != NULL); } break; case O_IP_SRCPORT: case O_IP_DSTPORT: /* * offset == 0 && proto != 0 is enough * to guarantee that we have a * packet with port info. */ if ((proto==IPPROTO_UDP || proto==IPPROTO_TCP) && offset == 0) { u_int16_t x = (cmd->opcode == O_IP_SRCPORT) ? src_port : dst_port ; u_int16_t *p = ((ipfw_insn_u16 *)cmd)->ports; int i; for (i = cmdlen - 1; !match && i>0; i--, p += 2) match = (x>=p[0] && x<=p[1]); } break; case O_ICMPTYPE: match = (offset == 0 && proto==IPPROTO_ICMP && icmptype_match(ICMP(ulp), (ipfw_insn_u32 *)cmd) ); break; #ifdef INET6 case O_ICMP6TYPE: match = is_ipv6 && offset == 0 && proto==IPPROTO_ICMPV6 && icmp6type_match( ICMP6(ulp)->icmp6_type, (ipfw_insn_u32 *)cmd); break; #endif /* INET6 */ case O_IPOPT: match = (is_ipv4 && ipopts_match(mtod(m, struct ip *), cmd) ); break; case O_IPVER: match = (is_ipv4 && cmd->arg1 == mtod(m, struct ip *)->ip_v); break; case O_IPID: case O_IPLEN: case O_IPTTL: if (is_ipv4) { /* only for IP packets */ uint16_t x; uint16_t *p; int i; if (cmd->opcode == O_IPLEN) x = ip_len; else if (cmd->opcode == O_IPTTL) x = mtod(m, struct ip *)->ip_ttl; else /* must be IPID */ x = ntohs(mtod(m, struct ip *)->ip_id); if (cmdlen == 1) { match = (cmd->arg1 == x); break; } /* otherwise we have ranges */ p = ((ipfw_insn_u16 *)cmd)->ports; i = cmdlen - 1; for (; !match && i>0; i--, p += 2) match = (x >= p[0] && x <= p[1]); } break; case O_IPPRECEDENCE: match = (is_ipv4 && (cmd->arg1 == (mtod(m, struct ip *)->ip_tos & 0xe0)) ); break; case O_IPTOS: match = (is_ipv4 && flags_match(cmd, mtod(m, struct ip *)->ip_tos)); break; case O_TCPDATALEN: if (proto == IPPROTO_TCP && offset == 0) { struct tcphdr *tcp; uint16_t x; uint16_t *p; int i; tcp = TCP(ulp); x = ip_len - ((ip->ip_hl + tcp->th_off) << 2); if (cmdlen == 1) { match = (cmd->arg1 == x); break; } /* otherwise we have ranges */ p = ((ipfw_insn_u16 *)cmd)->ports; i = cmdlen - 1; for (; !match && i>0; i--, p += 2) match = (x >= p[0] && x <= p[1]); } break; case O_TCPFLAGS: match = (proto == IPPROTO_TCP && offset == 0 && flags_match(cmd, TCP(ulp)->th_flags)); break; case O_TCPOPTS: match = (proto == IPPROTO_TCP && offset == 0 && tcpopts_match(TCP(ulp), cmd)); break; case O_TCPSEQ: match = (proto == IPPROTO_TCP && offset == 0 && ((ipfw_insn_u32 *)cmd)->d[0] == TCP(ulp)->th_seq); break; case O_TCPACK: match = (proto == IPPROTO_TCP && offset == 0 && ((ipfw_insn_u32 *)cmd)->d[0] == TCP(ulp)->th_ack); break; case O_TCPWIN: match = (proto == IPPROTO_TCP && offset == 0 && cmd->arg1 == TCP(ulp)->th_win); break; case O_ESTAB: /* reject packets which have SYN only */ /* XXX should i also check for TH_ACK ? */ match = (proto == IPPROTO_TCP && offset == 0 && (TCP(ulp)->th_flags & (TH_RST | TH_ACK | TH_SYN)) != TH_SYN); break; case O_ALTQ: { struct altq_tag *at; ipfw_insn_altq *altq = (ipfw_insn_altq *)cmd; match = 1; mtag = m_tag_find(m, PACKET_TAG_PF_QID, NULL); if (mtag != NULL) break; mtag = m_tag_get(PACKET_TAG_PF_QID, sizeof(struct altq_tag), M_NOWAIT); if (mtag == NULL) { /* * Let the packet fall back to the * default ALTQ. */ break; } at = (struct altq_tag *)(mtag+1); at->qid = altq->qid; if (is_ipv4) at->af = AF_INET; else at->af = AF_LINK; at->hdr = ip; m_tag_prepend(m, mtag); break; } case O_LOG: if (fw_verbose) ipfw_log(f, hlen, args, m, oif, offset); match = 1; break; case O_PROB: match = (random()<((ipfw_insn_u32 *)cmd)->d[0]); break; case O_VERREVPATH: /* Outgoing packets automatically pass/match */ match = ((oif != NULL) || (m->m_pkthdr.rcvif == NULL) || ( #ifdef INET6 is_ipv6 ? verify_path6(&(args->f_id.src_ip6), m->m_pkthdr.rcvif) : #endif verify_path(src_ip, m->m_pkthdr.rcvif))); break; case O_VERSRCREACH: /* Outgoing packets automatically pass/match */ match = (hlen > 0 && ((oif != NULL) || #ifdef INET6 is_ipv6 ? verify_path6(&(args->f_id.src_ip6), NULL) : #endif verify_path(src_ip, NULL))); break; case O_ANTISPOOF: /* Outgoing packets automatically pass/match */ if (oif == NULL && hlen > 0 && ( (is_ipv4 && in_localaddr(src_ip)) #ifdef INET6 || (is_ipv6 && in6_localaddr(&(args->f_id.src_ip6))) #endif )) match = #ifdef INET6 is_ipv6 ? verify_path6( &(args->f_id.src_ip6), m->m_pkthdr.rcvif) : #endif verify_path(src_ip, m->m_pkthdr.rcvif); else match = 1; break; case O_IPSEC: #ifdef FAST_IPSEC match = (m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL) != NULL); #endif #ifdef IPSEC match = (ipsec_getnhist(m) != 0); #endif /* otherwise no match */ break; #ifdef INET6 case O_IP6_SRC: match = is_ipv6 && IN6_ARE_ADDR_EQUAL(&args->f_id.src_ip6, &((ipfw_insn_ip6 *)cmd)->addr6); break; case O_IP6_DST: match = is_ipv6 && IN6_ARE_ADDR_EQUAL(&args->f_id.dst_ip6, &((ipfw_insn_ip6 *)cmd)->addr6); break; case O_IP6_SRC_MASK: if (is_ipv6) { ipfw_insn_ip6 *te = (ipfw_insn_ip6 *)cmd; struct in6_addr p = args->f_id.src_ip6; APPLY_MASK(&p, &te->mask6); match = IN6_ARE_ADDR_EQUAL(&te->addr6, &p); } break; case O_IP6_DST_MASK: if (is_ipv6) { ipfw_insn_ip6 *te = (ipfw_insn_ip6 *)cmd; struct in6_addr p = args->f_id.dst_ip6; APPLY_MASK(&p, &te->mask6); match = IN6_ARE_ADDR_EQUAL(&te->addr6, &p); } break; case O_IP6_SRC_ME: match= is_ipv6 && search_ip6_addr_net(&args->f_id.src_ip6); break; case O_IP6_DST_ME: match= is_ipv6 && search_ip6_addr_net(&args->f_id.dst_ip6); break; case O_FLOW6ID: match = is_ipv6 && flow6id_match(args->f_id.flow_id6, (ipfw_insn_u32 *) cmd); break; case O_EXT_HDR: match = is_ipv6 && (ext_hd & ((ipfw_insn *) cmd)->arg1); break; case O_IP6: match = is_ipv6; break; #endif case O_IP4: match = is_ipv4; break; case O_TAG: { uint32_t tag = (cmd->arg1 == IP_FW_TABLEARG) ? tablearg : cmd->arg1; /* Packet is already tagged with this tag? */ mtag = m_tag_locate(m, MTAG_IPFW, tag, NULL); /* We have `untag' action when F_NOT flag is * present. And we must remove this mtag from * mbuf and reset `match' to zero (`match' will * be inversed later). * Otherwise we should allocate new mtag and * push it into mbuf. */ if (cmd->len & F_NOT) { /* `untag' action */ if (mtag != NULL) m_tag_delete(m, mtag); } else if (mtag == NULL) { if ((mtag = m_tag_alloc(MTAG_IPFW, tag, 0, M_NOWAIT)) != NULL) m_tag_prepend(m, mtag); } match = (cmd->len & F_NOT) ? 0: 1; break; } case O_TAGGED: { uint32_t tag = (cmd->arg1 == IP_FW_TABLEARG) ? tablearg : cmd->arg1; if (cmdlen == 1) { match = m_tag_locate(m, MTAG_IPFW, tag, NULL) != NULL; break; } /* we have ranges */ for (mtag = m_tag_first(m); mtag != NULL && !match; mtag = m_tag_next(m, mtag)) { uint16_t *p; int i; if (mtag->m_tag_cookie != MTAG_IPFW) continue; p = ((ipfw_insn_u16 *)cmd)->ports; i = cmdlen - 1; for(; !match && i > 0; i--, p += 2) match = mtag->m_tag_id >= p[0] && mtag->m_tag_id <= p[1]; } break; } /* * The second set of opcodes represents 'actions', * i.e. the terminal part of a rule once the packet * matches all previous patterns. * Typically there is only one action for each rule, * and the opcode is stored at the end of the rule * (but there are exceptions -- see below). * * In general, here we set retval and terminate the * outer loop (would be a 'break 3' in some language, * but we need to do a 'goto done'). * * Exceptions: * O_COUNT and O_SKIPTO actions: * instead of terminating, we jump to the next rule * ('goto next_rule', equivalent to a 'break 2'), * or to the SKIPTO target ('goto again' after * having set f, cmd and l), respectively. * * O_TAG, O_LOG and O_ALTQ action parameters: * perform some action and set match = 1; * * O_LIMIT and O_KEEP_STATE: these opcodes are * not real 'actions', and are stored right * before the 'action' part of the rule. * These opcodes try to install an entry in the * state tables; if successful, we continue with * the next opcode (match=1; break;), otherwise * the packet * must be dropped * ('goto done' after setting retval); * * O_PROBE_STATE and O_CHECK_STATE: these opcodes * cause a lookup of the state table, and a jump * to the 'action' part of the parent rule * ('goto check_body') if an entry is found, or * (CHECK_STATE only) a jump to the next rule if * the entry is not found ('goto next_rule'). * The result of the lookup is cached to make * further instances of these opcodes are * effectively NOPs. */ case O_LIMIT: case O_KEEP_STATE: if (install_state(f, (ipfw_insn_limit *)cmd, args, tablearg)) { retval = IP_FW_DENY; goto done; /* error/limit violation */ } match = 1; break; case O_PROBE_STATE: case O_CHECK_STATE: /* * dynamic rules are checked at the first * keep-state or check-state occurrence, * with the result being stored in dyn_dir. * The compiler introduces a PROBE_STATE * instruction for us when we have a * KEEP_STATE (because PROBE_STATE needs * to be run first). */ if (dyn_dir == MATCH_UNKNOWN && (q = lookup_dyn_rule(&args->f_id, &dyn_dir, proto == IPPROTO_TCP ? TCP(ulp) : NULL)) != NULL) { /* * Found dynamic entry, update stats * and jump to the 'action' part of * the parent rule. */ q->pcnt++; q->bcnt += pktlen; f = q->rule; cmd = ACTION_PTR(f); l = f->cmd_len - f->act_ofs; IPFW_DYN_UNLOCK(); goto check_body; } /* * Dynamic entry not found. If CHECK_STATE, * skip to next rule, if PROBE_STATE just * ignore and continue with next opcode. */ if (cmd->opcode == O_CHECK_STATE) goto next_rule; match = 1; break; case O_ACCEPT: retval = 0; /* accept */ goto done; case O_PIPE: case O_QUEUE: args->rule = f; /* report matching rule */ if (cmd->arg1 == IP_FW_TABLEARG) args->cookie = tablearg; else args->cookie = cmd->arg1; retval = IP_FW_DUMMYNET; goto done; case O_DIVERT: case O_TEE: { struct divert_tag *dt; if (args->eh) /* not on layer 2 */ break; mtag = m_tag_get(PACKET_TAG_DIVERT, sizeof(struct divert_tag), M_NOWAIT); if (mtag == NULL) { /* XXX statistic */ /* drop packet */ IPFW_RUNLOCK(chain); return (IP_FW_DENY); } dt = (struct divert_tag *)(mtag+1); dt->cookie = f->rulenum; if (cmd->arg1 == IP_FW_TABLEARG) dt->info = tablearg; else dt->info = cmd->arg1; m_tag_prepend(m, mtag); retval = (cmd->opcode == O_DIVERT) ? IP_FW_DIVERT : IP_FW_TEE; goto done; } case O_COUNT: case O_SKIPTO: f->pcnt++; /* update stats */ f->bcnt += pktlen; f->timestamp = time_second; if (cmd->opcode == O_COUNT) goto next_rule; /* handle skipto */ if (f->next_rule == NULL) lookup_next_rule(f); f = f->next_rule; goto again; case O_REJECT: /* * Drop the packet and send a reject notice * if the packet is not ICMP (or is an ICMP * query), and it is not multicast/broadcast. */ if (hlen > 0 && is_ipv4 && offset == 0 && (proto != IPPROTO_ICMP || is_icmp_query(ICMP(ulp))) && !(m->m_flags & (M_BCAST|M_MCAST)) && !IN_MULTICAST(ntohl(dst_ip.s_addr))) { send_reject(args, cmd->arg1, offset,ip_len); m = args->m; } /* FALLTHROUGH */ #ifdef INET6 case O_UNREACH6: if (hlen > 0 && is_ipv6 && (proto != IPPROTO_ICMPV6 || (is_icmp6_query(args->f_id.flags) == 1)) && !(m->m_flags & (M_BCAST|M_MCAST)) && !IN6_IS_ADDR_MULTICAST(&args->f_id.dst_ip6)) { send_reject6(args, cmd->arg1, offset, hlen); m = args->m; } /* FALLTHROUGH */ #endif case O_DENY: retval = IP_FW_DENY; goto done; case O_FORWARD_IP: if (args->eh) /* not valid on layer2 pkts */ break; if (!q || dyn_dir == MATCH_FORWARD) args->next_hop = &((ipfw_insn_sa *)cmd)->sa; retval = IP_FW_PASS; goto done; case O_NETGRAPH: case O_NGTEE: args->rule = f; /* report matching rule */ if (cmd->arg1 == IP_FW_TABLEARG) args->cookie = tablearg; else args->cookie = cmd->arg1; retval = (cmd->opcode == O_NETGRAPH) ? IP_FW_NETGRAPH : IP_FW_NGTEE; goto done; default: panic("-- unknown opcode %d\n", cmd->opcode); } /* end of switch() on opcodes */ if (cmd->len & F_NOT) match = !match; if (match) { if (cmd->len & F_OR) skip_or = 1; } else { if (!(cmd->len & F_OR)) /* not an OR block, */ break; /* try next rule */ } } /* end of inner for, scan opcodes */ next_rule:; /* try next rule */ } /* end of outer for, scan rules */ printf("ipfw: ouch!, skip past end of rules, denying packet\n"); IPFW_RUNLOCK(chain); return (IP_FW_DENY); done: /* Update statistics */ f->pcnt++; f->bcnt += pktlen; f->timestamp = time_second; IPFW_RUNLOCK(chain); return (retval); pullup_failed: if (fw_verbose) printf("ipfw: pullup failed\n"); return (IP_FW_DENY); } /* * When a rule is added/deleted, clear the next_rule pointers in all rules. * These will be reconstructed on the fly as packets are matched. */ static void flush_rule_ptrs(struct ip_fw_chain *chain) { struct ip_fw *rule; IPFW_WLOCK_ASSERT(chain); for (rule = chain->rules; rule; rule = rule->next) rule->next_rule = NULL; } /* * When pipes/queues are deleted, clear the "pipe_ptr" pointer to a given * pipe/queue, or to all of them (match == NULL). */ void flush_pipe_ptrs(struct dn_flow_set *match) { struct ip_fw *rule; IPFW_WLOCK(&layer3_chain); for (rule = layer3_chain.rules; rule; rule = rule->next) { ipfw_insn_pipe *cmd = (ipfw_insn_pipe *)ACTION_PTR(rule); if (cmd->o.opcode != O_PIPE && cmd->o.opcode != O_QUEUE) continue; /* * XXX Use bcmp/bzero to handle pipe_ptr to overcome * possible alignment problems on 64-bit architectures. * This code is seldom used so we do not worry too * much about efficiency. */ if (match == NULL || !bcmp(&cmd->pipe_ptr, &match, sizeof(match)) ) bzero(&cmd->pipe_ptr, sizeof(cmd->pipe_ptr)); } IPFW_WUNLOCK(&layer3_chain); } /* * Add a new rule to the list. Copy the rule into a malloc'ed area, then * possibly create a rule number and add the rule to the list. * Update the rule_number in the input struct so the caller knows it as well. */ static int add_rule(struct ip_fw_chain *chain, struct ip_fw *input_rule) { struct ip_fw *rule, *f, *prev; int l = RULESIZE(input_rule); if (chain->rules == NULL && input_rule->rulenum != IPFW_DEFAULT_RULE) return (EINVAL); rule = malloc(l, M_IPFW, M_NOWAIT | M_ZERO); if (rule == NULL) return (ENOSPC); bcopy(input_rule, rule, l); rule->next = NULL; rule->next_rule = NULL; rule->pcnt = 0; rule->bcnt = 0; rule->timestamp = 0; IPFW_WLOCK(chain); if (chain->rules == NULL) { /* default rule */ chain->rules = rule; goto done; } /* * If rulenum is 0, find highest numbered rule before the * default rule, and add autoinc_step */ if (autoinc_step < 1) autoinc_step = 1; else if (autoinc_step > 1000) autoinc_step = 1000; if (rule->rulenum == 0) { /* * locate the highest numbered rule before default */ for (f = chain->rules; f; f = f->next) { if (f->rulenum == IPFW_DEFAULT_RULE) break; rule->rulenum = f->rulenum; } if (rule->rulenum < IPFW_DEFAULT_RULE - autoinc_step) rule->rulenum += autoinc_step; input_rule->rulenum = rule->rulenum; } /* * Now insert the new rule in the right place in the sorted list. */ for (prev = NULL, f = chain->rules; f; prev = f, f = f->next) { if (f->rulenum > rule->rulenum) { /* found the location */ if (prev) { rule->next = f; prev->next = rule; } else { /* head insert */ rule->next = chain->rules; chain->rules = rule; } break; } } flush_rule_ptrs(chain); done: static_count++; static_len += l; IPFW_WUNLOCK(chain); DEB(printf("ipfw: installed rule %d, static count now %d\n", rule->rulenum, static_count);) return (0); } /** * Remove a static rule (including derived * dynamic rules) * and place it on the ``reap list'' for later reclamation. * The caller is in charge of clearing rule pointers to avoid * dangling pointers. * @return a pointer to the next entry. * Arguments are not checked, so they better be correct. */ static struct ip_fw * remove_rule(struct ip_fw_chain *chain, struct ip_fw *rule, struct ip_fw *prev) { struct ip_fw *n; int l = RULESIZE(rule); IPFW_WLOCK_ASSERT(chain); n = rule->next; IPFW_DYN_LOCK(); remove_dyn_rule(rule, NULL /* force removal */); IPFW_DYN_UNLOCK(); if (prev == NULL) chain->rules = n; else prev->next = n; static_count--; static_len -= l; rule->next = chain->reap; chain->reap = rule; return n; } /** * Reclaim storage associated with a list of rules. This is * typically the list created using remove_rule. */ static void reap_rules(struct ip_fw *head) { struct ip_fw *rule; while ((rule = head) != NULL) { head = head->next; if (DUMMYNET_LOADED) ip_dn_ruledel_ptr(rule); free(rule, M_IPFW); } } /* * Remove all rules from a chain (except rules in set RESVD_SET * unless kill_default = 1). The caller is responsible for * reclaiming storage for the rules left in chain->reap. */ static void free_chain(struct ip_fw_chain *chain, int kill_default) { struct ip_fw *prev, *rule; IPFW_WLOCK_ASSERT(chain); flush_rule_ptrs(chain); /* more efficient to do outside the loop */ for (prev = NULL, rule = chain->rules; rule ; ) if (kill_default || rule->set != RESVD_SET) rule = remove_rule(chain, rule, prev); else { prev = rule; rule = rule->next; } } /** * Remove all rules with given number, and also do set manipulation. * Assumes chain != NULL && *chain != NULL. * * The argument is an u_int32_t. The low 16 bit are the rule or set number, * the next 8 bits are the new set, the top 8 bits are the command: * * 0 delete rules with given number * 1 delete rules with given set number * 2 move rules with given number to new set * 3 move rules with given set number to new set * 4 swap sets with given numbers */ static int del_entry(struct ip_fw_chain *chain, u_int32_t arg) { struct ip_fw *prev = NULL, *rule; u_int16_t rulenum; /* rule or old_set */ u_int8_t cmd, new_set; rulenum = arg & 0xffff; cmd = (arg >> 24) & 0xff; new_set = (arg >> 16) & 0xff; if (cmd > 4) return EINVAL; if (new_set > RESVD_SET) return EINVAL; if (cmd == 0 || cmd == 2) { if (rulenum >= IPFW_DEFAULT_RULE) return EINVAL; } else { if (rulenum > RESVD_SET) /* old_set */ return EINVAL; } IPFW_WLOCK(chain); rule = chain->rules; chain->reap = NULL; switch (cmd) { case 0: /* delete rules with given number */ /* * locate first rule to delete */ for (; rule->rulenum < rulenum; prev = rule, rule = rule->next) ; if (rule->rulenum != rulenum) { IPFW_WUNLOCK(chain); return EINVAL; } /* * flush pointers outside the loop, then delete all matching * rules. prev remains the same throughout the cycle. */ flush_rule_ptrs(chain); while (rule->rulenum == rulenum) rule = remove_rule(chain, rule, prev); break; case 1: /* delete all rules with given set number */ flush_rule_ptrs(chain); rule = chain->rules; while (rule->rulenum < IPFW_DEFAULT_RULE) if (rule->set == rulenum) rule = remove_rule(chain, rule, prev); else { prev = rule; rule = rule->next; } break; case 2: /* move rules with given number to new set */ rule = chain->rules; for (; rule->rulenum < IPFW_DEFAULT_RULE; rule = rule->next) if (rule->rulenum == rulenum) rule->set = new_set; break; case 3: /* move rules with given set number to new set */ for (; rule->rulenum < IPFW_DEFAULT_RULE; rule = rule->next) if (rule->set == rulenum) rule->set = new_set; break; case 4: /* swap two sets */ for (; rule->rulenum < IPFW_DEFAULT_RULE; rule = rule->next) if (rule->set == rulenum) rule->set = new_set; else if (rule->set == new_set) rule->set = rulenum; break; } /* * Look for rules to reclaim. We grab the list before * releasing the lock then reclaim them w/o the lock to * avoid a LOR with dummynet. */ rule = chain->reap; chain->reap = NULL; IPFW_WUNLOCK(chain); if (rule) reap_rules(rule); return 0; } /* * Clear counters for a specific rule. * The enclosing "table" is assumed locked. */ static void clear_counters(struct ip_fw *rule, int log_only) { ipfw_insn_log *l = (ipfw_insn_log *)ACTION_PTR(rule); if (log_only == 0) { rule->bcnt = rule->pcnt = 0; rule->timestamp = 0; } if (l->o.opcode == O_LOG) l->log_left = l->max_log; } /** * Reset some or all counters on firewall rules. * @arg frwl is null to clear all entries, or contains a specific * rule number. * @arg log_only is 1 if we only want to reset logs, zero otherwise. */ static int zero_entry(struct ip_fw_chain *chain, int rulenum, int log_only) { struct ip_fw *rule; char *msg; IPFW_WLOCK(chain); if (rulenum == 0) { norule_counter = 0; for (rule = chain->rules; rule; rule = rule->next) clear_counters(rule, log_only); msg = log_only ? "ipfw: All logging counts reset.\n" : "ipfw: Accounting cleared.\n"; } else { int cleared = 0; /* * We can have multiple rules with the same number, so we * need to clear them all. */ for (rule = chain->rules; rule; rule = rule->next) if (rule->rulenum == rulenum) { while (rule && rule->rulenum == rulenum) { clear_counters(rule, log_only); rule = rule->next; } cleared = 1; break; } if (!cleared) { /* we did not find any matching rules */ IPFW_WUNLOCK(chain); return (EINVAL); } msg = log_only ? "ipfw: Entry %d logging count reset.\n" : "ipfw: Entry %d cleared.\n"; } IPFW_WUNLOCK(chain); if (fw_verbose) log(LOG_SECURITY | LOG_NOTICE, msg, rulenum); return (0); } /* * Check validity of the structure before insert. * Fortunately rules are simple, so this mostly need to check rule sizes. */ static int check_ipfw_struct(struct ip_fw *rule, int size) { int l, cmdlen = 0; int have_action=0; ipfw_insn *cmd; if (size < sizeof(*rule)) { printf("ipfw: rule too short\n"); return (EINVAL); } /* first, check for valid size */ l = RULESIZE(rule); if (l != size) { printf("ipfw: size mismatch (have %d want %d)\n", size, l); return (EINVAL); } if (rule->act_ofs >= rule->cmd_len) { printf("ipfw: bogus action offset (%u > %u)\n", rule->act_ofs, rule->cmd_len - 1); return (EINVAL); } /* * Now go for the individual checks. Very simple ones, basically only * instruction sizes. */ for (l = rule->cmd_len, cmd = rule->cmd ; l > 0 ; l -= cmdlen, cmd += cmdlen) { cmdlen = F_LEN(cmd); if (cmdlen > l) { printf("ipfw: opcode %d size truncated\n", cmd->opcode); return EINVAL; } DEB(printf("ipfw: opcode %d\n", cmd->opcode);) switch (cmd->opcode) { case O_PROBE_STATE: case O_KEEP_STATE: case O_PROTO: case O_IP_SRC_ME: case O_IP_DST_ME: case O_LAYER2: case O_IN: case O_FRAG: case O_DIVERTED: case O_IPOPT: case O_IPTOS: case O_IPPRECEDENCE: case O_IPVER: case O_TCPWIN: case O_TCPFLAGS: case O_TCPOPTS: case O_ESTAB: case O_VERREVPATH: case O_VERSRCREACH: case O_ANTISPOOF: case O_IPSEC: #ifdef INET6 case O_IP6_SRC_ME: case O_IP6_DST_ME: case O_EXT_HDR: case O_IP6: #endif case O_IP4: case O_TAG: if (cmdlen != F_INSN_SIZE(ipfw_insn)) goto bad_size; break; case O_UID: case O_GID: case O_JAIL: case O_IP_SRC: case O_IP_DST: case O_TCPSEQ: case O_TCPACK: case O_PROB: case O_ICMPTYPE: if (cmdlen != F_INSN_SIZE(ipfw_insn_u32)) goto bad_size; break; case O_LIMIT: if (cmdlen != F_INSN_SIZE(ipfw_insn_limit)) goto bad_size; break; case O_LOG: if (cmdlen != F_INSN_SIZE(ipfw_insn_log)) goto bad_size; ((ipfw_insn_log *)cmd)->log_left = ((ipfw_insn_log *)cmd)->max_log; break; case O_IP_SRC_MASK: case O_IP_DST_MASK: /* only odd command lengths */ if ( !(cmdlen & 1) || cmdlen > 31) goto bad_size; break; case O_IP_SRC_SET: case O_IP_DST_SET: if (cmd->arg1 == 0 || cmd->arg1 > 256) { printf("ipfw: invalid set size %d\n", cmd->arg1); return EINVAL; } if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + (cmd->arg1+31)/32 ) goto bad_size; break; case O_IP_SRC_LOOKUP: case O_IP_DST_LOOKUP: if (cmd->arg1 >= IPFW_TABLES_MAX) { printf("ipfw: invalid table number %d\n", cmd->arg1); return (EINVAL); } if (cmdlen != F_INSN_SIZE(ipfw_insn) && cmdlen != F_INSN_SIZE(ipfw_insn_u32)) goto bad_size; break; case O_MACADDR2: if (cmdlen != F_INSN_SIZE(ipfw_insn_mac)) goto bad_size; break; case O_NOP: case O_IPID: case O_IPTTL: case O_IPLEN: case O_TCPDATALEN: case O_TAGGED: if (cmdlen < 1 || cmdlen > 31) goto bad_size; break; case O_MAC_TYPE: case O_IP_SRCPORT: case O_IP_DSTPORT: /* XXX artificial limit, 30 port pairs */ if (cmdlen < 2 || cmdlen > 31) goto bad_size; break; case O_RECV: case O_XMIT: case O_VIA: if (cmdlen != F_INSN_SIZE(ipfw_insn_if)) goto bad_size; break; case O_ALTQ: if (cmdlen != F_INSN_SIZE(ipfw_insn_altq)) goto bad_size; break; case O_PIPE: case O_QUEUE: if (cmdlen != F_INSN_SIZE(ipfw_insn_pipe)) goto bad_size; goto check_action; case O_FORWARD_IP: #ifdef IPFIREWALL_FORWARD if (cmdlen != F_INSN_SIZE(ipfw_insn_sa)) goto bad_size; goto check_action; #else return EINVAL; #endif case O_DIVERT: case O_TEE: if (ip_divert_ptr == NULL) return EINVAL; else goto check_size; case O_NETGRAPH: case O_NGTEE: if (!NG_IPFW_LOADED) return EINVAL; else goto check_size; case O_FORWARD_MAC: /* XXX not implemented yet */ case O_CHECK_STATE: case O_COUNT: case O_ACCEPT: case O_DENY: case O_REJECT: #ifdef INET6 case O_UNREACH6: #endif case O_SKIPTO: check_size: if (cmdlen != F_INSN_SIZE(ipfw_insn)) goto bad_size; check_action: if (have_action) { printf("ipfw: opcode %d, multiple actions" " not allowed\n", cmd->opcode); return EINVAL; } have_action = 1; if (l != cmdlen) { printf("ipfw: opcode %d, action must be" " last opcode\n", cmd->opcode); return EINVAL; } break; #ifdef INET6 case O_IP6_SRC: case O_IP6_DST: if (cmdlen != F_INSN_SIZE(struct in6_addr) + F_INSN_SIZE(ipfw_insn)) goto bad_size; break; case O_FLOW6ID: if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + ((ipfw_insn_u32 *)cmd)->o.arg1) goto bad_size; break; case O_IP6_SRC_MASK: case O_IP6_DST_MASK: if ( !(cmdlen & 1) || cmdlen > 127) goto bad_size; break; case O_ICMP6TYPE: if( cmdlen != F_INSN_SIZE( ipfw_insn_icmp6 ) ) goto bad_size; break; #endif default: switch (cmd->opcode) { #ifndef INET6 case O_IP6_SRC_ME: case O_IP6_DST_ME: case O_EXT_HDR: case O_IP6: case O_UNREACH6: case O_IP6_SRC: case O_IP6_DST: case O_FLOW6ID: case O_IP6_SRC_MASK: case O_IP6_DST_MASK: case O_ICMP6TYPE: printf("ipfw: no IPv6 support in kernel\n"); return EPROTONOSUPPORT; #endif default: printf("ipfw: opcode %d, unknown opcode\n", cmd->opcode); return EINVAL; } } } if (have_action == 0) { printf("ipfw: missing action\n"); return EINVAL; } return 0; bad_size: printf("ipfw: opcode %d size %d wrong\n", cmd->opcode, cmdlen); return EINVAL; } /* * Copy the static and dynamic rules to the supplied buffer * and return the amount of space actually used. */ static size_t ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space) { char *bp = buf; char *ep = bp + space; struct ip_fw *rule; int i; /* XXX this can take a long time and locking will block packet flow */ IPFW_RLOCK(chain); for (rule = chain->rules; rule ; rule = rule->next) { /* * Verify the entry fits in the buffer in case the * rules changed between calculating buffer space and * now. This would be better done using a generation * number but should suffice for now. */ i = RULESIZE(rule); if (bp + i <= ep) { bcopy(rule, bp, i); bcopy(&set_disable, &(((struct ip_fw *)bp)->next_rule), sizeof(set_disable)); bp += i; } } IPFW_RUNLOCK(chain); if (ipfw_dyn_v) { ipfw_dyn_rule *p, *last = NULL; IPFW_DYN_LOCK(); for (i = 0 ; i < curr_dyn_buckets; i++) for (p = ipfw_dyn_v[i] ; p != NULL; p = p->next) { if (bp + sizeof *p <= ep) { ipfw_dyn_rule *dst = (ipfw_dyn_rule *)bp; bcopy(p, dst, sizeof *p); bcopy(&(p->rule->rulenum), &(dst->rule), sizeof(p->rule->rulenum)); /* * store a non-null value in "next". * The userland code will interpret a * NULL here as a marker * for the last dynamic rule. */ bcopy(&dst, &dst->next, sizeof(dst)); last = dst; dst->expire = TIME_LEQ(dst->expire, time_second) ? 0 : dst->expire - time_second ; bp += sizeof(ipfw_dyn_rule); } } IPFW_DYN_UNLOCK(); if (last != NULL) /* mark last dynamic rule */ bzero(&last->next, sizeof(last)); } return (bp - (char *)buf); } /** * {set|get}sockopt parser. */ static int ipfw_ctl(struct sockopt *sopt) { #define RULE_MAXSIZE (256*sizeof(u_int32_t)) int error, rule_num; size_t size; struct ip_fw *buf, *rule; u_int32_t rulenum[2]; error = suser(sopt->sopt_td); if (error) return (error); /* * Disallow modifications in really-really secure mode, but still allow * the logging counters to be reset. */ if (sopt->sopt_name == IP_FW_ADD || (sopt->sopt_dir == SOPT_SET && sopt->sopt_name != IP_FW_RESETLOG)) { #if __FreeBSD_version >= 500034 error = securelevel_ge(sopt->sopt_td->td_ucred, 3); if (error) return (error); #else /* FreeBSD 4.x */ if (securelevel >= 3) return (EPERM); #endif } error = 0; switch (sopt->sopt_name) { case IP_FW_GET: /* * pass up a copy of the current rules. Static rules * come first (the last of which has number IPFW_DEFAULT_RULE), * followed by a possibly empty list of dynamic rule. * The last dynamic rule has NULL in the "next" field. * * Note that the calculated size is used to bound the * amount of data returned to the user. The rule set may * change between calculating the size and returning the * data in which case we'll just return what fits. */ size = static_len; /* size of static rules */ if (ipfw_dyn_v) /* add size of dyn.rules */ size += (dyn_count * sizeof(ipfw_dyn_rule)); /* * XXX todo: if the user passes a short length just to know * how much room is needed, do not bother filling up the * buffer, just jump to the sooptcopyout. */ buf = malloc(size, M_TEMP, M_WAITOK); error = sooptcopyout(sopt, buf, ipfw_getrules(&layer3_chain, buf, size)); free(buf, M_TEMP); break; case IP_FW_FLUSH: /* * Normally we cannot release the lock on each iteration. * We could do it here only because we start from the head all * the times so there is no risk of missing some entries. * On the other hand, the risk is that we end up with * a very inconsistent ruleset, so better keep the lock * around the whole cycle. * * XXX this code can be improved by resetting the head of * the list to point to the default rule, and then freeing * the old list without the need for a lock. */ IPFW_WLOCK(&layer3_chain); layer3_chain.reap = NULL; free_chain(&layer3_chain, 0 /* keep default rule */); rule = layer3_chain.reap; layer3_chain.reap = NULL; IPFW_WUNLOCK(&layer3_chain); if (rule != NULL) reap_rules(rule); break; case IP_FW_ADD: rule = malloc(RULE_MAXSIZE, M_TEMP, M_WAITOK); error = sooptcopyin(sopt, rule, RULE_MAXSIZE, sizeof(struct ip_fw) ); if (error == 0) error = check_ipfw_struct(rule, sopt->sopt_valsize); if (error == 0) { error = add_rule(&layer3_chain, rule); size = RULESIZE(rule); if (!error && sopt->sopt_dir == SOPT_GET) error = sooptcopyout(sopt, rule, size); } free(rule, M_TEMP); break; case IP_FW_DEL: /* * IP_FW_DEL is used for deleting single rules or sets, * and (ab)used to atomically manipulate sets. Argument size * is used to distinguish between the two: * sizeof(u_int32_t) * delete single rule or set of rules, * or reassign rules (or sets) to a different set. * 2*sizeof(u_int32_t) * atomic disable/enable sets. * first u_int32_t contains sets to be disabled, * second u_int32_t contains sets to be enabled. */ error = sooptcopyin(sopt, rulenum, 2*sizeof(u_int32_t), sizeof(u_int32_t)); if (error) break; size = sopt->sopt_valsize; if (size == sizeof(u_int32_t)) /* delete or reassign */ error = del_entry(&layer3_chain, rulenum[0]); else if (size == 2*sizeof(u_int32_t)) /* set enable/disable */ set_disable = (set_disable | rulenum[0]) & ~rulenum[1] & ~(1<sopt_val != 0) { error = sooptcopyin(sopt, &rule_num, sizeof(int), sizeof(int)); if (error) break; } error = zero_entry(&layer3_chain, rule_num, sopt->sopt_name == IP_FW_RESETLOG); break; case IP_FW_TABLE_ADD: { ipfw_table_entry ent; error = sooptcopyin(sopt, &ent, sizeof(ent), sizeof(ent)); if (error) break; error = add_table_entry(&layer3_chain, ent.tbl, ent.addr, ent.masklen, ent.value); } break; case IP_FW_TABLE_DEL: { ipfw_table_entry ent; error = sooptcopyin(sopt, &ent, sizeof(ent), sizeof(ent)); if (error) break; error = del_table_entry(&layer3_chain, ent.tbl, ent.addr, ent.masklen); } break; case IP_FW_TABLE_FLUSH: { u_int16_t tbl; error = sooptcopyin(sopt, &tbl, sizeof(tbl), sizeof(tbl)); if (error) break; IPFW_WLOCK(&layer3_chain); error = flush_table(&layer3_chain, tbl); IPFW_WUNLOCK(&layer3_chain); } break; case IP_FW_TABLE_GETSIZE: { u_int32_t tbl, cnt; if ((error = sooptcopyin(sopt, &tbl, sizeof(tbl), sizeof(tbl)))) break; IPFW_RLOCK(&layer3_chain); error = count_table(&layer3_chain, tbl, &cnt); IPFW_RUNLOCK(&layer3_chain); if (error) break; error = sooptcopyout(sopt, &cnt, sizeof(cnt)); } break; case IP_FW_TABLE_LIST: { ipfw_table *tbl; if (sopt->sopt_valsize < sizeof(*tbl)) { error = EINVAL; break; } size = sopt->sopt_valsize; tbl = malloc(size, M_TEMP, M_WAITOK); error = sooptcopyin(sopt, tbl, size, sizeof(*tbl)); if (error) { free(tbl, M_TEMP); break; } tbl->size = (size - sizeof(*tbl)) / sizeof(ipfw_table_entry); IPFW_RLOCK(&layer3_chain); error = dump_table(&layer3_chain, tbl); IPFW_RUNLOCK(&layer3_chain); if (error) { free(tbl, M_TEMP); break; } error = sooptcopyout(sopt, tbl, size); free(tbl, M_TEMP); } break; default: printf("ipfw: ipfw_ctl invalid option %d\n", sopt->sopt_name); error = EINVAL; } return (error); #undef RULE_MAXSIZE } /** * dummynet needs a reference to the default rule, because rules can be * deleted while packets hold a reference to them. When this happens, * dummynet changes the reference to the default rule (it could well be a * NULL pointer, but this way we do not need to check for the special * case, plus here he have info on the default behaviour). */ struct ip_fw *ip_fw_default_rule; /* * This procedure is only used to handle keepalives. It is invoked * every dyn_keepalive_period */ static void ipfw_tick(void * __unused unused) { struct mbuf *m0, *m, *mnext, **mtailp; int i; ipfw_dyn_rule *q; if (dyn_keepalive == 0 || ipfw_dyn_v == NULL || dyn_count == 0) goto done; /* * We make a chain of packets to go out here -- not deferring * until after we drop the IPFW dynamic rule lock would result * in a lock order reversal with the normal packet input -> ipfw * call stack. */ m0 = NULL; mtailp = &m0; IPFW_DYN_LOCK(); for (i = 0 ; i < curr_dyn_buckets ; i++) { for (q = ipfw_dyn_v[i] ; q ; q = q->next ) { if (q->dyn_type == O_LIMIT_PARENT) continue; if (q->id.proto != IPPROTO_TCP) continue; if ( (q->state & BOTH_SYN) != BOTH_SYN) continue; if (TIME_LEQ( time_second+dyn_keepalive_interval, q->expire)) continue; /* too early */ if (TIME_LEQ(q->expire, time_second)) continue; /* too late, rule expired */ - *mtailp = send_pkt(&(q->id), q->ack_rev - 1, + *mtailp = send_pkt(NULL, &(q->id), q->ack_rev - 1, q->ack_fwd, TH_SYN); if (*mtailp != NULL) mtailp = &(*mtailp)->m_nextpkt; - *mtailp = send_pkt(&(q->id), q->ack_fwd - 1, + *mtailp = send_pkt(NULL, &(q->id), q->ack_fwd - 1, q->ack_rev, 0); if (*mtailp != NULL) mtailp = &(*mtailp)->m_nextpkt; } } IPFW_DYN_UNLOCK(); for (m = mnext = m0; m != NULL; m = mnext) { mnext = m->m_nextpkt; m->m_nextpkt = NULL; ip_output(m, NULL, NULL, 0, NULL, NULL); } done: callout_reset(&ipfw_timeout, dyn_keepalive_period*hz, ipfw_tick, NULL); } int ipfw_init(void) { struct ip_fw default_rule; int error; #ifdef INET6 /* Setup IPv6 fw sysctl tree. */ sysctl_ctx_init(&ip6_fw_sysctl_ctx); ip6_fw_sysctl_tree = SYSCTL_ADD_NODE(&ip6_fw_sysctl_ctx, SYSCTL_STATIC_CHILDREN(_net_inet6_ip6), OID_AUTO, "fw", CTLFLAG_RW | CTLFLAG_SECURE, 0, "Firewall"); SYSCTL_ADD_INT(&ip6_fw_sysctl_ctx, SYSCTL_CHILDREN(ip6_fw_sysctl_tree), OID_AUTO, "deny_unknown_exthdrs", CTLFLAG_RW | CTLFLAG_SECURE, &fw_deny_unknown_exthdrs, 0, "Deny packets with unknown IPv6 Extension Headers"); #endif layer3_chain.rules = NULL; layer3_chain.want_write = 0; layer3_chain.busy_count = 0; cv_init(&layer3_chain.cv, "Condition variable for IPFW rw locks"); IPFW_LOCK_INIT(&layer3_chain); ipfw_dyn_rule_zone = uma_zcreate("IPFW dynamic rule zone", sizeof(ipfw_dyn_rule), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); IPFW_DYN_LOCK_INIT(); callout_init(&ipfw_timeout, NET_CALLOUT_MPSAFE); bzero(&default_rule, sizeof default_rule); default_rule.act_ofs = 0; default_rule.rulenum = IPFW_DEFAULT_RULE; default_rule.cmd_len = 1; default_rule.set = RESVD_SET; default_rule.cmd[0].len = 1; default_rule.cmd[0].opcode = #ifdef IPFIREWALL_DEFAULT_TO_ACCEPT 1 ? O_ACCEPT : #endif O_DENY; error = add_rule(&layer3_chain, &default_rule); if (error != 0) { printf("ipfw2: error %u initializing default rule " "(support disabled)\n", error); IPFW_DYN_LOCK_DESTROY(); IPFW_LOCK_DESTROY(&layer3_chain); uma_zdestroy(ipfw_dyn_rule_zone); return (error); } ip_fw_default_rule = layer3_chain.rules; printf("ipfw2 (+ipv6) initialized, divert %s, " "rule-based forwarding " #ifdef IPFIREWALL_FORWARD "enabled, " #else "disabled, " #endif "default to %s, logging ", #ifdef IPDIVERT "enabled", #else "loadable", #endif default_rule.cmd[0].opcode == O_ACCEPT ? "accept" : "deny"); #ifdef IPFIREWALL_VERBOSE fw_verbose = 1; #endif #ifdef IPFIREWALL_VERBOSE_LIMIT verbose_limit = IPFIREWALL_VERBOSE_LIMIT; #endif if (fw_verbose == 0) printf("disabled\n"); else if (verbose_limit == 0) printf("unlimited\n"); else printf("limited to %d packets/entry by default\n", verbose_limit); error = init_tables(&layer3_chain); if (error) { IPFW_DYN_LOCK_DESTROY(); IPFW_LOCK_DESTROY(&layer3_chain); uma_zdestroy(ipfw_dyn_rule_zone); return (error); } ip_fw_ctl_ptr = ipfw_ctl; ip_fw_chk_ptr = ipfw_chk; callout_reset(&ipfw_timeout, hz, ipfw_tick, NULL); return (0); } void ipfw_destroy(void) { struct ip_fw *reap; ip_fw_chk_ptr = NULL; ip_fw_ctl_ptr = NULL; callout_drain(&ipfw_timeout); IPFW_WLOCK(&layer3_chain); flush_tables(&layer3_chain); layer3_chain.reap = NULL; free_chain(&layer3_chain, 1 /* kill default rule */); reap = layer3_chain.reap, layer3_chain.reap = NULL; IPFW_WUNLOCK(&layer3_chain); if (reap != NULL) reap_rules(reap); IPFW_DYN_LOCK_DESTROY(); uma_zdestroy(ipfw_dyn_rule_zone); IPFW_LOCK_DESTROY(&layer3_chain); #ifdef INET6 /* Free IPv6 fw sysctl tree. */ sysctl_ctx_free(&ip6_fw_sysctl_ctx); #endif printf("IP firewall unloaded\n"); } Index: stable/6/sys/security/mac/mac_inet.c =================================================================== --- stable/6/sys/security/mac/mac_inet.c (revision 162447) +++ stable/6/sys/security/mac/mac_inet.c (revision 162448) @@ -1,292 +1,302 @@ /*- * Copyright (c) 1999-2002 Robert N. M. Watson * Copyright (c) 2001 Ilmar S. Habibulin * Copyright (c) 2001-2004 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed by Robert Watson and Ilmar Habibulin for the * TrustedBSD Project. * * This software was developed for the FreeBSD Project in part 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. * * 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_mac.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef MAC_DEBUG static unsigned int nmacinpcbs, nmacipqs; SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, inpcbs, CTLFLAG_RD, &nmacinpcbs, 0, "number of inpcbs in use"); SYSCTL_UINT(_security_mac_debug_counters, OID_AUTO, ipqs, CTLFLAG_RD, &nmacipqs, 0, "number of ipqs in use"); #endif static struct label * mac_inpcb_label_alloc(int flag) { struct label *label; int error; label = mac_labelzone_alloc(flag); if (label == NULL) return (NULL); MAC_CHECK(init_inpcb_label, label, flag); if (error) { MAC_PERFORM(destroy_inpcb_label, label); mac_labelzone_free(label); return (NULL); } MAC_DEBUG_COUNTER_INC(&nmacinpcbs); return (label); } int mac_init_inpcb(struct inpcb *inp, int flag) { inp->inp_label = mac_inpcb_label_alloc(flag); if (inp->inp_label == NULL) return (ENOMEM); return (0); } static struct label * mac_ipq_label_alloc(int flag) { struct label *label; int error; label = mac_labelzone_alloc(flag); if (label == NULL) return (NULL); MAC_CHECK(init_ipq_label, label, flag); if (error) { MAC_PERFORM(destroy_ipq_label, label); mac_labelzone_free(label); return (NULL); } MAC_DEBUG_COUNTER_INC(&nmacipqs); return (label); } int mac_init_ipq(struct ipq *ipq, int flag) { ipq->ipq_label = mac_ipq_label_alloc(flag); if (ipq->ipq_label == NULL) return (ENOMEM); return (0); } static void mac_inpcb_label_free(struct label *label) { MAC_PERFORM(destroy_inpcb_label, label); mac_labelzone_free(label); MAC_DEBUG_COUNTER_DEC(&nmacinpcbs); } void mac_destroy_inpcb(struct inpcb *inp) { mac_inpcb_label_free(inp->inp_label); inp->inp_label = NULL; } static void mac_ipq_label_free(struct label *label) { MAC_PERFORM(destroy_ipq_label, label); mac_labelzone_free(label); MAC_DEBUG_COUNTER_DEC(&nmacipqs); } void mac_destroy_ipq(struct ipq *ipq) { mac_ipq_label_free(ipq->ipq_label); ipq->ipq_label = NULL; } void mac_create_inpcb_from_socket(struct socket *so, struct inpcb *inp) { MAC_PERFORM(create_inpcb_from_socket, so, so->so_label, inp, inp->inp_label); } void mac_create_datagram_from_ipq(struct ipq *ipq, struct mbuf *datagram) { struct label *label; label = mac_mbuf_to_label(datagram); MAC_PERFORM(create_datagram_from_ipq, ipq, ipq->ipq_label, datagram, label); } void mac_create_fragment(struct mbuf *datagram, struct mbuf *fragment) { struct label *datagramlabel, *fragmentlabel; datagramlabel = mac_mbuf_to_label(datagram); fragmentlabel = mac_mbuf_to_label(fragment); MAC_PERFORM(create_fragment, datagram, datagramlabel, fragment, fragmentlabel); } void mac_create_ipq(struct mbuf *fragment, struct ipq *ipq) { struct label *label; label = mac_mbuf_to_label(fragment); MAC_PERFORM(create_ipq, fragment, label, ipq, ipq->ipq_label); } void mac_create_mbuf_from_inpcb(struct inpcb *inp, struct mbuf *m) { struct label *mlabel; INP_LOCK_ASSERT(inp); mlabel = mac_mbuf_to_label(m); MAC_PERFORM(create_mbuf_from_inpcb, inp, inp->inp_label, m, mlabel); } int mac_fragment_match(struct mbuf *fragment, struct ipq *ipq) { struct label *label; int result; label = mac_mbuf_to_label(fragment); result = 1; MAC_BOOLEAN(fragment_match, &&, fragment, label, ipq, ipq->ipq_label); return (result); } void mac_reflect_mbuf_icmp(struct mbuf *m) { struct label *label; label = mac_mbuf_to_label(m); MAC_PERFORM(reflect_mbuf_icmp, m, label); } void mac_reflect_mbuf_tcp(struct mbuf *m) { struct label *label; label = mac_mbuf_to_label(m); MAC_PERFORM(reflect_mbuf_tcp, m, label); } void mac_update_ipq(struct mbuf *fragment, struct ipq *ipq) { struct label *label; label = mac_mbuf_to_label(fragment); MAC_PERFORM(update_ipq, fragment, label, ipq, ipq->ipq_label); } int mac_check_inpcb_deliver(struct inpcb *inp, struct mbuf *m) { struct label *label; int error; M_ASSERTPKTHDR(m); if (!mac_enforce_socket) return (0); label = mac_mbuf_to_label(m); MAC_CHECK(check_inpcb_deliver, inp, inp->inp_label, m, label); return (error); } void mac_inpcb_sosetlabel(struct socket *so, struct inpcb *inp) { /* XXX: assert socket lock. */ INP_LOCK_ASSERT(inp); MAC_PERFORM(inpcb_sosetlabel, so, so->so_label, inp, inp->inp_label); } + +void +mac_create_mbuf_from_firewall(struct mbuf *m) +{ + struct label *label; + + M_ASSERTPKTHDR(m); + label = mac_mbuf_to_label(m); + MAC_PERFORM(create_mbuf_from_firewall, m, label); +} Index: stable/6/sys/security/mac_biba/mac_biba.c =================================================================== --- stable/6/sys/security/mac_biba/mac_biba.c (revision 162447) +++ stable/6/sys/security/mac_biba/mac_biba.c (revision 162448) @@ -1,3216 +1,3228 @@ /*- * Copyright (c) 1999-2002 Robert N. M. Watson * Copyright (c) 2001-2005 McAfee, Inc. * All rights reserved. * * This software was developed by Robert Watson for the TrustedBSD Project. * * This software was developed for the FreeBSD Project in part by McAfee * Research, the Security Research Division of McAfee, Inc. under * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA * CHATS research program. * * 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$ */ /* * Developed by the TrustedBSD Project. * Biba fixed label mandatory integrity policy. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include SYSCTL_DECL(_security_mac); SYSCTL_NODE(_security_mac, OID_AUTO, biba, CTLFLAG_RW, 0, "TrustedBSD mac_biba policy controls"); static int mac_biba_label_size = sizeof(struct mac_biba); SYSCTL_INT(_security_mac_biba, OID_AUTO, label_size, CTLFLAG_RD, &mac_biba_label_size, 0, "Size of struct mac_biba"); static int mac_biba_enabled = 1; SYSCTL_INT(_security_mac_biba, OID_AUTO, enabled, CTLFLAG_RW, &mac_biba_enabled, 0, "Enforce MAC/Biba policy"); TUNABLE_INT("security.mac.biba.enabled", &mac_biba_enabled); static int destroyed_not_inited; SYSCTL_INT(_security_mac_biba, OID_AUTO, destroyed_not_inited, CTLFLAG_RD, &destroyed_not_inited, 0, "Count of labels destroyed but not inited"); static int trust_all_interfaces = 0; SYSCTL_INT(_security_mac_biba, OID_AUTO, trust_all_interfaces, CTLFLAG_RD, &trust_all_interfaces, 0, "Consider all interfaces 'trusted' by MAC/Biba"); TUNABLE_INT("security.mac.biba.trust_all_interfaces", &trust_all_interfaces); static char trusted_interfaces[128]; SYSCTL_STRING(_security_mac_biba, OID_AUTO, trusted_interfaces, CTLFLAG_RD, trusted_interfaces, 0, "Interfaces considered 'trusted' by MAC/Biba"); TUNABLE_STR("security.mac.biba.trusted_interfaces", trusted_interfaces, sizeof(trusted_interfaces)); static int max_compartments = MAC_BIBA_MAX_COMPARTMENTS; SYSCTL_INT(_security_mac_biba, OID_AUTO, max_compartments, CTLFLAG_RD, &max_compartments, 0, "Maximum supported compartments"); static int ptys_equal = 0; SYSCTL_INT(_security_mac_biba, OID_AUTO, ptys_equal, CTLFLAG_RW, &ptys_equal, 0, "Label pty devices as biba/equal on create"); TUNABLE_INT("security.mac.biba.ptys_equal", &ptys_equal); static int interfaces_equal; SYSCTL_INT(_security_mac_biba, OID_AUTO, interfaces_equal, CTLFLAG_RW, &interfaces_equal, 0, "Label network interfaces as biba/equal on create"); TUNABLE_INT("security.mac.biba.interfaces_equal", &interfaces_equal); static int revocation_enabled = 0; SYSCTL_INT(_security_mac_biba, OID_AUTO, revocation_enabled, CTLFLAG_RW, &revocation_enabled, 0, "Revoke access to objects on relabel"); TUNABLE_INT("security.mac.biba.revocation_enabled", &revocation_enabled); static int mac_biba_slot; #define SLOT(l) ((struct mac_biba *)LABEL_TO_SLOT((l), mac_biba_slot).l_ptr) #define SLOT_SET(l, val) (LABEL_TO_SLOT((l), mac_biba_slot).l_ptr = (val)) static uma_zone_t zone_biba; static __inline int biba_bit_set_empty(u_char *set) { int i; for (i = 0; i < MAC_BIBA_MAX_COMPARTMENTS >> 3; i++) if (set[i] != 0) return (0); return (1); } static struct mac_biba * biba_alloc(int flag) { return (uma_zalloc(zone_biba, flag | M_ZERO)); } static void biba_free(struct mac_biba *mac_biba) { if (mac_biba != NULL) uma_zfree(zone_biba, mac_biba); else atomic_add_int(&destroyed_not_inited, 1); } static int biba_atmostflags(struct mac_biba *mac_biba, int flags) { if ((mac_biba->mb_flags & flags) != mac_biba->mb_flags) return (EINVAL); return (0); } static int mac_biba_dominate_element(struct mac_biba_element *a, struct mac_biba_element *b) { int bit; switch (a->mbe_type) { case MAC_BIBA_TYPE_EQUAL: case MAC_BIBA_TYPE_HIGH: return (1); case MAC_BIBA_TYPE_LOW: switch (b->mbe_type) { case MAC_BIBA_TYPE_GRADE: case MAC_BIBA_TYPE_HIGH: return (0); case MAC_BIBA_TYPE_EQUAL: case MAC_BIBA_TYPE_LOW: return (1); default: panic("mac_biba_dominate_element: b->mbe_type invalid"); } case MAC_BIBA_TYPE_GRADE: switch (b->mbe_type) { case MAC_BIBA_TYPE_EQUAL: case MAC_BIBA_TYPE_LOW: return (1); case MAC_BIBA_TYPE_HIGH: return (0); case MAC_BIBA_TYPE_GRADE: for (bit = 1; bit <= MAC_BIBA_MAX_COMPARTMENTS; bit++) if (!MAC_BIBA_BIT_TEST(bit, a->mbe_compartments) && MAC_BIBA_BIT_TEST(bit, b->mbe_compartments)) return (0); return (a->mbe_grade >= b->mbe_grade); default: panic("mac_biba_dominate_element: b->mbe_type invalid"); } default: panic("mac_biba_dominate_element: a->mbe_type invalid"); } return (0); } static int mac_biba_subject_dominate_high(struct mac_biba *mac_biba) { struct mac_biba_element *element; KASSERT((mac_biba->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("mac_biba_effective_in_range: mac_biba not effective")); element = &mac_biba->mb_effective; return (element->mbe_type == MAC_BIBA_TYPE_EQUAL || element->mbe_type == MAC_BIBA_TYPE_HIGH); } static int mac_biba_range_in_range(struct mac_biba *rangea, struct mac_biba *rangeb) { return (mac_biba_dominate_element(&rangeb->mb_rangehigh, &rangea->mb_rangehigh) && mac_biba_dominate_element(&rangea->mb_rangelow, &rangeb->mb_rangelow)); } static int mac_biba_effective_in_range(struct mac_biba *effective, struct mac_biba *range) { KASSERT((effective->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("mac_biba_effective_in_range: a not effective")); KASSERT((range->mb_flags & MAC_BIBA_FLAG_RANGE) != 0, ("mac_biba_effective_in_range: b not range")); return (mac_biba_dominate_element(&range->mb_rangehigh, &effective->mb_effective) && mac_biba_dominate_element(&effective->mb_effective, &range->mb_rangelow)); return (1); } static int mac_biba_dominate_effective(struct mac_biba *a, struct mac_biba *b) { KASSERT((a->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("mac_biba_dominate_effective: a not effective")); KASSERT((b->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("mac_biba_dominate_effective: b not effective")); return (mac_biba_dominate_element(&a->mb_effective, &b->mb_effective)); } static int mac_biba_equal_element(struct mac_biba_element *a, struct mac_biba_element *b) { if (a->mbe_type == MAC_BIBA_TYPE_EQUAL || b->mbe_type == MAC_BIBA_TYPE_EQUAL) return (1); return (a->mbe_type == b->mbe_type && a->mbe_grade == b->mbe_grade); } static int mac_biba_equal_effective(struct mac_biba *a, struct mac_biba *b) { KASSERT((a->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("mac_biba_equal_effective: a not effective")); KASSERT((b->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("mac_biba_equal_effective: b not effective")); return (mac_biba_equal_element(&a->mb_effective, &b->mb_effective)); } static int mac_biba_contains_equal(struct mac_biba *mac_biba) { if (mac_biba->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) if (mac_biba->mb_effective.mbe_type == MAC_BIBA_TYPE_EQUAL) return (1); if (mac_biba->mb_flags & MAC_BIBA_FLAG_RANGE) { if (mac_biba->mb_rangelow.mbe_type == MAC_BIBA_TYPE_EQUAL) return (1); if (mac_biba->mb_rangehigh.mbe_type == MAC_BIBA_TYPE_EQUAL) return (1); } return (0); } static int mac_biba_subject_privileged(struct mac_biba *mac_biba) { KASSERT((mac_biba->mb_flags & MAC_BIBA_FLAGS_BOTH) == MAC_BIBA_FLAGS_BOTH, ("mac_biba_subject_privileged: subject doesn't have both labels")); /* If the effective is EQUAL, it's ok. */ if (mac_biba->mb_effective.mbe_type == MAC_BIBA_TYPE_EQUAL) return (0); /* If either range endpoint is EQUAL, it's ok. */ if (mac_biba->mb_rangelow.mbe_type == MAC_BIBA_TYPE_EQUAL || mac_biba->mb_rangehigh.mbe_type == MAC_BIBA_TYPE_EQUAL) return (0); /* If the range is low-high, it's ok. */ if (mac_biba->mb_rangelow.mbe_type == MAC_BIBA_TYPE_LOW && mac_biba->mb_rangehigh.mbe_type == MAC_BIBA_TYPE_HIGH) return (0); /* It's not ok. */ return (EPERM); } static int mac_biba_high_effective(struct mac_biba *mac_biba) { KASSERT((mac_biba->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("mac_biba_equal_effective: mac_biba not effective")); return (mac_biba->mb_effective.mbe_type == MAC_BIBA_TYPE_HIGH); } static int mac_biba_valid(struct mac_biba *mac_biba) { if (mac_biba->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { switch (mac_biba->mb_effective.mbe_type) { case MAC_BIBA_TYPE_GRADE: break; case MAC_BIBA_TYPE_EQUAL: case MAC_BIBA_TYPE_HIGH: case MAC_BIBA_TYPE_LOW: if (mac_biba->mb_effective.mbe_grade != 0 || !MAC_BIBA_BIT_SET_EMPTY( mac_biba->mb_effective.mbe_compartments)) return (EINVAL); break; default: return (EINVAL); } } else { if (mac_biba->mb_effective.mbe_type != MAC_BIBA_TYPE_UNDEF) return (EINVAL); } if (mac_biba->mb_flags & MAC_BIBA_FLAG_RANGE) { switch (mac_biba->mb_rangelow.mbe_type) { case MAC_BIBA_TYPE_GRADE: break; case MAC_BIBA_TYPE_EQUAL: case MAC_BIBA_TYPE_HIGH: case MAC_BIBA_TYPE_LOW: if (mac_biba->mb_rangelow.mbe_grade != 0 || !MAC_BIBA_BIT_SET_EMPTY( mac_biba->mb_rangelow.mbe_compartments)) return (EINVAL); break; default: return (EINVAL); } switch (mac_biba->mb_rangehigh.mbe_type) { case MAC_BIBA_TYPE_GRADE: break; case MAC_BIBA_TYPE_EQUAL: case MAC_BIBA_TYPE_HIGH: case MAC_BIBA_TYPE_LOW: if (mac_biba->mb_rangehigh.mbe_grade != 0 || !MAC_BIBA_BIT_SET_EMPTY( mac_biba->mb_rangehigh.mbe_compartments)) return (EINVAL); break; default: return (EINVAL); } if (!mac_biba_dominate_element(&mac_biba->mb_rangehigh, &mac_biba->mb_rangelow)) return (EINVAL); } else { if (mac_biba->mb_rangelow.mbe_type != MAC_BIBA_TYPE_UNDEF || mac_biba->mb_rangehigh.mbe_type != MAC_BIBA_TYPE_UNDEF) return (EINVAL); } return (0); } static void mac_biba_set_range(struct mac_biba *mac_biba, u_short typelow, u_short gradelow, u_char *compartmentslow, u_short typehigh, u_short gradehigh, u_char *compartmentshigh) { mac_biba->mb_rangelow.mbe_type = typelow; mac_biba->mb_rangelow.mbe_grade = gradelow; if (compartmentslow != NULL) memcpy(mac_biba->mb_rangelow.mbe_compartments, compartmentslow, sizeof(mac_biba->mb_rangelow.mbe_compartments)); mac_biba->mb_rangehigh.mbe_type = typehigh; mac_biba->mb_rangehigh.mbe_grade = gradehigh; if (compartmentshigh != NULL) memcpy(mac_biba->mb_rangehigh.mbe_compartments, compartmentshigh, sizeof(mac_biba->mb_rangehigh.mbe_compartments)); mac_biba->mb_flags |= MAC_BIBA_FLAG_RANGE; } static void mac_biba_set_effective(struct mac_biba *mac_biba, u_short type, u_short grade, u_char *compartments) { mac_biba->mb_effective.mbe_type = type; mac_biba->mb_effective.mbe_grade = grade; if (compartments != NULL) memcpy(mac_biba->mb_effective.mbe_compartments, compartments, sizeof(mac_biba->mb_effective.mbe_compartments)); mac_biba->mb_flags |= MAC_BIBA_FLAG_EFFECTIVE; } static void mac_biba_copy_range(struct mac_biba *labelfrom, struct mac_biba *labelto) { KASSERT((labelfrom->mb_flags & MAC_BIBA_FLAG_RANGE) != 0, ("mac_biba_copy_range: labelfrom not range")); labelto->mb_rangelow = labelfrom->mb_rangelow; labelto->mb_rangehigh = labelfrom->mb_rangehigh; labelto->mb_flags |= MAC_BIBA_FLAG_RANGE; } static void mac_biba_copy_effective(struct mac_biba *labelfrom, struct mac_biba *labelto) { KASSERT((labelfrom->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) != 0, ("mac_biba_copy_effective: labelfrom not effective")); labelto->mb_effective = labelfrom->mb_effective; labelto->mb_flags |= MAC_BIBA_FLAG_EFFECTIVE; } static void mac_biba_copy(struct mac_biba *source, struct mac_biba *dest) { if (source->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) mac_biba_copy_effective(source, dest); if (source->mb_flags & MAC_BIBA_FLAG_RANGE) mac_biba_copy_range(source, dest); } /* * Policy module operations. */ static void mac_biba_init(struct mac_policy_conf *conf) { zone_biba = uma_zcreate("mac_biba", sizeof(struct mac_biba), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); } /* * Label operations. */ static void mac_biba_init_label(struct label *label) { SLOT_SET(label, biba_alloc(M_WAITOK)); } static int mac_biba_init_label_waitcheck(struct label *label, int flag) { SLOT_SET(label, biba_alloc(flag)); if (SLOT(label) == NULL) return (ENOMEM); return (0); } static void mac_biba_destroy_label(struct label *label) { biba_free(SLOT(label)); SLOT_SET(label, NULL); } /* * mac_biba_element_to_string() accepts an sbuf and Biba element. It * converts the Biba element to a string and stores the result in the * sbuf; if there isn't space in the sbuf, -1 is returned. */ static int mac_biba_element_to_string(struct sbuf *sb, struct mac_biba_element *element) { int i, first; switch (element->mbe_type) { case MAC_BIBA_TYPE_HIGH: return (sbuf_printf(sb, "high")); case MAC_BIBA_TYPE_LOW: return (sbuf_printf(sb, "low")); case MAC_BIBA_TYPE_EQUAL: return (sbuf_printf(sb, "equal")); case MAC_BIBA_TYPE_GRADE: if (sbuf_printf(sb, "%d", element->mbe_grade) == -1) return (-1); first = 1; for (i = 1; i <= MAC_BIBA_MAX_COMPARTMENTS; i++) { if (MAC_BIBA_BIT_TEST(i, element->mbe_compartments)) { if (first) { if (sbuf_putc(sb, ':') == -1) return (-1); if (sbuf_printf(sb, "%d", i) == -1) return (-1); first = 0; } else { if (sbuf_printf(sb, "+%d", i) == -1) return (-1); } } } return (0); default: panic("mac_biba_element_to_string: invalid type (%d)", element->mbe_type); } } /* * mac_biba_to_string() converts a Biba label to a string, and places * the results in the passed sbuf. It returns 0 on success, or EINVAL * if there isn't room in the sbuf. Note: the sbuf will be modified * even in a failure case, so the caller may need to revert the sbuf * by restoring the offset if that's undesired. */ static int mac_biba_to_string(struct sbuf *sb, struct mac_biba *mac_biba) { if (mac_biba->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { if (mac_biba_element_to_string(sb, &mac_biba->mb_effective) == -1) return (EINVAL); } if (mac_biba->mb_flags & MAC_BIBA_FLAG_RANGE) { if (sbuf_putc(sb, '(') == -1) return (EINVAL); if (mac_biba_element_to_string(sb, &mac_biba->mb_rangelow) == -1) return (EINVAL); if (sbuf_putc(sb, '-') == -1) return (EINVAL); if (mac_biba_element_to_string(sb, &mac_biba->mb_rangehigh) == -1) return (EINVAL); if (sbuf_putc(sb, ')') == -1) return (EINVAL); } return (0); } static int mac_biba_externalize_label(struct label *label, char *element_name, struct sbuf *sb, int *claimed) { struct mac_biba *mac_biba; if (strcmp(MAC_BIBA_LABEL_NAME, element_name) != 0) return (0); (*claimed)++; mac_biba = SLOT(label); return (mac_biba_to_string(sb, mac_biba)); } static int mac_biba_parse_element(struct mac_biba_element *element, char *string) { char *compartment, *end, *grade; int value; if (strcmp(string, "high") == 0 || strcmp(string, "hi") == 0) { element->mbe_type = MAC_BIBA_TYPE_HIGH; element->mbe_grade = MAC_BIBA_TYPE_UNDEF; } else if (strcmp(string, "low") == 0 || strcmp(string, "lo") == 0) { element->mbe_type = MAC_BIBA_TYPE_LOW; element->mbe_grade = MAC_BIBA_TYPE_UNDEF; } else if (strcmp(string, "equal") == 0 || strcmp(string, "eq") == 0) { element->mbe_type = MAC_BIBA_TYPE_EQUAL; element->mbe_grade = MAC_BIBA_TYPE_UNDEF; } else { element->mbe_type = MAC_BIBA_TYPE_GRADE; /* * Numeric grade piece of the element. */ grade = strsep(&string, ":"); value = strtol(grade, &end, 10); if (end == grade || *end != '\0') return (EINVAL); if (value < 0 || value > 65535) return (EINVAL); element->mbe_grade = value; /* * Optional compartment piece of the element. If none * are included, we assume that the label has no * compartments. */ if (string == NULL) return (0); if (*string == '\0') return (0); while ((compartment = strsep(&string, "+")) != NULL) { value = strtol(compartment, &end, 10); if (compartment == end || *end != '\0') return (EINVAL); if (value < 1 || value > MAC_BIBA_MAX_COMPARTMENTS) return (EINVAL); MAC_BIBA_BIT_SET(value, element->mbe_compartments); } } return (0); } /* * Note: destructively consumes the string, make a local copy before * calling if that's a problem. */ static int mac_biba_parse(struct mac_biba *mac_biba, char *string) { char *rangehigh, *rangelow, *effective; int error; effective = strsep(&string, "("); if (*effective == '\0') effective = NULL; if (string != NULL) { rangelow = strsep(&string, "-"); if (string == NULL) return (EINVAL); rangehigh = strsep(&string, ")"); if (string == NULL) return (EINVAL); if (*string != '\0') return (EINVAL); } else { rangelow = NULL; rangehigh = NULL; } KASSERT((rangelow != NULL && rangehigh != NULL) || (rangelow == NULL && rangehigh == NULL), ("mac_biba_parse: range mismatch")); bzero(mac_biba, sizeof(*mac_biba)); if (effective != NULL) { error = mac_biba_parse_element(&mac_biba->mb_effective, effective); if (error) return (error); mac_biba->mb_flags |= MAC_BIBA_FLAG_EFFECTIVE; } if (rangelow != NULL) { error = mac_biba_parse_element(&mac_biba->mb_rangelow, rangelow); if (error) return (error); error = mac_biba_parse_element(&mac_biba->mb_rangehigh, rangehigh); if (error) return (error); mac_biba->mb_flags |= MAC_BIBA_FLAG_RANGE; } error = mac_biba_valid(mac_biba); if (error) return (error); return (0); } static int mac_biba_internalize_label(struct label *label, char *element_name, char *element_data, int *claimed) { struct mac_biba *mac_biba, mac_biba_temp; int error; if (strcmp(MAC_BIBA_LABEL_NAME, element_name) != 0) return (0); (*claimed)++; error = mac_biba_parse(&mac_biba_temp, element_data); if (error) return (error); mac_biba = SLOT(label); *mac_biba = mac_biba_temp; return (0); } static void mac_biba_copy_label(struct label *src, struct label *dest) { *SLOT(dest) = *SLOT(src); } /* * Labeling event operations: file system objects, and things that look * a lot like file system objects. */ static void mac_biba_create_devfs_device(struct ucred *cred, struct mount *mp, struct cdev *dev, struct devfs_dirent *devfs_dirent, struct label *label) { struct mac_biba *mac_biba; int biba_type; mac_biba = SLOT(label); if (strcmp(dev->si_name, "null") == 0 || strcmp(dev->si_name, "zero") == 0 || strcmp(dev->si_name, "random") == 0 || strncmp(dev->si_name, "fd/", strlen("fd/")) == 0) biba_type = MAC_BIBA_TYPE_EQUAL; else if (ptys_equal && (strncmp(dev->si_name, "ttyp", strlen("ttyp")) == 0 || strncmp(dev->si_name, "ptyp", strlen("ptyp")) == 0)) biba_type = MAC_BIBA_TYPE_EQUAL; else biba_type = MAC_BIBA_TYPE_HIGH; mac_biba_set_effective(mac_biba, biba_type, 0, NULL); } static void mac_biba_create_devfs_directory(struct mount *mp, char *dirname, int dirnamelen, struct devfs_dirent *devfs_dirent, struct label *label) { struct mac_biba *mac_biba; mac_biba = SLOT(label); mac_biba_set_effective(mac_biba, MAC_BIBA_TYPE_HIGH, 0, NULL); } static void mac_biba_create_devfs_symlink(struct ucred *cred, struct mount *mp, struct devfs_dirent *dd, struct label *ddlabel, struct devfs_dirent *de, struct label *delabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(delabel); mac_biba_copy_effective(source, dest); } static void mac_biba_create_mount(struct ucred *cred, struct mount *mp, struct label *mntlabel, struct label *fslabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(mntlabel); mac_biba_copy_effective(source, dest); dest = SLOT(fslabel); mac_biba_copy_effective(source, dest); } static void mac_biba_relabel_vnode(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, struct label *label) { struct mac_biba *source, *dest; source = SLOT(label); dest = SLOT(vnodelabel); mac_biba_copy(source, dest); } static void mac_biba_update_devfsdirent(struct mount *mp, struct devfs_dirent *devfs_dirent, struct label *direntlabel, struct vnode *vp, struct label *vnodelabel) { struct mac_biba *source, *dest; source = SLOT(vnodelabel); dest = SLOT(direntlabel); mac_biba_copy(source, dest); } static void mac_biba_associate_vnode_devfs(struct mount *mp, struct label *fslabel, struct devfs_dirent *de, struct label *delabel, struct vnode *vp, struct label *vlabel) { struct mac_biba *source, *dest; source = SLOT(delabel); dest = SLOT(vlabel); mac_biba_copy_effective(source, dest); } static int mac_biba_associate_vnode_extattr(struct mount *mp, struct label *fslabel, struct vnode *vp, struct label *vlabel) { struct mac_biba temp, *source, *dest; int buflen, error; source = SLOT(fslabel); dest = SLOT(vlabel); buflen = sizeof(temp); bzero(&temp, buflen); error = vn_extattr_get(vp, IO_NODELOCKED, MAC_BIBA_EXTATTR_NAMESPACE, MAC_BIBA_EXTATTR_NAME, &buflen, (char *) &temp, curthread); if (error == ENOATTR || error == EOPNOTSUPP) { /* Fall back to the fslabel. */ mac_biba_copy_effective(source, dest); return (0); } else if (error) return (error); if (buflen != sizeof(temp)) { printf("mac_biba_associate_vnode_extattr: bad size %d\n", buflen); return (EPERM); } if (mac_biba_valid(&temp) != 0) { printf("mac_biba_associate_vnode_extattr: invalid\n"); return (EPERM); } if ((temp.mb_flags & MAC_BIBA_FLAGS_BOTH) != MAC_BIBA_FLAG_EFFECTIVE) { printf("mac_biba_associate_vnode_extattr: not effective\n"); return (EPERM); } mac_biba_copy_effective(&temp, dest); return (0); } static void mac_biba_associate_vnode_singlelabel(struct mount *mp, struct label *fslabel, struct vnode *vp, struct label *vlabel) { struct mac_biba *source, *dest; source = SLOT(fslabel); dest = SLOT(vlabel); mac_biba_copy_effective(source, dest); } static int mac_biba_create_vnode_extattr(struct ucred *cred, struct mount *mp, struct label *fslabel, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *vlabel, struct componentname *cnp) { struct mac_biba *source, *dest, temp; size_t buflen; int error; buflen = sizeof(temp); bzero(&temp, buflen); source = SLOT(cred->cr_label); dest = SLOT(vlabel); mac_biba_copy_effective(source, &temp); error = vn_extattr_set(vp, IO_NODELOCKED, MAC_BIBA_EXTATTR_NAMESPACE, MAC_BIBA_EXTATTR_NAME, buflen, (char *) &temp, curthread); if (error == 0) mac_biba_copy_effective(source, dest); return (error); } static int mac_biba_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp, struct label *vlabel, struct label *intlabel) { struct mac_biba *source, temp; size_t buflen; int error; buflen = sizeof(temp); bzero(&temp, buflen); source = SLOT(intlabel); if ((source->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) == 0) return (0); mac_biba_copy_effective(source, &temp); error = vn_extattr_set(vp, IO_NODELOCKED, MAC_BIBA_EXTATTR_NAMESPACE, MAC_BIBA_EXTATTR_NAME, buflen, (char *) &temp, curthread); return (error); } /* * Labeling event operations: IPC object. */ static void mac_biba_create_inpcb_from_socket(struct socket *so, struct label *solabel, struct inpcb *inp, struct label *inplabel) { struct mac_biba *source, *dest; source = SLOT(solabel); dest = SLOT(inplabel); mac_biba_copy_effective(source, dest); } static void mac_biba_create_mbuf_from_socket(struct socket *so, struct label *socketlabel, struct mbuf *m, struct label *mbuflabel) { struct mac_biba *source, *dest; source = SLOT(socketlabel); dest = SLOT(mbuflabel); mac_biba_copy_effective(source, dest); } static void mac_biba_create_socket(struct ucred *cred, struct socket *socket, struct label *socketlabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(socketlabel); mac_biba_copy_effective(source, dest); } static void mac_biba_create_pipe(struct ucred *cred, struct pipepair *pp, struct label *pipelabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(pipelabel); mac_biba_copy_effective(source, dest); } static void mac_biba_create_posix_sem(struct ucred *cred, struct ksem *ksemptr, struct label *ks_label) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(ks_label); mac_biba_copy_effective(source, dest); } static void mac_biba_create_socket_from_socket(struct socket *oldsocket, struct label *oldsocketlabel, struct socket *newsocket, struct label *newsocketlabel) { struct mac_biba *source, *dest; source = SLOT(oldsocketlabel); dest = SLOT(newsocketlabel); mac_biba_copy_effective(source, dest); } static void mac_biba_relabel_socket(struct ucred *cred, struct socket *socket, struct label *socketlabel, struct label *newlabel) { struct mac_biba *source, *dest; source = SLOT(newlabel); dest = SLOT(socketlabel); mac_biba_copy(source, dest); } static void mac_biba_relabel_pipe(struct ucred *cred, struct pipepair *pp, struct label *pipelabel, struct label *newlabel) { struct mac_biba *source, *dest; source = SLOT(newlabel); dest = SLOT(pipelabel); mac_biba_copy(source, dest); } static void mac_biba_set_socket_peer_from_mbuf(struct mbuf *mbuf, struct label *mbuflabel, struct socket *socket, struct label *socketpeerlabel) { struct mac_biba *source, *dest; source = SLOT(mbuflabel); dest = SLOT(socketpeerlabel); mac_biba_copy_effective(source, dest); } /* * Labeling event operations: System V IPC objects. */ static void mac_biba_create_sysv_msgmsg(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqlabel, struct msg *msgptr, struct label *msglabel) { struct mac_biba *source, *dest; /* Ignore the msgq label */ source = SLOT(cred->cr_label); dest = SLOT(msglabel); mac_biba_copy_effective(source, dest); } static void mac_biba_create_sysv_msgqueue(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqlabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(msqlabel); mac_biba_copy_effective(source, dest); } static void mac_biba_create_sysv_sem(struct ucred *cred, struct semid_kernel *semakptr, struct label *semalabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(semalabel); mac_biba_copy_effective(source, dest); } static void mac_biba_create_sysv_shm(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmlabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(shmlabel); mac_biba_copy_effective(source, dest); } /* * Labeling event operations: network objects. */ static void mac_biba_set_socket_peer_from_socket(struct socket *oldsocket, struct label *oldsocketlabel, struct socket *newsocket, struct label *newsocketpeerlabel) { struct mac_biba *source, *dest; source = SLOT(oldsocketlabel); dest = SLOT(newsocketpeerlabel); mac_biba_copy_effective(source, dest); } static void mac_biba_create_bpfdesc(struct ucred *cred, struct bpf_d *bpf_d, struct label *bpflabel) { struct mac_biba *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(bpflabel); mac_biba_copy_effective(source, dest); } static void mac_biba_create_ifnet(struct ifnet *ifnet, struct label *ifnetlabel) { char tifname[IFNAMSIZ], *p, *q; char tiflist[sizeof(trusted_interfaces)]; struct mac_biba *dest; int len, type; dest = SLOT(ifnetlabel); if (ifnet->if_type == IFT_LOOP || interfaces_equal != 0) { type = MAC_BIBA_TYPE_EQUAL; goto set; } if (trust_all_interfaces) { type = MAC_BIBA_TYPE_HIGH; goto set; } type = MAC_BIBA_TYPE_LOW; if (trusted_interfaces[0] == '\0' || !strvalid(trusted_interfaces, sizeof(trusted_interfaces))) goto set; bzero(tiflist, sizeof(tiflist)); for (p = trusted_interfaces, q = tiflist; *p != '\0'; p++, q++) if(*p != ' ' && *p != '\t') *q = *p; for (p = q = tiflist;; p++) { if (*p == ',' || *p == '\0') { len = p - q; if (len < IFNAMSIZ) { bzero(tifname, sizeof(tifname)); bcopy(q, tifname, len); if (strcmp(tifname, ifnet->if_xname) == 0) { type = MAC_BIBA_TYPE_HIGH; break; } } else { *p = '\0'; printf("mac_biba warning: interface name " "\"%s\" is too long (must be < %d)\n", q, IFNAMSIZ); } if (*p == '\0') break; q = p + 1; } } set: mac_biba_set_effective(dest, type, 0, NULL); mac_biba_set_range(dest, type, 0, NULL, type, 0, NULL); } static void mac_biba_create_ipq(struct mbuf *fragment, struct label *fragmentlabel, struct ipq *ipq, struct label *ipqlabel) { struct mac_biba *source, *dest; source = SLOT(fragmentlabel); dest = SLOT(ipqlabel); mac_biba_copy_effective(source, dest); } static void mac_biba_create_datagram_from_ipq(struct ipq *ipq, struct label *ipqlabel, struct mbuf *datagram, struct label *datagramlabel) { struct mac_biba *source, *dest; source = SLOT(ipqlabel); dest = SLOT(datagramlabel); /* Just use the head, since we require them all to match. */ mac_biba_copy_effective(source, dest); } static void mac_biba_create_fragment(struct mbuf *datagram, struct label *datagramlabel, struct mbuf *fragment, struct label *fragmentlabel) { struct mac_biba *source, *dest; source = SLOT(datagramlabel); dest = SLOT(fragmentlabel); mac_biba_copy_effective(source, dest); } static void mac_biba_create_mbuf_from_inpcb(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *source, *dest; source = SLOT(inplabel); dest = SLOT(mlabel); mac_biba_copy_effective(source, dest); } static void mac_biba_create_mbuf_linklayer(struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *mbuf, struct label *mbuflabel) { struct mac_biba *dest; dest = SLOT(mbuflabel); mac_biba_set_effective(dest, MAC_BIBA_TYPE_EQUAL, 0, NULL); } static void mac_biba_create_mbuf_from_bpfdesc(struct bpf_d *bpf_d, struct label *bpflabel, struct mbuf *mbuf, struct label *mbuflabel) { struct mac_biba *source, *dest; source = SLOT(bpflabel); dest = SLOT(mbuflabel); mac_biba_copy_effective(source, dest); } static void mac_biba_create_mbuf_from_ifnet(struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *m, struct label *mbuflabel) { struct mac_biba *source, *dest; source = SLOT(ifnetlabel); dest = SLOT(mbuflabel); mac_biba_copy_effective(source, dest); } static void mac_biba_create_mbuf_multicast_encap(struct mbuf *oldmbuf, struct label *oldmbuflabel, struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *newmbuf, struct label *newmbuflabel) { struct mac_biba *source, *dest; source = SLOT(oldmbuflabel); dest = SLOT(newmbuflabel); mac_biba_copy_effective(source, dest); } static void mac_biba_create_mbuf_netlayer(struct mbuf *oldmbuf, struct label *oldmbuflabel, struct mbuf *newmbuf, struct label *newmbuflabel) { struct mac_biba *source, *dest; source = SLOT(oldmbuflabel); dest = SLOT(newmbuflabel); mac_biba_copy_effective(source, dest); } static int mac_biba_fragment_match(struct mbuf *fragment, struct label *fragmentlabel, struct ipq *ipq, struct label *ipqlabel) { struct mac_biba *a, *b; a = SLOT(ipqlabel); b = SLOT(fragmentlabel); return (mac_biba_equal_effective(a, b)); } static void mac_biba_relabel_ifnet(struct ucred *cred, struct ifnet *ifnet, struct label *ifnetlabel, struct label *newlabel) { struct mac_biba *source, *dest; source = SLOT(newlabel); dest = SLOT(ifnetlabel); mac_biba_copy(source, dest); } static void mac_biba_update_ipq(struct mbuf *fragment, struct label *fragmentlabel, struct ipq *ipq, struct label *ipqlabel) { /* NOOP: we only accept matching labels, so no need to update */ } static void mac_biba_inpcb_sosetlabel(struct socket *so, struct label *solabel, struct inpcb *inp, struct label *inplabel) { struct mac_biba *source, *dest; source = SLOT(solabel); dest = SLOT(inplabel); mac_biba_copy(source, dest); } +static void +mac_biba_create_mbuf_from_firewall(struct mbuf *m, struct label *label) +{ + struct mac_biba *dest; + + dest = SLOT(label); + + /* XXX: where is the label for the firewall really comming from? */ + mac_biba_set_effective(dest, MAC_BIBA_TYPE_EQUAL, 0, NULL); +} + /* * Labeling event operations: processes. */ static void mac_biba_create_proc0(struct ucred *cred) { struct mac_biba *dest; dest = SLOT(cred->cr_label); mac_biba_set_effective(dest, MAC_BIBA_TYPE_EQUAL, 0, NULL); mac_biba_set_range(dest, MAC_BIBA_TYPE_LOW, 0, NULL, MAC_BIBA_TYPE_HIGH, 0, NULL); } static void mac_biba_create_proc1(struct ucred *cred) { struct mac_biba *dest; dest = SLOT(cred->cr_label); mac_biba_set_effective(dest, MAC_BIBA_TYPE_HIGH, 0, NULL); mac_biba_set_range(dest, MAC_BIBA_TYPE_LOW, 0, NULL, MAC_BIBA_TYPE_HIGH, 0, NULL); } static void mac_biba_relabel_cred(struct ucred *cred, struct label *newlabel) { struct mac_biba *source, *dest; source = SLOT(newlabel); dest = SLOT(cred->cr_label); mac_biba_copy(source, dest); } /* * Label cleanup/flush operations */ static void mac_biba_cleanup_sysv_msgmsg(struct label *msglabel) { bzero(SLOT(msglabel), sizeof(struct mac_biba)); } static void mac_biba_cleanup_sysv_msgqueue(struct label *msqlabel) { bzero(SLOT(msqlabel), sizeof(struct mac_biba)); } static void mac_biba_cleanup_sysv_sem(struct label *semalabel) { bzero(SLOT(semalabel), sizeof(struct mac_biba)); } static void mac_biba_cleanup_sysv_shm(struct label *shmlabel) { bzero(SLOT(shmlabel), sizeof(struct mac_biba)); } /* * Access control checks. */ static int mac_biba_check_bpfdesc_receive(struct bpf_d *bpf_d, struct label *bpflabel, struct ifnet *ifnet, struct label *ifnetlabel) { struct mac_biba *a, *b; if (!mac_biba_enabled) return (0); a = SLOT(bpflabel); b = SLOT(ifnetlabel); if (mac_biba_equal_effective(a, b)) return (0); return (EACCES); } static int mac_biba_check_cred_relabel(struct ucred *cred, struct label *newlabel) { struct mac_biba *subj, *new; int error; subj = SLOT(cred->cr_label); new = SLOT(newlabel); /* * If there is a Biba label update for the credential, it may * be an update of the effective, range, or both. */ error = biba_atmostflags(new, MAC_BIBA_FLAGS_BOTH); if (error) return (error); /* * If the Biba label is to be changed, authorize as appropriate. */ if (new->mb_flags & MAC_BIBA_FLAGS_BOTH) { /* * If the change request modifies both the Biba label * effective and range, check that the new effective will be * in the new range. */ if ((new->mb_flags & MAC_BIBA_FLAGS_BOTH) == MAC_BIBA_FLAGS_BOTH && !mac_biba_effective_in_range(new, new)) return (EINVAL); /* * To change the Biba effective label on a credential, the * new effective label must be in the current range. */ if (new->mb_flags & MAC_BIBA_FLAG_EFFECTIVE && !mac_biba_effective_in_range(new, subj)) return (EPERM); /* * To change the Biba range on a credential, the new * range label must be in the current range. */ if (new->mb_flags & MAC_BIBA_FLAG_RANGE && !mac_biba_range_in_range(new, subj)) return (EPERM); /* * To have EQUAL in any component of the new credential * Biba label, the subject must already have EQUAL in * their label. */ if (mac_biba_contains_equal(new)) { error = mac_biba_subject_privileged(subj); if (error) return (error); } } return (0); } static int mac_biba_check_cred_visible(struct ucred *u1, struct ucred *u2) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(u1->cr_label); obj = SLOT(u2->cr_label); /* XXX: range */ if (!mac_biba_dominate_effective(obj, subj)) return (ESRCH); return (0); } static int mac_biba_check_ifnet_relabel(struct ucred *cred, struct ifnet *ifnet, struct label *ifnetlabel, struct label *newlabel) { struct mac_biba *subj, *new; int error; subj = SLOT(cred->cr_label); new = SLOT(newlabel); /* * If there is a Biba label update for the interface, it may * be an update of the effective, range, or both. */ error = biba_atmostflags(new, MAC_BIBA_FLAGS_BOTH); if (error) return (error); /* * Relabling network interfaces requires Biba privilege. */ error = mac_biba_subject_privileged(subj); if (error) return (error); return (0); } static int mac_biba_check_ifnet_transmit(struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *m, struct label *mbuflabel) { struct mac_biba *p, *i; if (!mac_biba_enabled) return (0); p = SLOT(mbuflabel); i = SLOT(ifnetlabel); return (mac_biba_effective_in_range(p, i) ? 0 : EACCES); } static int mac_biba_check_inpcb_deliver(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel) { struct mac_biba *p, *i; if (!mac_biba_enabled) return (0); p = SLOT(mlabel); i = SLOT(inplabel); return (mac_biba_equal_effective(p, i) ? 0 : EACCES); } static int mac_biba_check_sysv_msgrcv(struct ucred *cred, struct msg *msgptr, struct label *msglabel) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msglabel); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_biba_check_sysv_msgrmid(struct ucred *cred, struct msg *msgptr, struct label *msglabel) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msglabel); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_biba_check_sysv_msqget(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_biba_check_sysv_msqsnd(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_biba_check_sysv_msqrcv(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_biba_check_sysv_msqctl(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel, int cmd) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); switch(cmd) { case IPC_RMID: case IPC_SET: if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); break; case IPC_STAT: if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); break; default: return (EACCES); } return (0); } static int mac_biba_check_sysv_semctl(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel, int cmd) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(semaklabel); switch(cmd) { case IPC_RMID: case IPC_SET: case SETVAL: case SETALL: if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); break; case IPC_STAT: case GETVAL: case GETPID: case GETNCNT: case GETZCNT: case GETALL: if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); break; default: return (EACCES); } return (0); } static int mac_biba_check_sysv_semget(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(semaklabel); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_biba_check_sysv_semop(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel, size_t accesstype) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(semaklabel); if (accesstype & SEM_R) if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); if (accesstype & SEM_A) if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_biba_check_sysv_shmat(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int shmflg) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmseglabel); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); if ((shmflg & SHM_RDONLY) == 0) { if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); } return (0); } static int mac_biba_check_sysv_shmctl(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int cmd) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmseglabel); switch(cmd) { case IPC_RMID: case IPC_SET: if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); break; case IPC_STAT: case SHM_STAT: if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); break; default: return (EACCES); } return (0); } static int mac_biba_check_sysv_shmget(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int shmflg) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmseglabel); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_biba_check_kld_load(struct ucred *cred, struct vnode *vp, struct label *label) { struct mac_biba *subj, *obj; int error; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); error = mac_biba_subject_privileged(subj); if (error) return (error); obj = SLOT(label); if (!mac_biba_high_effective(obj)) return (EACCES); return (0); } static int mac_biba_check_kld_unload(struct ucred *cred) { struct mac_biba *subj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); return (mac_biba_subject_privileged(subj)); } static int mac_biba_check_mount_stat(struct ucred *cred, struct mount *mp, struct label *mntlabel) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(mntlabel); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_biba_check_pipe_ioctl(struct ucred *cred, struct pipepair *pp, struct label *pipelabel, unsigned long cmd, void /* caddr_t */ *data) { if(!mac_biba_enabled) return (0); /* XXX: This will be implemented soon... */ return (0); } static int mac_biba_check_pipe_poll(struct ucred *cred, struct pipepair *pp, struct label *pipelabel) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT((pipelabel)); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_biba_check_pipe_read(struct ucred *cred, struct pipepair *pp, struct label *pipelabel) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT((pipelabel)); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_biba_check_pipe_relabel(struct ucred *cred, struct pipepair *pp, struct label *pipelabel, struct label *newlabel) { struct mac_biba *subj, *obj, *new; int error; new = SLOT(newlabel); subj = SLOT(cred->cr_label); obj = SLOT(pipelabel); /* * If there is a Biba label update for a pipe, it must be a * effective update. */ error = biba_atmostflags(new, MAC_BIBA_FLAG_EFFECTIVE); if (error) return (error); /* * To perform a relabel of a pipe (Biba label or not), Biba must * authorize the relabel. */ if (!mac_biba_effective_in_range(obj, subj)) return (EPERM); /* * If the Biba label is to be changed, authorize as appropriate. */ if (new->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { /* * To change the Biba label on a pipe, the new pipe label * must be in the subject range. */ if (!mac_biba_effective_in_range(new, subj)) return (EPERM); /* * To change the Biba label on a pipe to be EQUAL, the * subject must have appropriate privilege. */ if (mac_biba_contains_equal(new)) { error = mac_biba_subject_privileged(subj); if (error) return (error); } } return (0); } static int mac_biba_check_pipe_stat(struct ucred *cred, struct pipepair *pp, struct label *pipelabel) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT((pipelabel)); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_biba_check_pipe_write(struct ucred *cred, struct pipepair *pp, struct label *pipelabel) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT((pipelabel)); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_biba_check_posix_sem_write(struct ucred *cred, struct ksem *ksemptr, struct label *ks_label) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(ks_label); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_biba_check_posix_sem_rdonly(struct ucred *cred, struct ksem *ksemptr, struct label *ks_label) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(ks_label); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_biba_check_proc_debug(struct ucred *cred, struct proc *proc) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(proc->p_ucred->cr_label); /* XXX: range checks */ if (!mac_biba_dominate_effective(obj, subj)) return (ESRCH); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_biba_check_proc_sched(struct ucred *cred, struct proc *proc) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(proc->p_ucred->cr_label); /* XXX: range checks */ if (!mac_biba_dominate_effective(obj, subj)) return (ESRCH); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_biba_check_proc_signal(struct ucred *cred, struct proc *proc, int signum) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(proc->p_ucred->cr_label); /* XXX: range checks */ if (!mac_biba_dominate_effective(obj, subj)) return (ESRCH); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_biba_check_socket_deliver(struct socket *so, struct label *socketlabel, struct mbuf *m, struct label *mbuflabel) { struct mac_biba *p, *s; if (!mac_biba_enabled) return (0); p = SLOT(mbuflabel); s = SLOT(socketlabel); return (mac_biba_equal_effective(p, s) ? 0 : EACCES); } static int mac_biba_check_socket_relabel(struct ucred *cred, struct socket *so, struct label *socketlabel, struct label *newlabel) { struct mac_biba *subj, *obj, *new; int error; new = SLOT(newlabel); subj = SLOT(cred->cr_label); obj = SLOT(socketlabel); /* * If there is a Biba label update for the socket, it may be * an update of effective. */ error = biba_atmostflags(new, MAC_BIBA_FLAG_EFFECTIVE); if (error) return (error); /* * To relabel a socket, the old socket effective must be in the subject * range. */ if (!mac_biba_effective_in_range(obj, subj)) return (EPERM); /* * If the Biba label is to be changed, authorize as appropriate. */ if (new->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { /* * To relabel a socket, the new socket effective must be in * the subject range. */ if (!mac_biba_effective_in_range(new, subj)) return (EPERM); /* * To change the Biba label on the socket to contain EQUAL, * the subject must have appropriate privilege. */ if (mac_biba_contains_equal(new)) { error = mac_biba_subject_privileged(subj); if (error) return (error); } } return (0); } static int mac_biba_check_socket_visible(struct ucred *cred, struct socket *socket, struct label *socketlabel) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(socketlabel); if (!mac_biba_dominate_effective(obj, subj)) return (ENOENT); return (0); } static int mac_biba_check_sysarch_ioperm(struct ucred *cred) { struct mac_biba *subj; int error; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); error = mac_biba_subject_privileged(subj); if (error) return (error); return (0); } static int mac_biba_check_system_acct(struct ucred *cred, struct vnode *vp, struct label *label) { struct mac_biba *subj, *obj; int error; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); error = mac_biba_subject_privileged(subj); if (error) return (error); if (label == NULL) return (0); obj = SLOT(label); if (!mac_biba_high_effective(obj)) return (EACCES); return (0); } static int mac_biba_check_system_settime(struct ucred *cred) { struct mac_biba *subj; int error; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); error = mac_biba_subject_privileged(subj); if (error) return (error); return (0); } static int mac_biba_check_system_swapon(struct ucred *cred, struct vnode *vp, struct label *label) { struct mac_biba *subj, *obj; int error; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); error = mac_biba_subject_privileged(subj); if (error) return (error); if (!mac_biba_high_effective(obj)) return (EACCES); return (0); } static int mac_biba_check_system_swapoff(struct ucred *cred, struct vnode *vp, struct label *label) { struct mac_biba *subj, *obj; int error; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); error = mac_biba_subject_privileged(subj); if (error) return (error); return (0); } static int mac_biba_check_system_sysctl(struct ucred *cred, struct sysctl_oid *oidp, void *arg1, int arg2, struct sysctl_req *req) { struct mac_biba *subj; int error; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); /* * Treat sysctl variables without CTLFLAG_ANYBODY flag as * biba/high, but also require privilege to change them. */ if (req->newptr != NULL && (oidp->oid_kind & CTLFLAG_ANYBODY) == 0) { if (!mac_biba_subject_dominate_high(subj)) return (EACCES); error = mac_biba_subject_privileged(subj); if (error) return (error); } return (0); } static int mac_biba_check_vnode_chdir(struct ucred *cred, struct vnode *dvp, struct label *dlabel) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_biba_check_vnode_chroot(struct ucred *cred, struct vnode *dvp, struct label *dlabel) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_biba_check_vnode_create(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct componentname *cnp, struct vattr *vap) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_biba_check_vnode_delete(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, struct componentname *cnp) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); obj = SLOT(label); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_biba_check_vnode_deleteacl(struct ucred *cred, struct vnode *vp, struct label *label, acl_type_t type) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_biba_check_vnode_deleteextattr(struct ucred *cred, struct vnode *vp, struct label *label, int attrnamespace, const char *name) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_biba_check_vnode_exec(struct ucred *cred, struct vnode *vp, struct label *label, struct image_params *imgp, struct label *execlabel) { struct mac_biba *subj, *obj, *exec; int error; if (execlabel != NULL) { /* * We currently don't permit labels to be changed at * exec-time as part of Biba, so disallow non-NULL * Biba label elements in the execlabel. */ exec = SLOT(execlabel); error = biba_atmostflags(exec, 0); if (error) return (error); } if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_biba_check_vnode_getacl(struct ucred *cred, struct vnode *vp, struct label *label, acl_type_t type) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_biba_check_vnode_getextattr(struct ucred *cred, struct vnode *vp, struct label *label, int attrnamespace, const char *name, struct uio *uio) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_biba_check_vnode_link(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, struct componentname *cnp) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); obj = SLOT(label); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_biba_check_vnode_listextattr(struct ucred *cred, struct vnode *vp, struct label *label, int attrnamespace) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_biba_check_vnode_lookup(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct componentname *cnp) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_biba_check_vnode_mmap(struct ucred *cred, struct vnode *vp, struct label *label, int prot, int flags) { struct mac_biba *subj, *obj; /* * Rely on the use of open()-time protections to handle * non-revocation cases. */ if (!mac_biba_enabled || !revocation_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) { if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); } if (((prot & VM_PROT_WRITE) != 0) && ((flags & MAP_SHARED) != 0)) { if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); } return (0); } static int mac_biba_check_vnode_open(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, int acc_mode) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vnodelabel); /* XXX privilege override for admin? */ if (acc_mode & (VREAD | VEXEC | VSTAT)) { if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); } if (acc_mode & (VWRITE | VAPPEND | VADMIN)) { if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); } return (0); } static int mac_biba_check_vnode_poll(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *label) { struct mac_biba *subj, *obj; if (!mac_biba_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(label); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_biba_check_vnode_read(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *label) { struct mac_biba *subj, *obj; if (!mac_biba_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(label); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_biba_check_vnode_readdir(struct ucred *cred, struct vnode *dvp, struct label *dlabel) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_biba_check_vnode_readlink(struct ucred *cred, struct vnode *vp, struct label *label) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_biba_check_vnode_relabel(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, struct label *newlabel) { struct mac_biba *old, *new, *subj; int error; old = SLOT(vnodelabel); new = SLOT(newlabel); subj = SLOT(cred->cr_label); /* * If there is a Biba label update for the vnode, it must be a * effective label. */ error = biba_atmostflags(new, MAC_BIBA_FLAG_EFFECTIVE); if (error) return (error); /* * To perform a relabel of the vnode (Biba label or not), Biba must * authorize the relabel. */ if (!mac_biba_effective_in_range(old, subj)) return (EPERM); /* * If the Biba label is to be changed, authorize as appropriate. */ if (new->mb_flags & MAC_BIBA_FLAG_EFFECTIVE) { /* * To change the Biba label on a vnode, the new vnode label * must be in the subject range. */ if (!mac_biba_effective_in_range(new, subj)) return (EPERM); /* * To change the Biba label on the vnode to be EQUAL, * the subject must have appropriate privilege. */ if (mac_biba_contains_equal(new)) { error = mac_biba_subject_privileged(subj); if (error) return (error); } } return (0); } static int mac_biba_check_vnode_rename_from(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, struct componentname *cnp) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); obj = SLOT(label); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_biba_check_vnode_rename_to(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, int samedir, struct componentname *cnp) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); if (vp != NULL) { obj = SLOT(label); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); } return (0); } static int mac_biba_check_vnode_revoke(struct ucred *cred, struct vnode *vp, struct label *label) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_biba_check_vnode_setacl(struct ucred *cred, struct vnode *vp, struct label *label, acl_type_t type, struct acl *acl) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_biba_check_vnode_setextattr(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, int attrnamespace, const char *name, struct uio *uio) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vnodelabel); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); /* XXX: protect the MAC EA in a special way? */ return (0); } static int mac_biba_check_vnode_setflags(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, u_long flags) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vnodelabel); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_biba_check_vnode_setmode(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, mode_t mode) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vnodelabel); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_biba_check_vnode_setowner(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, uid_t uid, gid_t gid) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vnodelabel); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_biba_check_vnode_setutimes(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, struct timespec atime, struct timespec mtime) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vnodelabel); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_biba_check_vnode_stat(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vnodelabel) { struct mac_biba *subj, *obj; if (!mac_biba_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(vnodelabel); if (!mac_biba_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_biba_check_vnode_write(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *label) { struct mac_biba *subj, *obj; if (!mac_biba_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(label); if (!mac_biba_dominate_effective(subj, obj)) return (EACCES); return (0); } static void mac_biba_associate_nfsd_label(struct ucred *cred) { struct mac_biba *label; label = SLOT(cred->cr_label); mac_biba_set_effective(label, MAC_BIBA_TYPE_LOW, 0, NULL); mac_biba_set_range(label, MAC_BIBA_TYPE_LOW, 0, NULL, MAC_BIBA_TYPE_HIGH, 0, NULL); } static struct mac_policy_ops mac_biba_ops = { .mpo_init = mac_biba_init, .mpo_init_bpfdesc_label = mac_biba_init_label, .mpo_init_cred_label = mac_biba_init_label, .mpo_init_devfsdirent_label = mac_biba_init_label, .mpo_init_ifnet_label = mac_biba_init_label, .mpo_init_inpcb_label = mac_biba_init_label_waitcheck, .mpo_init_sysv_msgmsg_label = mac_biba_init_label, .mpo_init_sysv_msgqueue_label = mac_biba_init_label, .mpo_init_sysv_sem_label = mac_biba_init_label, .mpo_init_sysv_shm_label = mac_biba_init_label, .mpo_init_ipq_label = mac_biba_init_label_waitcheck, .mpo_init_mbuf_label = mac_biba_init_label_waitcheck, .mpo_init_mount_label = mac_biba_init_label, .mpo_init_mount_fs_label = mac_biba_init_label, .mpo_init_pipe_label = mac_biba_init_label, .mpo_init_posix_sem_label = mac_biba_init_label, .mpo_init_socket_label = mac_biba_init_label_waitcheck, .mpo_init_socket_peer_label = mac_biba_init_label_waitcheck, .mpo_init_vnode_label = mac_biba_init_label, .mpo_destroy_bpfdesc_label = mac_biba_destroy_label, .mpo_destroy_cred_label = mac_biba_destroy_label, .mpo_destroy_devfsdirent_label = mac_biba_destroy_label, .mpo_destroy_ifnet_label = mac_biba_destroy_label, .mpo_destroy_inpcb_label = mac_biba_destroy_label, .mpo_destroy_sysv_msgmsg_label = mac_biba_destroy_label, .mpo_destroy_sysv_msgqueue_label = mac_biba_destroy_label, .mpo_destroy_sysv_sem_label = mac_biba_destroy_label, .mpo_destroy_sysv_shm_label = mac_biba_destroy_label, .mpo_destroy_ipq_label = mac_biba_destroy_label, .mpo_destroy_mbuf_label = mac_biba_destroy_label, .mpo_destroy_mount_label = mac_biba_destroy_label, .mpo_destroy_mount_fs_label = mac_biba_destroy_label, .mpo_destroy_pipe_label = mac_biba_destroy_label, .mpo_destroy_posix_sem_label = mac_biba_destroy_label, .mpo_destroy_socket_label = mac_biba_destroy_label, .mpo_destroy_socket_peer_label = mac_biba_destroy_label, .mpo_destroy_vnode_label = mac_biba_destroy_label, .mpo_copy_cred_label = mac_biba_copy_label, .mpo_copy_ifnet_label = mac_biba_copy_label, .mpo_copy_mbuf_label = mac_biba_copy_label, .mpo_copy_pipe_label = mac_biba_copy_label, .mpo_copy_socket_label = mac_biba_copy_label, .mpo_copy_vnode_label = mac_biba_copy_label, .mpo_externalize_cred_label = mac_biba_externalize_label, .mpo_externalize_ifnet_label = mac_biba_externalize_label, .mpo_externalize_pipe_label = mac_biba_externalize_label, .mpo_externalize_socket_label = mac_biba_externalize_label, .mpo_externalize_socket_peer_label = mac_biba_externalize_label, .mpo_externalize_vnode_label = mac_biba_externalize_label, .mpo_internalize_cred_label = mac_biba_internalize_label, .mpo_internalize_ifnet_label = mac_biba_internalize_label, .mpo_internalize_pipe_label = mac_biba_internalize_label, .mpo_internalize_socket_label = mac_biba_internalize_label, .mpo_internalize_vnode_label = mac_biba_internalize_label, .mpo_create_devfs_device = mac_biba_create_devfs_device, .mpo_create_devfs_directory = mac_biba_create_devfs_directory, .mpo_create_devfs_symlink = mac_biba_create_devfs_symlink, .mpo_create_mount = mac_biba_create_mount, .mpo_relabel_vnode = mac_biba_relabel_vnode, .mpo_update_devfsdirent = mac_biba_update_devfsdirent, .mpo_associate_vnode_devfs = mac_biba_associate_vnode_devfs, .mpo_associate_vnode_extattr = mac_biba_associate_vnode_extattr, .mpo_associate_vnode_singlelabel = mac_biba_associate_vnode_singlelabel, .mpo_create_vnode_extattr = mac_biba_create_vnode_extattr, .mpo_setlabel_vnode_extattr = mac_biba_setlabel_vnode_extattr, .mpo_create_mbuf_from_socket = mac_biba_create_mbuf_from_socket, .mpo_create_pipe = mac_biba_create_pipe, .mpo_create_posix_sem = mac_biba_create_posix_sem, .mpo_create_socket = mac_biba_create_socket, .mpo_create_socket_from_socket = mac_biba_create_socket_from_socket, .mpo_relabel_pipe = mac_biba_relabel_pipe, .mpo_relabel_socket = mac_biba_relabel_socket, .mpo_set_socket_peer_from_mbuf = mac_biba_set_socket_peer_from_mbuf, .mpo_set_socket_peer_from_socket = mac_biba_set_socket_peer_from_socket, .mpo_create_bpfdesc = mac_biba_create_bpfdesc, .mpo_create_datagram_from_ipq = mac_biba_create_datagram_from_ipq, .mpo_create_fragment = mac_biba_create_fragment, .mpo_create_ifnet = mac_biba_create_ifnet, .mpo_create_inpcb_from_socket = mac_biba_create_inpcb_from_socket, .mpo_create_sysv_msgmsg = mac_biba_create_sysv_msgmsg, .mpo_create_sysv_msgqueue = mac_biba_create_sysv_msgqueue, .mpo_create_sysv_sem = mac_biba_create_sysv_sem, .mpo_create_sysv_shm = mac_biba_create_sysv_shm, .mpo_create_ipq = mac_biba_create_ipq, .mpo_create_mbuf_from_inpcb = mac_biba_create_mbuf_from_inpcb, .mpo_create_mbuf_linklayer = mac_biba_create_mbuf_linklayer, .mpo_create_mbuf_from_bpfdesc = mac_biba_create_mbuf_from_bpfdesc, .mpo_create_mbuf_from_ifnet = mac_biba_create_mbuf_from_ifnet, .mpo_create_mbuf_multicast_encap = mac_biba_create_mbuf_multicast_encap, .mpo_create_mbuf_netlayer = mac_biba_create_mbuf_netlayer, .mpo_fragment_match = mac_biba_fragment_match, .mpo_relabel_ifnet = mac_biba_relabel_ifnet, .mpo_update_ipq = mac_biba_update_ipq, .mpo_inpcb_sosetlabel = mac_biba_inpcb_sosetlabel, .mpo_create_proc0 = mac_biba_create_proc0, .mpo_create_proc1 = mac_biba_create_proc1, .mpo_relabel_cred = mac_biba_relabel_cred, .mpo_cleanup_sysv_msgmsg = mac_biba_cleanup_sysv_msgmsg, .mpo_cleanup_sysv_msgqueue = mac_biba_cleanup_sysv_msgqueue, .mpo_cleanup_sysv_sem = mac_biba_cleanup_sysv_sem, .mpo_cleanup_sysv_shm = mac_biba_cleanup_sysv_shm, .mpo_check_bpfdesc_receive = mac_biba_check_bpfdesc_receive, .mpo_check_cred_relabel = mac_biba_check_cred_relabel, .mpo_check_cred_visible = mac_biba_check_cred_visible, .mpo_check_ifnet_relabel = mac_biba_check_ifnet_relabel, .mpo_check_ifnet_transmit = mac_biba_check_ifnet_transmit, .mpo_check_inpcb_deliver = mac_biba_check_inpcb_deliver, .mpo_check_sysv_msgrcv = mac_biba_check_sysv_msgrcv, .mpo_check_sysv_msgrmid = mac_biba_check_sysv_msgrmid, .mpo_check_sysv_msqget = mac_biba_check_sysv_msqget, .mpo_check_sysv_msqsnd = mac_biba_check_sysv_msqsnd, .mpo_check_sysv_msqrcv = mac_biba_check_sysv_msqrcv, .mpo_check_sysv_msqctl = mac_biba_check_sysv_msqctl, .mpo_check_sysv_semctl = mac_biba_check_sysv_semctl, .mpo_check_sysv_semget = mac_biba_check_sysv_semget, .mpo_check_sysv_semop = mac_biba_check_sysv_semop, .mpo_check_sysv_shmat = mac_biba_check_sysv_shmat, .mpo_check_sysv_shmctl = mac_biba_check_sysv_shmctl, .mpo_check_sysv_shmget = mac_biba_check_sysv_shmget, .mpo_check_kld_load = mac_biba_check_kld_load, .mpo_check_kld_unload = mac_biba_check_kld_unload, .mpo_check_mount_stat = mac_biba_check_mount_stat, .mpo_check_pipe_ioctl = mac_biba_check_pipe_ioctl, .mpo_check_pipe_poll = mac_biba_check_pipe_poll, .mpo_check_pipe_read = mac_biba_check_pipe_read, .mpo_check_pipe_relabel = mac_biba_check_pipe_relabel, .mpo_check_pipe_stat = mac_biba_check_pipe_stat, .mpo_check_pipe_write = mac_biba_check_pipe_write, .mpo_check_posix_sem_destroy = mac_biba_check_posix_sem_write, .mpo_check_posix_sem_getvalue = mac_biba_check_posix_sem_rdonly, .mpo_check_posix_sem_open = mac_biba_check_posix_sem_write, .mpo_check_posix_sem_post = mac_biba_check_posix_sem_write, .mpo_check_posix_sem_unlink = mac_biba_check_posix_sem_write, .mpo_check_posix_sem_wait = mac_biba_check_posix_sem_write, .mpo_check_proc_debug = mac_biba_check_proc_debug, .mpo_check_proc_sched = mac_biba_check_proc_sched, .mpo_check_proc_signal = mac_biba_check_proc_signal, .mpo_check_socket_deliver = mac_biba_check_socket_deliver, .mpo_check_socket_relabel = mac_biba_check_socket_relabel, .mpo_check_socket_visible = mac_biba_check_socket_visible, .mpo_check_sysarch_ioperm = mac_biba_check_sysarch_ioperm, .mpo_check_system_acct = mac_biba_check_system_acct, .mpo_check_system_settime = mac_biba_check_system_settime, .mpo_check_system_swapon = mac_biba_check_system_swapon, .mpo_check_system_swapoff = mac_biba_check_system_swapoff, .mpo_check_system_sysctl = mac_biba_check_system_sysctl, .mpo_check_vnode_access = mac_biba_check_vnode_open, .mpo_check_vnode_chdir = mac_biba_check_vnode_chdir, .mpo_check_vnode_chroot = mac_biba_check_vnode_chroot, .mpo_check_vnode_create = mac_biba_check_vnode_create, .mpo_check_vnode_delete = mac_biba_check_vnode_delete, .mpo_check_vnode_deleteacl = mac_biba_check_vnode_deleteacl, .mpo_check_vnode_deleteextattr = mac_biba_check_vnode_deleteextattr, .mpo_check_vnode_exec = mac_biba_check_vnode_exec, .mpo_check_vnode_getacl = mac_biba_check_vnode_getacl, .mpo_check_vnode_getextattr = mac_biba_check_vnode_getextattr, .mpo_check_vnode_link = mac_biba_check_vnode_link, .mpo_check_vnode_listextattr = mac_biba_check_vnode_listextattr, .mpo_check_vnode_lookup = mac_biba_check_vnode_lookup, .mpo_check_vnode_mmap = mac_biba_check_vnode_mmap, .mpo_check_vnode_open = mac_biba_check_vnode_open, .mpo_check_vnode_poll = mac_biba_check_vnode_poll, .mpo_check_vnode_read = mac_biba_check_vnode_read, .mpo_check_vnode_readdir = mac_biba_check_vnode_readdir, .mpo_check_vnode_readlink = mac_biba_check_vnode_readlink, .mpo_check_vnode_relabel = mac_biba_check_vnode_relabel, .mpo_check_vnode_rename_from = mac_biba_check_vnode_rename_from, .mpo_check_vnode_rename_to = mac_biba_check_vnode_rename_to, .mpo_check_vnode_revoke = mac_biba_check_vnode_revoke, .mpo_check_vnode_setacl = mac_biba_check_vnode_setacl, .mpo_check_vnode_setextattr = mac_biba_check_vnode_setextattr, .mpo_check_vnode_setflags = mac_biba_check_vnode_setflags, .mpo_check_vnode_setmode = mac_biba_check_vnode_setmode, .mpo_check_vnode_setowner = mac_biba_check_vnode_setowner, .mpo_check_vnode_setutimes = mac_biba_check_vnode_setutimes, .mpo_check_vnode_stat = mac_biba_check_vnode_stat, .mpo_check_vnode_write = mac_biba_check_vnode_write, .mpo_associate_nfsd_label = mac_biba_associate_nfsd_label, + .mpo_create_mbuf_from_firewall = mac_biba_create_mbuf_from_firewall, }; MAC_POLICY_SET(&mac_biba_ops, mac_biba, "TrustedBSD MAC/Biba", MPC_LOADTIME_FLAG_NOTLATE | MPC_LOADTIME_FLAG_LABELMBUFS, &mac_biba_slot); Index: stable/6/sys/security/mac_lomac/mac_lomac.c =================================================================== --- stable/6/sys/security/mac_lomac/mac_lomac.c (revision 162447) +++ stable/6/sys/security/mac_lomac/mac_lomac.c (revision 162448) @@ -1,2692 +1,2704 @@ /*- * Copyright (c) 1999-2002 Robert N. M. Watson * Copyright (c) 2001-2005 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed by Robert Watson for the TrustedBSD Project. * * This software was developed for the FreeBSD Project in part by NAI Labs, * 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. * * 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$ */ /* * Developed by the TrustedBSD Project. * Low-watermark floating label mandatory integrity policy. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct mac_lomac_proc { struct mac_lomac mac_lomac; struct mtx mtx; }; SYSCTL_DECL(_security_mac); SYSCTL_NODE(_security_mac, OID_AUTO, lomac, CTLFLAG_RW, 0, "TrustedBSD mac_lomac policy controls"); static int mac_lomac_label_size = sizeof(struct mac_lomac); SYSCTL_INT(_security_mac_lomac, OID_AUTO, label_size, CTLFLAG_RD, &mac_lomac_label_size, 0, "Size of struct mac_lomac"); static int mac_lomac_enabled = 1; SYSCTL_INT(_security_mac_lomac, OID_AUTO, enabled, CTLFLAG_RW, &mac_lomac_enabled, 0, "Enforce MAC/LOMAC policy"); TUNABLE_INT("security.mac.lomac.enabled", &mac_lomac_enabled); static int destroyed_not_inited; SYSCTL_INT(_security_mac_lomac, OID_AUTO, destroyed_not_inited, CTLFLAG_RD, &destroyed_not_inited, 0, "Count of labels destroyed but not inited"); static int trust_all_interfaces = 0; SYSCTL_INT(_security_mac_lomac, OID_AUTO, trust_all_interfaces, CTLFLAG_RD, &trust_all_interfaces, 0, "Consider all interfaces 'trusted' by MAC/LOMAC"); TUNABLE_INT("security.mac.lomac.trust_all_interfaces", &trust_all_interfaces); static char trusted_interfaces[128]; SYSCTL_STRING(_security_mac_lomac, OID_AUTO, trusted_interfaces, CTLFLAG_RD, trusted_interfaces, 0, "Interfaces considered 'trusted' by MAC/LOMAC"); TUNABLE_STR("security.mac.lomac.trusted_interfaces", trusted_interfaces, sizeof(trusted_interfaces)); static int ptys_equal = 0; SYSCTL_INT(_security_mac_lomac, OID_AUTO, ptys_equal, CTLFLAG_RW, &ptys_equal, 0, "Label pty devices as lomac/equal on create"); TUNABLE_INT("security.mac.lomac.ptys_equal", &ptys_equal); static int revocation_enabled = 1; SYSCTL_INT(_security_mac_lomac, OID_AUTO, revocation_enabled, CTLFLAG_RW, &revocation_enabled, 0, "Revoke access to objects on relabel"); TUNABLE_INT("security.mac.lomac.revocation_enabled", &revocation_enabled); static int mac_lomac_slot; #define SLOT(l) ((struct mac_lomac *)LABEL_TO_SLOT((l), mac_lomac_slot).l_ptr) #define SLOT_SET(l, val) (LABEL_TO_SLOT((l), mac_lomac_slot).l_ptr = (val)) #define PSLOT(l) ((struct mac_lomac_proc *) \ LABEL_TO_SLOT((l), mac_lomac_slot).l_ptr) #define PSLOT_SET(l, val) (LABEL_TO_SLOT((l), mac_lomac_slot).l_ptr = (val)) MALLOC_DEFINE(M_MACLOMAC, "lomac label", "MAC/LOMAC labels"); static struct mac_lomac * lomac_alloc(int flag) { struct mac_lomac *mac_lomac; mac_lomac = malloc(sizeof(struct mac_lomac), M_MACLOMAC, M_ZERO | flag); return (mac_lomac); } static void lomac_free(struct mac_lomac *mac_lomac) { if (mac_lomac != NULL) free(mac_lomac, M_MACLOMAC); else atomic_add_int(&destroyed_not_inited, 1); } static int lomac_atmostflags(struct mac_lomac *mac_lomac, int flags) { if ((mac_lomac->ml_flags & flags) != mac_lomac->ml_flags) return (EINVAL); return (0); } static int mac_lomac_dominate_element(struct mac_lomac_element *a, struct mac_lomac_element *b) { switch (a->mle_type) { case MAC_LOMAC_TYPE_EQUAL: case MAC_LOMAC_TYPE_HIGH: return (1); case MAC_LOMAC_TYPE_LOW: switch (b->mle_type) { case MAC_LOMAC_TYPE_GRADE: case MAC_LOMAC_TYPE_HIGH: return (0); case MAC_LOMAC_TYPE_EQUAL: case MAC_LOMAC_TYPE_LOW: return (1); default: panic("mac_lomac_dominate_element: b->mle_type invalid"); } case MAC_LOMAC_TYPE_GRADE: switch (b->mle_type) { case MAC_LOMAC_TYPE_EQUAL: case MAC_LOMAC_TYPE_LOW: return (1); case MAC_LOMAC_TYPE_HIGH: return (0); case MAC_LOMAC_TYPE_GRADE: return (a->mle_grade >= b->mle_grade); default: panic("mac_lomac_dominate_element: b->mle_type invalid"); } default: panic("mac_lomac_dominate_element: a->mle_type invalid"); } } static int mac_lomac_range_in_range(struct mac_lomac *rangea, struct mac_lomac *rangeb) { return (mac_lomac_dominate_element(&rangeb->ml_rangehigh, &rangea->ml_rangehigh) && mac_lomac_dominate_element(&rangea->ml_rangelow, &rangeb->ml_rangelow)); } static int mac_lomac_single_in_range(struct mac_lomac *single, struct mac_lomac *range) { KASSERT((single->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("mac_lomac_single_in_range: a not single")); KASSERT((range->ml_flags & MAC_LOMAC_FLAG_RANGE) != 0, ("mac_lomac_single_in_range: b not range")); return (mac_lomac_dominate_element(&range->ml_rangehigh, &single->ml_single) && mac_lomac_dominate_element(&single->ml_single, &range->ml_rangelow)); } static int mac_lomac_auxsingle_in_range(struct mac_lomac *single, struct mac_lomac *range) { KASSERT((single->ml_flags & MAC_LOMAC_FLAG_AUX) != 0, ("mac_lomac_single_in_range: a not auxsingle")); KASSERT((range->ml_flags & MAC_LOMAC_FLAG_RANGE) != 0, ("mac_lomac_single_in_range: b not range")); return (mac_lomac_dominate_element(&range->ml_rangehigh, &single->ml_auxsingle) && mac_lomac_dominate_element(&single->ml_auxsingle, &range->ml_rangelow)); } static int mac_lomac_dominate_single(struct mac_lomac *a, struct mac_lomac *b) { KASSERT((a->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("mac_lomac_dominate_single: a not single")); KASSERT((b->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("mac_lomac_dominate_single: b not single")); return (mac_lomac_dominate_element(&a->ml_single, &b->ml_single)); } static int mac_lomac_subject_dominate(struct mac_lomac *a, struct mac_lomac *b) { KASSERT((~a->ml_flags & (MAC_LOMAC_FLAG_SINGLE | MAC_LOMAC_FLAG_RANGE)) == 0, ("mac_lomac_dominate_single: a not subject")); KASSERT((b->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("mac_lomac_dominate_single: b not single")); return (mac_lomac_dominate_element(&a->ml_rangehigh, &b->ml_single)); } static int mac_lomac_equal_element(struct mac_lomac_element *a, struct mac_lomac_element *b) { if (a->mle_type == MAC_LOMAC_TYPE_EQUAL || b->mle_type == MAC_LOMAC_TYPE_EQUAL) return (1); return (a->mle_type == b->mle_type && a->mle_grade == b->mle_grade); } static int mac_lomac_equal_single(struct mac_lomac *a, struct mac_lomac *b) { KASSERT((a->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("mac_lomac_equal_single: a not single")); KASSERT((b->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("mac_lomac_equal_single: b not single")); return (mac_lomac_equal_element(&a->ml_single, &b->ml_single)); } static int mac_lomac_contains_equal(struct mac_lomac *mac_lomac) { if (mac_lomac->ml_flags & MAC_LOMAC_FLAG_SINGLE) if (mac_lomac->ml_single.mle_type == MAC_LOMAC_TYPE_EQUAL) return (1); if (mac_lomac->ml_flags & MAC_LOMAC_FLAG_AUX) if (mac_lomac->ml_auxsingle.mle_type == MAC_LOMAC_TYPE_EQUAL) return (1); if (mac_lomac->ml_flags & MAC_LOMAC_FLAG_RANGE) { if (mac_lomac->ml_rangelow.mle_type == MAC_LOMAC_TYPE_EQUAL) return (1); if (mac_lomac->ml_rangehigh.mle_type == MAC_LOMAC_TYPE_EQUAL) return (1); } return (0); } static int mac_lomac_subject_privileged(struct mac_lomac *mac_lomac) { KASSERT((mac_lomac->ml_flags & MAC_LOMAC_FLAGS_BOTH) == MAC_LOMAC_FLAGS_BOTH, ("mac_lomac_subject_privileged: subject doesn't have both labels")); /* If the single is EQUAL, it's ok. */ if (mac_lomac->ml_single.mle_type == MAC_LOMAC_TYPE_EQUAL) return (0); /* If either range endpoint is EQUAL, it's ok. */ if (mac_lomac->ml_rangelow.mle_type == MAC_LOMAC_TYPE_EQUAL || mac_lomac->ml_rangehigh.mle_type == MAC_LOMAC_TYPE_EQUAL) return (0); /* If the range is low-high, it's ok. */ if (mac_lomac->ml_rangelow.mle_type == MAC_LOMAC_TYPE_LOW && mac_lomac->ml_rangehigh.mle_type == MAC_LOMAC_TYPE_HIGH) return (0); /* It's not ok. */ return (EPERM); } static int mac_lomac_high_single(struct mac_lomac *mac_lomac) { KASSERT((mac_lomac->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("mac_lomac_high_single: mac_lomac not single")); return (mac_lomac->ml_single.mle_type == MAC_LOMAC_TYPE_HIGH); } static int mac_lomac_valid(struct mac_lomac *mac_lomac) { if (mac_lomac->ml_flags & MAC_LOMAC_FLAG_SINGLE) { switch (mac_lomac->ml_single.mle_type) { case MAC_LOMAC_TYPE_GRADE: case MAC_LOMAC_TYPE_EQUAL: case MAC_LOMAC_TYPE_HIGH: case MAC_LOMAC_TYPE_LOW: break; default: return (EINVAL); } } else { if (mac_lomac->ml_single.mle_type != MAC_LOMAC_TYPE_UNDEF) return (EINVAL); } if (mac_lomac->ml_flags & MAC_LOMAC_FLAG_AUX) { switch (mac_lomac->ml_auxsingle.mle_type) { case MAC_LOMAC_TYPE_GRADE: case MAC_LOMAC_TYPE_EQUAL: case MAC_LOMAC_TYPE_HIGH: case MAC_LOMAC_TYPE_LOW: break; default: return (EINVAL); } } else { if (mac_lomac->ml_auxsingle.mle_type != MAC_LOMAC_TYPE_UNDEF) return (EINVAL); } if (mac_lomac->ml_flags & MAC_LOMAC_FLAG_RANGE) { switch (mac_lomac->ml_rangelow.mle_type) { case MAC_LOMAC_TYPE_GRADE: case MAC_LOMAC_TYPE_EQUAL: case MAC_LOMAC_TYPE_HIGH: case MAC_LOMAC_TYPE_LOW: break; default: return (EINVAL); } switch (mac_lomac->ml_rangehigh.mle_type) { case MAC_LOMAC_TYPE_GRADE: case MAC_LOMAC_TYPE_EQUAL: case MAC_LOMAC_TYPE_HIGH: case MAC_LOMAC_TYPE_LOW: break; default: return (EINVAL); } if (!mac_lomac_dominate_element(&mac_lomac->ml_rangehigh, &mac_lomac->ml_rangelow)) return (EINVAL); } else { if (mac_lomac->ml_rangelow.mle_type != MAC_LOMAC_TYPE_UNDEF || mac_lomac->ml_rangehigh.mle_type != MAC_LOMAC_TYPE_UNDEF) return (EINVAL); } return (0); } static void mac_lomac_set_range(struct mac_lomac *mac_lomac, u_short typelow, u_short gradelow, u_short typehigh, u_short gradehigh) { mac_lomac->ml_rangelow.mle_type = typelow; mac_lomac->ml_rangelow.mle_grade = gradelow; mac_lomac->ml_rangehigh.mle_type = typehigh; mac_lomac->ml_rangehigh.mle_grade = gradehigh; mac_lomac->ml_flags |= MAC_LOMAC_FLAG_RANGE; } static void mac_lomac_set_single(struct mac_lomac *mac_lomac, u_short type, u_short grade) { mac_lomac->ml_single.mle_type = type; mac_lomac->ml_single.mle_grade = grade; mac_lomac->ml_flags |= MAC_LOMAC_FLAG_SINGLE; } static void mac_lomac_copy_range(struct mac_lomac *labelfrom, struct mac_lomac *labelto) { KASSERT((labelfrom->ml_flags & MAC_LOMAC_FLAG_RANGE) != 0, ("mac_lomac_copy_range: labelfrom not range")); labelto->ml_rangelow = labelfrom->ml_rangelow; labelto->ml_rangehigh = labelfrom->ml_rangehigh; labelto->ml_flags |= MAC_LOMAC_FLAG_RANGE; } static void mac_lomac_copy_single(struct mac_lomac *labelfrom, struct mac_lomac *labelto) { KASSERT((labelfrom->ml_flags & MAC_LOMAC_FLAG_SINGLE) != 0, ("mac_lomac_copy_single: labelfrom not single")); labelto->ml_single = labelfrom->ml_single; labelto->ml_flags |= MAC_LOMAC_FLAG_SINGLE; } static void mac_lomac_copy_auxsingle(struct mac_lomac *labelfrom, struct mac_lomac *labelto) { KASSERT((labelfrom->ml_flags & MAC_LOMAC_FLAG_AUX) != 0, ("mac_lomac_copy_auxsingle: labelfrom not auxsingle")); labelto->ml_auxsingle = labelfrom->ml_auxsingle; labelto->ml_flags |= MAC_LOMAC_FLAG_AUX; } static void mac_lomac_copy(struct mac_lomac *source, struct mac_lomac *dest) { if (source->ml_flags & MAC_LOMAC_FLAG_SINGLE) mac_lomac_copy_single(source, dest); if (source->ml_flags & MAC_LOMAC_FLAG_AUX) mac_lomac_copy_auxsingle(source, dest); if (source->ml_flags & MAC_LOMAC_FLAG_RANGE) mac_lomac_copy_range(source, dest); } static int mac_lomac_to_string(struct sbuf *sb, struct mac_lomac *mac_lomac); static int maybe_demote(struct mac_lomac *subjlabel, struct mac_lomac *objlabel, const char *actionname, const char *objname, struct vnode *vpq) { struct sbuf subjlabel_sb, subjtext_sb, objlabel_sb; char *subjlabeltext, *objlabeltext, *subjtext; struct mac_lomac cached_subjlabel; struct mac_lomac_proc *subj; struct vattr va; struct proc *p; pid_t pgid; subj = PSLOT(curthread->td_proc->p_label); p = curthread->td_proc; mtx_lock(&subj->mtx); if (subj->mac_lomac.ml_flags & MAC_LOMAC_FLAG_UPDATE) { /* * Check to see if the pending demotion would be more or * less severe than this one, and keep the more severe. * This can only happen for a multi-threaded application. */ if (mac_lomac_dominate_single(objlabel, &subj->mac_lomac)) { mtx_unlock(&subj->mtx); return (0); } } bzero(&subj->mac_lomac, sizeof(subj->mac_lomac)); /* * Always demote the single label. */ mac_lomac_copy_single(objlabel, &subj->mac_lomac); /* * Start with the original range, then minimize each side of * the range to the point of not dominating the object. The * high side will always be demoted, of course. */ mac_lomac_copy_range(subjlabel, &subj->mac_lomac); if (!mac_lomac_dominate_element(&objlabel->ml_single, &subj->mac_lomac.ml_rangelow)) subj->mac_lomac.ml_rangelow = objlabel->ml_single; subj->mac_lomac.ml_rangehigh = objlabel->ml_single; subj->mac_lomac.ml_flags |= MAC_LOMAC_FLAG_UPDATE; mtx_lock_spin(&sched_lock); curthread->td_flags |= TDF_ASTPENDING; curthread->td_proc->p_sflag |= PS_MACPEND; mtx_unlock_spin(&sched_lock); /* * Avoid memory allocation while holding a mutex; cache the * label. */ mac_lomac_copy_single(&subj->mac_lomac, &cached_subjlabel); mtx_unlock(&subj->mtx); sbuf_new(&subjlabel_sb, NULL, 0, SBUF_AUTOEXTEND); mac_lomac_to_string(&subjlabel_sb, subjlabel); sbuf_finish(&subjlabel_sb); subjlabeltext = sbuf_data(&subjlabel_sb); sbuf_new(&subjtext_sb, NULL, 0, SBUF_AUTOEXTEND); mac_lomac_to_string(&subjtext_sb, &subj->mac_lomac); sbuf_finish(&subjtext_sb); subjtext = sbuf_data(&subjtext_sb); sbuf_new(&objlabel_sb, NULL, 0, SBUF_AUTOEXTEND); mac_lomac_to_string(&objlabel_sb, objlabel); sbuf_finish(&objlabel_sb); objlabeltext = sbuf_data(&objlabel_sb); pgid = p->p_pgrp->pg_id; /* XXX could be stale? */ if (vpq != NULL && VOP_GETATTR(vpq, &va, curthread->td_ucred, curthread) == 0) { log(LOG_INFO, "LOMAC: level-%s subject p%dg%du%d:%s demoted to" " level %s after %s a level-%s %s (inode=%ld, " "mountpount=%s)\n", subjlabeltext, p->p_pid, pgid, curthread->td_ucred->cr_uid, p->p_comm, subjtext, actionname, objlabeltext, objname, va.va_fileid, vpq->v_mount->mnt_stat.f_mntonname); } else { log(LOG_INFO, "LOMAC: level-%s subject p%dg%du%d:%s demoted to" " level %s after %s a level-%s %s\n", subjlabeltext, p->p_pid, pgid, curthread->td_ucred->cr_uid, p->p_comm, subjtext, actionname, objlabeltext, objname); } sbuf_delete(&subjlabel_sb); sbuf_delete(&subjtext_sb); sbuf_delete(&objlabel_sb); return (0); } /* * Relabel "to" to "from" only if "from" is a valid label (contains * at least a single), as for a relabel operation which may or may * not involve a relevant label. */ static void try_relabel(struct mac_lomac *from, struct mac_lomac *to) { if (from->ml_flags & MAC_LOMAC_FLAG_SINGLE) { bzero(to, sizeof(*to)); mac_lomac_copy(from, to); } } /* * Policy module operations. */ static void mac_lomac_init(struct mac_policy_conf *conf) { } /* * Label operations. */ static void mac_lomac_init_label(struct label *label) { SLOT_SET(label, lomac_alloc(M_WAITOK)); } static int mac_lomac_init_label_waitcheck(struct label *label, int flag) { SLOT_SET(label, lomac_alloc(flag)); if (SLOT(label) == NULL) return (ENOMEM); return (0); } static void mac_lomac_init_proc_label(struct label *label) { PSLOT_SET(label, malloc(sizeof(struct mac_lomac_proc), M_MACLOMAC, M_ZERO | M_WAITOK)); mtx_init(&PSLOT(label)->mtx, "MAC/Lomac proc lock", NULL, MTX_DEF); } static void mac_lomac_destroy_label(struct label *label) { lomac_free(SLOT(label)); SLOT_SET(label, NULL); } static void mac_lomac_destroy_proc_label(struct label *label) { mtx_destroy(&PSLOT(label)->mtx); FREE(PSLOT(label), M_MACLOMAC); PSLOT_SET(label, NULL); } static int mac_lomac_element_to_string(struct sbuf *sb, struct mac_lomac_element *element) { switch (element->mle_type) { case MAC_LOMAC_TYPE_HIGH: return (sbuf_printf(sb, "high")); case MAC_LOMAC_TYPE_LOW: return (sbuf_printf(sb, "low")); case MAC_LOMAC_TYPE_EQUAL: return (sbuf_printf(sb, "equal")); case MAC_LOMAC_TYPE_GRADE: return (sbuf_printf(sb, "%d", element->mle_grade)); default: panic("mac_lomac_element_to_string: invalid type (%d)", element->mle_type); } } static int mac_lomac_to_string(struct sbuf *sb, struct mac_lomac *mac_lomac) { if (mac_lomac->ml_flags & MAC_LOMAC_FLAG_SINGLE) { if (mac_lomac_element_to_string(sb, &mac_lomac->ml_single) == -1) return (EINVAL); } if (mac_lomac->ml_flags & MAC_LOMAC_FLAG_AUX) { if (sbuf_putc(sb, '[') == -1) return (EINVAL); if (mac_lomac_element_to_string(sb, &mac_lomac->ml_auxsingle) == -1) return (EINVAL); if (sbuf_putc(sb, ']') == -1) return (EINVAL); } if (mac_lomac->ml_flags & MAC_LOMAC_FLAG_RANGE) { if (sbuf_putc(sb, '(') == -1) return (EINVAL); if (mac_lomac_element_to_string(sb, &mac_lomac->ml_rangelow) == -1) return (EINVAL); if (sbuf_putc(sb, '-') == -1) return (EINVAL); if (mac_lomac_element_to_string(sb, &mac_lomac->ml_rangehigh) == -1) return (EINVAL); if (sbuf_putc(sb, ')') == -1) return (EINVAL); } return (0); } static int mac_lomac_externalize_label(struct label *label, char *element_name, struct sbuf *sb, int *claimed) { struct mac_lomac *mac_lomac; if (strcmp(MAC_LOMAC_LABEL_NAME, element_name) != 0) return (0); (*claimed)++; mac_lomac = SLOT(label); return (mac_lomac_to_string(sb, mac_lomac)); } static int mac_lomac_parse_element(struct mac_lomac_element *element, char *string) { if (strcmp(string, "high") == 0 || strcmp(string, "hi") == 0) { element->mle_type = MAC_LOMAC_TYPE_HIGH; element->mle_grade = MAC_LOMAC_TYPE_UNDEF; } else if (strcmp(string, "low") == 0 || strcmp(string, "lo") == 0) { element->mle_type = MAC_LOMAC_TYPE_LOW; element->mle_grade = MAC_LOMAC_TYPE_UNDEF; } else if (strcmp(string, "equal") == 0 || strcmp(string, "eq") == 0) { element->mle_type = MAC_LOMAC_TYPE_EQUAL; element->mle_grade = MAC_LOMAC_TYPE_UNDEF; } else { char *p0, *p1; int d; p0 = string; d = strtol(p0, &p1, 10); if (d < 0 || d > 65535) return (EINVAL); element->mle_type = MAC_LOMAC_TYPE_GRADE; element->mle_grade = d; if (p1 == p0 || *p1 != '\0') return (EINVAL); } return (0); } /* * Note: destructively consumes the string, make a local copy before * calling if that's a problem. */ static int mac_lomac_parse(struct mac_lomac *mac_lomac, char *string) { char *range, *rangeend, *rangehigh, *rangelow, *single, *auxsingle, *auxsingleend; int error; /* Do we have a range? */ single = string; range = index(string, '('); if (range == single) single = NULL; auxsingle = index(string, '['); if (auxsingle == single) single = NULL; if (range != NULL && auxsingle != NULL) return (EINVAL); rangelow = rangehigh = NULL; if (range != NULL) { /* Nul terminate the end of the single string. */ *range = '\0'; range++; rangelow = range; rangehigh = index(rangelow, '-'); if (rangehigh == NULL) return (EINVAL); rangehigh++; if (*rangelow == '\0' || *rangehigh == '\0') return (EINVAL); rangeend = index(rangehigh, ')'); if (rangeend == NULL) return (EINVAL); if (*(rangeend + 1) != '\0') return (EINVAL); /* Nul terminate the ends of the ranges. */ *(rangehigh - 1) = '\0'; *rangeend = '\0'; } KASSERT((rangelow != NULL && rangehigh != NULL) || (rangelow == NULL && rangehigh == NULL), ("mac_lomac_internalize_label: range mismatch")); if (auxsingle != NULL) { /* Nul terminate the end of the single string. */ *auxsingle = '\0'; auxsingle++; auxsingleend = index(auxsingle, ']'); if (auxsingleend == NULL) return (EINVAL); if (*(auxsingleend + 1) != '\0') return (EINVAL); /* Nul terminate the end of the auxsingle. */ *auxsingleend = '\0'; } bzero(mac_lomac, sizeof(*mac_lomac)); if (single != NULL) { error = mac_lomac_parse_element(&mac_lomac->ml_single, single); if (error) return (error); mac_lomac->ml_flags |= MAC_LOMAC_FLAG_SINGLE; } if (auxsingle != NULL) { error = mac_lomac_parse_element(&mac_lomac->ml_auxsingle, auxsingle); if (error) return (error); mac_lomac->ml_flags |= MAC_LOMAC_FLAG_AUX; } if (rangelow != NULL) { error = mac_lomac_parse_element(&mac_lomac->ml_rangelow, rangelow); if (error) return (error); error = mac_lomac_parse_element(&mac_lomac->ml_rangehigh, rangehigh); if (error) return (error); mac_lomac->ml_flags |= MAC_LOMAC_FLAG_RANGE; } error = mac_lomac_valid(mac_lomac); if (error) return (error); return (0); } static int mac_lomac_internalize_label(struct label *label, char *element_name, char *element_data, int *claimed) { struct mac_lomac *mac_lomac, mac_lomac_temp; int error; if (strcmp(MAC_LOMAC_LABEL_NAME, element_name) != 0) return (0); (*claimed)++; error = mac_lomac_parse(&mac_lomac_temp, element_data); if (error) return (error); mac_lomac = SLOT(label); *mac_lomac = mac_lomac_temp; return (0); } static void mac_lomac_copy_label(struct label *src, struct label *dest) { *SLOT(dest) = *SLOT(src); } /* * Labeling event operations: file system objects, and things that look * a lot like file system objects. */ static void mac_lomac_create_devfs_device(struct ucred *cred, struct mount *mp, struct cdev *dev, struct devfs_dirent *devfs_dirent, struct label *label) { struct mac_lomac *mac_lomac; int lomac_type; mac_lomac = SLOT(label); if (strcmp(dev->si_name, "null") == 0 || strcmp(dev->si_name, "zero") == 0 || strcmp(dev->si_name, "random") == 0 || strncmp(dev->si_name, "fd/", strlen("fd/")) == 0 || strncmp(dev->si_name, "ttyv", strlen("ttyv")) == 0) lomac_type = MAC_LOMAC_TYPE_EQUAL; else if (ptys_equal && (strncmp(dev->si_name, "ttyp", strlen("ttyp")) == 0 || strncmp(dev->si_name, "ptyp", strlen("ptyp")) == 0)) lomac_type = MAC_LOMAC_TYPE_EQUAL; else lomac_type = MAC_LOMAC_TYPE_HIGH; mac_lomac_set_single(mac_lomac, lomac_type, 0); } static void mac_lomac_create_devfs_directory(struct mount *mp, char *dirname, int dirnamelen, struct devfs_dirent *devfs_dirent, struct label *label) { struct mac_lomac *mac_lomac; mac_lomac = SLOT(label); mac_lomac_set_single(mac_lomac, MAC_LOMAC_TYPE_HIGH, 0); } static void mac_lomac_create_devfs_symlink(struct ucred *cred, struct mount *mp, struct devfs_dirent *dd, struct label *ddlabel, struct devfs_dirent *de, struct label *delabel) { struct mac_lomac *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(delabel); mac_lomac_copy_single(source, dest); } static void mac_lomac_create_mount(struct ucred *cred, struct mount *mp, struct label *mntlabel, struct label *fslabel) { struct mac_lomac *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(mntlabel); mac_lomac_copy_single(source, dest); dest = SLOT(fslabel); mac_lomac_copy_single(source, dest); } static void mac_lomac_relabel_vnode(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, struct label *label) { struct mac_lomac *source, *dest; source = SLOT(label); dest = SLOT(vnodelabel); try_relabel(source, dest); } static void mac_lomac_update_devfsdirent(struct mount *mp, struct devfs_dirent *devfs_dirent, struct label *direntlabel, struct vnode *vp, struct label *vnodelabel) { struct mac_lomac *source, *dest; source = SLOT(vnodelabel); dest = SLOT(direntlabel); mac_lomac_copy(source, dest); } static void mac_lomac_associate_vnode_devfs(struct mount *mp, struct label *fslabel, struct devfs_dirent *de, struct label *delabel, struct vnode *vp, struct label *vlabel) { struct mac_lomac *source, *dest; source = SLOT(delabel); dest = SLOT(vlabel); mac_lomac_copy_single(source, dest); } static int mac_lomac_associate_vnode_extattr(struct mount *mp, struct label *fslabel, struct vnode *vp, struct label *vlabel) { struct mac_lomac temp, *source, *dest; int buflen, error; source = SLOT(fslabel); dest = SLOT(vlabel); buflen = sizeof(temp); bzero(&temp, buflen); error = vn_extattr_get(vp, IO_NODELOCKED, MAC_LOMAC_EXTATTR_NAMESPACE, MAC_LOMAC_EXTATTR_NAME, &buflen, (char *)&temp, curthread); if (error == ENOATTR || error == EOPNOTSUPP) { /* Fall back to the fslabel. */ mac_lomac_copy_single(source, dest); return (0); } else if (error) return (error); if (buflen != sizeof(temp)) { if (buflen != sizeof(temp) - sizeof(temp.ml_auxsingle)) { printf("mac_lomac_associate_vnode_extattr: bad size %d\n", buflen); return (EPERM); } bzero(&temp.ml_auxsingle, sizeof(temp.ml_auxsingle)); buflen = sizeof(temp); (void)vn_extattr_set(vp, IO_NODELOCKED, MAC_LOMAC_EXTATTR_NAMESPACE, MAC_LOMAC_EXTATTR_NAME, buflen, (char *)&temp, curthread); } if (mac_lomac_valid(&temp) != 0) { printf("mac_lomac_associate_vnode_extattr: invalid\n"); return (EPERM); } if ((temp.ml_flags & MAC_LOMAC_FLAGS_BOTH) != MAC_LOMAC_FLAG_SINGLE) { printf("mac_lomac_associate_vnode_extattr: not single\n"); return (EPERM); } mac_lomac_copy_single(&temp, dest); return (0); } static void mac_lomac_associate_vnode_singlelabel(struct mount *mp, struct label *fslabel, struct vnode *vp, struct label *vlabel) { struct mac_lomac *source, *dest; source = SLOT(fslabel); dest = SLOT(vlabel); mac_lomac_copy_single(source, dest); } static int mac_lomac_create_vnode_extattr(struct ucred *cred, struct mount *mp, struct label *fslabel, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *vlabel, struct componentname *cnp) { struct mac_lomac *source, *dest, *dir, temp; size_t buflen; int error; buflen = sizeof(temp); bzero(&temp, buflen); source = SLOT(cred->cr_label); dest = SLOT(vlabel); dir = SLOT(dlabel); if (dir->ml_flags & MAC_LOMAC_FLAG_AUX) { mac_lomac_copy_auxsingle(dir, &temp); mac_lomac_set_single(&temp, dir->ml_auxsingle.mle_type, dir->ml_auxsingle.mle_grade); } else { mac_lomac_copy_single(source, &temp); } error = vn_extattr_set(vp, IO_NODELOCKED, MAC_LOMAC_EXTATTR_NAMESPACE, MAC_LOMAC_EXTATTR_NAME, buflen, (char *)&temp, curthread); if (error == 0) mac_lomac_copy(&temp, dest); return (error); } static int mac_lomac_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp, struct label *vlabel, struct label *intlabel) { struct mac_lomac *source, temp; size_t buflen; int error; buflen = sizeof(temp); bzero(&temp, buflen); source = SLOT(intlabel); if ((source->ml_flags & MAC_LOMAC_FLAG_SINGLE) == 0) return (0); mac_lomac_copy_single(source, &temp); error = vn_extattr_set(vp, IO_NODELOCKED, MAC_LOMAC_EXTATTR_NAMESPACE, MAC_LOMAC_EXTATTR_NAME, buflen, (char *)&temp, curthread); return (error); } /* * Labeling event operations: IPC object. */ static void mac_lomac_create_inpcb_from_socket(struct socket *so, struct label *solabel, struct inpcb *inp, struct label *inplabel) { struct mac_lomac *source, *dest; source = SLOT(solabel); dest = SLOT(inplabel); mac_lomac_copy_single(source, dest); } static void mac_lomac_create_mbuf_from_socket(struct socket *so, struct label *socketlabel, struct mbuf *m, struct label *mbuflabel) { struct mac_lomac *source, *dest; source = SLOT(socketlabel); dest = SLOT(mbuflabel); mac_lomac_copy_single(source, dest); } static void mac_lomac_create_socket(struct ucred *cred, struct socket *socket, struct label *socketlabel) { struct mac_lomac *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(socketlabel); mac_lomac_copy_single(source, dest); } static void mac_lomac_create_pipe(struct ucred *cred, struct pipepair *pp, struct label *pipelabel) { struct mac_lomac *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(pipelabel); mac_lomac_copy_single(source, dest); } static void mac_lomac_create_socket_from_socket(struct socket *oldsocket, struct label *oldsocketlabel, struct socket *newsocket, struct label *newsocketlabel) { struct mac_lomac *source, *dest; source = SLOT(oldsocketlabel); dest = SLOT(newsocketlabel); mac_lomac_copy_single(source, dest); } static void mac_lomac_relabel_socket(struct ucred *cred, struct socket *socket, struct label *socketlabel, struct label *newlabel) { struct mac_lomac *source, *dest; source = SLOT(newlabel); dest = SLOT(socketlabel); try_relabel(source, dest); } static void mac_lomac_relabel_pipe(struct ucred *cred, struct pipepair *pp, struct label *pipelabel, struct label *newlabel) { struct mac_lomac *source, *dest; source = SLOT(newlabel); dest = SLOT(pipelabel); try_relabel(source, dest); } static void mac_lomac_set_socket_peer_from_mbuf(struct mbuf *mbuf, struct label *mbuflabel, struct socket *socket, struct label *socketpeerlabel) { struct mac_lomac *source, *dest; source = SLOT(mbuflabel); dest = SLOT(socketpeerlabel); mac_lomac_copy_single(source, dest); } /* * Labeling event operations: network objects. */ static void mac_lomac_set_socket_peer_from_socket(struct socket *oldsocket, struct label *oldsocketlabel, struct socket *newsocket, struct label *newsocketpeerlabel) { struct mac_lomac *source, *dest; source = SLOT(oldsocketlabel); dest = SLOT(newsocketpeerlabel); mac_lomac_copy_single(source, dest); } static void mac_lomac_create_bpfdesc(struct ucred *cred, struct bpf_d *bpf_d, struct label *bpflabel) { struct mac_lomac *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(bpflabel); mac_lomac_copy_single(source, dest); } static void mac_lomac_create_ifnet(struct ifnet *ifnet, struct label *ifnetlabel) { char tifname[IFNAMSIZ], *p, *q; char tiflist[sizeof(trusted_interfaces)]; struct mac_lomac *dest; int len, grade; dest = SLOT(ifnetlabel); if (ifnet->if_type == IFT_LOOP) { grade = MAC_LOMAC_TYPE_EQUAL; goto set; } if (trust_all_interfaces) { grade = MAC_LOMAC_TYPE_HIGH; goto set; } grade = MAC_LOMAC_TYPE_LOW; if (trusted_interfaces[0] == '\0' || !strvalid(trusted_interfaces, sizeof(trusted_interfaces))) goto set; bzero(tiflist, sizeof(tiflist)); for (p = trusted_interfaces, q = tiflist; *p != '\0'; p++, q++) if(*p != ' ' && *p != '\t') *q = *p; for (p = q = tiflist;; p++) { if (*p == ',' || *p == '\0') { len = p - q; if (len < IFNAMSIZ) { bzero(tifname, sizeof(tifname)); bcopy(q, tifname, len); if (strcmp(tifname, ifnet->if_xname) == 0) { grade = MAC_LOMAC_TYPE_HIGH; break; } } else { *p = '\0'; printf("MAC/LOMAC warning: interface name " "\"%s\" is too long (must be < %d)\n", q, IFNAMSIZ); } if (*p == '\0') break; q = p + 1; } } set: mac_lomac_set_single(dest, grade, 0); mac_lomac_set_range(dest, grade, 0, grade, 0); } static void mac_lomac_create_ipq(struct mbuf *fragment, struct label *fragmentlabel, struct ipq *ipq, struct label *ipqlabel) { struct mac_lomac *source, *dest; source = SLOT(fragmentlabel); dest = SLOT(ipqlabel); mac_lomac_copy_single(source, dest); } static void mac_lomac_create_datagram_from_ipq(struct ipq *ipq, struct label *ipqlabel, struct mbuf *datagram, struct label *datagramlabel) { struct mac_lomac *source, *dest; source = SLOT(ipqlabel); dest = SLOT(datagramlabel); /* Just use the head, since we require them all to match. */ mac_lomac_copy_single(source, dest); } static void mac_lomac_create_fragment(struct mbuf *datagram, struct label *datagramlabel, struct mbuf *fragment, struct label *fragmentlabel) { struct mac_lomac *source, *dest; source = SLOT(datagramlabel); dest = SLOT(fragmentlabel); mac_lomac_copy_single(source, dest); } static void mac_lomac_create_mbuf_from_inpcb(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *source, *dest; source = SLOT(inplabel); dest = SLOT(mlabel); mac_lomac_copy_single(source, dest); } static void mac_lomac_create_mbuf_linklayer(struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *mbuf, struct label *mbuflabel) { struct mac_lomac *dest; dest = SLOT(mbuflabel); mac_lomac_set_single(dest, MAC_LOMAC_TYPE_EQUAL, 0); } static void mac_lomac_create_mbuf_from_bpfdesc(struct bpf_d *bpf_d, struct label *bpflabel, struct mbuf *mbuf, struct label *mbuflabel) { struct mac_lomac *source, *dest; source = SLOT(bpflabel); dest = SLOT(mbuflabel); mac_lomac_copy_single(source, dest); } static void mac_lomac_create_mbuf_from_ifnet(struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *m, struct label *mbuflabel) { struct mac_lomac *source, *dest; source = SLOT(ifnetlabel); dest = SLOT(mbuflabel); mac_lomac_copy_single(source, dest); } static void mac_lomac_create_mbuf_multicast_encap(struct mbuf *oldmbuf, struct label *oldmbuflabel, struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *newmbuf, struct label *newmbuflabel) { struct mac_lomac *source, *dest; source = SLOT(oldmbuflabel); dest = SLOT(newmbuflabel); mac_lomac_copy_single(source, dest); } static void mac_lomac_create_mbuf_netlayer(struct mbuf *oldmbuf, struct label *oldmbuflabel, struct mbuf *newmbuf, struct label *newmbuflabel) { struct mac_lomac *source, *dest; source = SLOT(oldmbuflabel); dest = SLOT(newmbuflabel); mac_lomac_copy_single(source, dest); } static int mac_lomac_fragment_match(struct mbuf *fragment, struct label *fragmentlabel, struct ipq *ipq, struct label *ipqlabel) { struct mac_lomac *a, *b; a = SLOT(ipqlabel); b = SLOT(fragmentlabel); return (mac_lomac_equal_single(a, b)); } static void mac_lomac_relabel_ifnet(struct ucred *cred, struct ifnet *ifnet, struct label *ifnetlabel, struct label *newlabel) { struct mac_lomac *source, *dest; source = SLOT(newlabel); dest = SLOT(ifnetlabel); try_relabel(source, dest); } static void mac_lomac_update_ipq(struct mbuf *fragment, struct label *fragmentlabel, struct ipq *ipq, struct label *ipqlabel) { /* NOOP: we only accept matching labels, so no need to update */ } static void mac_lomac_inpcb_sosetlabel(struct socket *so, struct label *solabel, struct inpcb *inp, struct label *inplabel) { struct mac_lomac *source, *dest; source = SLOT(solabel); dest = SLOT(inplabel); mac_lomac_copy_single(source, dest); } +static void +mac_lomac_create_mbuf_from_firewall(struct mbuf *m, struct label *label) +{ + struct mac_lomac *dest; + + dest = SLOT(label); + + /* XXX: where is the label for the firewall really comming from? */ + mac_lomac_set_single(dest, MAC_LOMAC_TYPE_EQUAL, 0); +} + /* * Labeling event operations: processes. */ static void mac_lomac_execve_transition(struct ucred *old, struct ucred *new, struct vnode *vp, struct label *vnodelabel, struct label *interpvnodelabel, struct image_params *imgp, struct label *execlabel) { struct mac_lomac *source, *dest, *obj, *robj; source = SLOT(old->cr_label); dest = SLOT(new->cr_label); obj = SLOT(vnodelabel); robj = interpvnodelabel != NULL ? SLOT(interpvnodelabel) : obj; mac_lomac_copy(source, dest); /* * If there's an auxiliary label on the real object, respect it * and assume that this level should be assumed immediately if * a higher level is currently in place. */ if (robj->ml_flags & MAC_LOMAC_FLAG_AUX && !mac_lomac_dominate_element(&robj->ml_auxsingle, &dest->ml_single) && mac_lomac_auxsingle_in_range(robj, dest)) mac_lomac_set_single(dest, robj->ml_auxsingle.mle_type, robj->ml_auxsingle.mle_grade); /* * Restructuring to use the execve transitioning mechanism * instead of the normal demotion mechanism here would be * difficult, so just copy the label over and perform standard * demotion. This is also non-optimal because it will result * in the intermediate label "new" being created and immediately * recycled. */ if (mac_lomac_enabled && revocation_enabled && !mac_lomac_dominate_single(obj, source)) (void)maybe_demote(source, obj, "executing", "file", vp); } static int mac_lomac_execve_will_transition(struct ucred *old, struct vnode *vp, struct label *vnodelabel, struct label *interpvnodelabel, struct image_params *imgp, struct label *execlabel) { struct mac_lomac *subj, *obj, *robj; if (!mac_lomac_enabled || !revocation_enabled) return (0); subj = SLOT(old->cr_label); obj = SLOT(vnodelabel); robj = interpvnodelabel != NULL ? SLOT(interpvnodelabel) : obj; return ((robj->ml_flags & MAC_LOMAC_FLAG_AUX && !mac_lomac_dominate_element(&robj->ml_auxsingle, &subj->ml_single) && mac_lomac_auxsingle_in_range(robj, subj)) || !mac_lomac_dominate_single(obj, subj)); } static void mac_lomac_create_proc0(struct ucred *cred) { struct mac_lomac *dest; dest = SLOT(cred->cr_label); mac_lomac_set_single(dest, MAC_LOMAC_TYPE_EQUAL, 0); mac_lomac_set_range(dest, MAC_LOMAC_TYPE_LOW, 0, MAC_LOMAC_TYPE_HIGH, 0); } static void mac_lomac_create_proc1(struct ucred *cred) { struct mac_lomac *dest; dest = SLOT(cred->cr_label); mac_lomac_set_single(dest, MAC_LOMAC_TYPE_HIGH, 0); mac_lomac_set_range(dest, MAC_LOMAC_TYPE_LOW, 0, MAC_LOMAC_TYPE_HIGH, 0); } static void mac_lomac_relabel_cred(struct ucred *cred, struct label *newlabel) { struct mac_lomac *source, *dest; source = SLOT(newlabel); dest = SLOT(cred->cr_label); try_relabel(source, dest); } /* * Access control checks. */ static int mac_lomac_check_bpfdesc_receive(struct bpf_d *bpf_d, struct label *bpflabel, struct ifnet *ifnet, struct label *ifnetlabel) { struct mac_lomac *a, *b; if (!mac_lomac_enabled) return (0); a = SLOT(bpflabel); b = SLOT(ifnetlabel); if (mac_lomac_equal_single(a, b)) return (0); return (EACCES); } static int mac_lomac_check_cred_relabel(struct ucred *cred, struct label *newlabel) { struct mac_lomac *subj, *new; int error; subj = SLOT(cred->cr_label); new = SLOT(newlabel); /* * If there is a LOMAC label update for the credential, it may * be an update of the single, range, or both. */ error = lomac_atmostflags(new, MAC_LOMAC_FLAGS_BOTH); if (error) return (error); /* * If the LOMAC label is to be changed, authorize as appropriate. */ if (new->ml_flags & MAC_LOMAC_FLAGS_BOTH) { /* * Fill in the missing parts from the previous label. */ if ((new->ml_flags & MAC_LOMAC_FLAG_SINGLE) == 0) mac_lomac_copy_single(subj, new); if ((new->ml_flags & MAC_LOMAC_FLAG_RANGE) == 0) mac_lomac_copy_range(subj, new); /* * To change the LOMAC range on a credential, the new * range label must be in the current range. */ if (!mac_lomac_range_in_range(new, subj)) return (EPERM); /* * To change the LOMAC single label on a credential, the * new single label must be in the new range. Implicitly * from the previous check, the new single is in the old * range. */ if (!mac_lomac_single_in_range(new, new)) return (EPERM); /* * To have EQUAL in any component of the new credential * LOMAC label, the subject must already have EQUAL in * their label. */ if (mac_lomac_contains_equal(new)) { error = mac_lomac_subject_privileged(subj); if (error) return (error); } /* * XXXMAC: Additional consistency tests regarding the * single and range of the new label might be performed * here. */ } return (0); } static int mac_lomac_check_cred_visible(struct ucred *u1, struct ucred *u2) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(u1->cr_label); obj = SLOT(u2->cr_label); /* XXX: range */ if (!mac_lomac_dominate_single(obj, subj)) return (ESRCH); return (0); } static int mac_lomac_check_ifnet_relabel(struct ucred *cred, struct ifnet *ifnet, struct label *ifnetlabel, struct label *newlabel) { struct mac_lomac *subj, *new; int error; subj = SLOT(cred->cr_label); new = SLOT(newlabel); /* * If there is a LOMAC label update for the interface, it may * be an update of the single, range, or both. */ error = lomac_atmostflags(new, MAC_LOMAC_FLAGS_BOTH); if (error) return (error); /* * Relabling network interfaces requires LOMAC privilege. */ error = mac_lomac_subject_privileged(subj); if (error) return (error); /* * If the LOMAC label is to be changed, authorize as appropriate. */ if (new->ml_flags & MAC_LOMAC_FLAGS_BOTH) { /* * Fill in the missing parts from the previous label. */ if ((new->ml_flags & MAC_LOMAC_FLAG_SINGLE) == 0) mac_lomac_copy_single(subj, new); if ((new->ml_flags & MAC_LOMAC_FLAG_RANGE) == 0) mac_lomac_copy_range(subj, new); /* * Rely on the traditional superuser status for the LOMAC * interface relabel requirements. XXXMAC: This will go * away. */ error = suser_cred(cred, 0); if (error) return (EPERM); /* * XXXMAC: Additional consistency tests regarding the single * and the range of the new label might be performed here. */ } return (0); } static int mac_lomac_check_ifnet_transmit(struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *m, struct label *mbuflabel) { struct mac_lomac *p, *i; if (!mac_lomac_enabled) return (0); p = SLOT(mbuflabel); i = SLOT(ifnetlabel); return (mac_lomac_single_in_range(p, i) ? 0 : EACCES); } static int mac_lomac_check_inpcb_deliver(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel) { struct mac_lomac *p, *i; if (!mac_lomac_enabled) return (0); p = SLOT(mlabel); i = SLOT(inplabel); return (mac_lomac_equal_single(p, i) ? 0 : EACCES); } static int mac_lomac_check_kld_load(struct ucred *cred, struct vnode *vp, struct label *label) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (mac_lomac_subject_privileged(subj)) return (EPERM); if (!mac_lomac_high_single(obj)) return (EACCES); return (0); } static int mac_lomac_check_kld_unload(struct ucred *cred) { struct mac_lomac *subj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); if (mac_lomac_subject_privileged(subj)) return (EPERM); return (0); } static int mac_lomac_check_pipe_ioctl(struct ucred *cred, struct pipepair *pp, struct label *pipelabel, unsigned long cmd, void /* caddr_t */ *data) { if(!mac_lomac_enabled) return (0); /* XXX: This will be implemented soon... */ return (0); } static int mac_lomac_check_pipe_read(struct ucred *cred, struct pipepair *pp, struct label *pipelabel) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT((pipelabel)); if (!mac_lomac_dominate_single(obj, subj)) return (maybe_demote(subj, obj, "reading", "pipe", NULL)); return (0); } static int mac_lomac_check_pipe_relabel(struct ucred *cred, struct pipepair *pp, struct label *pipelabel, struct label *newlabel) { struct mac_lomac *subj, *obj, *new; int error; new = SLOT(newlabel); subj = SLOT(cred->cr_label); obj = SLOT(pipelabel); /* * If there is a LOMAC label update for a pipe, it must be a * single update. */ error = lomac_atmostflags(new, MAC_LOMAC_FLAG_SINGLE); if (error) return (error); /* * To perform a relabel of a pipe (LOMAC label or not), LOMAC must * authorize the relabel. */ if (!mac_lomac_single_in_range(obj, subj)) return (EPERM); /* * If the LOMAC label is to be changed, authorize as appropriate. */ if (new->ml_flags & MAC_LOMAC_FLAG_SINGLE) { /* * To change the LOMAC label on a pipe, the new pipe label * must be in the subject range. */ if (!mac_lomac_single_in_range(new, subj)) return (EPERM); /* * To change the LOMAC label on a pipe to be EQUAL, the * subject must have appropriate privilege. */ if (mac_lomac_contains_equal(new)) { error = mac_lomac_subject_privileged(subj); if (error) return (error); } } return (0); } static int mac_lomac_check_pipe_write(struct ucred *cred, struct pipepair *pp, struct label *pipelabel) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT((pipelabel)); if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int mac_lomac_check_proc_debug(struct ucred *cred, struct proc *proc) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(proc->p_ucred->cr_label); /* XXX: range checks */ if (!mac_lomac_dominate_single(obj, subj)) return (ESRCH); if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int mac_lomac_check_proc_sched(struct ucred *cred, struct proc *proc) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(proc->p_ucred->cr_label); /* XXX: range checks */ if (!mac_lomac_dominate_single(obj, subj)) return (ESRCH); if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int mac_lomac_check_proc_signal(struct ucred *cred, struct proc *proc, int signum) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(proc->p_ucred->cr_label); /* XXX: range checks */ if (!mac_lomac_dominate_single(obj, subj)) return (ESRCH); if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int mac_lomac_check_socket_deliver(struct socket *so, struct label *socketlabel, struct mbuf *m, struct label *mbuflabel) { struct mac_lomac *p, *s; if (!mac_lomac_enabled) return (0); p = SLOT(mbuflabel); s = SLOT(socketlabel); return (mac_lomac_equal_single(p, s) ? 0 : EACCES); } static int mac_lomac_check_socket_relabel(struct ucred *cred, struct socket *socket, struct label *socketlabel, struct label *newlabel) { struct mac_lomac *subj, *obj, *new; int error; new = SLOT(newlabel); subj = SLOT(cred->cr_label); obj = SLOT(socketlabel); /* * If there is a LOMAC label update for the socket, it may be * an update of single. */ error = lomac_atmostflags(new, MAC_LOMAC_FLAG_SINGLE); if (error) return (error); /* * To relabel a socket, the old socket single must be in the subject * range. */ if (!mac_lomac_single_in_range(obj, subj)) return (EPERM); /* * If the LOMAC label is to be changed, authorize as appropriate. */ if (new->ml_flags & MAC_LOMAC_FLAG_SINGLE) { /* * To relabel a socket, the new socket single must be in * the subject range. */ if (!mac_lomac_single_in_range(new, subj)) return (EPERM); /* * To change the LOMAC label on the socket to contain EQUAL, * the subject must have appropriate privilege. */ if (mac_lomac_contains_equal(new)) { error = mac_lomac_subject_privileged(subj); if (error) return (error); } } return (0); } static int mac_lomac_check_socket_visible(struct ucred *cred, struct socket *socket, struct label *socketlabel) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(socketlabel); if (!mac_lomac_dominate_single(obj, subj)) return (ENOENT); return (0); } static int mac_lomac_check_system_swapon(struct ucred *cred, struct vnode *vp, struct label *label) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (mac_lomac_subject_privileged(subj)) return (EPERM); if (!mac_lomac_high_single(obj)) return (EACCES); return (0); } static int mac_lomac_check_system_sysctl(struct ucred *cred, struct sysctl_oid *oidp, void *arg1, int arg2, struct sysctl_req *req) { struct mac_lomac *subj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); /* * Treat sysctl variables without CTLFLAG_ANYBODY flag as * lomac/high, but also require privilege to change them. */ if (req->newptr != NULL && (oidp->oid_kind & CTLFLAG_ANYBODY) == 0) { #ifdef notdef if (!mac_lomac_subject_dominate_high(subj)) return (EACCES); #endif if (mac_lomac_subject_privileged(subj)) return (EPERM); } return (0); } static int mac_lomac_check_vnode_create(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct componentname *cnp, struct vattr *vap) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); if (obj->ml_flags & MAC_LOMAC_FLAG_AUX && !mac_lomac_dominate_element(&subj->ml_single, &obj->ml_auxsingle)) return (EACCES); return (0); } static int mac_lomac_check_vnode_delete(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, struct componentname *cnp) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); obj = SLOT(label); if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int mac_lomac_check_vnode_deleteacl(struct ucred *cred, struct vnode *vp, struct label *label, acl_type_t type) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int mac_lomac_check_vnode_link(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, struct componentname *cnp) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); obj = SLOT(label); if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int mac_lomac_check_vnode_mmap(struct ucred *cred, struct vnode *vp, struct label *label, int prot, int flags) { struct mac_lomac *subj, *obj; /* * Rely on the use of open()-time protections to handle * non-revocation cases. */ if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (((prot & VM_PROT_WRITE) != 0) && ((flags & MAP_SHARED) != 0)) { if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); } if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) { if (!mac_lomac_dominate_single(obj, subj)) return (maybe_demote(subj, obj, "mapping", "file", vp)); } return (0); } static void mac_lomac_check_vnode_mmap_downgrade(struct ucred *cred, struct vnode *vp, struct label *label, /* XXX vm_prot_t */ int *prot) { struct mac_lomac *subj, *obj; /* * Rely on the use of open()-time protections to handle * non-revocation cases. */ if (!mac_lomac_enabled || !revocation_enabled) return; subj = SLOT(cred->cr_label); obj = SLOT(label); if (!mac_lomac_subject_dominate(subj, obj)) *prot &= ~VM_PROT_WRITE; } static int mac_lomac_check_vnode_open(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, int acc_mode) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vnodelabel); /* XXX privilege override for admin? */ if (acc_mode & (VWRITE | VAPPEND | VADMIN)) { if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); } return (0); } static int mac_lomac_check_vnode_read(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *label) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(label); if (!mac_lomac_dominate_single(obj, subj)) return (maybe_demote(subj, obj, "reading", "file", vp)); return (0); } static int mac_lomac_check_vnode_relabel(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, struct label *newlabel) { struct mac_lomac *old, *new, *subj; int error; old = SLOT(vnodelabel); new = SLOT(newlabel); subj = SLOT(cred->cr_label); /* * If there is a LOMAC label update for the vnode, it must be a * single label, with an optional explicit auxiliary single. */ error = lomac_atmostflags(new, MAC_LOMAC_FLAG_SINGLE | MAC_LOMAC_FLAG_AUX); if (error) return (error); /* * To perform a relabel of the vnode (LOMAC label or not), LOMAC must * authorize the relabel. */ if (!mac_lomac_single_in_range(old, subj)) return (EPERM); /* * If the LOMAC label is to be changed, authorize as appropriate. */ if (new->ml_flags & MAC_LOMAC_FLAG_SINGLE) { /* * To change the LOMAC label on a vnode, the new vnode label * must be in the subject range. */ if (!mac_lomac_single_in_range(new, subj)) return (EPERM); /* * To change the LOMAC label on the vnode to be EQUAL, * the subject must have appropriate privilege. */ if (mac_lomac_contains_equal(new)) { error = mac_lomac_subject_privileged(subj); if (error) return (error); } } if (new->ml_flags & MAC_LOMAC_FLAG_AUX) { /* * Fill in the missing parts from the previous label. */ if ((new->ml_flags & MAC_LOMAC_FLAG_SINGLE) == 0) mac_lomac_copy_single(subj, new); /* * To change the auxiliary LOMAC label on a vnode, the new * vnode label must be in the subject range. */ if (!mac_lomac_auxsingle_in_range(new, subj)) return (EPERM); /* * To change the auxiliary LOMAC label on the vnode to be * EQUAL, the subject must have appropriate privilege. */ if (mac_lomac_contains_equal(new)) { error = mac_lomac_subject_privileged(subj); if (error) return (error); } } return (0); } static int mac_lomac_check_vnode_rename_from(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, struct componentname *cnp) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); obj = SLOT(label); if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int mac_lomac_check_vnode_rename_to(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, int samedir, struct componentname *cnp) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); if (vp != NULL) { obj = SLOT(label); if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); } return (0); } static int mac_lomac_check_vnode_revoke(struct ucred *cred, struct vnode *vp, struct label *label) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int mac_lomac_check_vnode_setacl(struct ucred *cred, struct vnode *vp, struct label *label, acl_type_t type, struct acl *acl) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int mac_lomac_check_vnode_setextattr(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, int attrnamespace, const char *name, struct uio *uio) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vnodelabel); if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); /* XXX: protect the MAC EA in a special way? */ return (0); } static int mac_lomac_check_vnode_setflags(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, u_long flags) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vnodelabel); if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int mac_lomac_check_vnode_setmode(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, mode_t mode) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vnodelabel); if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int mac_lomac_check_vnode_setowner(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, uid_t uid, gid_t gid) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vnodelabel); if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int mac_lomac_check_vnode_setutimes(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, struct timespec atime, struct timespec mtime) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vnodelabel); if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static int mac_lomac_check_vnode_write(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *label) { struct mac_lomac *subj, *obj; if (!mac_lomac_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(label); if (!mac_lomac_subject_dominate(subj, obj)) return (EACCES); return (0); } static void mac_lomac_thread_userret(struct thread *td) { struct proc *p = td->td_proc; struct mac_lomac_proc *subj = PSLOT(p->p_label); struct ucred *newcred, *oldcred; int dodrop; mtx_lock(&subj->mtx); if (subj->mac_lomac.ml_flags & MAC_LOMAC_FLAG_UPDATE) { dodrop = 0; mtx_unlock(&subj->mtx); newcred = crget(); /* * Prevent a lock order reversal in * mac_cred_mmapped_drop_perms; ideally, the other * user of subj->mtx wouldn't be holding Giant. */ mtx_lock(&Giant); PROC_LOCK(p); mtx_lock(&subj->mtx); /* * Check if we lost the race while allocating the cred. */ if ((subj->mac_lomac.ml_flags & MAC_LOMAC_FLAG_UPDATE) == 0) { crfree(newcred); goto out; } oldcred = p->p_ucred; crcopy(newcred, oldcred); crhold(newcred); mac_lomac_copy(&subj->mac_lomac, SLOT(newcred->cr_label)); p->p_ucred = newcred; crfree(oldcred); dodrop = 1; out: mtx_unlock(&subj->mtx); PROC_UNLOCK(p); if (dodrop) mac_cred_mmapped_drop_perms(curthread, newcred); mtx_unlock(&Giant); } else { mtx_unlock(&subj->mtx); } } static struct mac_policy_ops mac_lomac_ops = { .mpo_init = mac_lomac_init, .mpo_init_bpfdesc_label = mac_lomac_init_label, .mpo_init_cred_label = mac_lomac_init_label, .mpo_init_devfsdirent_label = mac_lomac_init_label, .mpo_init_ifnet_label = mac_lomac_init_label, .mpo_init_inpcb_label = mac_lomac_init_label_waitcheck, .mpo_init_ipq_label = mac_lomac_init_label_waitcheck, .mpo_init_mbuf_label = mac_lomac_init_label_waitcheck, .mpo_init_mount_label = mac_lomac_init_label, .mpo_init_mount_fs_label = mac_lomac_init_label, .mpo_init_pipe_label = mac_lomac_init_label, .mpo_init_proc_label = mac_lomac_init_proc_label, .mpo_init_socket_label = mac_lomac_init_label_waitcheck, .mpo_init_socket_peer_label = mac_lomac_init_label_waitcheck, .mpo_init_vnode_label = mac_lomac_init_label, .mpo_destroy_bpfdesc_label = mac_lomac_destroy_label, .mpo_destroy_cred_label = mac_lomac_destroy_label, .mpo_destroy_devfsdirent_label = mac_lomac_destroy_label, .mpo_destroy_ifnet_label = mac_lomac_destroy_label, .mpo_destroy_inpcb_label = mac_lomac_destroy_label, .mpo_destroy_ipq_label = mac_lomac_destroy_label, .mpo_destroy_mbuf_label = mac_lomac_destroy_label, .mpo_destroy_mount_label = mac_lomac_destroy_label, .mpo_destroy_mount_fs_label = mac_lomac_destroy_label, .mpo_destroy_pipe_label = mac_lomac_destroy_label, .mpo_destroy_proc_label = mac_lomac_destroy_proc_label, .mpo_destroy_socket_label = mac_lomac_destroy_label, .mpo_destroy_socket_peer_label = mac_lomac_destroy_label, .mpo_destroy_vnode_label = mac_lomac_destroy_label, .mpo_copy_cred_label = mac_lomac_copy_label, .mpo_copy_ifnet_label = mac_lomac_copy_label, .mpo_copy_mbuf_label = mac_lomac_copy_label, .mpo_copy_pipe_label = mac_lomac_copy_label, .mpo_copy_socket_label = mac_lomac_copy_label, .mpo_copy_vnode_label = mac_lomac_copy_label, .mpo_externalize_cred_label = mac_lomac_externalize_label, .mpo_externalize_ifnet_label = mac_lomac_externalize_label, .mpo_externalize_pipe_label = mac_lomac_externalize_label, .mpo_externalize_socket_label = mac_lomac_externalize_label, .mpo_externalize_socket_peer_label = mac_lomac_externalize_label, .mpo_externalize_vnode_label = mac_lomac_externalize_label, .mpo_internalize_cred_label = mac_lomac_internalize_label, .mpo_internalize_ifnet_label = mac_lomac_internalize_label, .mpo_internalize_pipe_label = mac_lomac_internalize_label, .mpo_internalize_socket_label = mac_lomac_internalize_label, .mpo_internalize_vnode_label = mac_lomac_internalize_label, .mpo_create_devfs_device = mac_lomac_create_devfs_device, .mpo_create_devfs_directory = mac_lomac_create_devfs_directory, .mpo_create_devfs_symlink = mac_lomac_create_devfs_symlink, .mpo_create_mount = mac_lomac_create_mount, .mpo_relabel_vnode = mac_lomac_relabel_vnode, .mpo_update_devfsdirent = mac_lomac_update_devfsdirent, .mpo_associate_vnode_devfs = mac_lomac_associate_vnode_devfs, .mpo_associate_vnode_extattr = mac_lomac_associate_vnode_extattr, .mpo_associate_vnode_singlelabel = mac_lomac_associate_vnode_singlelabel, .mpo_create_vnode_extattr = mac_lomac_create_vnode_extattr, .mpo_setlabel_vnode_extattr = mac_lomac_setlabel_vnode_extattr, .mpo_create_mbuf_from_socket = mac_lomac_create_mbuf_from_socket, .mpo_create_pipe = mac_lomac_create_pipe, .mpo_create_socket = mac_lomac_create_socket, .mpo_create_socket_from_socket = mac_lomac_create_socket_from_socket, .mpo_relabel_pipe = mac_lomac_relabel_pipe, .mpo_relabel_socket = mac_lomac_relabel_socket, .mpo_set_socket_peer_from_mbuf = mac_lomac_set_socket_peer_from_mbuf, .mpo_set_socket_peer_from_socket = mac_lomac_set_socket_peer_from_socket, .mpo_create_bpfdesc = mac_lomac_create_bpfdesc, .mpo_create_datagram_from_ipq = mac_lomac_create_datagram_from_ipq, .mpo_create_fragment = mac_lomac_create_fragment, .mpo_create_ifnet = mac_lomac_create_ifnet, .mpo_create_inpcb_from_socket = mac_lomac_create_inpcb_from_socket, .mpo_create_ipq = mac_lomac_create_ipq, .mpo_create_mbuf_from_inpcb = mac_lomac_create_mbuf_from_inpcb, .mpo_create_mbuf_linklayer = mac_lomac_create_mbuf_linklayer, .mpo_create_mbuf_from_bpfdesc = mac_lomac_create_mbuf_from_bpfdesc, .mpo_create_mbuf_from_ifnet = mac_lomac_create_mbuf_from_ifnet, .mpo_create_mbuf_multicast_encap = mac_lomac_create_mbuf_multicast_encap, .mpo_create_mbuf_netlayer = mac_lomac_create_mbuf_netlayer, .mpo_fragment_match = mac_lomac_fragment_match, .mpo_relabel_ifnet = mac_lomac_relabel_ifnet, .mpo_update_ipq = mac_lomac_update_ipq, .mpo_inpcb_sosetlabel = mac_lomac_inpcb_sosetlabel, .mpo_execve_transition = mac_lomac_execve_transition, .mpo_execve_will_transition = mac_lomac_execve_will_transition, .mpo_create_proc0 = mac_lomac_create_proc0, .mpo_create_proc1 = mac_lomac_create_proc1, .mpo_relabel_cred = mac_lomac_relabel_cred, .mpo_check_bpfdesc_receive = mac_lomac_check_bpfdesc_receive, .mpo_check_cred_relabel = mac_lomac_check_cred_relabel, .mpo_check_cred_visible = mac_lomac_check_cred_visible, .mpo_check_ifnet_relabel = mac_lomac_check_ifnet_relabel, .mpo_check_ifnet_transmit = mac_lomac_check_ifnet_transmit, .mpo_check_inpcb_deliver = mac_lomac_check_inpcb_deliver, .mpo_check_kld_load = mac_lomac_check_kld_load, .mpo_check_kld_unload = mac_lomac_check_kld_unload, .mpo_check_pipe_ioctl = mac_lomac_check_pipe_ioctl, .mpo_check_pipe_read = mac_lomac_check_pipe_read, .mpo_check_pipe_relabel = mac_lomac_check_pipe_relabel, .mpo_check_pipe_write = mac_lomac_check_pipe_write, .mpo_check_proc_debug = mac_lomac_check_proc_debug, .mpo_check_proc_sched = mac_lomac_check_proc_sched, .mpo_check_proc_signal = mac_lomac_check_proc_signal, .mpo_check_socket_deliver = mac_lomac_check_socket_deliver, .mpo_check_socket_relabel = mac_lomac_check_socket_relabel, .mpo_check_socket_visible = mac_lomac_check_socket_visible, .mpo_check_system_swapon = mac_lomac_check_system_swapon, .mpo_check_system_sysctl = mac_lomac_check_system_sysctl, .mpo_check_vnode_access = mac_lomac_check_vnode_open, .mpo_check_vnode_create = mac_lomac_check_vnode_create, .mpo_check_vnode_delete = mac_lomac_check_vnode_delete, .mpo_check_vnode_deleteacl = mac_lomac_check_vnode_deleteacl, .mpo_check_vnode_link = mac_lomac_check_vnode_link, .mpo_check_vnode_mmap = mac_lomac_check_vnode_mmap, .mpo_check_vnode_mmap_downgrade = mac_lomac_check_vnode_mmap_downgrade, .mpo_check_vnode_open = mac_lomac_check_vnode_open, .mpo_check_vnode_read = mac_lomac_check_vnode_read, .mpo_check_vnode_relabel = mac_lomac_check_vnode_relabel, .mpo_check_vnode_rename_from = mac_lomac_check_vnode_rename_from, .mpo_check_vnode_rename_to = mac_lomac_check_vnode_rename_to, .mpo_check_vnode_revoke = mac_lomac_check_vnode_revoke, .mpo_check_vnode_setacl = mac_lomac_check_vnode_setacl, .mpo_check_vnode_setextattr = mac_lomac_check_vnode_setextattr, .mpo_check_vnode_setflags = mac_lomac_check_vnode_setflags, .mpo_check_vnode_setmode = mac_lomac_check_vnode_setmode, .mpo_check_vnode_setowner = mac_lomac_check_vnode_setowner, .mpo_check_vnode_setutimes = mac_lomac_check_vnode_setutimes, .mpo_check_vnode_write = mac_lomac_check_vnode_write, .mpo_thread_userret = mac_lomac_thread_userret, + .mpo_create_mbuf_from_firewall = mac_lomac_create_mbuf_from_firewall, }; MAC_POLICY_SET(&mac_lomac_ops, mac_lomac, "TrustedBSD MAC/LOMAC", MPC_LOADTIME_FLAG_NOTLATE | MPC_LOADTIME_FLAG_LABELMBUFS, &mac_lomac_slot); Index: stable/6/sys/security/mac_mls/mac_mls.c =================================================================== --- stable/6/sys/security/mac_mls/mac_mls.c (revision 162447) +++ stable/6/sys/security/mac_mls/mac_mls.c (revision 162448) @@ -1,2978 +1,2990 @@ /*- * Copyright (c) 1999-2002 Robert N. M. Watson * Copyright (c) 2001-2005 McAfee, Inc. * All rights reserved. * * This software was developed by Robert Watson for the TrustedBSD Project. * * This software was developed for the FreeBSD Project in part by McAfee * Research, the Security Research Division of McAfee, Inc. under * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA * CHATS research program. * * 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$ */ /* * Developed by the TrustedBSD Project. * MLS fixed label mandatory confidentiality policy. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include SYSCTL_DECL(_security_mac); SYSCTL_NODE(_security_mac, OID_AUTO, mls, CTLFLAG_RW, 0, "TrustedBSD mac_mls policy controls"); static int mac_mls_label_size = sizeof(struct mac_mls); SYSCTL_INT(_security_mac_mls, OID_AUTO, label_size, CTLFLAG_RD, &mac_mls_label_size, 0, "Size of struct mac_mls"); static int mac_mls_enabled = 1; SYSCTL_INT(_security_mac_mls, OID_AUTO, enabled, CTLFLAG_RW, &mac_mls_enabled, 0, "Enforce MAC/MLS policy"); TUNABLE_INT("security.mac.mls.enabled", &mac_mls_enabled); static int destroyed_not_inited; SYSCTL_INT(_security_mac_mls, OID_AUTO, destroyed_not_inited, CTLFLAG_RD, &destroyed_not_inited, 0, "Count of labels destroyed but not inited"); static int ptys_equal = 0; SYSCTL_INT(_security_mac_mls, OID_AUTO, ptys_equal, CTLFLAG_RW, &ptys_equal, 0, "Label pty devices as mls/equal on create"); TUNABLE_INT("security.mac.mls.ptys_equal", &ptys_equal); static int revocation_enabled = 0; SYSCTL_INT(_security_mac_mls, OID_AUTO, revocation_enabled, CTLFLAG_RW, &revocation_enabled, 0, "Revoke access to objects on relabel"); TUNABLE_INT("security.mac.mls.revocation_enabled", &revocation_enabled); static int max_compartments = MAC_MLS_MAX_COMPARTMENTS; SYSCTL_INT(_security_mac_mls, OID_AUTO, max_compartments, CTLFLAG_RD, &max_compartments, 0, "Maximum compartments the policy supports"); static int mac_mls_slot; #define SLOT(l) ((struct mac_mls *)LABEL_TO_SLOT((l), mac_mls_slot).l_ptr) #define SLOT_SET(l, val) (LABEL_TO_SLOT((l), mac_mls_slot).l_ptr = (val)) static uma_zone_t zone_mls; static __inline int mls_bit_set_empty(u_char *set) { int i; for (i = 0; i < MAC_MLS_MAX_COMPARTMENTS >> 3; i++) if (set[i] != 0) return (0); return (1); } static struct mac_mls * mls_alloc(int flag) { return (uma_zalloc(zone_mls, flag | M_ZERO)); } static void mls_free(struct mac_mls *mac_mls) { if (mac_mls != NULL) uma_zfree(zone_mls, mac_mls); else atomic_add_int(&destroyed_not_inited, 1); } static int mls_atmostflags(struct mac_mls *mac_mls, int flags) { if ((mac_mls->mm_flags & flags) != mac_mls->mm_flags) return (EINVAL); return (0); } static int mac_mls_dominate_element(struct mac_mls_element *a, struct mac_mls_element *b) { int bit; switch (a->mme_type) { case MAC_MLS_TYPE_EQUAL: case MAC_MLS_TYPE_HIGH: return (1); case MAC_MLS_TYPE_LOW: switch (b->mme_type) { case MAC_MLS_TYPE_LEVEL: case MAC_MLS_TYPE_HIGH: return (0); case MAC_MLS_TYPE_EQUAL: case MAC_MLS_TYPE_LOW: return (1); default: panic("mac_mls_dominate_element: b->mme_type invalid"); } case MAC_MLS_TYPE_LEVEL: switch (b->mme_type) { case MAC_MLS_TYPE_EQUAL: case MAC_MLS_TYPE_LOW: return (1); case MAC_MLS_TYPE_HIGH: return (0); case MAC_MLS_TYPE_LEVEL: for (bit = 1; bit <= MAC_MLS_MAX_COMPARTMENTS; bit++) if (!MAC_MLS_BIT_TEST(bit, a->mme_compartments) && MAC_MLS_BIT_TEST(bit, b->mme_compartments)) return (0); return (a->mme_level >= b->mme_level); default: panic("mac_mls_dominate_element: b->mme_type invalid"); } default: panic("mac_mls_dominate_element: a->mme_type invalid"); } return (0); } static int mac_mls_range_in_range(struct mac_mls *rangea, struct mac_mls *rangeb) { return (mac_mls_dominate_element(&rangeb->mm_rangehigh, &rangea->mm_rangehigh) && mac_mls_dominate_element(&rangea->mm_rangelow, &rangeb->mm_rangelow)); } static int mac_mls_effective_in_range(struct mac_mls *effective, struct mac_mls *range) { KASSERT((effective->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, ("mac_mls_effective_in_range: a not effective")); KASSERT((range->mm_flags & MAC_MLS_FLAG_RANGE) != 0, ("mac_mls_effective_in_range: b not range")); return (mac_mls_dominate_element(&range->mm_rangehigh, &effective->mm_effective) && mac_mls_dominate_element(&effective->mm_effective, &range->mm_rangelow)); return (1); } static int mac_mls_dominate_effective(struct mac_mls *a, struct mac_mls *b) { KASSERT((a->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, ("mac_mls_dominate_effective: a not effective")); KASSERT((b->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, ("mac_mls_dominate_effective: b not effective")); return (mac_mls_dominate_element(&a->mm_effective, &b->mm_effective)); } static int mac_mls_equal_element(struct mac_mls_element *a, struct mac_mls_element *b) { if (a->mme_type == MAC_MLS_TYPE_EQUAL || b->mme_type == MAC_MLS_TYPE_EQUAL) return (1); return (a->mme_type == b->mme_type && a->mme_level == b->mme_level); } static int mac_mls_equal_effective(struct mac_mls *a, struct mac_mls *b) { KASSERT((a->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, ("mac_mls_equal_effective: a not effective")); KASSERT((b->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, ("mac_mls_equal_effective: b not effective")); return (mac_mls_equal_element(&a->mm_effective, &b->mm_effective)); } static int mac_mls_contains_equal(struct mac_mls *mac_mls) { if (mac_mls->mm_flags & MAC_MLS_FLAG_EFFECTIVE) if (mac_mls->mm_effective.mme_type == MAC_MLS_TYPE_EQUAL) return (1); if (mac_mls->mm_flags & MAC_MLS_FLAG_RANGE) { if (mac_mls->mm_rangelow.mme_type == MAC_MLS_TYPE_EQUAL) return (1); if (mac_mls->mm_rangehigh.mme_type == MAC_MLS_TYPE_EQUAL) return (1); } return (0); } static int mac_mls_subject_privileged(struct mac_mls *mac_mls) { KASSERT((mac_mls->mm_flags & MAC_MLS_FLAGS_BOTH) == MAC_MLS_FLAGS_BOTH, ("mac_mls_subject_privileged: subject doesn't have both labels")); /* If the effective is EQUAL, it's ok. */ if (mac_mls->mm_effective.mme_type == MAC_MLS_TYPE_EQUAL) return (0); /* If either range endpoint is EQUAL, it's ok. */ if (mac_mls->mm_rangelow.mme_type == MAC_MLS_TYPE_EQUAL || mac_mls->mm_rangehigh.mme_type == MAC_MLS_TYPE_EQUAL) return (0); /* If the range is low-high, it's ok. */ if (mac_mls->mm_rangelow.mme_type == MAC_MLS_TYPE_LOW && mac_mls->mm_rangehigh.mme_type == MAC_MLS_TYPE_HIGH) return (0); /* It's not ok. */ return (EPERM); } static int mac_mls_valid(struct mac_mls *mac_mls) { if (mac_mls->mm_flags & MAC_MLS_FLAG_EFFECTIVE) { switch (mac_mls->mm_effective.mme_type) { case MAC_MLS_TYPE_LEVEL: break; case MAC_MLS_TYPE_EQUAL: case MAC_MLS_TYPE_HIGH: case MAC_MLS_TYPE_LOW: if (mac_mls->mm_effective.mme_level != 0 || !MAC_MLS_BIT_SET_EMPTY( mac_mls->mm_effective.mme_compartments)) return (EINVAL); break; default: return (EINVAL); } } else { if (mac_mls->mm_effective.mme_type != MAC_MLS_TYPE_UNDEF) return (EINVAL); } if (mac_mls->mm_flags & MAC_MLS_FLAG_RANGE) { switch (mac_mls->mm_rangelow.mme_type) { case MAC_MLS_TYPE_LEVEL: break; case MAC_MLS_TYPE_EQUAL: case MAC_MLS_TYPE_HIGH: case MAC_MLS_TYPE_LOW: if (mac_mls->mm_rangelow.mme_level != 0 || !MAC_MLS_BIT_SET_EMPTY( mac_mls->mm_rangelow.mme_compartments)) return (EINVAL); break; default: return (EINVAL); } switch (mac_mls->mm_rangehigh.mme_type) { case MAC_MLS_TYPE_LEVEL: break; case MAC_MLS_TYPE_EQUAL: case MAC_MLS_TYPE_HIGH: case MAC_MLS_TYPE_LOW: if (mac_mls->mm_rangehigh.mme_level != 0 || !MAC_MLS_BIT_SET_EMPTY( mac_mls->mm_rangehigh.mme_compartments)) return (EINVAL); break; default: return (EINVAL); } if (!mac_mls_dominate_element(&mac_mls->mm_rangehigh, &mac_mls->mm_rangelow)) return (EINVAL); } else { if (mac_mls->mm_rangelow.mme_type != MAC_MLS_TYPE_UNDEF || mac_mls->mm_rangehigh.mme_type != MAC_MLS_TYPE_UNDEF) return (EINVAL); } return (0); } static void mac_mls_set_range(struct mac_mls *mac_mls, u_short typelow, u_short levellow, u_char *compartmentslow, u_short typehigh, u_short levelhigh, u_char *compartmentshigh) { mac_mls->mm_rangelow.mme_type = typelow; mac_mls->mm_rangelow.mme_level = levellow; if (compartmentslow != NULL) memcpy(mac_mls->mm_rangelow.mme_compartments, compartmentslow, sizeof(mac_mls->mm_rangelow.mme_compartments)); mac_mls->mm_rangehigh.mme_type = typehigh; mac_mls->mm_rangehigh.mme_level = levelhigh; if (compartmentshigh != NULL) memcpy(mac_mls->mm_rangehigh.mme_compartments, compartmentshigh, sizeof(mac_mls->mm_rangehigh.mme_compartments)); mac_mls->mm_flags |= MAC_MLS_FLAG_RANGE; } static void mac_mls_set_effective(struct mac_mls *mac_mls, u_short type, u_short level, u_char *compartments) { mac_mls->mm_effective.mme_type = type; mac_mls->mm_effective.mme_level = level; if (compartments != NULL) memcpy(mac_mls->mm_effective.mme_compartments, compartments, sizeof(mac_mls->mm_effective.mme_compartments)); mac_mls->mm_flags |= MAC_MLS_FLAG_EFFECTIVE; } static void mac_mls_copy_range(struct mac_mls *labelfrom, struct mac_mls *labelto) { KASSERT((labelfrom->mm_flags & MAC_MLS_FLAG_RANGE) != 0, ("mac_mls_copy_range: labelfrom not range")); labelto->mm_rangelow = labelfrom->mm_rangelow; labelto->mm_rangehigh = labelfrom->mm_rangehigh; labelto->mm_flags |= MAC_MLS_FLAG_RANGE; } static void mac_mls_copy_effective(struct mac_mls *labelfrom, struct mac_mls *labelto) { KASSERT((labelfrom->mm_flags & MAC_MLS_FLAG_EFFECTIVE) != 0, ("mac_mls_copy_effective: labelfrom not effective")); labelto->mm_effective = labelfrom->mm_effective; labelto->mm_flags |= MAC_MLS_FLAG_EFFECTIVE; } static void mac_mls_copy(struct mac_mls *source, struct mac_mls *dest) { if (source->mm_flags & MAC_MLS_FLAG_EFFECTIVE) mac_mls_copy_effective(source, dest); if (source->mm_flags & MAC_MLS_FLAG_RANGE) mac_mls_copy_range(source, dest); } /* * Policy module operations. */ static void mac_mls_init(struct mac_policy_conf *conf) { zone_mls = uma_zcreate("mac_mls", sizeof(struct mac_mls), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); } /* * Label operations. */ static void mac_mls_init_label(struct label *label) { SLOT_SET(label, mls_alloc(M_WAITOK)); } static int mac_mls_init_label_waitcheck(struct label *label, int flag) { SLOT_SET(label, mls_alloc(flag)); if (SLOT(label) == NULL) return (ENOMEM); return (0); } static void mac_mls_destroy_label(struct label *label) { mls_free(SLOT(label)); SLOT_SET(label, NULL); } /* * mac_mls_element_to_string() accepts an sbuf and MLS element. It * converts the MLS element to a string and stores the result in the * sbuf; if there isn't space in the sbuf, -1 is returned. */ static int mac_mls_element_to_string(struct sbuf *sb, struct mac_mls_element *element) { int i, first; switch (element->mme_type) { case MAC_MLS_TYPE_HIGH: return (sbuf_printf(sb, "high")); case MAC_MLS_TYPE_LOW: return (sbuf_printf(sb, "low")); case MAC_MLS_TYPE_EQUAL: return (sbuf_printf(sb, "equal")); case MAC_MLS_TYPE_LEVEL: if (sbuf_printf(sb, "%d", element->mme_level) == -1) return (-1); first = 1; for (i = 1; i <= MAC_MLS_MAX_COMPARTMENTS; i++) { if (MAC_MLS_BIT_TEST(i, element->mme_compartments)) { if (first) { if (sbuf_putc(sb, ':') == -1) return (-1); if (sbuf_printf(sb, "%d", i) == -1) return (-1); first = 0; } else { if (sbuf_printf(sb, "+%d", i) == -1) return (-1); } } } return (0); default: panic("mac_mls_element_to_string: invalid type (%d)", element->mme_type); } } /* * mac_mls_to_string() converts an MLS label to a string, and places * the results in the passed sbuf. It returns 0 on success, or EINVAL * if there isn't room in the sbuf. Note: the sbuf will be modified * even in a failure case, so the caller may need to revert the sbuf * by restoring the offset if that's undesired. */ static int mac_mls_to_string(struct sbuf *sb, struct mac_mls *mac_mls) { if (mac_mls->mm_flags & MAC_MLS_FLAG_EFFECTIVE) { if (mac_mls_element_to_string(sb, &mac_mls->mm_effective) == -1) return (EINVAL); } if (mac_mls->mm_flags & MAC_MLS_FLAG_RANGE) { if (sbuf_putc(sb, '(') == -1) return (EINVAL); if (mac_mls_element_to_string(sb, &mac_mls->mm_rangelow) == -1) return (EINVAL); if (sbuf_putc(sb, '-') == -1) return (EINVAL); if (mac_mls_element_to_string(sb, &mac_mls->mm_rangehigh) == -1) return (EINVAL); if (sbuf_putc(sb, ')') == -1) return (EINVAL); } return (0); } static int mac_mls_externalize_label(struct label *label, char *element_name, struct sbuf *sb, int *claimed) { struct mac_mls *mac_mls; if (strcmp(MAC_MLS_LABEL_NAME, element_name) != 0) return (0); (*claimed)++; mac_mls = SLOT(label); return (mac_mls_to_string(sb, mac_mls)); } static int mac_mls_parse_element(struct mac_mls_element *element, char *string) { char *compartment, *end, *level; int value; if (strcmp(string, "high") == 0 || strcmp(string, "hi") == 0) { element->mme_type = MAC_MLS_TYPE_HIGH; element->mme_level = MAC_MLS_TYPE_UNDEF; } else if (strcmp(string, "low") == 0 || strcmp(string, "lo") == 0) { element->mme_type = MAC_MLS_TYPE_LOW; element->mme_level = MAC_MLS_TYPE_UNDEF; } else if (strcmp(string, "equal") == 0 || strcmp(string, "eq") == 0) { element->mme_type = MAC_MLS_TYPE_EQUAL; element->mme_level = MAC_MLS_TYPE_UNDEF; } else { element->mme_type = MAC_MLS_TYPE_LEVEL; /* * Numeric level piece of the element. */ level = strsep(&string, ":"); value = strtol(level, &end, 10); if (end == level || *end != '\0') return (EINVAL); if (value < 0 || value > 65535) return (EINVAL); element->mme_level = value; /* * Optional compartment piece of the element. If none * are included, we assume that the label has no * compartments. */ if (string == NULL) return (0); if (*string == '\0') return (0); while ((compartment = strsep(&string, "+")) != NULL) { value = strtol(compartment, &end, 10); if (compartment == end || *end != '\0') return (EINVAL); if (value < 1 || value > MAC_MLS_MAX_COMPARTMENTS) return (EINVAL); MAC_MLS_BIT_SET(value, element->mme_compartments); } } return (0); } /* * Note: destructively consumes the string, make a local copy before * calling if that's a problem. */ static int mac_mls_parse(struct mac_mls *mac_mls, char *string) { char *rangehigh, *rangelow, *effective; int error; effective = strsep(&string, "("); if (*effective == '\0') effective = NULL; if (string != NULL) { rangelow = strsep(&string, "-"); if (string == NULL) return (EINVAL); rangehigh = strsep(&string, ")"); if (string == NULL) return (EINVAL); if (*string != '\0') return (EINVAL); } else { rangelow = NULL; rangehigh = NULL; } KASSERT((rangelow != NULL && rangehigh != NULL) || (rangelow == NULL && rangehigh == NULL), ("mac_mls_parse: range mismatch")); bzero(mac_mls, sizeof(*mac_mls)); if (effective != NULL) { error = mac_mls_parse_element(&mac_mls->mm_effective, effective); if (error) return (error); mac_mls->mm_flags |= MAC_MLS_FLAG_EFFECTIVE; } if (rangelow != NULL) { error = mac_mls_parse_element(&mac_mls->mm_rangelow, rangelow); if (error) return (error); error = mac_mls_parse_element(&mac_mls->mm_rangehigh, rangehigh); if (error) return (error); mac_mls->mm_flags |= MAC_MLS_FLAG_RANGE; } error = mac_mls_valid(mac_mls); if (error) return (error); return (0); } static int mac_mls_internalize_label(struct label *label, char *element_name, char *element_data, int *claimed) { struct mac_mls *mac_mls, mac_mls_temp; int error; if (strcmp(MAC_MLS_LABEL_NAME, element_name) != 0) return (0); (*claimed)++; error = mac_mls_parse(&mac_mls_temp, element_data); if (error) return (error); mac_mls = SLOT(label); *mac_mls = mac_mls_temp; return (0); } static void mac_mls_copy_label(struct label *src, struct label *dest) { *SLOT(dest) = *SLOT(src); } /* * Labeling event operations: file system objects, and things that look * a lot like file system objects. */ static void mac_mls_create_devfs_device(struct ucred *cred, struct mount *mp, struct cdev *dev, struct devfs_dirent *devfs_dirent, struct label *label) { struct mac_mls *mac_mls; int mls_type; mac_mls = SLOT(label); if (strcmp(dev->si_name, "null") == 0 || strcmp(dev->si_name, "zero") == 0 || strcmp(dev->si_name, "random") == 0 || strncmp(dev->si_name, "fd/", strlen("fd/")) == 0) mls_type = MAC_MLS_TYPE_EQUAL; else if (strcmp(dev->si_name, "kmem") == 0 || strcmp(dev->si_name, "mem") == 0) mls_type = MAC_MLS_TYPE_HIGH; else if (ptys_equal && (strncmp(dev->si_name, "ttyp", strlen("ttyp")) == 0 || strncmp(dev->si_name, "ptyp", strlen("ptyp")) == 0)) mls_type = MAC_MLS_TYPE_EQUAL; else mls_type = MAC_MLS_TYPE_LOW; mac_mls_set_effective(mac_mls, mls_type, 0, NULL); } static void mac_mls_create_devfs_directory(struct mount *mp, char *dirname, int dirnamelen, struct devfs_dirent *devfs_dirent, struct label *label) { struct mac_mls *mac_mls; mac_mls = SLOT(label); mac_mls_set_effective(mac_mls, MAC_MLS_TYPE_LOW, 0, NULL); } static void mac_mls_create_devfs_symlink(struct ucred *cred, struct mount *mp, struct devfs_dirent *dd, struct label *ddlabel, struct devfs_dirent *de, struct label *delabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(delabel); mac_mls_copy_effective(source, dest); } static void mac_mls_create_mount(struct ucred *cred, struct mount *mp, struct label *mntlabel, struct label *fslabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(mntlabel); mac_mls_copy_effective(source, dest); dest = SLOT(fslabel); mac_mls_copy_effective(source, dest); } static void mac_mls_relabel_vnode(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, struct label *label) { struct mac_mls *source, *dest; source = SLOT(label); dest = SLOT(vnodelabel); mac_mls_copy(source, dest); } static void mac_mls_update_devfsdirent(struct mount *mp, struct devfs_dirent *devfs_dirent, struct label *direntlabel, struct vnode *vp, struct label *vnodelabel) { struct mac_mls *source, *dest; source = SLOT(vnodelabel); dest = SLOT(direntlabel); mac_mls_copy_effective(source, dest); } static void mac_mls_associate_vnode_devfs(struct mount *mp, struct label *fslabel, struct devfs_dirent *de, struct label *delabel, struct vnode *vp, struct label *vlabel) { struct mac_mls *source, *dest; source = SLOT(delabel); dest = SLOT(vlabel); mac_mls_copy_effective(source, dest); } static int mac_mls_associate_vnode_extattr(struct mount *mp, struct label *fslabel, struct vnode *vp, struct label *vlabel) { struct mac_mls temp, *source, *dest; int buflen, error; source = SLOT(fslabel); dest = SLOT(vlabel); buflen = sizeof(temp); bzero(&temp, buflen); error = vn_extattr_get(vp, IO_NODELOCKED, MAC_MLS_EXTATTR_NAMESPACE, MAC_MLS_EXTATTR_NAME, &buflen, (char *) &temp, curthread); if (error == ENOATTR || error == EOPNOTSUPP) { /* Fall back to the fslabel. */ mac_mls_copy_effective(source, dest); return (0); } else if (error) return (error); if (buflen != sizeof(temp)) { printf("mac_mls_associate_vnode_extattr: bad size %d\n", buflen); return (EPERM); } if (mac_mls_valid(&temp) != 0) { printf("mac_mls_associate_vnode_extattr: invalid\n"); return (EPERM); } if ((temp.mm_flags & MAC_MLS_FLAGS_BOTH) != MAC_MLS_FLAG_EFFECTIVE) { printf("mac_mls_associated_vnode_extattr: not effective\n"); return (EPERM); } mac_mls_copy_effective(&temp, dest); return (0); } static void mac_mls_associate_vnode_singlelabel(struct mount *mp, struct label *fslabel, struct vnode *vp, struct label *vlabel) { struct mac_mls *source, *dest; source = SLOT(fslabel); dest = SLOT(vlabel); mac_mls_copy_effective(source, dest); } static int mac_mls_create_vnode_extattr(struct ucred *cred, struct mount *mp, struct label *fslabel, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *vlabel, struct componentname *cnp) { struct mac_mls *source, *dest, temp; size_t buflen; int error; buflen = sizeof(temp); bzero(&temp, buflen); source = SLOT(cred->cr_label); dest = SLOT(vlabel); mac_mls_copy_effective(source, &temp); error = vn_extattr_set(vp, IO_NODELOCKED, MAC_MLS_EXTATTR_NAMESPACE, MAC_MLS_EXTATTR_NAME, buflen, (char *) &temp, curthread); if (error == 0) mac_mls_copy_effective(source, dest); return (error); } static int mac_mls_setlabel_vnode_extattr(struct ucred *cred, struct vnode *vp, struct label *vlabel, struct label *intlabel) { struct mac_mls *source, temp; size_t buflen; int error; buflen = sizeof(temp); bzero(&temp, buflen); source = SLOT(intlabel); if ((source->mm_flags & MAC_MLS_FLAG_EFFECTIVE) == 0) return (0); mac_mls_copy_effective(source, &temp); error = vn_extattr_set(vp, IO_NODELOCKED, MAC_MLS_EXTATTR_NAMESPACE, MAC_MLS_EXTATTR_NAME, buflen, (char *) &temp, curthread); return (error); } /* * Labeling event operations: IPC object. */ static void mac_mls_create_inpcb_from_socket(struct socket *so, struct label *solabel, struct inpcb *inp, struct label *inplabel) { struct mac_mls *source, *dest; source = SLOT(solabel); dest = SLOT(inplabel); mac_mls_copy_effective(source, dest); } static void mac_mls_create_mbuf_from_socket(struct socket *so, struct label *socketlabel, struct mbuf *m, struct label *mbuflabel) { struct mac_mls *source, *dest; source = SLOT(socketlabel); dest = SLOT(mbuflabel); mac_mls_copy_effective(source, dest); } static void mac_mls_create_socket(struct ucred *cred, struct socket *socket, struct label *socketlabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(socketlabel); mac_mls_copy_effective(source, dest); } static void mac_mls_create_pipe(struct ucred *cred, struct pipepair *pp, struct label *pipelabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(pipelabel); mac_mls_copy_effective(source, dest); } static void mac_mls_create_posix_sem(struct ucred *cred, struct ksem *ksemptr, struct label *ks_label) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(ks_label); mac_mls_copy_effective(source, dest); } static void mac_mls_create_socket_from_socket(struct socket *oldsocket, struct label *oldsocketlabel, struct socket *newsocket, struct label *newsocketlabel) { struct mac_mls *source, *dest; source = SLOT(oldsocketlabel); dest = SLOT(newsocketlabel); mac_mls_copy_effective(source, dest); } static void mac_mls_relabel_socket(struct ucred *cred, struct socket *socket, struct label *socketlabel, struct label *newlabel) { struct mac_mls *source, *dest; source = SLOT(newlabel); dest = SLOT(socketlabel); mac_mls_copy(source, dest); } static void mac_mls_relabel_pipe(struct ucred *cred, struct pipepair *pp, struct label *pipelabel, struct label *newlabel) { struct mac_mls *source, *dest; source = SLOT(newlabel); dest = SLOT(pipelabel); mac_mls_copy(source, dest); } static void mac_mls_set_socket_peer_from_mbuf(struct mbuf *mbuf, struct label *mbuflabel, struct socket *socket, struct label *socketpeerlabel) { struct mac_mls *source, *dest; source = SLOT(mbuflabel); dest = SLOT(socketpeerlabel); mac_mls_copy_effective(source, dest); } /* * Labeling event operations: System V IPC objects. */ static void mac_mls_create_sysv_msgmsg(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqlabel, struct msg *msgptr, struct label *msglabel) { struct mac_mls *source, *dest; /* Ignore the msgq label */ source = SLOT(cred->cr_label); dest = SLOT(msglabel); mac_mls_copy_effective(source, dest); } static void mac_mls_create_sysv_msgqueue(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqlabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(msqlabel); mac_mls_copy_effective(source, dest); } static void mac_mls_create_sysv_sem(struct ucred *cred, struct semid_kernel *semakptr, struct label *semalabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(semalabel); mac_mls_copy_effective(source, dest); } static void mac_mls_create_sysv_shm(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmlabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(shmlabel); mac_mls_copy_effective(source, dest); } /* * Labeling event operations: network objects. */ static void mac_mls_set_socket_peer_from_socket(struct socket *oldsocket, struct label *oldsocketlabel, struct socket *newsocket, struct label *newsocketpeerlabel) { struct mac_mls *source, *dest; source = SLOT(oldsocketlabel); dest = SLOT(newsocketpeerlabel); mac_mls_copy_effective(source, dest); } static void mac_mls_create_bpfdesc(struct ucred *cred, struct bpf_d *bpf_d, struct label *bpflabel) { struct mac_mls *source, *dest; source = SLOT(cred->cr_label); dest = SLOT(bpflabel); mac_mls_copy_effective(source, dest); } static void mac_mls_create_ifnet(struct ifnet *ifnet, struct label *ifnetlabel) { struct mac_mls *dest; int type; dest = SLOT(ifnetlabel); if (ifnet->if_type == IFT_LOOP) type = MAC_MLS_TYPE_EQUAL; else type = MAC_MLS_TYPE_LOW; mac_mls_set_effective(dest, type, 0, NULL); mac_mls_set_range(dest, type, 0, NULL, type, 0, NULL); } static void mac_mls_create_ipq(struct mbuf *fragment, struct label *fragmentlabel, struct ipq *ipq, struct label *ipqlabel) { struct mac_mls *source, *dest; source = SLOT(fragmentlabel); dest = SLOT(ipqlabel); mac_mls_copy_effective(source, dest); } static void mac_mls_create_datagram_from_ipq(struct ipq *ipq, struct label *ipqlabel, struct mbuf *datagram, struct label *datagramlabel) { struct mac_mls *source, *dest; source = SLOT(ipqlabel); dest = SLOT(datagramlabel); /* Just use the head, since we require them all to match. */ mac_mls_copy_effective(source, dest); } static void mac_mls_create_fragment(struct mbuf *datagram, struct label *datagramlabel, struct mbuf *fragment, struct label *fragmentlabel) { struct mac_mls *source, *dest; source = SLOT(datagramlabel); dest = SLOT(fragmentlabel); mac_mls_copy_effective(source, dest); } static void mac_mls_create_mbuf_from_inpcb(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *source, *dest; source = SLOT(inplabel); dest = SLOT(mlabel); mac_mls_copy_effective(source, dest); } static void mac_mls_create_mbuf_linklayer(struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *mbuf, struct label *mbuflabel) { struct mac_mls *dest; dest = SLOT(mbuflabel); mac_mls_set_effective(dest, MAC_MLS_TYPE_EQUAL, 0, NULL); } static void mac_mls_create_mbuf_from_bpfdesc(struct bpf_d *bpf_d, struct label *bpflabel, struct mbuf *mbuf, struct label *mbuflabel) { struct mac_mls *source, *dest; source = SLOT(bpflabel); dest = SLOT(mbuflabel); mac_mls_copy_effective(source, dest); } static void mac_mls_create_mbuf_from_ifnet(struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *m, struct label *mbuflabel) { struct mac_mls *source, *dest; source = SLOT(ifnetlabel); dest = SLOT(mbuflabel); mac_mls_copy_effective(source, dest); } static void mac_mls_create_mbuf_multicast_encap(struct mbuf *oldmbuf, struct label *oldmbuflabel, struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *newmbuf, struct label *newmbuflabel) { struct mac_mls *source, *dest; source = SLOT(oldmbuflabel); dest = SLOT(newmbuflabel); mac_mls_copy_effective(source, dest); } static void mac_mls_create_mbuf_netlayer(struct mbuf *oldmbuf, struct label *oldmbuflabel, struct mbuf *newmbuf, struct label *newmbuflabel) { struct mac_mls *source, *dest; source = SLOT(oldmbuflabel); dest = SLOT(newmbuflabel); mac_mls_copy_effective(source, dest); } static int mac_mls_fragment_match(struct mbuf *fragment, struct label *fragmentlabel, struct ipq *ipq, struct label *ipqlabel) { struct mac_mls *a, *b; a = SLOT(ipqlabel); b = SLOT(fragmentlabel); return (mac_mls_equal_effective(a, b)); } static void mac_mls_relabel_ifnet(struct ucred *cred, struct ifnet *ifnet, struct label *ifnetlabel, struct label *newlabel) { struct mac_mls *source, *dest; source = SLOT(newlabel); dest = SLOT(ifnetlabel); mac_mls_copy(source, dest); } static void mac_mls_update_ipq(struct mbuf *fragment, struct label *fragmentlabel, struct ipq *ipq, struct label *ipqlabel) { /* NOOP: we only accept matching labels, so no need to update */ } static void mac_mls_inpcb_sosetlabel(struct socket *so, struct label *solabel, struct inpcb *inp, struct label *inplabel) { struct mac_mls *source, *dest; source = SLOT(solabel); dest = SLOT(inplabel); mac_mls_copy(source, dest); } +static void +mac_mls_create_mbuf_from_firewall(struct mbuf *m, struct label *mbuflabel) +{ + struct mac_mls *dest; + + dest = SLOT(mbuflabel); + + /* XXX: where is the label for the firewall really comming from? */ + mac_mls_set_effective(dest, MAC_MLS_TYPE_EQUAL, 0, NULL); +} + /* * Labeling event operations: processes. */ static void mac_mls_create_proc0(struct ucred *cred) { struct mac_mls *dest; dest = SLOT(cred->cr_label); mac_mls_set_effective(dest, MAC_MLS_TYPE_EQUAL, 0, NULL); mac_mls_set_range(dest, MAC_MLS_TYPE_LOW, 0, NULL, MAC_MLS_TYPE_HIGH, 0, NULL); } static void mac_mls_create_proc1(struct ucred *cred) { struct mac_mls *dest; dest = SLOT(cred->cr_label); mac_mls_set_effective(dest, MAC_MLS_TYPE_LOW, 0, NULL); mac_mls_set_range(dest, MAC_MLS_TYPE_LOW, 0, NULL, MAC_MLS_TYPE_HIGH, 0, NULL); } static void mac_mls_relabel_cred(struct ucred *cred, struct label *newlabel) { struct mac_mls *source, *dest; source = SLOT(newlabel); dest = SLOT(cred->cr_label); mac_mls_copy(source, dest); } /* * Label cleanup/flush operations. */ static void mac_mls_cleanup_sysv_msgmsg(struct label *msglabel) { bzero(SLOT(msglabel), sizeof(struct mac_mls)); } static void mac_mls_cleanup_sysv_msgqueue(struct label *msqlabel) { bzero(SLOT(msqlabel), sizeof(struct mac_mls)); } static void mac_mls_cleanup_sysv_sem(struct label *semalabel) { bzero(SLOT(semalabel), sizeof(struct mac_mls)); } static void mac_mls_cleanup_sysv_shm(struct label *shmlabel) { bzero(SLOT(shmlabel), sizeof(struct mac_mls)); } /* * Access control checks. */ static int mac_mls_check_bpfdesc_receive(struct bpf_d *bpf_d, struct label *bpflabel, struct ifnet *ifnet, struct label *ifnetlabel) { struct mac_mls *a, *b; if (!mac_mls_enabled) return (0); a = SLOT(bpflabel); b = SLOT(ifnetlabel); if (mac_mls_equal_effective(a, b)) return (0); return (EACCES); } static int mac_mls_check_cred_relabel(struct ucred *cred, struct label *newlabel) { struct mac_mls *subj, *new; int error; subj = SLOT(cred->cr_label); new = SLOT(newlabel); /* * If there is an MLS label update for the credential, it may be * an update of effective, range, or both. */ error = mls_atmostflags(new, MAC_MLS_FLAGS_BOTH); if (error) return (error); /* * If the MLS label is to be changed, authorize as appropriate. */ if (new->mm_flags & MAC_MLS_FLAGS_BOTH) { /* * If the change request modifies both the MLS label effective * and range, check that the new effective will be in the * new range. */ if ((new->mm_flags & MAC_MLS_FLAGS_BOTH) == MAC_MLS_FLAGS_BOTH && !mac_mls_effective_in_range(new, new)) return (EINVAL); /* * To change the MLS effective label on a credential, the * new effective label must be in the current range. */ if (new->mm_flags & MAC_MLS_FLAG_EFFECTIVE && !mac_mls_effective_in_range(new, subj)) return (EPERM); /* * To change the MLS range label on a credential, the * new range must be in the current range. */ if (new->mm_flags & MAC_MLS_FLAG_RANGE && !mac_mls_range_in_range(new, subj)) return (EPERM); /* * To have EQUAL in any component of the new credential * MLS label, the subject must already have EQUAL in * their label. */ if (mac_mls_contains_equal(new)) { error = mac_mls_subject_privileged(subj); if (error) return (error); } } return (0); } static int mac_mls_check_cred_visible(struct ucred *u1, struct ucred *u2) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(u1->cr_label); obj = SLOT(u2->cr_label); /* XXX: range */ if (!mac_mls_dominate_effective(subj, obj)) return (ESRCH); return (0); } static int mac_mls_check_ifnet_relabel(struct ucred *cred, struct ifnet *ifnet, struct label *ifnetlabel, struct label *newlabel) { struct mac_mls *subj, *new; int error; subj = SLOT(cred->cr_label); new = SLOT(newlabel); /* * If there is an MLS label update for the interface, it may * be an update of effective, range, or both. */ error = mls_atmostflags(new, MAC_MLS_FLAGS_BOTH); if (error) return (error); /* * Relabeling network interfaces requires MLS privilege. */ error = mac_mls_subject_privileged(subj); return (0); } static int mac_mls_check_ifnet_transmit(struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *m, struct label *mbuflabel) { struct mac_mls *p, *i; if (!mac_mls_enabled) return (0); p = SLOT(mbuflabel); i = SLOT(ifnetlabel); return (mac_mls_effective_in_range(p, i) ? 0 : EACCES); } static int mac_mls_check_inpcb_deliver(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel) { struct mac_mls *p, *i; if (!mac_mls_enabled) return (0); p = SLOT(mlabel); i = SLOT(inplabel); return (mac_mls_equal_effective(p, i) ? 0 : EACCES); } static int mac_mls_check_sysv_msgrcv(struct ucred *cred, struct msg *msgptr, struct label *msglabel) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msglabel); if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_mls_check_sysv_msgrmid(struct ucred *cred, struct msg *msgptr, struct label *msglabel) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msglabel); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_mls_check_sysv_msqget(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_mls_check_sysv_msqsnd(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_mls_check_sysv_msqrcv(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_mls_check_sysv_msqctl(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel, int cmd) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(msqklabel); switch(cmd) { case IPC_RMID: case IPC_SET: if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); break; case IPC_STAT: if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); break; default: return (EACCES); } return (0); } static int mac_mls_check_sysv_semctl(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel, int cmd) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(semaklabel); switch(cmd) { case IPC_RMID: case IPC_SET: case SETVAL: case SETALL: if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); break; case IPC_STAT: case GETVAL: case GETPID: case GETNCNT: case GETZCNT: case GETALL: if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); break; default: return (EACCES); } return (0); } static int mac_mls_check_sysv_semget(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(semaklabel); if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_mls_check_sysv_semop(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel, size_t accesstype) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(semaklabel); if( accesstype & SEM_R ) if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); if( accesstype & SEM_A ) if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_mls_check_sysv_shmat(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int shmflg) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmseglabel); if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); if ((shmflg & SHM_RDONLY) == 0) if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_mls_check_sysv_shmctl(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int cmd) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmseglabel); switch(cmd) { case IPC_RMID: case IPC_SET: if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); break; case IPC_STAT: case SHM_STAT: if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); break; default: return (EACCES); } return (0); } static int mac_mls_check_sysv_shmget(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int shmflg) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(shmseglabel); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_mls_check_mount_stat(struct ucred *cred, struct mount *mp, struct label *mntlabel) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(mntlabel); if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_mls_check_pipe_ioctl(struct ucred *cred, struct pipepair *pp, struct label *pipelabel, unsigned long cmd, void /* caddr_t */ *data) { if(!mac_mls_enabled) return (0); /* XXX: This will be implemented soon... */ return (0); } static int mac_mls_check_pipe_poll(struct ucred *cred, struct pipepair *pp, struct label *pipelabel) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT((pipelabel)); if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_mls_check_pipe_read(struct ucred *cred, struct pipepair *pp, struct label *pipelabel) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT((pipelabel)); if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_mls_check_pipe_relabel(struct ucred *cred, struct pipepair *pp, struct label *pipelabel, struct label *newlabel) { struct mac_mls *subj, *obj, *new; int error; new = SLOT(newlabel); subj = SLOT(cred->cr_label); obj = SLOT(pipelabel); /* * If there is an MLS label update for a pipe, it must be a * effective update. */ error = mls_atmostflags(new, MAC_MLS_FLAG_EFFECTIVE); if (error) return (error); /* * To perform a relabel of a pipe (MLS label or not), MLS must * authorize the relabel. */ if (!mac_mls_effective_in_range(obj, subj)) return (EPERM); /* * If the MLS label is to be changed, authorize as appropriate. */ if (new->mm_flags & MAC_MLS_FLAG_EFFECTIVE) { /* * To change the MLS label on a pipe, the new pipe label * must be in the subject range. */ if (!mac_mls_effective_in_range(new, subj)) return (EPERM); /* * To change the MLS label on a pipe to be EQUAL, the * subject must have appropriate privilege. */ if (mac_mls_contains_equal(new)) { error = mac_mls_subject_privileged(subj); if (error) return (error); } } return (0); } static int mac_mls_check_pipe_stat(struct ucred *cred, struct pipepair *pp, struct label *pipelabel) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT((pipelabel)); if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_mls_check_pipe_write(struct ucred *cred, struct pipepair *pp, struct label *pipelabel) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT((pipelabel)); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_mls_check_posix_sem_write(struct ucred *cred, struct ksem *ksemptr, struct label *ks_label) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(ks_label); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_mls_check_posix_sem_rdonly(struct ucred *cred, struct ksem *ksemptr, struct label *ks_label) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(ks_label); if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_mls_check_proc_debug(struct ucred *cred, struct proc *proc) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(proc->p_ucred->cr_label); /* XXX: range checks */ if (!mac_mls_dominate_effective(subj, obj)) return (ESRCH); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_mls_check_proc_sched(struct ucred *cred, struct proc *proc) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(proc->p_ucred->cr_label); /* XXX: range checks */ if (!mac_mls_dominate_effective(subj, obj)) return (ESRCH); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_mls_check_proc_signal(struct ucred *cred, struct proc *proc, int signum) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(proc->p_ucred->cr_label); /* XXX: range checks */ if (!mac_mls_dominate_effective(subj, obj)) return (ESRCH); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_mls_check_socket_deliver(struct socket *so, struct label *socketlabel, struct mbuf *m, struct label *mbuflabel) { struct mac_mls *p, *s; if (!mac_mls_enabled) return (0); p = SLOT(mbuflabel); s = SLOT(socketlabel); return (mac_mls_equal_effective(p, s) ? 0 : EACCES); } static int mac_mls_check_socket_relabel(struct ucred *cred, struct socket *socket, struct label *socketlabel, struct label *newlabel) { struct mac_mls *subj, *obj, *new; int error; new = SLOT(newlabel); subj = SLOT(cred->cr_label); obj = SLOT(socketlabel); /* * If there is an MLS label update for the socket, it may be * an update of effective. */ error = mls_atmostflags(new, MAC_MLS_FLAG_EFFECTIVE); if (error) return (error); /* * To relabel a socket, the old socket effective must be in the subject * range. */ if (!mac_mls_effective_in_range(obj, subj)) return (EPERM); /* * If the MLS label is to be changed, authorize as appropriate. */ if (new->mm_flags & MAC_MLS_FLAG_EFFECTIVE) { /* * To relabel a socket, the new socket effective must be in * the subject range. */ if (!mac_mls_effective_in_range(new, subj)) return (EPERM); /* * To change the MLS label on the socket to contain EQUAL, * the subject must have appropriate privilege. */ if (mac_mls_contains_equal(new)) { error = mac_mls_subject_privileged(subj); if (error) return (error); } } return (0); } static int mac_mls_check_socket_visible(struct ucred *cred, struct socket *socket, struct label *socketlabel) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(socketlabel); if (!mac_mls_dominate_effective(subj, obj)) return (ENOENT); return (0); } static int mac_mls_check_system_swapon(struct ucred *cred, struct vnode *vp, struct label *label) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (!mac_mls_dominate_effective(obj, subj) || !mac_mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_mls_check_vnode_chdir(struct ucred *cred, struct vnode *dvp, struct label *dlabel) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_mls_check_vnode_chroot(struct ucred *cred, struct vnode *dvp, struct label *dlabel) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_mls_check_vnode_create(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct componentname *cnp, struct vattr *vap) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_mls_check_vnode_delete(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, struct componentname *cnp) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); obj = SLOT(label); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_mls_check_vnode_deleteacl(struct ucred *cred, struct vnode *vp, struct label *label, acl_type_t type) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_mls_check_vnode_deleteextattr(struct ucred *cred, struct vnode *vp, struct label *label, int attrnamespace, const char *name) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_mls_check_vnode_exec(struct ucred *cred, struct vnode *vp, struct label *label, struct image_params *imgp, struct label *execlabel) { struct mac_mls *subj, *obj, *exec; int error; if (execlabel != NULL) { /* * We currently don't permit labels to be changed at * exec-time as part of MLS, so disallow non-NULL * MLS label elements in the execlabel. */ exec = SLOT(execlabel); error = mls_atmostflags(exec, 0); if (error) return (error); } if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_mls_check_vnode_getacl(struct ucred *cred, struct vnode *vp, struct label *label, acl_type_t type) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_mls_check_vnode_getextattr(struct ucred *cred, struct vnode *vp, struct label *label, int attrnamespace, const char *name, struct uio *uio) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_mls_check_vnode_link(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, struct componentname *cnp) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); obj = SLOT(dlabel); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_mls_check_vnode_listextattr(struct ucred *cred, struct vnode *vp, struct label *label, int attrnamespace) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_mls_check_vnode_lookup(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct componentname *cnp) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_mls_check_vnode_mmap(struct ucred *cred, struct vnode *vp, struct label *label, int prot, int flags) { struct mac_mls *subj, *obj; /* * Rely on the use of open()-time protections to handle * non-revocation cases. */ if (!mac_mls_enabled || !revocation_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) { if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); } if (((prot & VM_PROT_WRITE) != 0) && ((flags & MAP_SHARED) != 0)) { if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); } return (0); } static int mac_mls_check_vnode_open(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, int acc_mode) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vnodelabel); /* XXX privilege override for admin? */ if (acc_mode & (VREAD | VEXEC | VSTAT)) { if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); } if (acc_mode & (VWRITE | VAPPEND | VADMIN)) { if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); } return (0); } static int mac_mls_check_vnode_poll(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *label) { struct mac_mls *subj, *obj; if (!mac_mls_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(label); if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_mls_check_vnode_read(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *label) { struct mac_mls *subj, *obj; if (!mac_mls_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(label); if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_mls_check_vnode_readdir(struct ucred *cred, struct vnode *dvp, struct label *dlabel) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_mls_check_vnode_readlink(struct ucred *cred, struct vnode *vp, struct label *vnodelabel) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vnodelabel); if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_mls_check_vnode_relabel(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, struct label *newlabel) { struct mac_mls *old, *new, *subj; int error; old = SLOT(vnodelabel); new = SLOT(newlabel); subj = SLOT(cred->cr_label); /* * If there is an MLS label update for the vnode, it must be a * effective label. */ error = mls_atmostflags(new, MAC_MLS_FLAG_EFFECTIVE); if (error) return (error); /* * To perform a relabel of the vnode (MLS label or not), MLS must * authorize the relabel. */ if (!mac_mls_effective_in_range(old, subj)) return (EPERM); /* * If the MLS label is to be changed, authorize as appropriate. */ if (new->mm_flags & MAC_MLS_FLAG_EFFECTIVE) { /* * To change the MLS label on a vnode, the new vnode label * must be in the subject range. */ if (!mac_mls_effective_in_range(new, subj)) return (EPERM); /* * To change the MLS label on the vnode to be EQUAL, * the subject must have appropriate privilege. */ if (mac_mls_contains_equal(new)) { error = mac_mls_subject_privileged(subj); if (error) return (error); } } return (0); } static int mac_mls_check_vnode_rename_from(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, struct componentname *cnp) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); obj = SLOT(label); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_mls_check_vnode_rename_to(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, int samedir, struct componentname *cnp) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(dlabel); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); if (vp != NULL) { obj = SLOT(label); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); } return (0); } static int mac_mls_check_vnode_revoke(struct ucred *cred, struct vnode *vp, struct label *label) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_mls_check_vnode_setacl(struct ucred *cred, struct vnode *vp, struct label *label, acl_type_t type, struct acl *acl) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(label); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_mls_check_vnode_setextattr(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, int attrnamespace, const char *name, struct uio *uio) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vnodelabel); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); /* XXX: protect the MAC EA in a special way? */ return (0); } static int mac_mls_check_vnode_setflags(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, u_long flags) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vnodelabel); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_mls_check_vnode_setmode(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, mode_t mode) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vnodelabel); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_mls_check_vnode_setowner(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, uid_t uid, gid_t gid) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vnodelabel); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_mls_check_vnode_setutimes(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, struct timespec atime, struct timespec mtime) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(cred->cr_label); obj = SLOT(vnodelabel); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static int mac_mls_check_vnode_stat(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *vnodelabel) { struct mac_mls *subj, *obj; if (!mac_mls_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(vnodelabel); if (!mac_mls_dominate_effective(subj, obj)) return (EACCES); return (0); } static int mac_mls_check_vnode_write(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *label) { struct mac_mls *subj, *obj; if (!mac_mls_enabled || !revocation_enabled) return (0); subj = SLOT(active_cred->cr_label); obj = SLOT(label); if (!mac_mls_dominate_effective(obj, subj)) return (EACCES); return (0); } static void mac_mls_associate_nfsd_label(struct ucred *cred) { struct mac_mls *label; label = SLOT(cred->cr_label); mac_mls_set_effective(label, MAC_MLS_TYPE_LOW, 0, NULL); mac_mls_set_range(label, MAC_MLS_TYPE_LOW, 0, NULL, MAC_MLS_TYPE_HIGH, 0, NULL); } static struct mac_policy_ops mac_mls_ops = { .mpo_init = mac_mls_init, .mpo_init_bpfdesc_label = mac_mls_init_label, .mpo_init_cred_label = mac_mls_init_label, .mpo_init_devfsdirent_label = mac_mls_init_label, .mpo_init_ifnet_label = mac_mls_init_label, .mpo_init_inpcb_label = mac_mls_init_label_waitcheck, .mpo_init_sysv_msgmsg_label = mac_mls_init_label, .mpo_init_sysv_msgqueue_label = mac_mls_init_label, .mpo_init_sysv_sem_label = mac_mls_init_label, .mpo_init_sysv_shm_label = mac_mls_init_label, .mpo_init_ipq_label = mac_mls_init_label_waitcheck, .mpo_init_mbuf_label = mac_mls_init_label_waitcheck, .mpo_init_mount_label = mac_mls_init_label, .mpo_init_mount_fs_label = mac_mls_init_label, .mpo_init_pipe_label = mac_mls_init_label, .mpo_init_posix_sem_label = mac_mls_init_label, .mpo_init_socket_label = mac_mls_init_label_waitcheck, .mpo_init_socket_peer_label = mac_mls_init_label_waitcheck, .mpo_init_vnode_label = mac_mls_init_label, .mpo_destroy_bpfdesc_label = mac_mls_destroy_label, .mpo_destroy_cred_label = mac_mls_destroy_label, .mpo_destroy_devfsdirent_label = mac_mls_destroy_label, .mpo_destroy_ifnet_label = mac_mls_destroy_label, .mpo_destroy_inpcb_label = mac_mls_destroy_label, .mpo_destroy_sysv_msgmsg_label = mac_mls_destroy_label, .mpo_destroy_sysv_msgqueue_label = mac_mls_destroy_label, .mpo_destroy_sysv_sem_label = mac_mls_destroy_label, .mpo_destroy_sysv_shm_label = mac_mls_destroy_label, .mpo_destroy_ipq_label = mac_mls_destroy_label, .mpo_destroy_mbuf_label = mac_mls_destroy_label, .mpo_destroy_mount_label = mac_mls_destroy_label, .mpo_destroy_mount_fs_label = mac_mls_destroy_label, .mpo_destroy_pipe_label = mac_mls_destroy_label, .mpo_destroy_posix_sem_label = mac_mls_destroy_label, .mpo_destroy_socket_label = mac_mls_destroy_label, .mpo_destroy_socket_peer_label = mac_mls_destroy_label, .mpo_destroy_vnode_label = mac_mls_destroy_label, .mpo_copy_cred_label = mac_mls_copy_label, .mpo_copy_ifnet_label = mac_mls_copy_label, .mpo_copy_mbuf_label = mac_mls_copy_label, .mpo_copy_pipe_label = mac_mls_copy_label, .mpo_copy_socket_label = mac_mls_copy_label, .mpo_copy_vnode_label = mac_mls_copy_label, .mpo_externalize_cred_label = mac_mls_externalize_label, .mpo_externalize_ifnet_label = mac_mls_externalize_label, .mpo_externalize_pipe_label = mac_mls_externalize_label, .mpo_externalize_socket_label = mac_mls_externalize_label, .mpo_externalize_socket_peer_label = mac_mls_externalize_label, .mpo_externalize_vnode_label = mac_mls_externalize_label, .mpo_internalize_cred_label = mac_mls_internalize_label, .mpo_internalize_ifnet_label = mac_mls_internalize_label, .mpo_internalize_pipe_label = mac_mls_internalize_label, .mpo_internalize_socket_label = mac_mls_internalize_label, .mpo_internalize_vnode_label = mac_mls_internalize_label, .mpo_create_devfs_device = mac_mls_create_devfs_device, .mpo_create_devfs_directory = mac_mls_create_devfs_directory, .mpo_create_devfs_symlink = mac_mls_create_devfs_symlink, .mpo_create_mount = mac_mls_create_mount, .mpo_relabel_vnode = mac_mls_relabel_vnode, .mpo_update_devfsdirent = mac_mls_update_devfsdirent, .mpo_associate_vnode_devfs = mac_mls_associate_vnode_devfs, .mpo_associate_vnode_extattr = mac_mls_associate_vnode_extattr, .mpo_associate_vnode_singlelabel = mac_mls_associate_vnode_singlelabel, .mpo_create_vnode_extattr = mac_mls_create_vnode_extattr, .mpo_setlabel_vnode_extattr = mac_mls_setlabel_vnode_extattr, .mpo_create_mbuf_from_socket = mac_mls_create_mbuf_from_socket, .mpo_create_pipe = mac_mls_create_pipe, .mpo_create_posix_sem = mac_mls_create_posix_sem, .mpo_create_socket = mac_mls_create_socket, .mpo_create_socket_from_socket = mac_mls_create_socket_from_socket, .mpo_relabel_pipe = mac_mls_relabel_pipe, .mpo_relabel_socket = mac_mls_relabel_socket, .mpo_set_socket_peer_from_mbuf = mac_mls_set_socket_peer_from_mbuf, .mpo_set_socket_peer_from_socket = mac_mls_set_socket_peer_from_socket, .mpo_create_bpfdesc = mac_mls_create_bpfdesc, .mpo_create_datagram_from_ipq = mac_mls_create_datagram_from_ipq, .mpo_create_fragment = mac_mls_create_fragment, .mpo_create_ifnet = mac_mls_create_ifnet, .mpo_create_inpcb_from_socket = mac_mls_create_inpcb_from_socket, .mpo_create_ipq = mac_mls_create_ipq, .mpo_create_sysv_msgmsg = mac_mls_create_sysv_msgmsg, .mpo_create_sysv_msgqueue = mac_mls_create_sysv_msgqueue, .mpo_create_sysv_sem = mac_mls_create_sysv_sem, .mpo_create_sysv_shm = mac_mls_create_sysv_shm, .mpo_create_mbuf_from_inpcb = mac_mls_create_mbuf_from_inpcb, .mpo_create_mbuf_linklayer = mac_mls_create_mbuf_linklayer, .mpo_create_mbuf_from_bpfdesc = mac_mls_create_mbuf_from_bpfdesc, .mpo_create_mbuf_from_ifnet = mac_mls_create_mbuf_from_ifnet, .mpo_create_mbuf_multicast_encap = mac_mls_create_mbuf_multicast_encap, .mpo_create_mbuf_netlayer = mac_mls_create_mbuf_netlayer, .mpo_fragment_match = mac_mls_fragment_match, .mpo_relabel_ifnet = mac_mls_relabel_ifnet, .mpo_update_ipq = mac_mls_update_ipq, .mpo_inpcb_sosetlabel = mac_mls_inpcb_sosetlabel, .mpo_create_proc0 = mac_mls_create_proc0, .mpo_create_proc1 = mac_mls_create_proc1, .mpo_relabel_cred = mac_mls_relabel_cred, .mpo_cleanup_sysv_msgmsg = mac_mls_cleanup_sysv_msgmsg, .mpo_cleanup_sysv_msgqueue = mac_mls_cleanup_sysv_msgqueue, .mpo_cleanup_sysv_sem = mac_mls_cleanup_sysv_sem, .mpo_cleanup_sysv_shm = mac_mls_cleanup_sysv_shm, .mpo_check_bpfdesc_receive = mac_mls_check_bpfdesc_receive, .mpo_check_cred_relabel = mac_mls_check_cred_relabel, .mpo_check_cred_visible = mac_mls_check_cred_visible, .mpo_check_ifnet_relabel = mac_mls_check_ifnet_relabel, .mpo_check_ifnet_transmit = mac_mls_check_ifnet_transmit, .mpo_check_inpcb_deliver = mac_mls_check_inpcb_deliver, .mpo_check_sysv_msgrcv = mac_mls_check_sysv_msgrcv, .mpo_check_sysv_msgrmid = mac_mls_check_sysv_msgrmid, .mpo_check_sysv_msqget = mac_mls_check_sysv_msqget, .mpo_check_sysv_msqsnd = mac_mls_check_sysv_msqsnd, .mpo_check_sysv_msqrcv = mac_mls_check_sysv_msqrcv, .mpo_check_sysv_msqctl = mac_mls_check_sysv_msqctl, .mpo_check_sysv_semctl = mac_mls_check_sysv_semctl, .mpo_check_sysv_semget = mac_mls_check_sysv_semget, .mpo_check_sysv_semop = mac_mls_check_sysv_semop, .mpo_check_sysv_shmat = mac_mls_check_sysv_shmat, .mpo_check_sysv_shmctl = mac_mls_check_sysv_shmctl, .mpo_check_sysv_shmget = mac_mls_check_sysv_shmget, .mpo_check_mount_stat = mac_mls_check_mount_stat, .mpo_check_pipe_ioctl = mac_mls_check_pipe_ioctl, .mpo_check_pipe_poll = mac_mls_check_pipe_poll, .mpo_check_pipe_read = mac_mls_check_pipe_read, .mpo_check_pipe_relabel = mac_mls_check_pipe_relabel, .mpo_check_pipe_stat = mac_mls_check_pipe_stat, .mpo_check_pipe_write = mac_mls_check_pipe_write, .mpo_check_posix_sem_destroy = mac_mls_check_posix_sem_write, .mpo_check_posix_sem_getvalue = mac_mls_check_posix_sem_rdonly, .mpo_check_posix_sem_open = mac_mls_check_posix_sem_write, .mpo_check_posix_sem_post = mac_mls_check_posix_sem_write, .mpo_check_posix_sem_unlink = mac_mls_check_posix_sem_write, .mpo_check_posix_sem_wait = mac_mls_check_posix_sem_write, .mpo_check_proc_debug = mac_mls_check_proc_debug, .mpo_check_proc_sched = mac_mls_check_proc_sched, .mpo_check_proc_signal = mac_mls_check_proc_signal, .mpo_check_socket_deliver = mac_mls_check_socket_deliver, .mpo_check_socket_relabel = mac_mls_check_socket_relabel, .mpo_check_socket_visible = mac_mls_check_socket_visible, .mpo_check_system_swapon = mac_mls_check_system_swapon, .mpo_check_vnode_access = mac_mls_check_vnode_open, .mpo_check_vnode_chdir = mac_mls_check_vnode_chdir, .mpo_check_vnode_chroot = mac_mls_check_vnode_chroot, .mpo_check_vnode_create = mac_mls_check_vnode_create, .mpo_check_vnode_delete = mac_mls_check_vnode_delete, .mpo_check_vnode_deleteacl = mac_mls_check_vnode_deleteacl, .mpo_check_vnode_deleteextattr = mac_mls_check_vnode_deleteextattr, .mpo_check_vnode_exec = mac_mls_check_vnode_exec, .mpo_check_vnode_getacl = mac_mls_check_vnode_getacl, .mpo_check_vnode_getextattr = mac_mls_check_vnode_getextattr, .mpo_check_vnode_link = mac_mls_check_vnode_link, .mpo_check_vnode_listextattr = mac_mls_check_vnode_listextattr, .mpo_check_vnode_lookup = mac_mls_check_vnode_lookup, .mpo_check_vnode_mmap = mac_mls_check_vnode_mmap, .mpo_check_vnode_open = mac_mls_check_vnode_open, .mpo_check_vnode_poll = mac_mls_check_vnode_poll, .mpo_check_vnode_read = mac_mls_check_vnode_read, .mpo_check_vnode_readdir = mac_mls_check_vnode_readdir, .mpo_check_vnode_readlink = mac_mls_check_vnode_readlink, .mpo_check_vnode_relabel = mac_mls_check_vnode_relabel, .mpo_check_vnode_rename_from = mac_mls_check_vnode_rename_from, .mpo_check_vnode_rename_to = mac_mls_check_vnode_rename_to, .mpo_check_vnode_revoke = mac_mls_check_vnode_revoke, .mpo_check_vnode_setacl = mac_mls_check_vnode_setacl, .mpo_check_vnode_setextattr = mac_mls_check_vnode_setextattr, .mpo_check_vnode_setflags = mac_mls_check_vnode_setflags, .mpo_check_vnode_setmode = mac_mls_check_vnode_setmode, .mpo_check_vnode_setowner = mac_mls_check_vnode_setowner, .mpo_check_vnode_setutimes = mac_mls_check_vnode_setutimes, .mpo_check_vnode_stat = mac_mls_check_vnode_stat, .mpo_check_vnode_write = mac_mls_check_vnode_write, .mpo_associate_nfsd_label = mac_mls_associate_nfsd_label, + .mpo_create_mbuf_from_firewall = mac_mls_create_mbuf_from_firewall, }; MAC_POLICY_SET(&mac_mls_ops, mac_mls, "TrustedBSD MAC/MLS", MPC_LOADTIME_FLAG_NOTLATE | MPC_LOADTIME_FLAG_LABELMBUFS, &mac_mls_slot); Index: stable/6/sys/sys/mac.h =================================================================== --- stable/6/sys/sys/mac.h (revision 162447) +++ stable/6/sys/sys/mac.h (revision 162448) @@ -1,476 +1,477 @@ /*- * Copyright (c) 1999-2002 Robert N. M. Watson * Copyright (c) 2001-2005 Networks Associates Technology, Inc. * Copyright (c) 2005 SPARTA, Inc. * All rights reserved. * * This software was developed by Robert Watson for the TrustedBSD Project. * * This software was developed for the FreeBSD Project in part 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"). * * 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$ */ /* * Userland/kernel interface for Mandatory Access Control. * * The POSIX.1e implementation page may be reached at: * http://www.trustedbsd.org/ */ #ifndef _SYS_MAC_H_ #define _SYS_MAC_H_ #include #ifndef _POSIX_MAC #define _POSIX_MAC #endif /* * MAC framework-related constants and limits. */ #define MAC_MAX_POLICY_NAME 32 #define MAC_MAX_LABEL_ELEMENT_NAME 32 #define MAC_MAX_LABEL_ELEMENT_DATA 4096 #define MAC_MAX_LABEL_BUF_LEN 8192 struct mac { size_t m_buflen; char *m_string; }; typedef struct mac *mac_t; #ifndef _KERNEL /* * Location of the userland MAC framework configuration file. mac.conf * binds policy names to shared libraries that understand those policies, * as well as setting defaults for MAC-aware applications. */ #define MAC_CONFFILE "/etc/mac.conf" /* * Extended non-POSIX.1e interfaces that offer additional services * available from the userland and kernel MAC frameworks. */ __BEGIN_DECLS int mac_execve(char *fname, char **argv, char **envv, mac_t _label); int mac_free(mac_t _label); int mac_from_text(mac_t *_label, const char *_text); int mac_get_fd(int _fd, mac_t _label); int mac_get_file(const char *_path, mac_t _label); int mac_get_link(const char *_path, mac_t _label); int mac_get_peer(int _fd, mac_t _label); int mac_get_pid(pid_t _pid, mac_t _label); int mac_get_proc(mac_t _label); int mac_is_present(const char *_policyname); int mac_prepare(mac_t *_label, const char *_elements); int mac_prepare_file_label(mac_t *_label); int mac_prepare_ifnet_label(mac_t *_label); int mac_prepare_process_label(mac_t *_label); int mac_prepare_type(mac_t *_label, const char *_type); int mac_set_fd(int _fildes, const mac_t _label); int mac_set_file(const char *_path, mac_t _label); int mac_set_link(const char *_path, mac_t _label); int mac_set_proc(const mac_t _label); int mac_syscall(const char *_policyname, int _call, void *_arg); int mac_to_text(mac_t mac, char **_text); __END_DECLS #else /* _KERNEL */ /* * Kernel functions to manage and evaluate labels. */ struct bpf_d; struct cdev; struct componentname; struct devfs_dirent; struct ifnet; struct ifreq; struct inpcb; struct image_params; struct inpcb; struct ipq; struct ksem; struct m_tag; struct mbuf; struct mount; struct msg; struct msqid_kernel; struct proc; struct semid_kernel; struct shmid_kernel; struct sockaddr; struct socket; struct sysctl_oid; struct sysctl_req; struct pipepair; struct thread; struct timespec; struct ucred; struct uio; struct vattr; struct vnode; #include /* XXX acl_type_t */ struct vop_setlabel_args; /* * Label operations. */ void mac_init_bpfdesc(struct bpf_d *); void mac_init_cred(struct ucred *); void mac_init_devfsdirent(struct devfs_dirent *); void mac_init_ifnet(struct ifnet *); int mac_init_inpcb(struct inpcb *, int flag); void mac_init_sysv_msgmsg(struct msg *); void mac_init_sysv_msgqueue(struct msqid_kernel*); void mac_init_sysv_sem(struct semid_kernel*); void mac_init_sysv_shm(struct shmid_kernel*); int mac_init_ipq(struct ipq *, int flag); int mac_init_socket(struct socket *, int flag); void mac_init_pipe(struct pipepair *); void mac_init_posix_sem(struct ksem *); int mac_init_mbuf(struct mbuf *mbuf, int flag); int mac_init_mbuf_tag(struct m_tag *, int flag); void mac_init_mount(struct mount *); void mac_init_proc(struct proc *); void mac_init_vnode(struct vnode *); void mac_copy_mbuf(struct mbuf *m_from, struct mbuf *m_to); void mac_copy_mbuf_tag(struct m_tag *, struct m_tag *); void mac_copy_vnode_label(struct label *, struct label *label); void mac_destroy_bpfdesc(struct bpf_d *); void mac_destroy_cred(struct ucred *); void mac_destroy_devfsdirent(struct devfs_dirent *); void mac_destroy_ifnet(struct ifnet *); void mac_destroy_inpcb(struct inpcb *); void mac_destroy_sysv_msgmsg(struct msg *); void mac_destroy_sysv_msgqueue(struct msqid_kernel *); void mac_destroy_sysv_sem(struct semid_kernel *); void mac_destroy_sysv_shm(struct shmid_kernel *); void mac_destroy_ipq(struct ipq *); void mac_destroy_socket(struct socket *); void mac_destroy_pipe(struct pipepair *); void mac_destroy_posix_sem(struct ksem *); void mac_destroy_proc(struct proc *); void mac_destroy_mbuf_tag(struct m_tag *); void mac_destroy_mount(struct mount *); void mac_destroy_vnode(struct vnode *); struct label *mac_cred_label_alloc(void); void mac_cred_label_free(struct label *label); struct label *mac_vnode_label_alloc(void); void mac_vnode_label_free(struct label *label); /* * Labeling event operations: file system objects, and things that * look a lot like file system objects. */ void mac_associate_vnode_devfs(struct mount *mp, struct devfs_dirent *de, struct vnode *vp); int mac_associate_vnode_extattr(struct mount *mp, struct vnode *vp); void mac_associate_vnode_singlelabel(struct mount *mp, struct vnode *vp); void mac_create_devfs_device(struct ucred *cred, struct mount *mp, struct cdev *dev, struct devfs_dirent *de); void mac_create_devfs_directory(struct mount *mp, char *dirname, int dirnamelen, struct devfs_dirent *de); void mac_create_devfs_symlink(struct ucred *cred, struct mount *mp, struct devfs_dirent *dd, struct devfs_dirent *de); int mac_create_vnode_extattr(struct ucred *cred, struct mount *mp, struct vnode *dvp, struct vnode *vp, struct componentname *cnp); void mac_create_mount(struct ucred *cred, struct mount *mp); void mac_relabel_vnode(struct ucred *cred, struct vnode *vp, struct label *newlabel); void mac_update_devfsdirent(struct mount *mp, struct devfs_dirent *de, struct vnode *vp); /* * Labeling event operations: IPC objects. */ void mac_create_mbuf_from_socket(struct socket *so, struct mbuf *m); void mac_create_socket(struct ucred *cred, struct socket *socket); void mac_create_socket_from_socket(struct socket *oldsocket, struct socket *newsocket); void mac_set_socket_peer_from_mbuf(struct mbuf *mbuf, struct socket *socket); void mac_set_socket_peer_from_socket(struct socket *oldsocket, struct socket *newsocket); void mac_create_pipe(struct ucred *cred, struct pipepair *pp); /* * Labeling event operations: System V IPC primitives */ void mac_create_sysv_msgmsg(struct ucred *cred, struct msqid_kernel *msqkptr, struct msg *msgptr); void mac_create_sysv_msgqueue(struct ucred *cred, struct msqid_kernel *msqkptr); void mac_create_sysv_sem(struct ucred *cred, struct semid_kernel *semakptr); void mac_create_sysv_shm(struct ucred *cred, struct shmid_kernel *shmsegptr); /* * Labeling event operations: POSIX (global/inter-process) semaphores. */ void mac_create_posix_sem(struct ucred *cred, struct ksem *ksemptr); /* * Labeling event operations: network objects. */ void mac_create_bpfdesc(struct ucred *cred, struct bpf_d *bpf_d); void mac_create_ifnet(struct ifnet *ifp); void mac_create_inpcb_from_socket(struct socket *so, struct inpcb *inp); void mac_create_ipq(struct mbuf *fragment, struct ipq *ipq); void mac_create_datagram_from_ipq(struct ipq *ipq, struct mbuf *datagram); void mac_create_fragment(struct mbuf *datagram, struct mbuf *fragment); void mac_create_mbuf_from_inpcb(struct inpcb *inp, struct mbuf *m); void mac_create_mbuf_linklayer(struct ifnet *ifnet, struct mbuf *m); void mac_create_mbuf_from_bpfdesc(struct bpf_d *bpf_d, struct mbuf *m); void mac_create_mbuf_from_ifnet(struct ifnet *ifnet, struct mbuf *m); void mac_create_mbuf_multicast_encap(struct mbuf *oldmbuf, struct ifnet *ifnet, struct mbuf *newmbuf); void mac_create_mbuf_netlayer(struct mbuf *oldmbuf, struct mbuf *newmbuf); int mac_fragment_match(struct mbuf *fragment, struct ipq *ipq); void mac_reflect_mbuf_icmp(struct mbuf *m); void mac_reflect_mbuf_tcp(struct mbuf *m); void mac_update_ipq(struct mbuf *fragment, struct ipq *ipq); void mac_inpcb_sosetlabel(struct socket *so, struct inpcb *inp); +void mac_create_mbuf_from_firewall(struct mbuf *m); /* * Labeling event operations: processes. */ void mac_copy_cred(struct ucred *cr1, struct ucred *cr2); int mac_execve_enter(struct image_params *imgp, struct mac *mac_p); void mac_execve_exit(struct image_params *imgp); void mac_execve_transition(struct ucred *old, struct ucred *new, struct vnode *vp, struct label *interpvnodelabel, struct image_params *imgp); int mac_execve_will_transition(struct ucred *old, struct vnode *vp, struct label *interpvnodelabel, struct image_params *imgp); void mac_create_proc0(struct ucred *cred); void mac_create_proc1(struct ucred *cred); void mac_thread_userret(struct thread *td); /* * Label cleanup operation: This is the inverse complement for the * mac_create and associate type of hooks. This hook lets the policy * module(s) perform a cleanup/flushing operation on the label * associated with the objects, without freeing up the space allocated. * This hook is useful in cases where it is desirable to remove any * labeling reference when recycling any object to a pool. This hook * does not replace the mac_destroy hooks. */ void mac_cleanup_sysv_msgmsg(struct msg *msgptr); void mac_cleanup_sysv_msgqueue(struct msqid_kernel *msqkptr); void mac_cleanup_sysv_sem(struct semid_kernel *semakptr); void mac_cleanup_sysv_shm(struct shmid_kernel *shmsegptr); /* Access control checks. */ int mac_check_bpfdesc_receive(struct bpf_d *bpf_d, struct ifnet *ifnet); int mac_check_cred_visible(struct ucred *u1, struct ucred *u2); int mac_check_ifnet_transmit(struct ifnet *ifnet, struct mbuf *m); int mac_check_inpcb_deliver(struct inpcb *inp, struct mbuf *m); int mac_check_sysv_msgmsq(struct ucred *cred, struct msg *msgptr, struct msqid_kernel *msqkptr); int mac_check_sysv_msgrcv(struct ucred *cred, struct msg *msgptr); int mac_check_sysv_msgrmid(struct ucred *cred, struct msg *msgptr); int mac_check_sysv_msqget(struct ucred *cred, struct msqid_kernel *msqkptr); int mac_check_sysv_msqsnd(struct ucred *cred, struct msqid_kernel *msqkptr); int mac_check_sysv_msqrcv(struct ucred *cred, struct msqid_kernel *msqkptr); int mac_check_sysv_msqctl(struct ucred *cred, struct msqid_kernel *msqkptr, int cmd); int mac_check_sysv_semctl(struct ucred *cred, struct semid_kernel *semakptr, int cmd); int mac_check_sysv_semget(struct ucred *cred, struct semid_kernel *semakptr); int mac_check_sysv_semop(struct ucred *cred,struct semid_kernel *semakptr, size_t accesstype); int mac_check_sysv_shmat(struct ucred *cred, struct shmid_kernel *shmsegptr, int shmflg); int mac_check_sysv_shmctl(struct ucred *cred, struct shmid_kernel *shmsegptr, int cmd); int mac_check_sysv_shmdt(struct ucred *cred, struct shmid_kernel *shmsegptr); int mac_check_sysv_shmget(struct ucred *cred, struct shmid_kernel *shmsegptr, int shmflg); int mac_check_kenv_dump(struct ucred *cred); int mac_check_kenv_get(struct ucred *cred, char *name); int mac_check_kenv_set(struct ucred *cred, char *name, char *value); int mac_check_kenv_unset(struct ucred *cred, char *name); int mac_check_kld_load(struct ucred *cred, struct vnode *vp); int mac_check_kld_stat(struct ucred *cred); int mac_check_kld_unload(struct ucred *cred); int mac_check_mount_stat(struct ucred *cred, struct mount *mp); int mac_check_pipe_ioctl(struct ucred *cred, struct pipepair *pp, unsigned long cmd, void *data); int mac_check_pipe_poll(struct ucred *cred, struct pipepair *pp); int mac_check_pipe_read(struct ucred *cred, struct pipepair *pp); int mac_check_pipe_stat(struct ucred *cred, struct pipepair *pp); int mac_check_pipe_write(struct ucred *cred, struct pipepair *pp); int mac_check_posix_sem_destroy(struct ucred *cred, struct ksem *ksemptr); int mac_check_posix_sem_getvalue(struct ucred *cred,struct ksem *ksemptr); int mac_check_posix_sem_open(struct ucred *cred, struct ksem *ksemptr); int mac_check_posix_sem_post(struct ucred *cred, struct ksem *ksemptr); int mac_check_posix_sem_unlink(struct ucred *cred, struct ksem *ksemptr); int mac_check_posix_sem_wait(struct ucred *cred, struct ksem *ksemptr); int mac_check_proc_debug(struct ucred *cred, struct proc *proc); int mac_check_proc_sched(struct ucred *cred, struct proc *proc); int mac_check_proc_setuid(struct proc *proc, struct ucred *cred, uid_t uid); int mac_check_proc_seteuid(struct proc *proc, struct ucred *cred, uid_t euid); int mac_check_proc_setgid(struct proc *proc, struct ucred *cred, gid_t gid); int mac_check_proc_setegid(struct proc *proc, struct ucred *cred, gid_t egid); int mac_check_proc_setgroups(struct proc *proc, struct ucred *cred, int ngroups, gid_t *gidset); int mac_check_proc_setreuid(struct proc *proc, struct ucred *cred, uid_t ruid, uid_t euid); int mac_check_proc_setregid(struct proc *proc, struct ucred *cred, gid_t rgid, gid_t egid); int mac_check_proc_setresuid(struct proc *proc, struct ucred *cred, uid_t ruid, uid_t euid, uid_t suid); int mac_check_proc_setresgid(struct proc *proc, struct ucred *cred, gid_t rgid, gid_t egid, gid_t sgid); int mac_check_proc_signal(struct ucred *cred, struct proc *proc, int signum); int mac_check_proc_wait(struct ucred *cred, struct proc *proc); int mac_check_socket_accept(struct ucred *cred, struct socket *so); int mac_check_socket_bind(struct ucred *cred, struct socket *so, struct sockaddr *sockaddr); int mac_check_socket_connect(struct ucred *cred, struct socket *so, struct sockaddr *sockaddr); int mac_check_socket_create(struct ucred *cred, int domain, int type, int protocol); int mac_check_socket_deliver(struct socket *so, struct mbuf *m); int mac_check_socket_listen(struct ucred *cred, struct socket *so); int mac_check_socket_poll(struct ucred *cred, struct socket *so); int mac_check_socket_receive(struct ucred *cred, struct socket *so); int mac_check_socket_send(struct ucred *cred, struct socket *so); int mac_check_socket_stat(struct ucred *cred, struct socket *so); int mac_check_socket_visible(struct ucred *cred, struct socket *so); int mac_check_sysarch_ioperm(struct ucred *cred); int mac_check_system_acct(struct ucred *cred, struct vnode *vp); int mac_check_system_nfsd(struct ucred *cred); int mac_check_system_reboot(struct ucred *cred, int howto); int mac_check_system_settime(struct ucred *cred); int mac_check_system_swapon(struct ucred *cred, struct vnode *vp); int mac_check_system_swapoff(struct ucred *cred, struct vnode *vp); int mac_check_system_sysctl(struct ucred *cred, struct sysctl_oid *oidp, void *arg1, int arg2, struct sysctl_req *req); int mac_check_vnode_access(struct ucred *cred, struct vnode *vp, int acc_mode); int mac_check_vnode_chdir(struct ucred *cred, struct vnode *dvp); int mac_check_vnode_chroot(struct ucred *cred, struct vnode *dvp); int mac_check_vnode_create(struct ucred *cred, struct vnode *dvp, struct componentname *cnp, struct vattr *vap); int mac_check_vnode_delete(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp); int mac_check_vnode_deleteacl(struct ucred *cred, struct vnode *vp, acl_type_t type); int mac_check_vnode_deleteextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name); int mac_check_vnode_exec(struct ucred *cred, struct vnode *vp, struct image_params *imgp); int mac_check_vnode_getacl(struct ucred *cred, struct vnode *vp, acl_type_t type); int mac_check_vnode_getextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio); int mac_check_vnode_link(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp); int mac_check_vnode_listextattr(struct ucred *cred, struct vnode *vp, int attrnamespace); int mac_check_vnode_lookup(struct ucred *cred, struct vnode *dvp, struct componentname *cnp); int mac_check_vnode_mmap(struct ucred *cred, struct vnode *vp, int prot, int flags); int mac_check_vnode_mprotect(struct ucred *cred, struct vnode *vp, int prot); int mac_check_vnode_open(struct ucred *cred, struct vnode *vp, int acc_mode); int mac_check_vnode_poll(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp); int mac_check_vnode_read(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp); int mac_check_vnode_readdir(struct ucred *cred, struct vnode *vp); int mac_check_vnode_readlink(struct ucred *cred, struct vnode *vp); int mac_check_vnode_rename_from(struct ucred *cred, struct vnode *dvp, struct vnode *vp, struct componentname *cnp); int mac_check_vnode_rename_to(struct ucred *cred, struct vnode *dvp, struct vnode *vp, int samedir, struct componentname *cnp); int mac_check_vnode_revoke(struct ucred *cred, struct vnode *vp); int mac_check_vnode_setacl(struct ucred *cred, struct vnode *vp, acl_type_t type, struct acl *acl); int mac_check_vnode_setextattr(struct ucred *cred, struct vnode *vp, int attrnamespace, const char *name, struct uio *uio); int mac_check_vnode_setflags(struct ucred *cred, struct vnode *vp, u_long flags); int mac_check_vnode_setmode(struct ucred *cred, struct vnode *vp, mode_t mode); int mac_check_vnode_setowner(struct ucred *cred, struct vnode *vp, uid_t uid, gid_t gid); int mac_check_vnode_setutimes(struct ucred *cred, struct vnode *vp, struct timespec atime, struct timespec mtime); int mac_check_vnode_stat(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp); int mac_check_vnode_write(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp); int mac_getsockopt_label(struct ucred *cred, struct socket *so, struct mac *extmac); int mac_getsockopt_peerlabel(struct ucred *cred, struct socket *so, struct mac *extmac); int mac_ioctl_ifnet_get(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet); int mac_ioctl_ifnet_set(struct ucred *cred, struct ifreq *ifr, struct ifnet *ifnet); int mac_setsockopt_label(struct ucred *cred, struct socket *so, struct mac *extmac); int mac_pipe_label_set(struct ucred *cred, struct pipepair *pp, struct label *label); void mac_cred_mmapped_drop_perms(struct thread *td, struct ucred *cred); void mac_associate_nfsd_label(struct ucred *cred); /* * Calls to help various file systems implement labeling functionality * using their existing EA implementation. */ int vop_stdsetlabel_ea(struct vop_setlabel_args *ap); #endif /* !_KERNEL */ #endif /* !_SYS_MAC_H_ */ Index: stable/6/sys/sys/mac_policy.h =================================================================== --- stable/6/sys/sys/mac_policy.h (revision 162447) +++ stable/6/sys/sys/mac_policy.h (revision 162448) @@ -1,652 +1,653 @@ /*- * Copyright (c) 1999-2002 Robert N. M. Watson * Copyright (c) 2001-2005 Networks Associates Technology, Inc. * Copyright (c) 2005 SPARTA, Inc. * All rights reserved. * * This software was developed by Robert Watson for the TrustedBSD Project. * * This software was developed for the FreeBSD Project in part 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"). * * 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$ */ /* * Kernel interface for MAC policy modules. */ #ifndef _SYS_MAC_POLICY_H_ #define _SYS_MAC_POLICY_H_ /*- * Pluggable access control policy definition structure. * * List of operations that are performed as part of the implementation * of a MAC policy. Policy implementors declare operations with a * mac_policy_ops structure, and using the MAC_POLICY_SET() macro. * If an entry point is not declared, then then the policy will be ignored * during evaluation of that event or check. * * Operations are sorted first by general class of operation, then * alphabetically. */ struct acl; struct bpf_d; struct componentname; struct devfs_dirent; struct ifnet; struct image_params; struct inpcb; struct ipq; struct ksem; struct label; struct mac_policy_conf; struct mbuf; struct mount; struct msqid_kernel; struct pipepair; struct proc; struct sbuf; struct semid_kernel; struct shmid_kernel; struct sockaddr; struct socket; struct sysctl_oid; struct sysctl_req; struct thread; struct ucred; struct uio; struct vattr; struct vnode; struct mac_policy_ops { /* * Policy module operations. */ void (*mpo_destroy)(struct mac_policy_conf *mpc); void (*mpo_init)(struct mac_policy_conf *mpc); /* * General policy-directed security system call so that policies may * implement new services without reserving explicit system call * numbers. */ int (*mpo_syscall)(struct thread *td, int call, void *arg); /* * Label operations. Initialize label storage, destroy label * storage, recycle for re-use without init/destroy, copy a label to * initialized storage, and externalize/internalize from/to * initialized storage. */ void (*mpo_init_bpfdesc_label)(struct label *label); void (*mpo_init_cred_label)(struct label *label); void (*mpo_init_devfsdirent_label)(struct label *label); void (*_mpo_placeholder0)(void); void (*mpo_init_ifnet_label)(struct label *label); int (*mpo_init_inpcb_label)(struct label *label, int flag); void (*mpo_init_sysv_msgmsg_label)(struct label *label); void (*mpo_init_sysv_msgqueue_label)(struct label *label); void (*mpo_init_sysv_sem_label)(struct label *label); void (*mpo_init_sysv_shm_label)(struct label *label); int (*mpo_init_ipq_label)(struct label *label, int flag); int (*mpo_init_mbuf_label)(struct label *label, int flag); void (*mpo_init_mount_label)(struct label *label); void (*mpo_init_mount_fs_label)(struct label *label); int (*mpo_init_socket_label)(struct label *label, int flag); int (*mpo_init_socket_peer_label)(struct label *label, int flag); void (*mpo_init_pipe_label)(struct label *label); void (*mpo_init_posix_sem_label)(struct label *label); void (*mpo_init_proc_label)(struct label *label); void (*mpo_init_vnode_label)(struct label *label); void (*mpo_destroy_bpfdesc_label)(struct label *label); void (*mpo_destroy_cred_label)(struct label *label); void (*mpo_destroy_devfsdirent_label)(struct label *label); void (*_mpo_placeholder1)(void); void (*mpo_destroy_ifnet_label)(struct label *label); void (*mpo_destroy_inpcb_label)(struct label *label); void (*mpo_destroy_sysv_msgmsg_label)(struct label *label); void (*mpo_destroy_sysv_msgqueue_label)(struct label *label); void (*mpo_destroy_sysv_sem_label)(struct label *label); void (*mpo_destroy_sysv_shm_label)(struct label *label); void (*mpo_destroy_ipq_label)(struct label *label); void (*mpo_destroy_mbuf_label)(struct label *label); void (*mpo_destroy_mount_label)(struct label *label); void (*mpo_destroy_mount_fs_label)(struct label *label); void (*mpo_destroy_socket_label)(struct label *label); void (*mpo_destroy_socket_peer_label)(struct label *label); void (*mpo_destroy_pipe_label)(struct label *label); void (*mpo_destroy_posix_sem_label)(struct label *label); void (*mpo_destroy_proc_label)(struct label *label); void (*mpo_destroy_vnode_label)(struct label *label); void (*mpo_cleanup_sysv_msgmsg)(struct label *msglabel); void (*mpo_cleanup_sysv_msgqueue)(struct label *msqlabel); void (*mpo_cleanup_sysv_sem)(struct label *semalabel); void (*mpo_cleanup_sysv_shm)(struct label *shmlabel); void (*mpo_copy_cred_label)(struct label *src, struct label *dest); void (*mpo_copy_ifnet_label)(struct label *src, struct label *dest); void (*mpo_copy_mbuf_label)(struct label *src, struct label *dest); void (*_mpo_placeholder2)(void); void (*mpo_copy_pipe_label)(struct label *src, struct label *dest); void (*mpo_copy_socket_label)(struct label *src, struct label *dest); void (*mpo_copy_vnode_label)(struct label *src, struct label *dest); int (*mpo_externalize_cred_label)(struct label *label, char *element_name, struct sbuf *sb, int *claimed); int (*mpo_externalize_ifnet_label)(struct label *label, char *element_name, struct sbuf *sb, int *claimed); void (*_mpo_placeholder3)(void); int (*mpo_externalize_pipe_label)(struct label *label, char *element_name, struct sbuf *sb, int *claimed); int (*mpo_externalize_socket_label)(struct label *label, char *element_name, struct sbuf *sb, int *claimed); int (*mpo_externalize_socket_peer_label)(struct label *label, char *element_name, struct sbuf *sb, int *claimed); int (*mpo_externalize_vnode_label)(struct label *label, char *element_name, struct sbuf *sb, int *claimed); int (*mpo_internalize_cred_label)(struct label *label, char *element_name, char *element_data, int *claimed); int (*mpo_internalize_ifnet_label)(struct label *label, char *element_name, char *element_data, int *claimed); void (*_mpo_placeholder4)(void); int (*mpo_internalize_pipe_label)(struct label *label, char *element_name, char *element_data, int *claimed); int (*mpo_internalize_socket_label)(struct label *label, char *element_name, char *element_data, int *claimed); int (*mpo_internalize_vnode_label)(struct label *label, char *element_name, char *element_data, int *claimed); /* * Labeling event operations: file system objects, and things that * look a lot like file system objects. */ void (*mpo_associate_vnode_devfs)(struct mount *mp, struct label *fslabel, struct devfs_dirent *de, struct label *delabel, struct vnode *vp, struct label *vlabel); int (*mpo_associate_vnode_extattr)(struct mount *mp, struct label *fslabel, struct vnode *vp, struct label *vlabel); void (*mpo_associate_vnode_singlelabel)(struct mount *mp, struct label *fslabel, struct vnode *vp, struct label *vlabel); void (*mpo_create_devfs_device)(struct ucred *cred, struct mount *mp, struct cdev *dev, struct devfs_dirent *de, struct label *label); void (*mpo_create_devfs_directory)(struct mount *mp, char *dirname, int dirnamelen, struct devfs_dirent *de, struct label *label); void (*mpo_create_devfs_symlink)(struct ucred *cred, struct mount *mp, struct devfs_dirent *dd, struct label *ddlabel, struct devfs_dirent *de, struct label *delabel); void (*_mpo_placeholder5)(void); int (*mpo_create_vnode_extattr)(struct ucred *cred, struct mount *mp, struct label *fslabel, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *vlabel, struct componentname *cnp); void (*mpo_create_mount)(struct ucred *cred, struct mount *mp, struct label *mntlabel, struct label *fslabel); void (*mpo_relabel_vnode)(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, struct label *label); int (*mpo_setlabel_vnode_extattr)(struct ucred *cred, struct vnode *vp, struct label *vlabel, struct label *intlabel); void (*mpo_update_devfsdirent)(struct mount *mp, struct devfs_dirent *devfs_dirent, struct label *direntlabel, struct vnode *vp, struct label *vnodelabel); /* * Labeling event operations: IPC objects. */ void (*mpo_create_mbuf_from_socket)(struct socket *so, struct label *socketlabel, struct mbuf *m, struct label *mbuflabel); void (*mpo_create_socket)(struct ucred *cred, struct socket *so, struct label *socketlabel); void (*mpo_create_socket_from_socket)(struct socket *oldsocket, struct label *oldsocketlabel, struct socket *newsocket, struct label *newsocketlabel); void (*mpo_relabel_socket)(struct ucred *cred, struct socket *so, struct label *oldlabel, struct label *newlabel); void (*mpo_relabel_pipe)(struct ucred *cred, struct pipepair *pp, struct label *oldlabel, struct label *newlabel); void (*mpo_set_socket_peer_from_mbuf)(struct mbuf *mbuf, struct label *mbuflabel, struct socket *so, struct label *socketpeerlabel); void (*mpo_set_socket_peer_from_socket)(struct socket *oldsocket, struct label *oldsocketlabel, struct socket *newsocket, struct label *newsocketpeerlabel); void (*mpo_create_pipe)(struct ucred *cred, struct pipepair *pp, struct label *pipelabel); /* * Labeling event operations: System V IPC primitives. */ void (*mpo_create_sysv_msgmsg)(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqlabel, struct msg *msgptr, struct label *msglabel); void (*mpo_create_sysv_msgqueue)(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqlabel); void (*mpo_create_sysv_sem)(struct ucred *cred, struct semid_kernel *semakptr, struct label *semalabel); void (*mpo_create_sysv_shm)(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmlabel); /* * Labeling event operations: POSIX (global/inter-process) semaphores. */ void (*mpo_create_posix_sem)(struct ucred *cred, struct ksem *ksemptr, struct label *ks_label); /* * Labeling event operations: network objects. */ void (*mpo_create_bpfdesc)(struct ucred *cred, struct bpf_d *bpf_d, struct label *bpflabel); void (*mpo_create_ifnet)(struct ifnet *ifnet, struct label *ifnetlabel); void (*mpo_create_inpcb_from_socket)(struct socket *so, struct label *solabel, struct inpcb *inp, struct label *inplabel); void (*mpo_create_ipq)(struct mbuf *fragment, struct label *fragmentlabel, struct ipq *ipq, struct label *ipqlabel); void (*mpo_create_datagram_from_ipq) (struct ipq *ipq, struct label *ipqlabel, struct mbuf *datagram, struct label *datagramlabel); void (*mpo_create_fragment)(struct mbuf *datagram, struct label *datagramlabel, struct mbuf *fragment, struct label *fragmentlabel); void (*mpo_create_mbuf_from_inpcb)(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel); void (*mpo_create_mbuf_linklayer)(struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *mbuf, struct label *mbuflabel); void (*mpo_create_mbuf_from_bpfdesc)(struct bpf_d *bpf_d, struct label *bpflabel, struct mbuf *mbuf, struct label *mbuflabel); void (*mpo_create_mbuf_from_ifnet)(struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *mbuf, struct label *mbuflabel); void (*mpo_create_mbuf_multicast_encap)(struct mbuf *oldmbuf, struct label *oldmbuflabel, struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *newmbuf, struct label *newmbuflabel); void (*mpo_create_mbuf_netlayer)(struct mbuf *oldmbuf, struct label *oldmbuflabel, struct mbuf *newmbuf, struct label *newmbuflabel); int (*mpo_fragment_match)(struct mbuf *fragment, struct label *fragmentlabel, struct ipq *ipq, struct label *ipqlabel); void (*mpo_reflect_mbuf_icmp)(struct mbuf *m, struct label *mlabel); void (*mpo_reflect_mbuf_tcp)(struct mbuf *m, struct label *mlabel); void (*mpo_relabel_ifnet)(struct ucred *cred, struct ifnet *ifnet, struct label *ifnetlabel, struct label *newlabel); void (*mpo_update_ipq)(struct mbuf *fragment, struct label *fragmentlabel, struct ipq *ipq, struct label *ipqlabel); void (*mpo_inpcb_sosetlabel)(struct socket *so, struct label *label, struct inpcb *inp, struct label *inplabel); /* * Labeling event operations: processes. */ void (*mpo_execve_transition)(struct ucred *old, struct ucred *new, struct vnode *vp, struct label *vnodelabel, struct label *interpvnodelabel, struct image_params *imgp, struct label *execlabel); int (*mpo_execve_will_transition)(struct ucred *old, struct vnode *vp, struct label *vnodelabel, struct label *interpvnodelabel, struct image_params *imgp, struct label *execlabel); void (*mpo_create_proc0)(struct ucred *cred); void (*mpo_create_proc1)(struct ucred *cred); void (*mpo_relabel_cred)(struct ucred *cred, struct label *newlabel); void (*_mpo_placeholder6)(void); void (*mpo_thread_userret)(struct thread *thread); /* * Access control checks. */ int (*mpo_check_bpfdesc_receive)(struct bpf_d *bpf_d, struct label *bpflabel, struct ifnet *ifnet, struct label *ifnetlabel); void (*_mpo_placeholder7)(void); int (*mpo_check_cred_relabel)(struct ucred *cred, struct label *newlabel); int (*mpo_check_cred_visible)(struct ucred *u1, struct ucred *u2); void (*mpo_associate_nfsd_label)(struct ucred *cred); - void (*_mpo_placeholder9)(void); + void (*mpo_create_mbuf_from_firewall)(struct mbuf *m, + struct label *label); void (*_mpo_placeholder10)(void); void (*_mpo_placeholder11)(void); void (*_mpo_placeholder12)(void); void (*_mpo_placeholder13)(void); void (*_mpo_placeholder14)(void); void (*_mpo_placeholder15)(void); void (*_mpo_placeholder16)(void); void (*_mpo_placeholder17)(void); void (*_mpo_placeholder18)(void); int (*mpo_check_ifnet_relabel)(struct ucred *cred, struct ifnet *ifnet, struct label *ifnetlabel, struct label *newlabel); int (*mpo_check_ifnet_transmit)(struct ifnet *ifnet, struct label *ifnetlabel, struct mbuf *m, struct label *mbuflabel); int (*mpo_check_inpcb_deliver)(struct inpcb *inp, struct label *inplabel, struct mbuf *m, struct label *mlabel); int (*mpo_check_sysv_msgmsq)(struct ucred *cred, struct msg *msgptr, struct label *msglabel, struct msqid_kernel *msqkptr, struct label *msqklabel); int (*mpo_check_sysv_msgrcv)(struct ucred *cred, struct msg *msgptr, struct label *msglabel); int (*mpo_check_sysv_msgrmid)(struct ucred *cred, struct msg *msgptr, struct label *msglabel); int (*mpo_check_sysv_msqget)(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel); int (*mpo_check_sysv_msqsnd)(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel); int (*mpo_check_sysv_msqrcv)(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel); int (*mpo_check_sysv_msqctl)(struct ucred *cred, struct msqid_kernel *msqkptr, struct label *msqklabel, int cmd); int (*mpo_check_sysv_semctl)(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel, int cmd); int (*mpo_check_sysv_semget)(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel); int (*mpo_check_sysv_semop)(struct ucred *cred, struct semid_kernel *semakptr, struct label *semaklabel, size_t accesstype); int (*mpo_check_sysv_shmat)(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int shmflg); int (*mpo_check_sysv_shmctl)(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int cmd); int (*mpo_check_sysv_shmdt)(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel); int (*mpo_check_sysv_shmget)(struct ucred *cred, struct shmid_kernel *shmsegptr, struct label *shmseglabel, int shmflg); int (*mpo_check_kenv_dump)(struct ucred *cred); int (*mpo_check_kenv_get)(struct ucred *cred, char *name); int (*mpo_check_kenv_set)(struct ucred *cred, char *name, char *value); int (*mpo_check_kenv_unset)(struct ucred *cred, char *name); int (*mpo_check_kld_load)(struct ucred *cred, struct vnode *vp, struct label *vlabel); int (*mpo_check_kld_stat)(struct ucred *cred); int (*mpo_check_kld_unload)(struct ucred *cred); void (*_mpo_placeholder19)(void); void (*_mpo_placeholder20)(void); int (*mpo_check_mount_stat)(struct ucred *cred, struct mount *mp, struct label *mntlabel); void (*_mpo_placeholder21)(void); int (*mpo_check_pipe_ioctl)(struct ucred *cred, struct pipepair *pp, struct label *pipelabel, unsigned long cmd, void *data); int (*mpo_check_pipe_poll)(struct ucred *cred, struct pipepair *pp, struct label *pipelabel); int (*mpo_check_pipe_read)(struct ucred *cred, struct pipepair *pp, struct label *pipelabel); int (*mpo_check_pipe_relabel)(struct ucred *cred, struct pipepair *pp, struct label *pipelabel, struct label *newlabel); int (*mpo_check_pipe_stat)(struct ucred *cred, struct pipepair *pp, struct label *pipelabel); int (*mpo_check_pipe_write)(struct ucred *cred, struct pipepair *pp, struct label *pipelabel); int (*mpo_check_posix_sem_destroy)(struct ucred *cred, struct ksem *ksemptr, struct label *ks_label); int (*mpo_check_posix_sem_getvalue)(struct ucred *cred, struct ksem *ksemptr, struct label *ks_label); int (*mpo_check_posix_sem_open)(struct ucred *cred, struct ksem *ksemptr, struct label *ks_label); int (*mpo_check_posix_sem_post)(struct ucred *cred, struct ksem *ksemptr, struct label *ks_label); int (*mpo_check_posix_sem_unlink)(struct ucred *cred, struct ksem *ksemptr, struct label *ks_label); int (*mpo_check_posix_sem_wait)(struct ucred *cred, struct ksem *ksemptr, struct label *ks_label); int (*mpo_check_proc_debug)(struct ucred *cred, struct proc *proc); int (*mpo_check_proc_sched)(struct ucred *cred, struct proc *proc); int (*mpo_check_proc_setuid)(struct ucred *cred, uid_t uid); int (*mpo_check_proc_seteuid)(struct ucred *cred, uid_t euid); int (*mpo_check_proc_setgid)(struct ucred *cred, gid_t gid); int (*mpo_check_proc_setegid)(struct ucred *cred, gid_t egid); int (*mpo_check_proc_setgroups)(struct ucred *cred, int ngroups, gid_t *gidset); int (*mpo_check_proc_setreuid)(struct ucred *cred, uid_t ruid, uid_t euid); int (*mpo_check_proc_setregid)(struct ucred *cred, gid_t rgid, gid_t egid); int (*mpo_check_proc_setresuid)(struct ucred *cred, uid_t ruid, uid_t euid, uid_t suid); int (*mpo_check_proc_setresgid)(struct ucred *cred, gid_t rgid, gid_t egid, gid_t sgid); int (*mpo_check_proc_signal)(struct ucred *cred, struct proc *proc, int signum); int (*mpo_check_proc_wait)(struct ucred *cred, struct proc *proc); int (*mpo_check_socket_accept)(struct ucred *cred, struct socket *so, struct label *socketlabel); int (*mpo_check_socket_bind)(struct ucred *cred, struct socket *so, struct label *socketlabel, struct sockaddr *sockaddr); int (*mpo_check_socket_connect)(struct ucred *cred, struct socket *so, struct label *socketlabel, struct sockaddr *sockaddr); int (*mpo_check_socket_create)(struct ucred *cred, int domain, int type, int protocol); int (*mpo_check_socket_deliver)(struct socket *so, struct label *socketlabel, struct mbuf *m, struct label *mbuflabel); void (*_mpo_placeholder22)(void); int (*mpo_check_socket_listen)(struct ucred *cred, struct socket *so, struct label *socketlabel); int (*mpo_check_socket_poll)(struct ucred *cred, struct socket *so, struct label *socketlabel); int (*mpo_check_socket_receive)(struct ucred *cred, struct socket *so, struct label *socketlabel); int (*mpo_check_socket_relabel)(struct ucred *cred, struct socket *so, struct label *socketlabel, struct label *newlabel); int (*mpo_check_socket_send)(struct ucred *cred, struct socket *so, struct label *socketlabel); int (*mpo_check_socket_stat)(struct ucred *cred, struct socket *so, struct label *socketlabel); int (*mpo_check_socket_visible)(struct ucred *cred, struct socket *so, struct label *socketlabel); int (*mpo_check_sysarch_ioperm)(struct ucred *cred); int (*mpo_check_system_acct)(struct ucred *cred, struct vnode *vp, struct label *vlabel); int (*mpo_check_system_nfsd)(struct ucred *cred); int (*mpo_check_system_reboot)(struct ucred *cred, int howto); int (*mpo_check_system_settime)(struct ucred *cred); int (*mpo_check_system_swapon)(struct ucred *cred, struct vnode *vp, struct label *label); int (*mpo_check_system_swapoff)(struct ucred *cred, struct vnode *vp, struct label *label); int (*mpo_check_system_sysctl)(struct ucred *cred, struct sysctl_oid *oidp, void *arg1, int arg2, struct sysctl_req *req); void (*_mpo_placeholder23)(void); int (*mpo_check_vnode_access)(struct ucred *cred, struct vnode *vp, struct label *label, int acc_mode); int (*mpo_check_vnode_chdir)(struct ucred *cred, struct vnode *dvp, struct label *dlabel); int (*mpo_check_vnode_chroot)(struct ucred *cred, struct vnode *dvp, struct label *dlabel); int (*mpo_check_vnode_create)(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct componentname *cnp, struct vattr *vap); int (*mpo_check_vnode_delete)(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, struct componentname *cnp); int (*mpo_check_vnode_deleteacl)(struct ucred *cred, struct vnode *vp, struct label *label, acl_type_t type); int (*mpo_check_vnode_deleteextattr)(struct ucred *cred, struct vnode *vp, struct label *label, int attrnamespace, const char *name); int (*mpo_check_vnode_exec)(struct ucred *cred, struct vnode *vp, struct label *label, struct image_params *imgp, struct label *execlabel); int (*mpo_check_vnode_getacl)(struct ucred *cred, struct vnode *vp, struct label *label, acl_type_t type); int (*mpo_check_vnode_getextattr)(struct ucred *cred, struct vnode *vp, struct label *label, int attrnamespace, const char *name, struct uio *uio); void (*_mpo_placeholder24)(void); int (*mpo_check_vnode_link)(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, struct componentname *cnp); int (*mpo_check_vnode_listextattr)(struct ucred *cred, struct vnode *vp, struct label *label, int attrnamespace); int (*mpo_check_vnode_lookup)(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct componentname *cnp); int (*mpo_check_vnode_mmap)(struct ucred *cred, struct vnode *vp, struct label *label, int prot, int flags); void (*mpo_check_vnode_mmap_downgrade)(struct ucred *cred, struct vnode *vp, struct label *label, int *prot); int (*mpo_check_vnode_mprotect)(struct ucred *cred, struct vnode *vp, struct label *label, int prot); int (*mpo_check_vnode_open)(struct ucred *cred, struct vnode *vp, struct label *label, int acc_mode); int (*mpo_check_vnode_poll)(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *label); int (*mpo_check_vnode_read)(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *label); int (*mpo_check_vnode_readdir)(struct ucred *cred, struct vnode *dvp, struct label *dlabel); int (*mpo_check_vnode_readlink)(struct ucred *cred, struct vnode *vp, struct label *label); int (*mpo_check_vnode_relabel)(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, struct label *newlabel); int (*mpo_check_vnode_rename_from)(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, struct componentname *cnp); int (*mpo_check_vnode_rename_to)(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, int samedir, struct componentname *cnp); int (*mpo_check_vnode_revoke)(struct ucred *cred, struct vnode *vp, struct label *label); int (*mpo_check_vnode_setacl)(struct ucred *cred, struct vnode *vp, struct label *label, acl_type_t type, struct acl *acl); int (*mpo_check_vnode_setextattr)(struct ucred *cred, struct vnode *vp, struct label *label, int attrnamespace, const char *name, struct uio *uio); int (*mpo_check_vnode_setflags)(struct ucred *cred, struct vnode *vp, struct label *label, u_long flags); int (*mpo_check_vnode_setmode)(struct ucred *cred, struct vnode *vp, struct label *label, mode_t mode); int (*mpo_check_vnode_setowner)(struct ucred *cred, struct vnode *vp, struct label *label, uid_t uid, gid_t gid); int (*mpo_check_vnode_setutimes)(struct ucred *cred, struct vnode *vp, struct label *label, struct timespec atime, struct timespec mtime); int (*mpo_check_vnode_stat)(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *label); int (*mpo_check_vnode_write)(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *label); }; /* * struct mac_policy_conf is the registration structure for policies, and is * provided to the MAC Framework using MAC_POLICY_SET() to invoke a SYSINIT * to register the policy. In general, the fields are immutable, with the * exception of the "security field", run-time flags, and policy list entry, * which are managed by the MAC Framework. Be careful when modifying this * structure, as its layout is statically compiled into all policies. */ struct mac_policy_conf { char *mpc_name; /* policy name */ char *mpc_fullname; /* policy full name */ struct mac_policy_ops *mpc_ops; /* policy operations */ int mpc_loadtime_flags; /* flags */ int *mpc_field_off; /* security field */ int mpc_runtime_flags; /* flags */ LIST_ENTRY(mac_policy_conf) mpc_list; /* global list */ }; /* Flags for the mpc_loadtime_flags field. */ #define MPC_LOADTIME_FLAG_NOTLATE 0x00000001 #define MPC_LOADTIME_FLAG_UNLOADOK 0x00000002 #define MPC_LOADTIME_FLAG_LABELMBUFS 0x00000004 /* Flags for the mpc_runtime_flags field. */ #define MPC_RUNTIME_FLAG_REGISTERED 0x00000001 #define MAC_POLICY_SET(mpops, mpname, mpfullname, mpflags, privdata_wanted) \ static struct mac_policy_conf mpname##_mac_policy_conf = { \ #mpname, \ mpfullname, \ mpops, \ mpflags, \ privdata_wanted, \ 0, \ }; \ static moduledata_t mpname##_mod = { \ #mpname, \ mac_policy_modevent, \ &mpname##_mac_policy_conf \ }; \ MODULE_DEPEND(mpname, kernel_mac_support, 2, 2, 2); \ DECLARE_MODULE(mpname, mpname##_mod, SI_SUB_MAC_POLICY, \ SI_ORDER_MIDDLE) int mac_policy_modevent(module_t mod, int type, void *data); #define LABEL_TO_SLOT(l, s) (l)->l_perpolicy[s] #endif /* !_SYS_MAC_POLICY_H_ */