From owner-freebsd-bugs@FreeBSD.ORG Tue Dec 16 01:20:16 2003 Return-Path: Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 5ED0C16A4D0 for ; Tue, 16 Dec 2003 01:20:16 -0800 (PST) Received: from freefall.freebsd.org (freefall.freebsd.org [216.136.204.21]) by mx1.FreeBSD.org (Postfix) with ESMTP id 76ED743D55 for ; Tue, 16 Dec 2003 01:20:06 -0800 (PST) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) hBG9K6FR047890 for ; Tue, 16 Dec 2003 01:20:06 -0800 (PST) (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.12.10/8.12.10/Submit) id hBG9K6U2047889; Tue, 16 Dec 2003 01:20:06 -0800 (PST) (envelope-from gnats) Resent-Date: Tue, 16 Dec 2003 01:20:06 -0800 (PST) Resent-Message-Id: <200312160920.hBG9K6U2047889@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, "Roman Y. Bogdanov" Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 4D26116A4CE for ; 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 ; 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 ; 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" To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-2.0 Subject: kern/60293: FreeBSD arp poison patch X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.1 Precedence: list List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 16 Dec 2003 09:20:16 -0000 >Number: 60293 >Category: kern >Synopsis: FreeBSD arp poison patch >Confidential: no >Severity: non-critical >Priority: medium >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: update >Submitter-Id: current-users >Arrival-Date: Tue Dec 16 01:20:06 PST 2003 >Closed-Date: >Last-Modified: >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 + */ + #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: >Unformatted: