From nobody@FreeBSD.org  Tue Dec 16 01:10:07 2003
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id 4D26116A4CE
	for <freebsd-gnats-submit@FreeBSD.org>; Tue, 16 Dec 2003 01:10:07 -0800 (PST)
Received: from www.freebsd.org (www.freebsd.org [216.136.204.117])
	by mx1.FreeBSD.org (Postfix) with ESMTP id AFDCC43D49
	for <freebsd-gnats-submit@FreeBSD.org>; Tue, 16 Dec 2003 01:10:04 -0800 (PST)
	(envelope-from nobody@FreeBSD.org)
Received: from www.freebsd.org (localhost [127.0.0.1])
	by www.freebsd.org (8.12.10/8.12.10) with ESMTP id hBG9A3dL091659
	for <freebsd-gnats-submit@FreeBSD.org>; Tue, 16 Dec 2003 01:10:03 -0800 (PST)
	(envelope-from nobody@www.freebsd.org)
Received: (from nobody@localhost)
	by www.freebsd.org (8.12.10/8.12.10/Submit) id hBG9A3nt091658;
	Tue, 16 Dec 2003 01:10:03 -0800 (PST)
	(envelope-from nobody)
Message-Id: <200312160910.hBG9A3nt091658@www.freebsd.org>
Date: Tue, 16 Dec 2003 01:10:03 -0800 (PST)
From: "Roman Y. Bogdanov" <brj@vzletka.net>
To: freebsd-gnats-submit@FreeBSD.org
Subject: FreeBSD arp poison patch
X-Send-Pr-Version: www-2.0

>Number:         60293
>Category:       kern
>Synopsis:       [netinet] [patch] FreeBSD arp poison patch
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-net
>State:          suspended
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          update
>Submitter-Id:   current-users
>Arrival-Date:   Tue Dec 16 01:20:06 PST 2003
>Closed-Date:    
>Last-Modified:  Sun May 04 04:26:45 UTC 2014
>Originator:     Roman Y. Bogdanov
>Release:        FreeBSD 4.9 release
>Organization:
brj.pp.ru
>Environment:
n/a
>Description:
There is well known problem arp poisoning problem in FreeBSD. If 
arp reply is received without request FreeBSD logs error 
into syslog, but changes arp table entry. It makes possibility 
for local atacker to change arp cache entry. In network this 
behaviour can only occure when adapter changes it's MAC address. 

This like a linux kernel patch (2.4.18 and .19 tested) to resisting ARP spoofing - http://groups.google.com/groups?selm=001901c28c23%2414c08320%240101fea9%40r66.ru&oe=UTF-8&output=gplain
>How-To-Repeat:
n/a
>Fix:
Attached is patch to check old MAC address before changing 
arp entry by sending unicast arp request to this MAC. If old MAC 
replies, no changes to arp table is made and attack is logged.

[PATCH]

--- if_ether.c.old	Tue Dec  2 19:57:08 2003
+++ if_ether.c	Wed Dec  3 10:41:38 2003
@@ -40,6 +40,10 @@
  *	add "inuse/lock" bit (or ref. count) along with valid bit
  */
 
+/*
+ * ARP-posion Antidote code (c) 2003 by Bert <bert@furry.ru>
+ */
+
 #include "opt_inet.h"
 #include "opt_bdg.h"
 
@@ -82,6 +86,10 @@
 static int arpt_keep = (20*60); /* once resolved, good for 20 more minutes */
 static int arpt_down = 20;	/* once declared down, don't send for 20 sec */
 
+static int arp_secure = 1;
+static int arpt_debug = 0;
+
+
 SYSCTL_INT(_net_link_ether_inet, OID_AUTO, prune_intvl, CTLFLAG_RW,
 	   &arpt_prune, 0, "");
 SYSCTL_INT(_net_link_ether_inet, OID_AUTO, max_age, CTLFLAG_RW, 
@@ -96,6 +104,9 @@
 	struct	rtentry *la_rt;
 	struct	mbuf *la_hold;		/* last packet until resolved/timeout */
 	long	la_asked;		/* last time we QUERIED for this addr */
+	long	la_ack;			/* Arp reply acknowledgement flag */	
+        u_char  check_eth[ETHER_ADDR_LEN]; /* MAC address, before it changing */
+	struct in_addr	check_ip;
 #define la_timer la_rt->rt_rmx.rmx_expire /* deletion time in seconds */
 };
 
