summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorMike Frantzen <frantzen@cvs.openbsd.org>2003-05-14 23:46:46 +0000
committerMike Frantzen <frantzen@cvs.openbsd.org>2003-05-14 23:46:46 +0000
commitc1e46b36af9a1c7488130a5e81b91e46bd23cb42 (patch)
treec40cd4bb245ccdda7a133c1ecd4082264861a883 /sys
parent1e5afed5e193388914826cd4c6af8560153e5355 (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')
-rw-r--r--sys/net/pf.c41
-rw-r--r--sys/net/pf_norm.c124
-rw-r--r--sys/net/pfvar.h45
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,
+ &copyback))
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);