summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorDaniel Hartmeier <dhartmei@cvs.openbsd.org>2003-01-31 19:22:12 +0000
committerDaniel Hartmeier <dhartmei@cvs.openbsd.org>2003-01-31 19:22:12 +0000
commit645a2c4ed8596a17f7f2f7410dd0e42bc2da3bbb (patch)
tree0548517555864e8faf94cad82325ef33fbbd07a0 /sys
parentf4ce3b660858014b79f1369d61232ac3d8d1bab1 (diff)
Check protocol (TCP/UDP/ICMP/ICMP6) checksums of all incoming packets,
and drop packets with invalid checksums. Without such a check, pf would return RST/ICMP errors even for packets with invalid checksums, which could be used to detect the presence of the firewall, reported by "Ed White" in http://www.phrack.org/phrack/60/p60-0x0c.txt. To minimize the cost of checksum calculations, mbuf flags set by network interfaces capable of hardware checksumming are honoured, and set when pf performs the calculation, so the TCP/IP stack itself will not repeat the calculation for the same packet later on. ok mcbride@ and henning@
Diffstat (limited to 'sys')
-rw-r--r--sys/net/pf.c118
1 files changed, 117 insertions, 1 deletions
diff --git a/sys/net/pf.c b/sys/net/pf.c
index bb7c571a8bc..a128a9389f2 100644
--- a/sys/net/pf.c
+++ b/sys/net/pf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pf.c,v 1.312 2003/01/31 19:09:12 dhartmei Exp $ */
+/* $OpenBSD: pf.c,v 1.313 2003/01/31 19:22:11 dhartmei Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -67,6 +67,7 @@
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/udp_var.h>
+#include <netinet/icmp_var.h>
#include <dev/rndvar.h>
#include <net/pfvar.h>
@@ -234,6 +235,8 @@ int pf_socket_lookup(uid_t *, gid_t *, int, sa_family_t,
int, struct pf_pdesc *);
u_int8_t pf_get_wscale(struct mbuf *, int, u_int16_t,
sa_family_t);
+int pf_check_proto_cksum(struct mbuf *, int, int,
+ u_int8_t, sa_family_t);
struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX] =
{ { &pf_state_pl, PFSTATE_HIWAT }, { &pf_frent_pl, PFFRAG_FRENT_HIWAT } };
@@ -4091,6 +4094,89 @@ bad:
}
#endif /* INET6 */
+
+/*
+ * 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:
+ case IPPROTO_ICMPV6:
+ 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) {
+ 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;
+ case AF_INET6:
+ if (m->m_len < sizeof(struct ip6_hdr))
+ return (1);
+ sum = in6_cksum(m, p, off, len);
+ break;
+ 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;
+ case IPPROTO_ICMPV6:
+ icmp6stat.icp6s_checksum++;
+ break;
+ }
+ return (1);
+ }
+ m->m_pkthdr.csum |= flag_ok;
+ return (0);
+}
+
#ifdef INET
int
pf_test(int dir, struct ifnet *ifp, struct mbuf **m0)
@@ -4160,6 +4246,11 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0)
log = action != PF_PASS;
goto done;
}
+ if (dir == PF_IN && pf_check_proto_cksum(m, off,
+ h->ip_len - off, IPPROTO_TCP, AF_INET)) {
+ action = PF_DROP;
+ goto done;
+ }
pd.p_len = pd.tot_len - off - (th.th_off << 2);
action = pf_normalize_tcp(dir, ifp, m, 0, off, h, &pd);
if (action == PF_DROP)
@@ -4182,6 +4273,11 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0)
log = action != PF_PASS;
goto done;
}
+ if (dir == PF_IN && uh.uh_sum && pf_check_proto_cksum(m,
+ off, h->ip_len - off, IPPROTO_UDP, AF_INET)) {
+ action = PF_DROP;
+ goto done;
+ }
action = pf_test_state_udp(&s, dir, ifp, m, 0, off, h, &pd);
if (action == PF_PASS) {
r = s->rule.ptr;
@@ -4200,6 +4296,11 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0)
log = action != PF_PASS;
goto done;
}
+ if (dir == PF_IN && pf_check_proto_cksum(m, off,
+ h->ip_len - off, IPPROTO_ICMP, AF_INET)) {
+ action = PF_DROP;
+ goto done;
+ }
action = pf_test_state_icmp(&s, dir, ifp, m, 0, off, h, &pd);
if (action == PF_PASS) {
r = s->rule.ptr;
@@ -4377,6 +4478,11 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0)
log = action != PF_PASS;
goto done;
}
+ if (dir == PF_IN && pf_check_proto_cksum(m, off,
+ ntohs(h->ip6_plen), IPPROTO_TCP, AF_INET6)) {
+ action = PF_DROP;
+ goto done;
+ }
pd.p_len = pd.tot_len - off - (th.th_off << 2);
action = pf_normalize_tcp(dir, ifp, m, 0, off, h, &pd);
if (action == PF_DROP)
@@ -4399,6 +4505,11 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0)
log = action != PF_PASS;
goto done;
}
+ if (dir == PF_IN && uh.uh_sum && pf_check_proto_cksum(m,
+ off, ntohs(h->ip6_plen), IPPROTO_UDP, AF_INET6)) {
+ action = PF_DROP;
+ goto done;
+ }
action = pf_test_state_udp(&s, dir, ifp, m, 0, off, h, &pd);
if (action == PF_PASS) {
r = s->rule.ptr;
@@ -4417,6 +4528,11 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0)
log = action != PF_PASS;
goto done;
}
+ if (dir == PF_IN && pf_check_proto_cksum(m, off,
+ ntohs(h->ip6_plen), IPPROTO_ICMPV6, AF_INET6)) {
+ action = PF_DROP;
+ goto done;
+ }
action = pf_test_state_icmp(&s, dir, ifp, m, 0, off, h, &pd);
if (action == PF_PASS) {
r = s->rule.ptr;