diff options
author | Mike Frantzen <frantzen@cvs.openbsd.org> | 2003-05-14 23:46:46 +0000 |
---|---|---|
committer | Mike Frantzen <frantzen@cvs.openbsd.org> | 2003-05-14 23:46:46 +0000 |
commit | c1e46b36af9a1c7488130a5e81b91e46bd23cb42 (patch) | |
tree | c40cd4bb245ccdda7a133c1ecd4082264861a883 /sys/net | |
parent | 1e5afed5e193388914826cd4c6af8560153e5355 (diff) |
- modulate TCP Timestamps so they can't be used to detect NAT and to preclude
remote uptime determination
- scrub modifier "reassemble tcp" turns on stateful TCP normalizations
ok henning@ dhartmei@
Diffstat (limited to 'sys/net')
-rw-r--r-- | sys/net/pf.c | 41 | ||||
-rw-r--r-- | sys/net/pf_norm.c | 124 | ||||
-rw-r--r-- | sys/net/pfvar.h | 45 |
3 files changed, 169 insertions, 41 deletions
diff --git a/sys/net/pf.c b/sys/net/pf.c index 634bfcf82c9..0658317dffa 100644 --- a/sys/net/pf.c +++ b/sys/net/pf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pf.c,v 1.352 2003/05/14 21:50:56 henning Exp $ */ +/* $OpenBSD: pf.c,v 1.353 2003/05/14 23:46:45 frantzen Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -128,8 +128,6 @@ u_int16_t pf_cksum_fixup(u_int16_t, u_int16_t, u_int16_t, 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); -void pf_change_a(u_int32_t *, u_int16_t *, u_int32_t, - u_int8_t); #ifdef INET6 void pf_change_a6(struct pf_addr *, u_int16_t *, struct pf_addr *, u_int8_t); @@ -181,8 +179,6 @@ int pf_test_state_icmp(struct pf_state **, int, void *, struct pf_pdesc *); int pf_test_state_other(struct pf_state **, int, struct ifnet *, struct pf_pdesc *); -void *pf_pull_hdr(struct mbuf *, int, void *, int, - u_short *, u_short *, sa_family_t); void pf_calc_skip_steps(struct pf_rulequeue *); void pf_rule_set_qid(struct pf_rulequeue *); u_int32_t pf_qname_to_qid(char *); @@ -947,12 +943,15 @@ pf_change_ap(struct pf_addr *a, u_int16_t *p, u_int16_t *ic, u_int16_t *pc, } } + +/* Changes a u_int32_t. Uses a void * so there are no align restrictions */ void -pf_change_a(u_int32_t *a, u_int16_t *c, u_int32_t an, u_int8_t u) +pf_change_a(void *a, u_int16_t *c, u_int32_t an, u_int8_t u) { - u_int32_t ao = *a; + u_int32_t ao; - *a = an; + 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); } @@ -2137,7 +2136,8 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, return (PF_DROP); } - if (r->keep_state || nat != NULL || rdr != NULL) { + if (r->keep_state || nat != NULL || rdr != NULL || + (pd->flags & PFDESC_TCP_NORM)) { /* create new state */ u_int16_t len; struct pf_state *s = NULL; @@ -2232,11 +2232,18 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, s->bytes = pd->tot_len; if ((pd->flags & PFDESC_TCP_NORM) && pf_normalize_tcp_init(m, - pd, th, &s->src, &s->dst)) { + off, pd, th, &s->src, &s->dst)) { REASON_SET(&reason, PFRES_MEMORY); 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->src, + &s->dst, &rewrite)) { + pf_normalize_tcp_cleanup(s); + pool_put(&pf_state_pl, s); + return (PF_DROP); + } if (pf_insert_state(s)) { pf_normalize_tcp_cleanup(s); REASON_SET(&reason, PFRES_MEMORY); @@ -3092,6 +3099,7 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct ifnet *ifp, u_int32_t ack, end, seq; u_int8_t sws, dws; int ackskew; + int copyback = 0; struct pf_state_peer *src, *dst; key.af = pd->af; @@ -3129,7 +3137,7 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct ifnet *ifp, if ((pd->flags & PFDESC_TCP_NORM || dst->scrub) && src->scrub == NULL) { - if (pf_normalize_tcp_init(m, pd, th, src, dst)) { + if (pf_normalize_tcp_init(m, off, pd, th, src, dst)) { REASON_SET(reason, PFRES_MEMORY); return (PF_DROP); } @@ -3143,6 +3151,7 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct ifnet *ifp, 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); } @@ -3194,6 +3203,7 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct ifnet *ifp, 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) @@ -3376,8 +3386,9 @@ 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)) + if (dst->scrub || src->scrub) { + if (pf_normalize_tcp_stateful(m, off, pd, reason, th, src, dst, + ©back)) return (PF_DROP); } @@ -3394,8 +3405,8 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct ifnet *ifp, &th->th_sum, &(*state)->lan.addr, (*state)->lan.port, 0, pd->af); m_copyback(m, off, sizeof(*th), (caddr_t)th); - } else if (src->seqdiff) { - /* Copyback sequence modulation */ + } else if (copyback) { + /* Copyback sequence modulation or stateful scrub changes */ m_copyback(m, off, sizeof(*th), (caddr_t)th); } diff --git a/sys/net/pf_norm.c b/sys/net/pf_norm.c index b0d2dd79efd..58e333f24e9 100644 --- a/sys/net/pf_norm.c +++ b/sys/net/pf_norm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pf_norm.c,v 1.58 2003/05/14 08:42:00 canacar Exp $ */ +/* $OpenBSD: pf_norm.c,v 1.59 2003/05/14 23:46:45 frantzen Exp $ */ /* * Copyright 2001 Niels Provos <provos@citi.umich.edu> @@ -37,6 +37,7 @@ #include <sys/time.h> #include <sys/pool.h> +#include <dev/rndvar.h> #include <net/if.h> #include <net/if_types.h> #include <net/bpf.h> @@ -1038,6 +1039,9 @@ pf_normalize_tcp(int dir, struct ifnet *ifp, struct mbuf *m, int ipoff, else r->packets++; + if (rm->rule_flag & PFRULE_REASSEMBLE_TCP) + pd->flags |= PFDESC_TCP_NORM; + flags = th->th_flags; if (flags & TH_SYN) { /* Illegal packet */ @@ -1090,9 +1094,6 @@ 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: @@ -1103,14 +1104,18 @@ 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, +pf_normalize_tcp_init(struct mbuf *m, int off, struct pf_pdesc *pd, struct tcphdr *th, struct pf_state_peer *src, struct pf_state_peer *dst) { + u_int8_t hdr[60]; + u_int8_t *opt; + KASSERT(src->scrub == NULL); src->scrub = pool_get(&pf_state_scrub_pl, PR_NOWAIT); if (src->scrub == NULL) return (1); + bzero(src->scrub, sizeof(*src->scrub)); switch (pd->af) { #ifdef INET @@ -1128,6 +1133,44 @@ pf_normalize_tcp_init(struct mbuf *m, struct pf_pdesc *pd, } #endif /* INET6 */ } + + + /* + * All normalizations below are only begun if we see the start of + * the connections. They must all set an enabled bit in pfss_flags + */ + if ((th->th_flags & TH_SYN) == 0) + return 0; + + + if (th->th_off > (sizeof(struct tcphdr) >> 2) && src->scrub && + pf_pull_hdr(m, off, hdr, th->th_off << 2, NULL, NULL, pd->af)) { + /* Diddle with TCP options */ + int hlen; + opt = hdr + sizeof(struct tcphdr); + hlen = (th->th_off << 2) - sizeof(struct tcphdr); + while (hlen >= TCPOLEN_TIMESTAMP) { + switch (*opt) { + case TCPOPT_EOL: /* FALLTHROUH */ + case TCPOPT_NOP: + opt++; + hlen--; + break; + case TCPOPT_TIMESTAMP: + if (opt[1] >= TCPOLEN_TIMESTAMP) { + src->scrub->pfss_flags |= + PFSS_TIMESTAMP; + src->scrub->pfss_ts_mod = arc4random(); + } + /* FALLTHROUGH */ + default: + hlen -= opt[1]; + opt += opt[1]; + break; + } + } + } + return (0); } @@ -1143,11 +1186,15 @@ pf_normalize_tcp_cleanup(struct pf_state *state) } 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) +pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, + u_short *reason, struct tcphdr *th, struct pf_state_peer *src, + struct pf_state_peer *dst, int *writeback) { - KASSERT(src->scrub || dst->scrub); + u_int8_t hdr[60]; + u_int8_t *opt; + int copyback = 0; + KASSERT(src->scrub || dst->scrub); /* * Enforce the minimum TTL seen for this connection. Negate a common @@ -1179,6 +1226,67 @@ pf_normalize_tcp_stateful(struct mbuf *m, struct pf_pdesc *pd, u_short *reason, #endif /* INET6 */ } + if (th->th_off > (sizeof(struct tcphdr) >> 2) && + ((src->scrub && (src->scrub->pfss_flags & PFSS_TIMESTAMP)) || + (dst->scrub && (dst->scrub->pfss_flags & PFSS_TIMESTAMP))) && + pf_pull_hdr(m, off, hdr, th->th_off << 2, NULL, NULL, pd->af)) { + /* Diddle with TCP options */ + int hlen; + opt = hdr + sizeof(struct tcphdr); + hlen = (th->th_off << 2) - sizeof(struct tcphdr); + while (hlen >= TCPOLEN_TIMESTAMP) { + switch (*opt) { + case TCPOPT_EOL: /* FALLTHROUH */ + case TCPOPT_NOP: + opt++; + hlen--; + break; + case TCPOPT_TIMESTAMP: + /* Modulate the timestamps. Can be used for + * NAT detection, OS uptime determination or + * reboot detection. + */ + if (opt[1] >= TCPOLEN_TIMESTAMP) { + u_int32_t ts_value; + if (src->scrub && + (src->scrub->pfss_flags & + PFSS_TIMESTAMP)) { + memcpy(&ts_value, &opt[2], + sizeof(u_int32_t)); + ts_value = htonl(ntohl(ts_value) + + src->scrub->pfss_ts_mod); + pf_change_a(&opt[2], + &th->th_sum, ts_value, 0); + copyback = 1; + } + if (dst->scrub && + (dst->scrub->pfss_flags & + PFSS_TIMESTAMP)) { + memcpy(&ts_value, &opt[6], + sizeof(u_int32_t)); + ts_value = htonl(ntohl(ts_value) + - dst->scrub->pfss_ts_mod); + pf_change_a(&opt[6], + &th->th_sum, ts_value, 0); + copyback = 1; + } + } + /* FALLTHROUGH */ + default: + hlen -= opt[1]; + opt += opt[1]; + break; + } + } + if (copyback) { + /* Copyback the options, caller copys back header */ + *writeback = 1; + m_copyback(m, off + sizeof(struct tcphdr), + (th->th_off << 2) - sizeof(struct tcphdr), hdr + + sizeof(struct tcphdr)); + } + } + /* I have a dream.... TCP segment reassembly.... */ return (0); diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index 5c66a953274..5d81302ee51 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pfvar.h,v 1.148 2003/05/14 08:42:00 canacar Exp $ */ +/* $OpenBSD: pfvar.h,v 1.149 2003/05/14 23:46:45 frantzen Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -375,6 +375,7 @@ struct pf_rule { struct pf_rule_uid uid; struct pf_rule_gid gid; + u_int32_t rule_flag; u_int8_t action; u_int8_t direction; u_int8_t log; @@ -388,10 +389,8 @@ struct pf_rule { u_int8_t proto; u_int8_t type; u_int8_t code; - u_int8_t flags; u_int8_t flagset; - u_int8_t rule_flag; u_int8_t min_ttl; u_int8_t allow_opts; u_int8_t rt; @@ -399,23 +398,29 @@ struct pf_rule { u_int8_t tos; }; -#define PFRULE_DROP 0x00 -#define PFRULE_RETURNRST 0x01 -#define PFRULE_NODF 0x02 -#define PFRULE_FRAGMENT 0x04 -#define PFRULE_RETURNICMP 0x08 -#define PFRULE_FRAGCROP 0x10 /* non-buffering frag cache */ -#define PFRULE_FRAGDROP 0x20 /* drop funny fragments */ -#define PFRULE_RETURN 0x40 -#define PFRULE_RANDOMID 0x80 +/* rule flags */ +#define PFRULE_DROP 0x0000 +#define PFRULE_RETURNRST 0x0001 +#define PFRULE_FRAGMENT 0x0002 +#define PFRULE_RETURNICMP 0x0004 +#define PFRULE_RETURN 0x0008 + +/* scrub flags */ +#define PFRULE_NODF 0x0100 +#define PFRULE_FRAGCROP 0x0200 /* non-buffering frag cache */ +#define PFRULE_FRAGDROP 0x0400 /* drop funny fragments */ +#define PFRULE_RANDOMID 0x0800 +#define PFRULE_REASSEMBLE_TCP 0x1000 #define PFSTATE_HIWAT 10000 /* default state table size */ struct pf_state_scrub { - u_int8_t pfss_ttl; /* stashed TTL */ + u_int16_t pfss_flags; +#define PFSS_TIMESTAMP 0x0001 /* modulate timestamp */ + u_int8_t pfss_ttl; /* stashed TTL */ u_int8_t pad; - u_int16_t pad2; + u_int32_t pfss_ts_mod; /* timestamp modulation */ }; struct pf_state_host { @@ -612,7 +617,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 */ +#define PFDESC_TCP_NORM 0x0001 /* TCP shall be statefully scrubbed */ sa_family_t af; u_int8_t proto; u_int8_t tos; @@ -1034,6 +1039,9 @@ int pf_test(int, struct ifnet *, struct mbuf **); int pf_test6(int, struct ifnet *, struct mbuf **); #endif /* INET */ +void *pf_pull_hdr(struct mbuf *, int, void *, int, u_short *, u_short *, + sa_family_t); +void pf_change_a(void *, u_int16_t *, u_int32_t, u_int8_t); int pflog_packet(struct ifnet *, struct mbuf *, sa_family_t, u_int8_t, u_int8_t, struct pf_rule *, struct pf_rule *, struct pf_ruleset *); int pf_match_addr(u_int8_t, struct pf_addr *, struct pf_addr *, @@ -1048,10 +1056,11 @@ 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 *, +int pf_normalize_tcp_init(struct mbuf *, int, struct pf_pdesc *, struct tcphdr *, struct pf_state_peer *, struct pf_state_peer *); +int pf_normalize_tcp_stateful(struct mbuf *, int, struct pf_pdesc *, + u_short *, struct tcphdr *, struct pf_state_peer *, + struct pf_state_peer *, int *); u_int32_t pf_state_expires(const struct pf_state *); void pf_purge_expired_fragments(void); |