summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Frantzen <frantzen@cvs.openbsd.org>2003-05-11 20:44:04 +0000
committerMike Frantzen <frantzen@cvs.openbsd.org>2003-05-11 20:44:04 +0000
commit916b8d52a41cbdb80f79ab7dd5b7f013043d9bec (patch)
treea42eef3c39452caef14b40ed6ce859c41c9fea7a
parentf9a1f0ece404c573625c5d9d8e5928b98b594832 (diff)
the start of stateful TCP scrubbing. dynamically determine the highest TTL of
each side of the TCP connection and prevent it from being reduced ok pb@ dhartmei@
-rw-r--r--sys/net/pf.c37
-rw-r--r--sys/net/pf_norm.c93
-rw-r--r--sys/net/pfvar.h25
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);