From b69c89adb37234853e74d18184996feb701c2023 Mon Sep 17 00:00:00 2001 From: Wen Heping Date: Mon, 28 Mar 2011 03:08:45 +0000 Subject: [PATCH] - Allow IPv6 subnets to be whitelisted/auto-accepted Obtained from http://wilmer.gaa.st/blog/archives/61-spamass-milter-and-IPv6.html PR: ports/155979 Submitted by: "Mikhail T." (maintainer) --- mail/spamass-milter/Makefile | 7 +- mail/spamass-milter/files/extra-patch-ipv6 | 270 +++++++++++++++++++++ 2 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 mail/spamass-milter/files/extra-patch-ipv6 diff --git a/mail/spamass-milter/Makefile b/mail/spamass-milter/Makefile index f8352e671bbb..1fffd3f1088b 100644 --- a/mail/spamass-milter/Makefile +++ b/mail/spamass-milter/Makefile @@ -25,7 +25,8 @@ MAKE_JOBS_SAFE= yes OPTIONS= ADDAUTH_PATCH "Bypass checks for SMTP AUTH connections" off \ REJECTTEXT_PATCH "Customize SMTP reject message" off \ LDAP "LDAP support" off \ - SENDMAIL_PORT "Build against sendmail port" off + SENDMAIL_PORT "Build against sendmail port" off \ + IPV6 "Apply IPv6 whitelist patch" off .include @@ -42,6 +43,10 @@ EXTRA_PATCHES+= ${FILESDIR}/extra-patch-rejecttext1 NEW_ARGS:= ${NEW_ARGS}R: .endif +.if defined(WITH_IPV6) +EXTRA_PATCHES+= ${FILESDIR}/extra-patch-ipv6 +.endif + # extra-patch-options is modified in pre-patch .if ${ORIG_ARGS} != ${NEW_ARGS} EXTRA_PATCHES+= ${WRKDIR}/extra-patch-options diff --git a/mail/spamass-milter/files/extra-patch-ipv6 b/mail/spamass-milter/files/extra-patch-ipv6 new file mode 100644 index 000000000000..e5ee5c62724c --- /dev/null +++ b/mail/spamass-milter/files/extra-patch-ipv6 @@ -0,0 +1,270 @@ +diff -ur orig/spamass-milter.cpp spamass-milter.cpp +--- orig/spamass-milter.cpp 2010-01-31 11:35:47.000000000 +0000 ++++ spamass-milter.cpp 2008-01-09 01:20:38.000000000 +0000 +@@ -88,6 +88,7 @@ + #include "subst_poll.h" + #endif + #include ++#include + + // C++ includes + #include +@@ -721,12 +722,19 @@ + 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); ++ debug(D_FUNC, "Remote address: %s", sctx->connect_ip); + } + sctx->assassin = NULL; + sctx->helo = NULL; +@@ -740,10 +748,12 @@ + } + /* debug(D_ALWAYS, "ZZZ set private context to %p", sctx); */ + +- if (ip_in_networklist(sctx->connect_ip, &ignorenets)) ++ //debug(D_FUNC, "sctx->connect_ip: `%d'", sctx->connect_ip.sin_family); ++ ++ 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; + } +@@ -815,7 +825,7 @@ + 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; +@@ -2089,69 +2099,119 @@ + { + 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; ++ ++ { ++ char *snet = strdup(inet_ntoa(net)); ++ debug(D_MISC, "Adding %s/%s to network list", snet, inet_ntoa(mask)); ++ free(snet); ++ } + +- if (tmask) ++ 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; ++ list->num_nets++; ++ } else if (inet_pton(AF_INET6, tnet, &net6)) + { +- if (strchr(tmask, '.') == NULL) ++ 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; ++ list->num_nets++; + } else +- mask.s_addr = 0xffffffff; +- + { +- char *snet = strdup(inet_ntoa(net)); +- debug(D_MISC, "Adding %s/%s to network list", snet, inet_ntoa(mask)); +- free(snet); ++ fprintf(stderr, "Could not parse \"%s\" as a network\n", tnet); ++ exit(1); + } + +- 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)); ++ ++ //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", inet_ntoa(list->nets[i].net4.network)); ++ debug(D_NET, "/%s", inet_ntoa(list->nets[i].net4.netmask)); ++ 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; ++ ++ 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 -ur orig/spamass-milter.h spamass-milter.h +--- orig/spamass-milter.h 2006-03-23 22:07:55.000000000 +0000 ++++ spamass-milter.h 2008-01-01 23:55:44.000000000 +0000 +@@ -56,16 +56,30 @@ + 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; ++ } net4; ++ struct ++ { ++ uint8_t af; ++ struct in6_addr network; ++ int netmask; /* Just the number of bits for IPv6 */ ++ } net6; + }; + + /* an array of networks */ + struct networklist + { +- struct net *nets; ++ union net *nets; + int num_nets; + }; + +@@ -165,7 +179,7 @@ + /* 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; + SpamAssassin *assassin; // pointer to the SA object if we're processing a message + }; +@@ -182,7 +196,7 @@ + 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(char *macro, char *scope);