@@ -115,6 +126,14 @@
 SYSCTL_INT(_net_link_ether_inet, OID_AUTO, proxyall, CTLFLAG_RW,
 	   &arp_proxyall, 0, "");
 
+/* sysctl arp antidote variable. Default is "on" */
+SYSCTL_INT(_net_link_ether_inet, OID_AUTO, arp_antidote, CTLFLAG_RW,
+	   &arp_secure, 0, "");
+	   
+/* sysctl arp debug variable. Default is "off" */
+SYSCTL_INT(_net_link_ether_inet, OID_AUTO, arp_debug, CTLFLAG_RW,
+	   &arpt_debug, 0, "");
+
 static void	arp_rtrequest __P((int, struct rtentry *, struct rt_addrinfo *));
 static void	arprequest __P((struct ifnet *,
 			struct in_addr *, struct in_addr *, u_char *));
@@ -663,14 +682,73 @@
 			    ifp->if_name, ifp->if_unit);
 		    goto reply;
 		}
+		
+		/* Print announced packet if debug is enabled */
+		if (sdl->sdl_alen && arpt_debug) {
+                  log(LOG_INFO, "arp: Got MAC address %*D from %s via %s%d\n", 
+		      ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
+		      inet_ntoa(isaddr),
+		      ifp->if_name, ifp->if_unit);
+		}
+		
+		/* Check possible reply with old MAC-address*/
+		if (arp_secure && rt->rt_expire && 
+		    sdl->sdl_alen && la->la_ack == 1 && 
+		    !bcmp(ar_sha(ah), LLADDR(sdl), sdl->sdl_alen)) 
+		{
+		  /* Got old MAC address. Check it with saved */
+		  if (!bcmp(ar_sha(ah), la->check_eth, sdl->sdl_alen)) {
+		    /* Addresses are equal. MAC address verified */
+		    if (arpt_debug)
+                      log(LOG_INFO, "arp: MAC address %*D from %s verified.\n",
+		          ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
+		          inet_ntoa(isaddr));
+		    la->la_ack = 0;
+		  } else {
+		    /* Make an arp entry "static" */
+		    rt->rt_expire = 0;
+		    /* Reply with another MAC */
+                    log(LOG_ERR, "arp: ARP poison detected!, attacker from %*D trying to infect %*D (%s) in my ARP table!\n",
+		      sdl->sdl_alen, (u_char *)&la->check_eth, ":",
+		      sdl->sdl_alen, (u_char *)ar_sha(ah), ":",
+		      inet_ntoa(la->check_ip));
+		      
+		    /* Clear cached MAC */
+		    bzero(&la->check_eth, sizeof(la->check_eth));
+		    la->la_ack = 0;
+                    m_free(m);
+                    return;
+		  }
+		}
+		
 		if (sdl->sdl_alen &&
 		    bcmp(ar_sha(ah), LLADDR(sdl), sdl->sdl_alen)) {
-			if (rt->rt_expire)
-			    log(LOG_INFO, "arp: %s moved from %*D to %*D on %s%d\n",
-				inet_ntoa(isaddr),
-				ifp->if_addrlen, (u_char *)LLADDR(sdl), ":",
-				ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
-				ifp->if_name, ifp->if_unit);
+			if (rt->rt_expire) {
+			    if (arp_secure) {
+                              if (la->la_ack == 0) {
+			        if (arpt_debug)
+                                  log(LOG_INFO, "arp: Got new MAC address %*D from %s (%s%d). Now verifying.\n",
+				      ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
+				      inet_ntoa(isaddr),
+				      ifp->if_name, ifp->if_unit);
+				/* Check old MAC address for alive */
+                                arprequest(ifp, &myaddr, &isaddr, IF_LLADDR(ifp));
+				(void)memcpy((u_char *)&la->check_eth, (u_char *)ar_sha(ah), sdl->sdl_alen);
+				la->check_ip.s_addr = isaddr.s_addr;
+				
+                                la->la_ack++;
+				la->la_asked++;
+                                m_free(m);
+                                return;
+			      }
+			    } else {
+			       log(LOG_INFO, "arp: %s moved from %*D to %*D on %s%d\n",
+			          inet_ntoa(isaddr),
+			  	  ifp->if_addrlen, (u_char *)LLADDR(sdl), ":",
+				  ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
+				  ifp->if_name, ifp->if_unit);
+			    }
+			}
 			else {
 			    log(LOG_ERR,
 				"arp: %*D attempts to modify permanent entry for %s on %s%d\n",

>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->bms 
Responsible-Changed-By: bms 
Responsible-Changed-When: Wed Jun 16 00:14:50 GMT 2004 
Responsible-Changed-Why:  
I'll try to look at this 

http://www.freebsd.org/cgi/query-pr.cgi?pr=60293 
State-Changed-From-To: open->analyzed 
State-Changed-By: bms 
State-Changed-When: Sat Jul 3 16:08:22 GMT 2004 
State-Changed-Why:  
Full analysis to follow 

http://www.freebsd.org/cgi/query-pr.cgi?pr=60293 

From: Bruce M Simpson <bms@spc.org>
To: "Roman Y. Bogdanov" <brj@vzletka.net>, Bert <bert@furry.ru>
Cc: freebsd-gnats-submit@FreeBSD.org, freebsd-net@FreeBSD.org
Subject: Re: kern/60293: FreeBSD arp poison patch
Date: Sat, 3 Jul 2004 17:06:09 +0100

 --ai3I8gwHc37+ASRI
 Content-Type: text/plain; charset=us-ascii
 Content-Disposition: inline
 
 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
 
 --ai3I8gwHc37+ASRI
 Content-Type: text/plain; charset=us-ascii
 Content-Disposition: attachment; filename="arp-poison-antidote.diff"
 
 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;
 
 --ai3I8gwHc37+ASRI--

From: Max Artemev <bert@furry.ru>
To: freebsd-gnats-submit@FreeBSD.org
Cc:  
Subject: Re: kern/60293: FreeBSD arp poison patch
Date: Sat, 31 Jul 2004 17:49:36 +0400

 Hello, freebsd-gnats-submit.
 
 http://www.freebsd.org/cgi/query-pr.cgi?pr=kern/60293
 
 Also, please clarify your license regarding this code, I am assuming that
  it was submitted under the BSD license.
 
 
 -- 
 WBR, Bert
 
 

From: "Roman Y. Bogdanov" <sam@brj.pp.ru>
To: freebsd-gnats-submit@FreeBSD.org, brj@vzletka.net
Cc:  
Subject: Re: kern/60293: FreeBSD arp poison patch
Date: Mon, 2 Aug 2004 00:30:07 +0800

  Unfortunately I have replaced a place of work.
  Old email brj@vzletka.net - was removed by my employer.
  Please use current email to follow up the PR.
 
 --
  Roman Y. Bogdanov                     /"\  ASCII RIBBON Campaign
  http://brj.pp.ru/                     \ /  NO HTML/PDF/RTF in e-mail
  + 7 3912 541843 [work, office]         X   NO MSWord docs in e-mail
  + 7 3912 505653 [personal, mobile]    / \  NO attachments in e-mail
 
 
State-Changed-From-To: analyzed->suspended 
State-Changed-By: bms 
State-Changed-When: Wed Aug 2 13:01:22 UTC 2006 
State-Changed-Why:  
Not ready for prime time, IMHO. 


Responsible-Changed-From-To: bms->freebsd-net 
Responsible-Changed-By: bms 
Responsible-Changed-When: Wed Aug 2 13:01:22 UTC 2006 
Responsible-Changed-Why:  
Disowning it due to lack of cycles/focus. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=60293 
>Unformatted:
