diff options
-rw-r--r-- | sys/net/pf.c | 37 | ||||
-rw-r--r-- | sys/net/pf_norm.c | 93 | ||||
-rw-r--r-- | sys/net/pfvar.h | 25 |
3 files changed, 143 insertions, 12 deletions
diff --git a/sys/net/pf.c b/sys/net/pf.c index f23be58515b..6f2fcf66365 100644 --- a/sys/net/pf.c +++ b/sys/net/pf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pf.c,v 1.344 2003/05/11 01:17:15 dhartmei Exp $ */ +/* $OpenBSD: pf.c,v 1.345 2003/05/11 20:44:03 frantzen Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -166,7 +166,7 @@ int pf_test_fragment(struct pf_rule **, int, struct pf_pdesc *, struct pf_rule **); int pf_test_state_tcp(struct pf_state **, int, struct ifnet *, struct mbuf *, int, int, - void *, struct pf_pdesc *); + void *, struct pf_pdesc *, u_short *); int pf_test_state_udp(struct pf_state **, int, struct ifnet *, struct mbuf *, int, int, void *, struct pf_pdesc *); @@ -195,8 +195,6 @@ int pf_map_addr(u_int8_t, struct pf_pool *, int pf_get_sport(sa_family_t, u_int8_t, struct pf_pool *, struct pf_addr *, struct pf_addr *, u_int16_t, struct pf_addr *, u_int16_t*, u_int16_t, u_int16_t); -int pf_normalize_tcp(int, struct ifnet *, struct mbuf *, - int, int, void *, struct pf_pdesc *); void pf_route(struct mbuf **, struct pf_rule *, int, struct ifnet *, struct pf_state *); void pf_route6(struct mbuf **, struct pf_rule *, int, @@ -480,6 +478,7 @@ pf_purge_expired_states(void) if (--cur->state->anchor.ptr->states <= 0) pf_rm_rule(NULL, cur->state->anchor.ptr); + pf_normalize_tcp_cleanup(cur->state); pool_put(&pf_state_pl, cur->state); pool_put(&pf_tree_pl, cur); pool_put(&pf_tree_pl, peer); @@ -2126,7 +2125,15 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, s->expire = s->creation + TIMEOUT(r, PFTM_TCP_FIRST_PACKET); s->packets = 1; s->bytes = pd->tot_len; + + if ((pd->flags & PFDESC_TCP_NORM) && pf_normalize_tcp_init(m, + pd, th, &s->src, &s->dst)) { + REASON_SET(&reason, PFRES_MEMORY); + pool_put(&pf_state_pl, s); + return (PF_DROP); + } if (pf_insert_state(s)) { + pf_normalize_tcp_cleanup(s); REASON_SET(&reason, PFRES_MEMORY); pool_put(&pf_state_pl, s); return (PF_DROP); @@ -2878,7 +2885,8 @@ pf_test_fragment(struct pf_rule **rm, int direction, struct ifnet *ifp, int pf_test_state_tcp(struct pf_state **state, int direction, struct ifnet *ifp, - struct mbuf *m, int ipoff, int off, void *h, struct pf_pdesc *pd) + struct mbuf *m, int ipoff, int off, void *h, struct pf_pdesc *pd, + u_short *reason) { struct pf_tree_node key; struct tcphdr *th = pd->hdr.tcp; @@ -2921,6 +2929,14 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct ifnet *ifp, 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, pd, th, src, dst)) { + REASON_SET(reason, PFRES_MEMORY); + return (PF_DROP); + } + } + /* Deferred generation of sequence number modulator */ if (dst->seqdiff) { while ((src->seqdiff = arc4random()) == 0) @@ -3149,6 +3165,11 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct ifnet *ifp, return (PF_DROP); } + if ((pd->flags & PFDESC_TCP_NORM) && (dst->scrub || src->scrub)) { + if (pf_normalize_tcp_stateful(m, pd, reason, th, src, dst)) + return (PF_DROP); + } + /* Any packets which have gotten here are to be passed */ /* translate source/destination address, if needed */ @@ -4391,7 +4412,8 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0) action = pf_normalize_tcp(dir, ifp, m, 0, off, h, &pd); if (action == PF_DROP) break; - action = pf_test_state_tcp(&s, dir, ifp, m, 0, off, h, &pd); + action = pf_test_state_tcp(&s, dir, ifp, m, 0, off, h, &pd, + &reason); if (action == PF_PASS) { r = s->rule.ptr; log = s->log; @@ -4622,7 +4644,8 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0) action = pf_normalize_tcp(dir, ifp, m, 0, off, h, &pd); if (action == PF_DROP) break; - action = pf_test_state_tcp(&s, dir, ifp, m, 0, off, h, &pd); + action = pf_test_state_tcp(&s, dir, ifp, m, 0, off, h, &pd, + &reason); if (action == PF_PASS) { r = s->rule.ptr; log = s->log; diff --git a/sys/net/pf_norm.c b/sys/net/pf_norm.c index e4dc2689e5d..a75942c42d8 100644 --- a/sys/net/pf_norm.c +++ b/sys/net/pf_norm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pf_norm.c,v 1.56 2003/04/05 20:20:58 cedric Exp $ */ +/* $OpenBSD: pf_norm.c,v 1.57 2003/05/11 20:44:03 frantzen Exp $ */ /* * Copyright 2001 Niels Provos <provos@citi.umich.edu> @@ -53,6 +53,10 @@ #include <netinet/udp.h> #include <netinet/ip_icmp.h> +#ifdef INET6 +#include <netinet/ip6.h> +#endif /* INET6 */ + #include <net/pfvar.h> struct pf_frent { @@ -120,6 +124,7 @@ int pf_normalize_tcpopt(struct pf_rule *, struct mbuf *, /* Globals */ struct pool pf_frent_pl, pf_frag_pl, pf_cache_pl, pf_cent_pl; +struct pool pf_state_scrub_pl; int pf_nfrents, pf_ncache; void @@ -133,6 +138,8 @@ pf_normalize_init(void) "pffrcache", NULL); pool_init(&pf_cent_pl, sizeof(struct pf_frcache), 0, 0, 0, "pffrcent", NULL); + pool_init(&pf_state_scrub_pl, sizeof(struct pf_state_scrub), 0, 0, 0, + "pfstscr", NULL); pool_sethiwat(&pf_frag_pl, PFFRAG_FRAG_HIWAT); pool_sethardlimit(&pf_frent_pl, PFFRAG_FRENT_HIWAT, NULL, 0); @@ -1083,6 +1090,9 @@ pf_normalize_tcp(int dir, struct ifnet *ifp, struct mbuf *m, int ipoff, if (rewrite) m_copyback(m, off, sizeof(*th), (caddr_t)th); + /* Inform the state code to do stateful normalizations */ + pd->flags |= PFDESC_TCP_NORM; + return (PF_PASS); tcp_drop: @@ -1093,6 +1103,87 @@ pf_normalize_tcp(int dir, struct ifnet *ifp, struct mbuf *m, int ipoff, } int +pf_normalize_tcp_init(struct mbuf *m, struct pf_pdesc *pd, + struct tcphdr *th, struct pf_state_peer *src, struct pf_state_peer *dst) +{ + KASSERT(src->scrub == NULL); + + src->scrub = pool_get(&pf_state_scrub_pl, PR_NOWAIT); + if (src->scrub == NULL) + return (1); + + switch (pd->af) { +#ifdef INET + case AF_INET: { + struct ip *h = mtod(m, struct ip *); + src->scrub->pfss_ttl = h->ip_ttl; + break; + } +#endif /* INET */ +#ifdef INET6 + case AF_INET6: { + struct ip6_hdr *h = mtod(m, struct ip6_hdr *); + src->scrub->pfss_ttl = h->ip6_hlim; + break; + } +#endif /* INET6 */ + } + return (0); +} + +void +pf_normalize_tcp_cleanup(struct pf_state *state) +{ + if (state->src.scrub) + pool_put(&pf_state_scrub_pl, state->src.scrub); + if (state->dst.scrub) + pool_put(&pf_state_scrub_pl, state->dst.scrub); + + /* Someday... flush the TCP segment reassembly descriptors. */ +} + +int +pf_normalize_tcp_stateful(struct mbuf *m, struct pf_pdesc *pd, u_short *reason, + struct tcphdr *th, struct pf_state_peer *src, struct pf_state_peer *dst) +{ + KASSERT(src->scrub || dst->scrub); + + + /* + * Enforce the minimum TTL seen for this connection. Negate a common + * technique to evade an intrusion detection system and confuse + * firewall state code. + */ + switch (pd->af) { +#ifdef INET + case AF_INET: { + if (src->scrub) { + struct ip *h = mtod(m, struct ip *); + if (h->ip_ttl > src->scrub->pfss_ttl) + src->scrub->pfss_ttl = h->ip_ttl; + h->ip_ttl = src->scrub->pfss_ttl; + } + break; + } +#endif /* INET */ +#ifdef INET6 + case AF_INET6: { + if (dst->scrub) { + struct ip6_hdr *h = mtod(m, struct ip6_hdr *); + if (h->ip6_hlim > src->scrub->pfss_ttl) + src->scrub->pfss_ttl = h->ip6_hlim; + h->ip6_hlim = src->scrub->pfss_ttl; + } + break; + } +#endif /* INET6 */ + } + + + /* I have a dream.... TCP segment reassembly.... */ + return (0); +} +int pf_normalize_tcpopt(struct pf_rule *r, struct mbuf *m, struct tcphdr *th, int off) { diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index 8bbb3ca529f..17adab061bd 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pfvar.h,v 1.142 2003/04/30 12:30:27 cedric Exp $ */ +/* $OpenBSD: pfvar.h,v 1.143 2003/05/11 20:44:03 frantzen Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -399,6 +399,13 @@ struct pf_rule { #define PFSTATE_HIWAT 10000 /* default state table size */ + +struct pf_state_scrub { + u_int8_t pfss_ttl; /* stashed TTL */ + u_int8_t pad; + u_int16_t pad2; +}; + struct pf_state_host { struct pf_addr addr; u_int16_t port; @@ -409,9 +416,10 @@ struct pf_state_peer { u_int32_t seqlo; /* Max sequence number sent */ u_int32_t seqhi; /* Max the other end ACKd + win */ u_int32_t seqdiff; /* Sequence number modulator */ - u_int16_t max_win; - u_int8_t state; - u_int8_t wscale; + u_int16_t max_win; /* largest window (pre scaling) */ + u_int8_t state; /* active state level */ + u_int8_t wscale; /* window scaling factor */ + struct pf_state_scrub *scrub; /* state is scrubbed */ }; struct pf_state { @@ -591,6 +599,7 @@ struct pf_pdesc { u_int32_t p_len; /* total length of payload */ u_int16_t flags; /* Let SCRUB trigger behavior in * state code. Easier than tags */ +#define PFDESC_TCP_NORM 0x0001 /* TCP was normalized */ sa_family_t af; u_int8_t proto; u_int8_t tos; @@ -977,6 +986,7 @@ extern u_int32_t pf_qname_to_qid(char *); extern void pf_update_anchor_rules(void); extern struct pool pf_tree_pl, pf_rule_pl, pf_addr_pl; extern struct pool pf_state_pl, pf_altq_pl, pf_pooladdr_pl; +extern struct pool pf_state_scrub_pl; extern void pf_purge_timeout(void *); extern void pf_purge_expired_states(void); extern int pf_insert_state(struct pf_state *); @@ -1009,6 +1019,13 @@ int pf_match_gid(u_int8_t, gid_t, gid_t, gid_t); void pf_normalize_init(void); int pf_normalize_ip(struct mbuf **, int, struct ifnet *, u_short *); +int pf_normalize_tcp(int, struct ifnet *, struct mbuf *, int, int, void *, + struct pf_pdesc *); +void pf_normalize_tcp_cleanup(struct pf_state *); +int pf_normalize_tcp_init(struct mbuf *, struct pf_pdesc *, struct tcphdr *, + struct pf_state_peer *, struct pf_state_peer *); +int pf_normalize_tcp_stateful(struct mbuf *, struct pf_pdesc *, u_short *, + struct tcphdr *, struct pf_state_peer *, struct pf_state_peer *); void pf_purge_expired_fragments(void); int pf_routable(struct pf_addr *addr, sa_family_t af); void pfr_initialize(void); |