[spamass-milter] Add IPv6 whitelisting support (#630263)

Paul Howarth pghmcfc at fedoraproject.org
Thu Sep 23 20:11:22 UTC 2010


commit 1bbf06710be667c8e87522f54bfad4d51fafc74f
Author: Paul Howarth <paul at city-fan.org>
Date:   Thu Sep 23 21:10:54 2010 +0100

    Add IPv6 whitelisting support (#630263)

 spamass-milter-0.3.1-ipv6.patch |  297 +++++++++++++++++++++++++++++++++++++++
 spamass-milter.spec             |   10 ++-
 2 files changed, 306 insertions(+), 1 deletions(-)
---
diff --git a/spamass-milter-0.3.1-ipv6.patch b/spamass-milter-0.3.1-ipv6.patch
new file mode 100644
index 0000000..6fb1467
--- /dev/null
+++ b/spamass-milter-0.3.1-ipv6.patch
@@ -0,0 +1,297 @@
+diff -up spamass-milter-0.3.1/spamass-milter.cpp.ipv6 spamass-milter-0.3.1/spamass-milter.cpp
+--- spamass-milter-0.3.1/spamass-milter.cpp.ipv6	2010-09-23 16:26:36.227224902 +0100
++++ spamass-milter-0.3.1/spamass-milter.cpp	2010-09-23 17:25:22.307099331 +0100
+@@ -88,6 +88,7 @@
+ #include "subst_poll.h"
+ #endif
+ #include <errno.h>
++#include <netdb.h>
+ 
+ #include <grp.h>
+ 
+@@ -718,12 +719,18 @@ mlfi_connect(SMFICTX * ctx, char *hostna
+ 	sctx = (struct context *)malloc(sizeof(*sctx));
+ 	if (!hostaddr)
+ 	{
++		static struct sockaddr_in localhost;
++		
+ 		/* not a socket; probably a local user calling sendmail directly */
+ 		/* set to 127.0.0.1 */
+-		sctx->connect_ip.s_addr = htonl(INADDR_LOOPBACK);
++		strcpy(sctx->connect_ip, "127.0.0.1");
++		localhost.sin_family = AF_INET;
++		localhost.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
++		hostaddr = (struct sockaddr*) &localhost;
+ 	} else
+ 	{
+-		sctx->connect_ip = ((struct sockaddr_in *) hostaddr)->sin_addr;
++		getnameinfo(hostaddr, sizeof(struct sockaddr_in6),
++		            sctx->connect_ip, 63, NULL, 0, NI_NUMERICHOST);
+ 	}
+ 	sctx->assassin = NULL;
+ 	sctx->helo = NULL;
+@@ -758,12 +765,12 @@ mlfi_connect(SMFICTX * ctx, char *hostna
+ 		debug(D_ALWAYS, "smfi_setpriv failed!");
+ 		return SMFIS_TEMPFAIL;
+ 	}
+-	/* debug(D_ALWAYS, "ZZZ set private context to %p", sctx); */
+ 
+-	if (ip_in_networklist(sctx->connect_ip, &ignorenets))
++	debug(D_NET, "Checking %s against:", sctx->connect_ip);
++	if (ip_in_networklist(hostaddr, &ignorenets))
+ 	{
+ 		debug(D_NET, "%s is in our ignore list - accepting message",
+-		    inet_ntoa(sctx->connect_ip));
++		      sctx->connect_ip);
+ 		debug(D_FUNC, "mlfi_connect: exit ignore");
+ 		return SMFIS_ACCEPT;
+ 	}
+@@ -807,7 +814,6 @@ mlfi_envfrom(SMFICTX* ctx, char** envfro
+     debug(D_ALWAYS, "smfi_getpriv failed!");
+     return SMFIS_TEMPFAIL;
+   }
+-  /* debug(D_ALWAYS, "ZZZ got private context %p", sctx); */
+ 
+   if (ignore_authenticated_senders)
+   {
+@@ -835,7 +841,7 @@ mlfi_envfrom(SMFICTX* ctx, char** envfro
+       return SMFIS_TEMPFAIL;
+     };
+   
+-  assassin->set_connectip(string(inet_ntoa(sctx->connect_ip)));
++  assassin->set_connectip(string(sctx->connect_ip));
+ 
+   // Store a pointer to the assassin object in our context struct
+   sctx->assassin = assassin;
+@@ -2128,69 +2134,135 @@ void parse_networklist(char *string, str
+ 	{
+ 		char *tnet = strsep(&token, "/");
+ 		char *tmask = token;
+-		struct in_addr net, mask;
++		struct in_addr net;
++		struct in6_addr net6;
+ 
+ 		if (list->num_nets % 10 == 0)
+-			list->nets = (struct net*)realloc(list->nets, sizeof(*list->nets) * (list->num_nets + 10));
++			list->nets = (union net*)realloc(list->nets, sizeof(*list->nets) * (list->num_nets + 10));
+ 
+-		if (!inet_aton(tnet, &net))
++		if (inet_pton(AF_INET, tnet, &net))
+ 		{
+-			fprintf(stderr, "Could not parse \"%s\" as a network\n", tnet);
+-			exit(1);
+-		}
++			struct in_addr mask;
++			
++			if (tmask)
++			{
++				if (strchr(tmask, '.') == NULL)
++				{
++					/* CIDR */
++					unsigned int bits;
++					int ret;
++					ret = sscanf(tmask, "%u", &bits);
++					if (ret != 1 || bits > 32)
++					{
++						fprintf(stderr,"%s: bad CIDR value", tmask);
++						exit(1);
++					}
++					mask.s_addr = htonl(~((1L << (32 - bits)) - 1) & 0xffffffff);
++				} else if (!inet_pton(AF_INET6, tmask, &mask))
++				{
++					fprintf(stderr, "Could not parse \"%s\" as a netmask\n", tmask);
++					exit(1);
++				}
++			} else
++				mask.s_addr = 0xffffffff;
+ 
+-		if (tmask)
+-		{
+-			if (strchr(tmask, '.') == NULL)
++			net.s_addr = net.s_addr & mask.s_addr;
++			list->nets[list->num_nets].net4.af = AF_INET;
++			list->nets[list->num_nets].net4.network = net;
++			list->nets[list->num_nets].net4.netmask = mask;
++		} else if (inet_pton(AF_INET6, tnet, &net6))
++		{
++			int mask;
++			
++			if (tmask)
+ 			{
+-				/* CIDR */
+-				unsigned int bits;
+-				int ret;
+-				ret = sscanf(tmask, "%u", &bits);
+-				if (ret != 1 || bits > 32)
++				if (sscanf(tmask, "%d", &mask) != 1 || mask > 128)
+ 				{
+ 					fprintf(stderr,"%s: bad CIDR value", tmask);
+ 					exit(1);
+ 				}
+-				mask.s_addr = htonl(~((1L << (32 - bits)) - 1) & 0xffffffff);
+-			} else if (!inet_aton(tmask, &mask))
+-			{
+-				fprintf(stderr, "Could not parse \"%s\" as a netmask\n", tmask);
+-				exit(1);
+-			}
++			} else
++				mask = 128;
++			
++			list->nets[list->num_nets].net6.af = AF_INET6;
++			list->nets[list->num_nets].net6.network = net6;
++			list->nets[list->num_nets].net6.netmask = mask;
+ 		} else
+-			mask.s_addr = 0xffffffff;
++		{
++			fprintf(stderr, "Could not parse \"%s\" as a network\n", tnet);
++			exit(1);
++		}
+ 
+ 		{
+-			char *snet = strdup(inet_ntoa(net));
+-			debug(D_MISC, "Adding %s/%s to network list", snet, inet_ntoa(mask));
+-			free(snet);
++			int af = list->nets[list->num_nets].net.af;
++			char addrbuf[INET6_ADDRSTRLEN];
++			char maskbuf[4];
++			char *maskstr;
++
++			if (af == AF_INET6) {
++				inet_ntop(af, &list->nets[list->num_nets].net6.network,
++					addrbuf, INET6_ADDRSTRLEN);
++				sprintf(maskbuf, "%d", list->nets[list->num_nets].net6.netmask);
++				maskstr = maskbuf;
++				list->nets[list->num_nets].net6.addrstr = strdup(addrbuf);
++				list->nets[list->num_nets].net6.maskstr = strdup(maskbuf);
++			} else
++			{
++				inet_ntop(af, &list->nets[list->num_nets].net4.network,
++					addrbuf, INET6_ADDRSTRLEN);
++				maskstr = inet_ntoa(list->nets[list->num_nets].net4.netmask);
++				list->nets[list->num_nets].net4.addrstr = strdup(addrbuf);
++				list->nets[list->num_nets].net4.maskstr = strdup(maskstr);
++			}
++			debug(D_MISC, "Added %s/%s to network list", addrbuf, maskstr);
+ 		}
+ 
+-		net.s_addr = net.s_addr & mask.s_addr;
+-		list->nets[list->num_nets].network = net;
+-		list->nets[list->num_nets].netmask = mask;
+ 		list->num_nets++;
+ 	}
+ 	free(string);
+ }
+ 
+-int ip_in_networklist(struct in_addr ip, struct networklist *list)
++int ip_in_networklist(struct sockaddr *addr, struct networklist *list)
+ {
+ 	int i;
+ 
+ 	if (list->num_nets == 0)
+ 		return 0;
+-		
+-	debug(D_NET, "Checking %s against:", inet_ntoa(ip));
++	
+ 	for (i = 0; i < list->num_nets; i++)
+ 	{
+-		debug(D_NET, "%s", inet_ntoa(list->nets[i].network));
+-		debug(D_NET, "/%s", inet_ntoa(list->nets[i].netmask));
+-		if ((ip.s_addr & list->nets[i].netmask.s_addr) == list->nets[i].network.s_addr)
+-        {
+-        	debug(D_NET, "Hit!");
+-			return 1;
++		if (list->nets[i].net.af == AF_INET && addr->sa_family == AF_INET)
++		{
++			struct in_addr ip = ((struct sockaddr_in *)addr)->sin_addr;
++			
++			debug(D_NET, "%s/%s", list->nets[i].net4.addrstr, list->nets[i].net4.maskstr);
++			if ((ip.s_addr & list->nets[i].net4.netmask.s_addr) == list->nets[i].net4.network.s_addr)
++			{
++				debug(D_NET, "Hit!");
++				return 1;
++			}
++		} else if (list->nets[i].net.af == AF_INET6 && addr->sa_family == AF_INET6)
++		{
++			u_int8_t *ip = ((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr;
++			int mask, j;
++
++			debug(D_NET, "%s/%s", list->nets[i].net6.addrstr, list->nets[i].net6.maskstr);
++			mask = list->nets[i].net6.netmask;
++			for (j = 0; j < 16 && mask > 0; j++, mask -= 8)
++			{
++				unsigned char bytemask;
++				
++				bytemask = (mask < 8) ? ~((1L << (8 - mask)) - 1) : 0xff;
++				
++				if ((ip[j] & bytemask) != (list->nets[i].net6.network.s6_addr[j] & bytemask))
++					break;
++			}
++			
++			if (mask <= 0)
++			{
++				debug(D_NET, "Hit!");
++				return 1;
++			}
+ 		}
+ 	}
+ 
+diff -up spamass-milter-0.3.1/spamass-milter.h.ipv6 spamass-milter-0.3.1/spamass-milter.h
+--- spamass-milter-0.3.1/spamass-milter.h.ipv6	2010-09-23 16:26:36.224160445 +0100
++++ spamass-milter-0.3.1/spamass-milter.h	2010-09-23 17:00:16.487410690 +0100
+@@ -56,16 +56,34 @@ sfsistat mlfi_abort(SMFICTX*);
+ extern struct smfiDesc smfilter;
+ 
+ /* struct describing a single network */
+-struct net
++union net
+ {
+-	struct in_addr network;
+-	struct in_addr netmask;
++	struct
++	{
++		uint8_t af;
++	} net;
++	struct
++	{
++		uint8_t af;
++		struct in_addr network;
++		struct in_addr netmask;
++		char *addrstr;
++		char *maskstr;
++	} net4;
++	struct
++	{
++		uint8_t af;
++		struct in6_addr network;
++		int netmask; /* Just the number of bits for IPv6 */
++		char *addrstr;
++		char *maskstr;
++	} net6;
+ };
+ 
+ /* an array of networks */
+ struct networklist
+ {
+-	struct net *nets;
++	union net *nets;
+ 	int num_nets;
+ };
+ 
+@@ -162,7 +180,7 @@ public:  
+ /* Private data structure to carry per-client data between calls */
+ struct context
+ {
+-	struct in_addr connect_ip;	// remote IP address
++	char connect_ip[64];	// remote IP address
+ 	char *helo;
+ 	char *our_fqdn;
+ 	char *sender_address;
+@@ -184,7 +202,7 @@ string::size_type find_nocase(const stri
+ int cmp_nocase_partial(const string&, const string&);
+ void closeall(int fd);
+ void parse_networklist(char *string, struct networklist *list);
+-int ip_in_networklist(struct in_addr ip, struct networklist *list);
++int ip_in_networklist(struct sockaddr *addr, struct networklist *list);
+ void parse_debuglevel(char* string);
+ char *strlwr(char *str);
+ void warnmacro(const char *macro, const char *scope);
diff --git a/spamass-milter.spec b/spamass-milter.spec
index f61ee72..24a0403 100644
--- a/spamass-milter.spec
+++ b/spamass-milter.spec
@@ -1,7 +1,7 @@
 Summary:	Milter (mail filter) for spamassassin
 Name:		spamass-milter
 Version:	0.3.1
-Release:	20%{?dist}
+Release:	21%{?dist}
 License:	GPLv2+
 Group:		System Environment/Daemons
 URL:		http://savannah.nongnu.org/projects/spamass-milt/
@@ -18,6 +18,8 @@ Patch2:		spamass-milter-0.3.1-authuser.patch
 Patch3:		spamass-milter-0.3.1-rcvd.patch
 Patch4:		spamass-milter-0.3.1-bits.patch
 Patch5:		spamass-milter-0.3.1-group.patch
+# Patch not yet submitted upstream
+Patch7:		spamass-milter-0.3.1-ipv6.patch
 # Fedora-specific patches
 Patch10:	spamass-milter-0.3.1-pathnames.patch
 BuildRoot:	%{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
@@ -73,6 +75,9 @@ socket to communicate with the Postfix MTA.
 # Add -g option for group-writable socket for Postfix support (#452248)
 %patch5 -p1 -b .group
 
+# Add IPv6 whitelisting support
+%patch7 -p1 -b .ipv6
+
 # Local patch for initscript and socket paths
 %patch10 -p1 -b .pathnames
 
@@ -147,6 +152,9 @@ fi
 %dir %attr(-,sa-milt,postfix) %{_localstatedir}/run/spamass-milter/postfix/
 
 %changelog
+* Thu Sep 23 2010 Paul Howarth <paul at city-fan.org> 0.3.1-21
+- Add IPv6 whitelisting support (#630263)
+
 * Tue Jun  8 2010 Paul Howarth <paul at city-fan.org> 0.3.1-20
 - RHEL-6 onwards have noarch subpackages, so make spamass-milter-postfix one
 


More information about the scm-commits mailing list