kern/60293: FreeBSD arp poison patch
Bruce M Simpson
bms at spc.org
Sat Jul 3 09:06:18 PDT 2004
Here's a cleaned up version of your patch which works against FreeBSD-CURRENT.
I have mixed feelings about committing this; it raises the bar somewhat
but it does not completely close the hole.
Let's say Alice and Bob are talking to each other with IPv4 over Ethernet.
Making the arp cache entry permanent is probably a bad idea for Alice. She
should be allowed to expire out the entry at some point, which admittedly
provides a window for the cache poisoning attack, but the potential for
abuse is increased if the attacker is able to race Bob in creating the
original entry in Alice's cache.
I've attempted to mitigate this by using a simple linearly computed threshold,
but this still doesn't really solve the problem.
Might suggest we review the following papers on the subject:
http://www.cs.utexas.edu/users/chuang/comnet0103.pdf
http://www.acsac.org/1999/papers/fri-b-0830-dutta.pdf
http://alor.antifork.org/projects/s-arp/article.pdf
And code:
http://alor.antifork.org/projects/s-arp/sarpd-0.0.9-devel.tar.gz
The daemon above could probably be adapted for *BSD use by setting
IFF_NOARP on an Ethernet network interface, removing the subnet route
and re-adding it with RTF_XRESOLVE|RTX_CLONING, and adding bpf/rtsock
support to the userland code.
Also, please clarify your license regarding this code, I am assuming that
it was submitted under the BSD license.
Regards,
BMS
-------------- next part --------------
Index: src/sys/conf/options
===================================================================
RCS file: /home/ncvs/src/sys/conf/options,v
retrieving revision 1.457
diff -u -p -r1.457 options
--- src/sys/conf/options 29 Jun 2004 02:30:12 -0000 1.457
+++ src/sys/conf/options 3 Jul 2004 13:16:11 -0000
@@ -305,6 +305,7 @@ ALTQ_CDNR opt_altq.h
ALTQ_PRIQ opt_altq.h
ALTQ_NOPCC opt_altq.h
ALTQ_DEBUG opt_altq.h
+ARP_VERIFY_REPLY opt_inet.h
BOOTP opt_bootp.h
BOOTP_COMPAT opt_bootp.h
BOOTP_NFSROOT opt_bootp.h
Index: src/sys/netinet/if_ether.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/if_ether.c,v
retrieving revision 1.128
diff -u -p -r1.128 if_ether.c
--- src/sys/netinet/if_ether.c 13 Jun 2004 10:54:36 -0000 1.128
+++ src/sys/netinet/if_ether.c 3 Jul 2004 15:50:50 -0000
@@ -57,8 +57,8 @@
#include <net/route.h>
#include <net/netisr.h>
#include <net/if_llc.h>
-#ifdef BRIDGE
#include <net/ethernet.h>
+#ifdef BRIDGE
#include <net/bridge.h>
#endif
@@ -95,8 +95,16 @@ struct llinfo_arp {
struct mbuf *la_hold; /* last packet until resolved/timeout */
u_short la_preempt; /* countdown for pre-expiry arps */
u_short la_asked; /* #times we QUERIED following expiration */
-#define la_timer la_rt->rt_rmx.rmx_expire /* deletion time in seconds */
+#ifdef ARP_VERIFY_REPLY
+ struct in_addr la_opaddr; /* XXX original protocol level
+ * address for verification */
+ u_short la_ack; /* #times original MAC acknowledged an
+ * ARP reply which we then RE-QUERIED */
+ u_char la_olladdr[ETHER_ADDR_LEN]; /* XXX original MAC for
+ * verification */
+#endif
};
+#define la_timer la_rt->rt_rmx.rmx_expire /* deletion time in seconds */
static LIST_HEAD(, llinfo_arp) llinfo_arp;
@@ -106,6 +114,9 @@ static int arp_allocated;
static int arp_maxtries = 5;
static int useloopback = 1; /* use loopback interface for local traffic */
static int arp_proxyall = 0;
+#ifdef ARP_VERIFY_REPLY
+static int arp_verifyrep = 0;
+#endif
static struct callout arp_callout;
SYSCTL_INT(_net_link_ether_inet, OID_AUTO, maxtries, CTLFLAG_RW,
@@ -114,6 +125,10 @@ SYSCTL_INT(_net_link_ether_inet, OID_AUT
&useloopback, 0, "");
SYSCTL_INT(_net_link_ether_inet, OID_AUTO, proxyall, CTLFLAG_RW,
&arp_proxyall, 0, "");
+#ifdef ARP_VERIFY_REPLY
+SYSCTL_INT(_net_link_ether_inet, OID_AUTO, verify_reply, CTLFLAG_RW,
+ &arp_verifyrep, 0, "Verify untrusted ARP traffic");
+#endif
static void arp_init(void);
static void arp_rtrequest(int, struct rtentry *, struct rt_addrinfo *);
@@ -605,18 +620,104 @@ match:
ifp->if_xname);
goto reply;
}
+#ifdef ARP_VERIFY_REPLY
+ if (arp_verifyrep && rt->rt_expire &&
+ sdl->sdl_alen == ETHER_ADDR_LEN && /* IPv4 check ok */
+ la->la_ack == 1 &&
+ !bcmp(ar_sha(ah), LLADDR(sdl), ETHER_ADDR_LEN)) {
+ /*
+ * We have seen gratuitous ARP from this MAC for this
+ * protocol level address on the wire in the past.
+ * Check if the learned address was verified by a
+ * further unicast ARP reply/request sequence.
+ */
+ if (!bcmp(ar_sha(ah), la->la_olladdr, ETHER_ADDR_LEN)) {
+ /*
+ * The source MAC address of this reply
+ * matches the previously learned entry.
+ */
+ if (bootverbose && log_arp_movements)
+ log(LOG_INFO,
+"arp: reply for %s from %*D on %s verified ok\n",
+ inet_ntoa(isaddr),
+ ETHER_ADDR_LEN,
+ (u_char *)ar_sha(ah), ":",
+ ifp->if_xname);
+ la->la_ack = 0;
+ } else {
+ /*
+ * Someone is now attempting to spoof a
+ * learned protocol level address in the
+ * current gratuitous reply being handled.
+ *
+ * Clamp expunge period to a threshold
+ * derived from the following formula.
+ */
+ int expire = (((uint32_t)arpt_keep +
+ time_second) * la->la_ack) / 2;
+ rt->rt_expire = MAX(rt->rt_expire, expire);
+
+ /*
+ * Log an appropriate message, and
+ * discard the incoming reply.
+ */
+ log(LOG_ERR,
+"arp: untrusted reply for %s from %*D on %s (learned MAC is %*D)\n",
+ inet_ntoa(la->la_opaddr),
+ ETHER_ADDR_LEN,
+ (u_char *)&la->la_olladdr, ":",
+ ifp->if_xname,
+ ETHER_ADDR_LEN,
+ (u_char *)ar_sha(ah), ":");
+ bzero(&la->la_olladdr, sizeof(la->la_olladdr));
+ la->la_ack = 0;
+ m_free(m);
+ return;
+ }
+ }
+#endif /* ARP_VERIFY_REPLY */
if (sdl->sdl_alen &&
bcmp(ar_sha(ah), LLADDR(sdl), sdl->sdl_alen)) {
if (rt->rt_expire) {
- if (log_arp_movements)
- log(LOG_INFO, "arp: %s moved from %*D to %*D on %s\n",
- inet_ntoa(isaddr),
+#ifdef ARP_VERIFY_REPLY
+ if (arp_verifyrep) {
+ /*
+ * If this protocol-level and link-level
+ * address was not previously verified,
+ * verify it now by sending another
+ * unicast ARP request back to the
+ * originator.
+ */
+ if (la->la_ack == 0) {
+ if (bootverbose && log_arp_movements)
+ log(LOG_INFO,
+"arp: verifying reply for %s from %*D on %s\n", inet_ntoa(isaddr),
+ ETHER_ADDR_LEN,
+ (u_char *)ar_sha(ah), ":",
+ ifp->if_xname);
+
+ arprequest(ifp, &myaddr, &isaddr,
+ IF_LLADDR(ifp));
+ bcopy(ar_sha(ah), &la->la_olladdr,
+ ETHER_ADDR_LEN);
+ bcopy(&isaddr, &la->la_opaddr,
+ ETHER_ADDR_LEN);
+ la->la_ack++;
+ la->la_asked++;
+ m_free(m);
+ return;
+ }
+ } else
+#endif /* ARP_VERIFY_REPLY */
+ if (log_arp_movements)
+ log(LOG_INFO,
+"arp: %s moved from %*D to %*D on %s\n", inet_ntoa(isaddr),
ifp->if_addrlen, (u_char *)LLADDR(sdl), ":",
ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
ifp->if_xname);
} else {
log(LOG_ERR,
- "arp: %*D attempts to modify permanent entry for %s on %s\n",
+"arp: %*D attempts to modify permanent entry for %s on %s\n",
ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
inet_ntoa(isaddr), ifp->if_xname);
goto reply;
More information about the freebsd-net
mailing list