spamass-milter (0.3.2-1) UNRELEASED; urgency=low
* New upstream release
- * No longer create home directory for spamass-milter; remove on purge if it exists (closes: #668717)
-
+ * No longer create home directory for spamass-milter; remove on purge if
+ it exists (closes: #668717)
+ * Use autotools-dev to update config.sub/config.guess (closes: #539791)
+ * Include support for ipv6 (Thanks to Wilmer van der Gaast) (closes: #569483)
+ * Add SMTP auth information to received header (Thanks to Knut Arne
+ Bjørndal) (closes: #627989)
+
-- Don Armstrong <don@debian.org> Mon, 13 Jun 2011 17:00:00 -0700
spamass-milter (0.3.1-10) unstable; urgency=low
--- /dev/null
+Index: spamass-milter/spamass-milter.cpp
+===================================================================
+--- spamass-milter.orig/spamass-milter.cpp 2012-06-21 13:03:25.000000000 -0700
++++ spamass-milter/spamass-milter.cpp 2012-06-21 13:03:42.000000000 -0700
+@@ -88,6 +88,7 @@
+ #include "subst_poll.h"
+ #endif
+ #include <errno.h>
++#include <netdb.h>
+
+ // C++ includes
+ #include <cstdio>
+@@ -678,6 +679,8 @@
+ {
+ struct context *sctx;
+ int rv;
++ struct sockaddr_in localhost;
++
+
+ debug(D_FUNC, "mlfi_connect: enter");
+
+@@ -687,10 +690,15 @@
+ {
+ /* 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;
+@@ -704,10 +712,10 @@
+ }
+ /* debug(D_ALWAYS, "ZZZ set private context to %p", sctx); */
+
+- if (ip_in_networklist(sctx->connect_ip, &ignorenets))
++ 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;
+ }
+@@ -779,7 +787,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;
+@@ -2033,69 +2041,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;
+
+- if (tmask)
++ {
++ char *snet = strdup(inet_ntoa(net));
++ debug(D_MISC, "Adding %s/%s to network list", snet, inet_ntoa(mask));
++ free(snet);
++ }
++
++ 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;
++ }
+ }
+ }
+
+Index: spamass-milter/spamass-milter.h
+===================================================================
+--- spamass-milter.orig/spamass-milter.h 2012-06-21 13:03:25.000000000 -0700
++++ spamass-milter/spamass-milter.h 2012-06-21 13:03:42.000000000 -0700
+@@ -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);
--- /dev/null
+Index: spamass-milter/spamass-milter.cpp
+===================================================================
+--- spamass-milter.orig/spamass-milter.cpp 2012-06-21 13:03:42.000000000 -0700
++++ spamass-milter/spamass-milter.cpp 2012-06-21 14:02:32.000000000 -0700
+@@ -898,7 +898,8 @@
+
+ */
+ const char *macro_b, *macro_i, *macro_j, *macro_r,
+- *macro_s, *macro_v, *macro_Z, *macro__;
++ *macro_s, *macro_v, *macro_Z, *macro__,
++ *macro_auth_type, *macro_auth_ssf;
+ char date[32];
+
+ /* RFC 822 date. */
+@@ -935,7 +936,22 @@
+ macro_r = "SMTP";
+ warnmacro("r", "ENVRCPT");
+ }
+-
++
++ /* Authenticated bits Information */
++ macro_auth_type = smfi_getsymval(ctx, "{auth_type}");
++ if (!macro_auth_type)
++ {
++ macro_auth_type = "";
++ warnmacro("auth_type", "ENVRCPT");
++ }
++
++ macro_auth_ssf = smfi_getsymval(ctx, "{auth_ssf}");
++ if (!macro_auth_ssf)
++ {
++ macro_auth_ssf = "";
++ warnmacro("auth_ssf", "ENVRCPT");
++ }
++
+ /* Sendmail currently cannot pass us the {s} macro, but
+ I do not know why. Leave this in for the day sendmail is
+ fixed. Until that day, use the value remembered by
+@@ -974,11 +990,22 @@
+ assassin->output((string)"X-Envelope-From: "+assassin->from()+"\r\n");
+ assassin->output((string)"X-Envelope-To: "+envrcpt[0]+"\r\n");
+
+- assassin->output((string)
+- "Received: from "+macro_s+" ("+macro__+")\r\n\t"+
+- "by "+macro_j+" ("+macro_v+"/"+macro_Z+") with "+macro_r+" id "+macro_i+"\r\n\t"+
++ string rec_header;
++ rec_header=(string)"Received: from "+macro_s+" ("+macro__+")\r\n\t";
++ if (strlen(macro_auth_type)!=0) {
++ rec_header+=(string)"(authenticated";
++ if (strlen(macro_auth_ssf)!=0) {
++ rec_header+=(string)" bits="+macro_auth_ssf;
++ }
++ rec_header+=(string)")\r\n\t";
++ }
++ rec_header+=(string)"by "+macro_j+"("+macro_v+"/"+macro_Z+") with "+
++ macro_r+" id "+macro_i+"\r\n\t"+
+ macro_b+"\r\n\t"+
+- "(envelope-from "+assassin->from()+")\r\n");
++ "(envelope-from "+assassin->from()+"\r\n";
++
++ debug(D_SPAMC, "Received header for spamc: %s", rec_header.c_str());
++ assassin->output(rec_header);
+
+ } else
+ assassin->output((string)"X-Envelope-To: "+envrcpt[0]+"\r\n");
--- /dev/null
+Index: spamass-milter/spamass-milter.cpp
+===================================================================
+--- spamass-milter.orig/spamass-milter.cpp 2012-06-21 13:02:08.000000000 -0700
++++ spamass-milter/spamass-milter.cpp 2012-06-21 13:02:15.000000000 -0700
+@@ -363,7 +363,7 @@
+ // }}}
+
+ /* Update a header if SA changes it, or add it if it is new. */
+-void update_or_insert(SpamAssassin* assassin, SMFICTX* ctx, string oldstring, t_setter setter, char *header )
++void update_or_insert(SpamAssassin* assassin, SMFICTX* ctx, string oldstring, t_setter setter, const char *header )
+ {
+ string::size_type eoh1 = assassin->d().find("\n\n");
+ string::size_type eoh2 = assassin->d().find("\n\r\n");
+@@ -386,15 +386,16 @@
+ {
+ /* change if old one was present, append if non-null */
+ char* cstr = const_cast<char*>(newstring.c_str());
++ char* hstr = const_cast<char*>(header);
+ if (oldsize > 0)
+ {
+ debug(D_UORI, "u_or_i: changing");
+- smfi_chgheader(ctx, header, 1, newstring.size() > 0 ?
++ smfi_chgheader(ctx, hstr, 1, newstring.size() > 0 ?
+ cstr : NULL );
+ } else if (newstring.size() > 0)
+ {
+ debug(D_UORI, "u_or_i: inserting");
+- smfi_addheader(ctx, header, cstr);
++ smfi_addheader(ctx, hstr, cstr);
+ }
+ } else
+ {
--- /dev/null
+Index: spamass-milter/spamass-milter.cpp
+===================================================================
+--- spamass-milter.orig/spamass-milter.cpp 2012-06-21 13:02:06.000000000 -0700
++++ spamass-milter/spamass-milter.cpp 2012-06-21 13:02:08.000000000 -0700
+@@ -967,7 +967,7 @@
+
+ assassin->output((string)
+ "Received: from "+macro_s+" ("+macro__+")\r\n\t"+
+- "by "+macro_j+"("+macro_v+"/"+macro_Z+") with "+macro_r+" id "+macro_i+";\r\n\t"+
++ "by "+macro_j+" ("+macro_v+"/"+macro_Z+") with "+macro_r+" id "+macro_i+"\r\n\t"+
+ macro_b+"\r\n\t"+
+ "(envelope-from "+assassin->from()+")\r\n");
+
--- /dev/null
+Index: spamass-milter/spamass-milter.1.in
+===================================================================
+--- spamass-milter.orig/spamass-milter.1.in 2012-06-21 12:53:44.000000000 -0700
++++ spamass-milter/spamass-milter.1.in 2012-06-21 12:56:56.000000000 -0700
+@@ -14,6 +14,7 @@
+ .Op Fl e Ar defaultdomain
+ .Op Fl f
+ .Op Fl i Ar networks
++.Op Fl I
+ .Op Fl m
+ .Op Fl M
+ .Op Fl P Ar pidfile
+@@ -119,6 +120,8 @@
+ flags will append to the list.
+ For example, if you list all your internal networks, no outgoing emails
+ will be filtered.
++.It Fl I
++Ignores messages if the sender has authenticated via SMTP AUTH.
+ .It Fl m
+ Disables modification of the
+ .Ql Subject:
+Index: spamass-milter/spamass-milter.cpp
+===================================================================
+--- spamass-milter.orig/spamass-milter.cpp 2012-06-21 12:53:49.000000000 -0700
++++ spamass-milter/spamass-milter.cpp 2012-06-21 12:56:56.000000000 -0700
+@@ -169,6 +169,7 @@
+ char *spambucket;
+ bool flag_full_email = false; /* pass full email address to spamc */
+ bool flag_expand = false; /* alias/virtusertable expansion */
++bool ignore_authenticated_senders = false;
+ bool warnedmacro = false; /* have we logged that we couldn't fetch a macro? */
+
+ // {{{ main()
+@@ -177,7 +178,7 @@
+ main(int argc, char* argv[])
+ {
+ int c, err = 0;
+- const char *args = "fd:mMp:P:r:u:D:i:b:B:e:x";
++ const char *args = "fd:mMp:P:r:u:D:i:Ib:B:e:x";
+ char *sock = NULL;
+ bool dofork = false;
+ char *pidfilename = NULL;
+@@ -209,6 +210,10 @@
+ debug(D_MISC, "Parsing ignore list");
+ parse_networklist(optarg, &ignorenets);
+ break;
++ case 'I':
++ debug(D_MISC, "Ignore authenticated senders");
++ ignore_authenticated_senders = true;
++ break;
+ case 'm':
+ dontmodifyspam = true;
+ smfilter.xxfi_flags &= ~SMFIF_CHGBODY;
+@@ -276,7 +281,7 @@
+ cout << PACKAGE_NAME << " - Version " << PACKAGE_VERSION << endl;
+ cout << "SpamAssassin Sendmail Milter Plugin" << endl;
+ cout << "Usage: spamass-milter -p socket [-b|-B bucket] [-d xx[,yy...]] [-D host]" << endl;
+- cout << " [-e defaultdomain] [-f] [-i networks] [-m] [-M]" << endl;
++ cout << " [-e defaultdomain] [-f] [-i networks] [-I] [-m] [-M]" << endl;
+ cout << " [-P pidfile] [-r nn] [-u defaultuser] [-x]" << endl;
+ cout << " [-- spamc args ]" << endl;
+ cout << " -p socket: path to create socket" << endl;
+@@ -290,6 +295,7 @@
+ cout << " -f: fork into background" << endl;
+ cout << " -i: skip (ignore) checks from these IPs or netblocks" << endl;
+ cout << " example: -i 192.168.12.5,10.0.0.0/8,172.16.0.0/255.255.0.0" << endl;
++ cout << " -I: skip (ignore) checks if sender is authenticated" << endl;
+ cout << " -m: don't modify body, Content-type: or Subject:" << endl;
+ cout << " -M: don't modify the message at all" << endl;
+ cout << " -P pidfile: Put processid in pidfile" << endl;
+@@ -746,6 +753,22 @@
+ }
+ /* debug(D_ALWAYS, "ZZZ got private context %p", sctx); */
+
++ if (ignore_authenticated_senders)
++ {
++ char *auth_authen;
++
++ auth_authen = smfi_getsymval(ctx, "{auth_authen}");
++ debug(D_MISC, "auth_authen=%s", auth_authen ?: "<unauthenticated>");
++
++ if (auth_authen)
++ {
++ debug(D_MISC, "sender authenticated (%s) - accepting message",
++ auth_authen);
++ debug(D_FUNC, "mlfi_envfrom: exit ignore");
++ return SMFIS_ACCEPT;
++ }
++ }
++
+ debug(D_FUNC, "mlfi_envfrom: enter");
+ try {
+ // launch new SpamAssassin
--- /dev/null
+ignore_by_smtp_auth
+fix_spacing_in_received_header
+fix_header_char_cast
+add_ipv6_support
+auth_in_received