# HG changeset patch # User carl # Date 1127063998 25200 # Node ID 962a1f8f1d9f8a4580fa661e83c0879398b95cc3 # Parent 946fc1bcfb2c2399be3554cbec621a2a4046b367 add verify statement to verify addresses with better mx host diff -r 946fc1bcfb2c -r 962a1f8f1d9f ChangeLog --- a/ChangeLog Sun Aug 07 11:26:37 2005 -0700 +++ b/ChangeLog Sun Sep 18 10:19:58 2005 -0700 @@ -1,5 +1,12 @@ $Id$ +5.4 2005-09-18 + Add verify statement to specify the smtp host to be used to verify + envelope from / recipient pairs. + + Authenticated clients are now exempt from all white/black listing + in addition to the dnsbl lookups. + 5.3 2005-08-07 Properly quit if the config file has syntax errors on startup. Send mail to root if the config file needs to be reloaded since it was diff -r 946fc1bcfb2c -r 962a1f8f1d9f dnsbl.spec.in --- a/dnsbl.spec.in Sun Aug 07 11:26:37 2005 -0700 +++ b/dnsbl.spec.in Sun Sep 18 10:19:58 2005 -0700 @@ -1,6 +1,6 @@ Summary: DNSBL Sendmail Milter Name: dnsbl -Version: 5.3 +Version: 5.4 Release: 2 Copyright: GPL Group: System Environment/Daemons diff -r 946fc1bcfb2c -r 962a1f8f1d9f package.bash --- a/package.bash Sun Aug 07 11:26:37 2005 -0700 +++ b/package.bash Sun Sep 18 10:19:58 2005 -0700 @@ -1,6 +1,6 @@ #!/bin/bash -VER=dnsbl-5.3 +VER=dnsbl-5.4 mkdir $VER target1=/home/httpd/html/510sg/util/dnsbl.tar.gz target2=/home/httpd/html/510sg/dnsbl.conf diff -r 946fc1bcfb2c -r 962a1f8f1d9f sendmail.st Binary file sendmail.st has changed diff -r 946fc1bcfb2c -r 962a1f8f1d9f src/context.cpp --- a/src/context.cpp Sun Aug 07 11:26:37 2005 -0700 +++ b/src/context.cpp Sun Sep 18 10:19:58 2005 -0700 @@ -8,6 +8,16 @@ #include "includes.h" +// needed for socket io +#include +#include +#include +#include +#include +#include +#include +#include + static char* context_version="$Id$"; char *token_black; @@ -40,10 +50,290 @@ char *token_substitute; char *token_tld; char *token_unknown; +char *token_verify; char *token_white; +char *token_myhostname; +char myhostname[HOST_NAME_MAX+1]; + +verify_map verifiers; string_set all_strings; // owns all the strings, only modified by the config loader thread -const int maxlen = 1000; +const int maxlen = 1000; // used for snprintf buffers +const int maxage = 120; // smtp verify sockets older than this are ancient +extern int NULL_SOCKET; +extern time_t ERROR_SOCKET_TIME; // number of seconds between attempts to open a socket an smtp host for address verification + + +int SMTP::writer() { + // log("writer() sees buffer with %s", buffer); + // log("writer() sees error %d", (int)error); + int rs = 0; + if (!error) { + int len = strlen(buffer); + while (rs < len) { + int ws = write(fd, buffer+rs, len-rs); + if (ws > 0) { + rs += ws; + } + else { + // peer closed the socket! + rs = 0; + error = true; + break; + } + } + } + return rs; +} + + +int SMTP::reader() { + // read some bytes terminated by lf or end of buffer. + // we may have a multi line response or part thereof in the buffer. + // log("reader() sees error %d", (int)error); + if (error) return 0; + int len = maxlen-1; // room for null terminator + while (pending < len) { + int ws = read(fd, buffer+pending, len-pending); + if (ws > 0) { + pending += ws; + if (buffer[pending-1] == '\n') break; + } + else { + // peer closed the socket! + pending = 0; + error = true; + break; + } + } + buffer[pending] = '\0'; + // log("reader() sees buffer with %s", buffer); + return pending; +} + + +int SMTP::read_line() { + char *lf = strchr(buffer, '\n'); + if (!lf) { + reader(); // get a lf + lf = strchr(buffer, '\n'); + if (!lf) lf = buffer + pending - 1; + } + return (lf-buffer)+1; // number of bytes in this line +} + + +int SMTP::flush_line(int r) { + if (pending > r) memmove(buffer, buffer+r, pending-r); + pending -= r; +} + + +int SMTP::read_response() { + pending = 0; + buffer[pending] = '\0'; + while (true) { + int r = read_line(); + // log("read_response() sees line with %s", buffer); + // log("read_response() sees line length %d", r); + if (r == 0) return 0; // failed to read any bytes + if ((r > 4) && (buffer[3] == '-')) { + flush_line(r); + continue; + } + return atoi(buffer); + } + return 0; +} + + +int SMTP::cmd(char *c) { + if (c) { + init(); + append(c); + } + append("\r\n"); + writer(); + return read_response(); +} + + +int SMTP::helo() { + if (read_response() != 220) return 0; + init(); + append("HELO "); + append(token_myhostname); + return cmd(NULL); +} + + +int SMTP::rset() { + return cmd("RSET"); +} + + +int SMTP::from(char *f) { + init(); + append("MAIL FROM:<"); + append(f); + append(">"); + return cmd(NULL); +} + + +int SMTP::rcpt(char *t) { + init(); + append("RCPT TO:<"); + append(t); + append(">"); + return cmd(NULL); +} + + +int SMTP::quit() { + int rc = cmd("QUIT"); + shutdown(fd, SHUT_RDWR); + close(fd); + return rc; +} + + +// void SMTP::log(char *m, int v) { +// char buf[maxlen]; +// snprintf(buf, maxlen, m, v); +// my_syslog(buf); +// } +// +// +// void SMTP::log(char *m, char *v) { +// char buf[maxlen]; +// snprintf(buf, maxlen, m, v); +// my_syslog(buf); +// } +// +// +VERIFY::VERIFY(char *h) { + host = h; + last_err = 0; + pthread_mutex_init(&mutex, 0); +} + + +void VERIFY::closer() { + bool ok = true; + while (ok) { + int fd = 0; + pthread_mutex_lock(&mutex); + if (sockets.empty()) { + ok = false; + } + else { + time_t t = times.front(); + time_t now = time(NULL); + if ((now - t) > maxage) { + // this socket is ancient, remove it + fd = sockets.front(); + times.pop_front(); + sockets.pop_front(); + } + else { + ok = false; + } + } + pthread_mutex_unlock(&mutex); + if (fd) { + SMTP s(fd); + s.quit(); // closes the fd + // s.log("closer() closes ancient %d", fd); + } + } +} + + +int VERIFY::get_socket() { + int sock = NULL_SOCKET; + pthread_mutex_lock(&mutex); + if (!sockets.empty()) { + sock = sockets.front(); + times.pop_front(); + sockets.pop_front(); + // SMTP::log("get_socket() %d from cache", sock); + } + pthread_mutex_unlock(&mutex); + + if (sock == NULL_SOCKET) { + time_t now = time(NULL); + if ((now - last_err) > ERROR_SOCKET_TIME) { + // nothing recent, maybe this time it will work + hostent *h = gethostbyname(host); + if (h) { + sockaddr_in server; + server.sin_family = h->h_addrtype; + server.sin_port = htons(25); + memcpy(&server.sin_addr, h->h_addr_list[0], h->h_length); + sock = socket(PF_INET, SOCK_STREAM, 0); + if (sock != NULL_SOCKET) { + bool rc = (connect(sock, (sockaddr *)&server, sizeof(server)) == 0); + if (!rc) { + shutdown(sock, SHUT_RDWR); + close(sock); + sock = NULL_SOCKET; + last_err = now; + } + } + else last_err = now; + } + else last_err = now; + } + if (sock != NULL_SOCKET) { + SMTP s(sock); + if (s.helo() != 250) { + put_socket(sock, true); + sock = NULL_SOCKET; + } + } + } + return sock; +} + + +void VERIFY::put_socket(int fd, bool err) { + if (err) { + // SMTP::log("put_socket() %d with error, close it", fd); + shutdown(fd, SHUT_RDWR); + close(fd); + last_err = time(NULL); + } + else { + // SMTP::log("put_socket() %d", fd); + pthread_mutex_lock(&mutex); + time_t now = time(NULL); + times.push_back(now); + sockets.push_back(fd); + pthread_mutex_unlock(&mutex); + } +} + + +bool VERIFY::ok(char *from, char *to) { + if (host == token_myhostname) return true; + int fd = get_socket(); + if (fd == NULL_SOCKET) return true; // cannot verify right now, we have socket errors + SMTP s(fd); + s.rset(); + int rc; + rc = s.from(from); + // s.log("verify::ok from sees %d", rc); + if (rc != 250) { + put_socket(fd, s.err()); + return (rc >= 500) ? false : true; + } + rc = s.rcpt(to); + // s.log("verify::ok rcpt sees %d", rc); + put_socket(fd, s.err()); + return (rc >= 500) ? false : true; +} + DNSBL::DNSBL(char *n, char *s, char *m) { name = n; @@ -155,6 +445,7 @@ CONTEXT::CONTEXT(CONTEXTP parent_, char *name_) { parent = parent_; name = name_; + verify_host = NULL; env_from_default = (parent) ? token_inherit : token_unknown; content_filtering = (parent) ? parent->content_filtering : false; content_suffix = NULL; @@ -203,6 +494,26 @@ } +VERIFYP CONTEXT::find_verify(char *to) { + if (verify_host && (verify_host != token_myhostname) && cover_env_to(to)) { + verify_map::iterator i = verifiers.find(verify_host); + if (i == verifiers.end()) { + if (debug_syslog) { + char buf[maxlen]; + snprintf(buf, maxlen, "cannot find struc for %s", verify_host); + my_syslog(buf); + } + return NULL; + } + VERIFYP v = (*i).second; + + return v; + } + else if (parent) return parent->find_verify(to); + else return NULL; +} + + char *CONTEXT::find_from(char *from) { char *rc = token_inherit; string_map::iterator i = env_from.find(from); @@ -389,6 +700,10 @@ } printf("%s }; \n", indent); + if (verify_host) { + printf("%s verify %s; \n", indent, verify_host); + } + for (context_map::iterator i=children.begin(); i!=children.end(); i++) { CONTEXTP c = (*i).second; c->dump(level+1); @@ -682,6 +997,17 @@ //////////////////////////////////////////////// // +bool parse_verify(TOKEN &tok, CONFIG &dc, CONTEXT &me); +bool parse_verify(TOKEN &tok, CONFIG &dc, CONTEXT &me) { + char *host = tok.next(); + if (!tsa(tok, token_semi)) return false; + me.set_verify(host); + add_verify_host(host); +} + + +//////////////////////////////////////////////// +// bool parse_envfrom(TOKEN &tok, CONFIG &dc, CONTEXT &me); bool parse_envfrom(TOKEN &tok, CONFIG &dc, CONTEXT &me) { char *st = tok.next(); @@ -782,6 +1108,9 @@ else if (have == token_envto) { if (!parse_envto(tok, dc, *con)) return false; } + else if (have == token_verify) { + if (!parse_verify(tok, dc, *con)) return false; + } else if (have == token_envfrom) { if (!parse_envfrom(tok, dc, *con)) return false; } @@ -827,6 +1156,33 @@ //////////////////////////////////////////////// +// setup a new smtp verify host +// +void add_verify_host(char *host) { + verify_map::iterator i = verifiers.find(host); + if (i == verifiers.end()) { + VERIFYP v = new VERIFY(host); + verifiers[host] = v; + } +} + + +//////////////////////////////////////////////// +// thread to check for verify hosts with old sockets that we can close +// +void* verify_closer(void *arg) { + while (true) { + sleep(maxage); + for (verify_map::iterator i=verifiers.begin(); i!=verifiers.end(); i++) { + VERIFYP v = (*i).second; + v->closer(); + } + } + return NULL; +} + + +//////////////////////////////////////////////// // init the tokens // void token_init() { @@ -860,5 +1216,12 @@ token_substitute = register_string("substitute"); token_tld = register_string("tld"); token_unknown = register_string("unknown"); + token_verify = register_string("verify"); token_white = register_string("white"); + + if (gethostname(myhostname, HOST_NAME_MAX+1) != 0) { + strncpy(myhostname, "localhost", HOST_NAME_MAX+1); } + myhostname[HOST_NAME_MAX] = '\0'; // ensure null termination + token_myhostname = register_string(myhostname); +} diff -r 946fc1bcfb2c -r 962a1f8f1d9f src/context.h --- a/src/context.h Sun Aug 07 11:26:37 2005 -0700 +++ b/src/context.h Sun Sep 18 10:19:58 2005 -0700 @@ -12,18 +12,63 @@ class DNSBL; class CONTEXT; +class VERIFY; class recorder; typedef map string_map; typedef set int_set; +typedef list fd_list; +typedef list time_list; typedef list string_list; typedef DNSBL * DNSBLP; +typedef VERIFY * VERIFYP; typedef list dnsblp_list; typedef map dnsblp_map; typedef CONTEXT * CONTEXTP; typedef list context_list; typedef map context_map; typedef map ns_mapper; +typedef map verify_map; + +class SMTP { + static const int maxlen = 1000; + int fd; + bool error; + int pending; // unread bytes in buffer, not including the null terminator + char buffer[maxlen]; +public: + SMTP(int f) {fd = f; error = false;}; + void init() {pending = 0; buffer[0] = '\0';}; + void append(char *c) {strncat(buffer, c, max(0, maxlen-1-(int)strlen(c)));}; + bool err() {return error;}; + int writer(); + int reader(); + int read_line(); + int read_response(); + int flush_line(int r); + int cmd(char *c); + int helo(); + int rset(); + int from(char *f); + int rcpt(char *t); + int quit(); + // static void log(char *m, int v); + // static void log(char *m, char *v); +}; + +class VERIFY { + char *host; // host to be used to verify recipient addresses + time_t last_err; // time of last socket error + pthread_mutex_t mutex; // protect the lists of sockets and timestamps + fd_list sockets; // open sockets, ready to be used + time_list times; // last timestamp when this socket was used +public: + VERIFY(char *h); + void closer(); // if the oldest socket is ancient, close it + int get_socket(); + void put_socket(int fd, bool err); + bool ok(char *from, char *to); +}; struct DNSBL { char *name; // nickname for this dns based list @@ -37,7 +82,8 @@ CONTEXTP parent; char * name; context_map children; // map child context names to their contexts - string_set env_to; // + string_set env_to; // this context applies to these envelope recipients + char * verify_host; // use this smtp host to verify email addresses string_map env_from; // map senders to white/black/unknown context_map env_from_context; // map senders to a child context char * env_from_default; // default value for senders that are not found in the map white/black/unknown/inherit @@ -65,6 +111,10 @@ bool allow_env_to(char *to) {return (parent) ? parent->cover_env_to(to) : true;}; bool cover_env_to(char *to); + void set_verify(char *host) {verify_host = host;}; + char* get_verify() {return verify_host;}; + VERIFYP find_verify(char *to); + void add_to(char *to) {env_to.insert(to);}; void add_from(char *from, char *status) {env_from[from] = status;}; void add_from_context(char *from, CONTEXTP con) {env_from_context[from] = con;}; @@ -159,6 +209,9 @@ extern char *token_unknown; extern char *token_white; +extern char *token_myhostname; + +extern verify_map verifiers; // map of smtp hosts to verify structures, owns all the verify structures extern string_set all_strings; // owns all the strings, only modified by the config loader thread void discard(string_set &s); @@ -166,6 +219,8 @@ char* register_string(char *name); CONFIG *parse_config(char *fn); bool load_conf(CONFIG &dc, char *fn); +void add_verify_host(char *host); +void* verify_closer(void *arg); void token_init(); #endif diff -r 946fc1bcfb2c -r 962a1f8f1d9f src/dnsbl.cpp --- a/src/dnsbl.cpp Sun Aug 07 11:26:37 2005 -0700 +++ b/src/dnsbl.cpp Sun Sep 18 10:19:58 2005 -0700 @@ -17,13 +17,6 @@ -e f|t Print the results of looking up from address f and to address t in the current config - -TODO: - -1) Add option for using smtp connections to verify addresses from backup -mx machines. This allows the backup mx to learn the valid addresses -on the primary machine. - */ @@ -98,7 +91,7 @@ int NULL_SOCKET = -1; char *resolver_port = NULL; // unix domain socket to talk to the dns resolver process int resolver_socket = NULL_SOCKET; // socket used to listen for resolver requests -time_t ERROR_SOCKET_TIME = 60; // number of seconds between attempts to open the spam filter socket +time_t ERROR_SOCKET_TIME = 60; // number of seconds between attempts to open a socket to the dns resolver process time_t last_error_time; int resolver_sock_count = 0; // protected with fd_pool_mutex int resolver_pool_size = 0; // protected with fd_pool_mutex @@ -430,7 +423,7 @@ while (true) { // read a question int rs = 0; - while (true) { + while (rs < maxq) { int ns = read(socket, question+rs, maxq-rs); if (ns > 0) { rs += ns; @@ -447,6 +440,7 @@ return; } } + question[rs-1] = '\0'; // ensure null termination // find the answer #ifdef NS_PACKETSZ @@ -627,7 +621,6 @@ // bool check_dnsbl(mlfiPriv &priv, dnsblp_list &dnsbll, DNSBLP &rejectlist); bool check_dnsbl(mlfiPriv &priv, dnsblp_list &dnsbll, DNSBLP &rejectlist) { - if (priv.authenticated) return false; for (dnsblp_list::iterator i=dnsbll.begin(); i!=dnsbll.end(); i++) { DNSBLP dp = *i; // non null by construction bool st; @@ -805,16 +798,20 @@ char *rcptaddr = rcpt[0]; char *loto = to_lower_string(rcptaddr); CONTEXT &con = *(dc.find_context(loto)->find_context(priv.mailaddr)); + VERIFYP ver = con.find_verify(loto); if (debug_syslog > 1) { char buf[maxlen]; char msg[maxlen]; snprintf(msg, sizeof(msg), "from <%s> to <%s> using context %s", priv.mailaddr, loto, con.get_full_name(buf,maxlen)); my_syslog(&priv, msg); } + free(loto); char *fromvalue = con.find_from(priv.mailaddr); - free(loto); status st; - if (fromvalue == token_black) { + if (priv.authenticated) { + st = white; + } + else if (fromvalue == token_black) { st = black; } else if (fromvalue == token_white) { @@ -834,12 +831,21 @@ smfi_setreply(ctx, "550", "5.7.1", buf); return SMFIS_REJECT; } - else if (st == black) { + if (st == black) { // reject the recipient based on blacklisting either from or to smfi_setreply(ctx, "550", "5.7.1", "no such user"); return SMFIS_REJECT; } - else { + if (ver && (st != white)) { + // try to verify this from/to pair of addresses since it is not explicitly whitelisted + char *loto = to_lower_string(rcptaddr); + bool rc = ver->ok(priv.mailaddr, loto); + free(loto); + if (!rc) { + smfi_setreply(ctx, "550", "5.7.1", "no such user"); + return SMFIS_REJECT; + } + } // accept the recipient if (!con.get_content_filtering()) st = white; if (st == oksofar) { @@ -852,7 +858,6 @@ } return SMFIS_CONTINUE; } -} sfsistat mlfi_body(SMFICTX *ctx, u_char *data, size_t len) { @@ -1055,7 +1060,7 @@ fprintf(stderr, "-c will load and dump the config to stdout\n"); fprintf(stderr, "-s will stress test the config loading code by repeating the load/free cycle\n"); fprintf(stderr, " in an infinte loop.\n"); - fprintf(stderr, "-d will set the syslog message level, currently 0 to 3"); + fprintf(stderr, "-d will set the syslog message level, currently 0 to 3\n"); fprintf(stderr, "-e will print the results of looking up the from and to addresses in the\n"); fprintf(stderr, " current config. The | character is used to separate the from and to\n"); fprintf(stderr, " addresses in the argument to the -e switch\n"); @@ -1358,6 +1363,10 @@ my_syslog("failed to create config loader thread"); if (pthread_detach(tid)) my_syslog("failed to detach config loader thread"); + if (pthread_create(&tid, 0, verify_closer, 0)) + my_syslog("failed to create verify closer thread"); + if (pthread_detach(tid)) + my_syslog("failed to detach verify closer thread"); time_t starting = time(NULL); int rc = smfi_main(); diff -r 946fc1bcfb2c -r 962a1f8f1d9f test.bash --- a/test.bash Sun Aug 07 11:26:37 2005 -0700 +++ b/test.bash Sun Sep 18 10:19:58 2005 -0700 @@ -33,10 +33,7 @@ fi fi -if [ "$1" == "comp" ]; then - exit -fi - +if [ "$1" == "test" ]; then # build the test.cf file make test.cf @@ -60,3 +57,4 @@ echo eventually "'"kill -KILL $P2 $P3"'" +fi diff -r 946fc1bcfb2c -r 962a1f8f1d9f test.cf --- a/test.cf Sun Aug 07 11:26:37 2005 -0700 +++ b/test.cf Sun Sep 18 10:19:58 2005 -0700 @@ -1,5 +1,5 @@ # -# Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers. +# Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers. # All rights reserved. # Copyright (c) 1983, 1995 Eric P. Allman. All rights reserved. # Copyright (c) 1988, 1993 @@ -16,8 +16,8 @@ ##### ##### SENDMAIL CONFIGURATION FILE ##### -##### built by root@ns.five-ten-sg.com on Mon Jan 3 13:23:43 PST 2005 -##### in /usr/src/rh8/gpl/dnsbl +##### built by root@ns.five-ten-sg.com on Sat Sep 17 18:06:39 PDT 2005 +##### in /usr/usr/cvs/gpl/dnsbl ##### using /usr/share/sendmail-cf/ as configuration include directory ##### ###################################################################### @@ -140,6 +140,7 @@ # ... define this only if sendmail cannot automatically determine your domain #Dj$w.Foo.COM +# host/domain names ending with a token in class P are canonical CP. # "Smart" relay host (may be null) @@ -172,7 +173,7 @@ # macro storage map Kmacro macro # possible values for TLS_connection in access map -C{tls}VERIFY ENCR +C{Tls}VERIFY ENCR @@ -212,7 +213,7 @@ Kgenerics hash /etc/mail/genericstable.db # Configuration version number -DZ8.12.8 +DZ8.13.1 ############### @@ -385,10 +386,12 @@ #O Timeout.queuereturn.normal=5d #O Timeout.queuereturn.urgent=2d #O Timeout.queuereturn.non-urgent=7d +#O Timeout.queuereturn.dsn=5d O Timeout.queuewarn=4h #O Timeout.queuewarn.normal=4h #O Timeout.queuewarn.urgent=1h #O Timeout.queuewarn.non-urgent=12h +#O Timeout.queuewarn.dsn=4h #O Timeout.hoststatus=30m #O Timeout.resolver.retrans=5s #O Timeout.resolver.retrans.first=5s @@ -410,7 +413,7 @@ O SuperSafe=True # status file -O StatusFile=/usr/src/rh8/gpl/dnsbl/sendmail.st +O StatusFile=/usr/usr/cvs/gpl/dnsbl/sendmail.st # time zone handling: # if undefined, use system default @@ -427,6 +430,9 @@ # fallback MX host #O FallbackMXhost=fall.back.host.net +# fallback smart host +#O FallbackSmartHost=fall.back.host.net + # if we are the best MX host for a site, try it directly instead of config err #O TryNullMXList=False @@ -436,6 +442,9 @@ # load average at which we refuse connections O RefuseLA=8 +# log interval when refusing connections for this long +#O RejectLogInterval=3h + # load average at which we delay connections; 0 means no limit #O DelayLA=0 @@ -445,6 +454,9 @@ # maximum number of new connections per second O ConnectionRateThrottle=1 +# Width of the window +#O ConnectionRateWindowSize=60s + # work recipient factor #O RecipientFactor=30000 @@ -517,7 +529,7 @@ #O RunAsUser=sendmail # maximum number of recipients per SMTP envelope -#O MaxRecipientsPerMessage=100 +#O MaxRecipientsPerMessage=0 # limit the rate recipients per SMTP envelope are accepted # once the threshold number of recipients have been rejected @@ -562,9 +574,15 @@ # lookup type to find information about local mailboxes #O MailboxDatabase=pw +# override compile time flag REQUIRES_DIR_FSYNC +#O RequiresDirfsync=true + # list of authentication mechanisms O AuthMechanisms=LOGIN PLAIN +# Authentication realm +#O AuthRealm + # default authentication information for outgoing connections #O DefaultAuthInfo=/etc/mail/default-auth-info @@ -586,6 +604,7 @@ O Milter.macros.helo={tls_version}, {cipher}, {cipher_bits}, {cert_subject}, {cert_issuer} O Milter.macros.envfrom=i, {auth_type}, {auth_authen}, {auth_ssf}, {auth_author}, {mail_mailer}, {mail_host}, {mail_addr} O Milter.macros.envrcpt={rcpt_mailer}, {rcpt_host}, {rcpt_addr} +O Milter.macros.eom={msg_id} # CA directory #O CACertPath @@ -599,6 +618,8 @@ #O ClientCertFile # Client private key #O ClientKeyFile +# File containing certificate revocation lists +#O CRLFile # DHParameters (only required if DSA/DH is used) #O DHParameters # Random data source (required for systems without /dev/urandom under OpenSSL) @@ -858,7 +879,7 @@ # handle numeric address spec R$* < @ [ $+ ] > $* $: $>ParseLocal $1 < @ [ $2 ] > $3 numeric internet spec -R$* < @ [ $+ ] > $* $1 < @ [ $2 ] : $S > $3 Add smart host to path +R$* < @ [ $+ ] > $* $: $1 < @ [ $2 ] : $S > $3 Add smart host to path R$* < @ [ $+ ] : > $* $#esmtp $@ [$2] $: $1 < @ [$2] > $3 no smarthost: send R$* < @ [ $+ ] : $- : $*> $* $#$3 $@ $4 $: $1 < @ [$2] > $5 smarthost with mailer R$* < @ [ $+ ] : $+ > $* $#esmtp $@ $3 $: $1 < @ [$2] > $4 smarthost without mailer @@ -973,7 +994,8 @@ SMailerToTriple=95 R< > $* $@ $1 strip off null relay R< error : $-.$-.$- : $+ > $* $#error $@ $1.$2.$3 $: $4 -R< error : $- $+ > $* $#error $@ $(dequote $1 $) $: $2 +R< error : $- : $+ > $* $#error $@ $(dequote $1 $) $: $2 +R< error : $+ > $* $#error $: $1 R< local : $* > $* $>CanonLocal < $1 > $2 R< $~[ : $+ @ $+ > $*<$*>$* $# $1 $@ $3 $: $2<@$3> use literal user R< $~[ : $+ > $* $# $1 $@ $2 $: $3 try qualified mailer @@ -1168,6 +1190,7 @@ R<$={Accept}> <$*> $@ $1 return value of lookup R <$*> $#error $@ 5.7.1 $: "550 Access denied" R <$*> $#discard $: discard +R <$*> $#error $@ quarantine $: $1 R <$*> $#error $@ $1.$2.$3 $: $4 R <$*> $#error $: $1 R<$* > <$*> $#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later." @@ -1249,6 +1272,7 @@ R $* $#error $@ 5.1.8 $: "553 Domain of sender address " $&f " does not exist" R<$={Accept}> $* $# $1 accept from access map R $* $#discard $: discard +R $* $#error $@ quarantine $: $1 R $* $#error $@ 5.7.1 $: "550 Access denied" R $* $#error $@ $1.$2.$3 $: $4 R $* $#error $: $1 @@ -1352,7 +1376,7 @@ # check client name: first: did it resolve? R$* $: < $&{client_resolve} > -R $#TEMP $@ 4.7.1 $: "450 Relaying temporarily denied. Cannot resolve PTR record for " $&{client_addr} +R $#TEMP $@ 4.4.0 $: "450 Relaying temporarily denied. Cannot resolve PTR record for " $&{client_addr} R $#error $@ 5.7.1 $: "550 Relaying denied. IP name possibly forged " $&{client_name} R $#error $@ 5.7.1 $: "550 Relaying denied. IP name lookup failed " $&{client_name} R$* $: <@> $&{client_name} @@ -1375,16 +1399,18 @@ R$* <@ $* > $@ $1 <@ $2 > R$+ $@ $1 <@ $j > -SDelay_TLS_Client +SDelay_TLS_Clt # authenticated? R$* $: $1 $| $>"tls_client" $&{verify} $| MAIL R$* $| $#$+ $#$2 +R$* $| $* $# $1 R$* $# $1 -SDelay_TLS_Client2 +SDelay_TLS_Clt2 # authenticated? R$* $: $1 $| $>"tls_client" $&{verify} $| MAIL R$* $| $#$+ $#$2 +R$* $| $* $@ $1 R$* $@ $1 # call all necessary rulesets @@ -1394,7 +1420,7 @@ R$+ $: $1 $| $>checkrcpt $1 R$+ $| $#error $* $#error $2 R$+ $| $#discard $* $#discard $2 -R$+ $| $#$* $@ $>"Delay_TLS_Client" $2 +R$+ $| $#$* $@ $>"Delay_TLS_Clt" $2 R$+ $| $* $: $>FullAddr $>CanonAddr $1 R $+ < @ $=w > $: <> $1 < @ $2 > $| R $+ < @ $* > $: <> $1 < @ $2 > $| @@ -1402,7 +1428,7 @@ R<> $* $| <$+> $: <@> $1 $| $>SearchList $| <$2> <> R<@> $* $| $* $: $2 $1 reverse result # is the recipient a spam friend? -R $+ $@ $>"Delay_TLS_Client2" SPAMFRIEND +R $+ $@ $>"Delay_TLS_Clt2" SPAMFRIEND R<$*> $+ $: $2 R$* $: $1 $| $>checkmail <$&f> R$* $| $#$* $#$2 @@ -1506,10 +1532,10 @@ ###################################################################### # class with valid marks for SearchList -C{src}E F D U +C{Src}E F D U SSearchList # just call the ruleset with the name of the tag... nice trick... -R<$+> $| <$={src}:$*> <$*> $: <$1> $| <$4> $| $>$2 <$3> <$1> <> +R<$+> $| <$={Src}:$*> <$*> $: <$1> $| <$4> $| $>$2 <$3> <$1> <> R<$+> $| <> $| <> $@ R<$+> $| <$+> $| <> $@ $>SearchList <$1> $| <$2> R<$+> $| <$*> $| <$+> <> $@ <$3> @@ -1530,7 +1556,7 @@ R$@ $| $* $#error $@ 5.7.1 $: "550 not authenticated" R$* $| $&{auth_authen} $@ identical R$* $| <$&{auth_authen}> $@ identical -R$* $| $* $: $1 $| $>"Local_trust_auth" $1 +R$* $| $* $: $1 $| $>"Local_trust_auth" $2 R$* $| $#$* $#$2 R$* $#error $@ 5.7.1 $: "550 " $&{auth_authen} " not allowed to act as " $&{auth_author} @@ -1624,16 +1650,16 @@ STLS_connection R$* $| <$*>$* $: $1 $| <$2> # create the appropriate error codes -R$* $| $: $1 $| <503:5.7.0> <$2 $3> -R$* $| $: $1 $| <403:4.7.0> <$2 $3> -R$* $| <$={tls} $*> $: $1 $| <403:4.7.0> <$2 $3> +R$* $| $: $1 $| <503:5.7.0> <$2 $3> +R$* $| $: $1 $| <403:4.7.0> <$2 $3> +R$* $| <$={Tls} $*> $: $1 $| <403:4.7.0> <$2 $3> # deal with TLS handshake failures: abort RSOFTWARE $| <$-:$+> $* $#error $@ $2 $: $1 " TLS handshake failed." RSOFTWARE $| $* $#error $@ 4.7.0 $: "403 TLS handshake failed." R$* $| <$*> $: <$2> <> $1 R$* $| <$*> $: <$2> <$3> $1 -R$* $| <$*> <$={tls}:$->$* $: <$2> <$3:$4> <> $1 -R$* $| <$*> <$={tls}:$- + $+>$* $: <$2> <$3:$4> <$5> $1 +R$* $| <$*> <$={Tls}:$->$* $: <$2> <$3:$4> <> $1 +R$* $| <$*> <$={Tls}:$- + $+>$* $: <$2> <$3:$4> <$5> $1 R$* $| $* $@ OK # authentication required: give appropriate error # other side did authenticate (via STARTTLS) diff -r 946fc1bcfb2c -r 962a1f8f1d9f test.mc --- a/test.mc Sun Aug 07 11:26:37 2005 -0700 +++ b/test.mc Sun Sep 18 10:19:58 2005 -0700 @@ -33,7 +33,7 @@ define(`confTO_IDENT', `0') define(`MAIL_SETTINGS_DIR', `/etc/mail/') define(`PROCMAIL_MAILER_PATH',`/usr/bin/procmail') -define(`STATUS_FILE', /usr/src/rh8/gpl/dnsbl/sendmail.st) +define(`STATUS_FILE', /usr/usr/cvs/gpl/dnsbl/sendmail.st) define(`MILTER', 1) DAEMON_OPTIONS(`port=26')dnl FEATURE(always_add_domain) diff -r 946fc1bcfb2c -r 962a1f8f1d9f xml/dnsbl.in --- a/xml/dnsbl.in Sun Aug 07 11:26:37 2005 -0700 +++ b/xml/dnsbl.in Sun Sep 18 10:19:58 2005 -0700 @@ -2,7 +2,7 @@ -DNSBL Sendmail milter - Version 5.3 +DNSBL Sendmail milter - Version 5.4
Introduction
@@ -89,8 +89,9 @@
Filtering Procedure

If the client has authenticated with sendmail, the mail is accepted, -the dns lists are not checked, and the body content is not scanned. -Otherwise, we follow these steps for each recipient. +the filtering contexts are not used, the dns lists are not checked, and +the body content is not scanned. Otherwise, we follow these steps for +each recipient.

    @@ -111,6 +112,14 @@ point to a child context. If such an entry is found, we switch to that child filtering context. +

  1. If the filtering context specifies a verification host, and +the envelope to email address is covered by this filtering context, and +the verification host is not our own hostname, we open an smtp +conversation with that verification host. The current envelope from and +recipient to values are passed to that verification host. If we receive +anything other than a 250 response those commands, we reject the current +recipient with "no such user". +

  2. We lookup [1) the full envelope from email address, 2) the domain name part of the envelope from address, 3) the user@ part of the envelope from address] in the filtering context env_from statement.