Add authenticated bits information into the dummy generated Received-header for SpamAssassin to facilitate adding a rule to score mail from authenticated clients. Discussion: http://bugzilla.redhat.com/496769 http://www.gossamer-threads.com/lists/spamassassin/users/146948 This patch also moves some of the macro collection to the ENVFROM callback, where the required macros are available by default. --- README +++ README @@ -55,16 +55,28 @@ configuring sendmail through m4 & the se adding the lines INPUT_MAIL_FILTER(`spamassassin', `S=local:/var/run/sendmail/spamass.sock, F=, T=C:15m;S:4m;R:4m;E:10m')dnl -define(`confMILTER_MACROS_CONNECT',`t, b, j, _, {daemon_name}, {if_name}, {if_addr}')dnl -define(`confMILTER_MACROS_HELO',`s, {tls_version}, {cipher}, {cipher_bits}, {cert_subject}, {cert_issuer}')dnl -define(`confMILTER_MACROS_ENVFROM',`{auth_authen}, {auth_type}')dnl -define(`confMILTER_MACROS_ENVRCPT',`r, v, Z')dnl +define(`confMILTER_MACROS_ENVRCPT',confMILTER_MACROS_ENVRCPT`, b, r, v, Z')dnl + should do the trick. Of course you need to modify the path of the socket if you put another one into the startup script. The timeouts have been increased somewhat because SpamAssassin may chew on it for a little while on a slow machine. +If you are using multiple milter mail filters on your mail server, you may +have overridden the default values of some of the confMILTER_MACROS_* +macros whilst configuring the other filters. You need to ensure that at +least the following values are present: + +confMILTER_MACROS_CONNECT must include the {j} and {_} macros +(all included by default) + +confMILTER_MACROS_ENVFROM must include the {i}, {auth_authen}, {auth_ssf} +and {auth_type} macros (all included by default) + +confMILTER_MACROS_ENVRCPT must include the {b}, {r}, {v}, and {Z} macros + + Now recreate sendmail.cf, restart sendmail and experiment around a bit with the setup to make sure it is working. --- spamass-milter.cpp +++ spamass-milter.cpp @@ -702,6 +702,7 @@ sfsistat mlfi_connect(SMFICTX * ctx, char *hostname, _SOCK_ADDR * hostaddr) { struct context *sctx; + const char *macro_j, *macro__; int rv; debug(D_FUNC, "mlfi_connect: enter"); @@ -726,8 +727,31 @@ mlfi_connect(SMFICTX * ctx, char *hostna } sctx->assassin = NULL; sctx->helo = NULL; - - /* store a pointer to it with setpriv */ + sctx->our_fqdn = NULL; + sctx->sender_address = NULL; + sctx->queueid = NULL; + sctx->auth_authen = NULL; + sctx->auth_ssf = NULL; + + /* store our FQDN */ + macro_j = smfi_getsymval(ctx, const_cast("j")); + if (!macro_j) + { + macro_j = "localhost"; + warnmacro("j", "CONNECT"); + } + sctx->our_fqdn = strdup(macro_j); + + /* store the validated sending site's address */ + macro__ = smfi_getsymval(ctx, const_cast("_")); + if (!macro__) + { + macro__ = "unknown"; + warnmacro("_", "CONNECT"); + } + sctx->sender_address = strdup(macro__); + + /* store a pointer to our private data with setpriv */ rv = smfi_setpriv(ctx, sctx); if (rv != MI_SUCCESS) { @@ -778,7 +802,7 @@ mlfi_envfrom(SMFICTX* ctx, char** envfro { SpamAssassin* assassin; struct context *sctx = (struct context *)smfi_getpriv(ctx); - const char *queueid; + const char *queueid, *macro_auth_ssf, *macro_auth_authen; if (sctx == NULL) { @@ -814,17 +838,44 @@ mlfi_envfrom(SMFICTX* ctx, char** envfro // remember the MAIL FROM address assassin->set_from(string(envfrom[0])); - + + // remember the queueid for this message queueid=smfi_getsymval(ctx, const_cast("i")); if (!queueid) { queueid="unknown"; warnmacro("i", "ENVFROM"); } - assassin->queueid = queueid; - + sctx->queueid = strdup(queueid); debug(D_MISC, "queueid=%s", queueid); + // remember the SMTP AUTH login name + macro_auth_authen = smfi_getsymval(ctx, const_cast("{auth_authen}")); + if (!macro_auth_authen) + { + macro_auth_authen = ""; + // Don't issue a warning for the auth_authen macro as + // it is likely to be unset much of the time - it's + // only set if the client has authenticated. + // + // Similarly, we only issue warnings for the other + // auth-related macros if {auth_authen) is available. + // + // warnmacro("auth_authen", "ENVFROM"); + } + sctx->auth_authen = strdup(macro_auth_authen); + + // remember the SASL cipher bits + macro_auth_ssf = smfi_getsymval(ctx, const_cast("{auth_ssf}")); + if (!macro_auth_ssf) + { + macro_auth_ssf = ""; + if (strlen(macro_auth_authen)) { + warnmacro("auth_ssf", "ENVFROM"); + } + } + sctx->auth_ssf = strdup(macro_auth_ssf); + // tell Milter to continue debug(D_FUNC, "mlfi_envfrom: exit"); @@ -918,7 +969,8 @@ mlfi_envrcpt(SMFICTX* ctx, char** envrcp */ 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_ssf, *macro_auth_authen; char date[32]; /* RFC 822 date. */ @@ -933,20 +985,13 @@ mlfi_envrcpt(SMFICTX* ctx, char** envrcp } /* queue ID */ - macro_i = smfi_getsymval(ctx, const_cast("i")); - if (!macro_i) - { - macro_i = "unknown"; - warnmacro("i", "ENVRCPT"); - } + macro_i = sctx->queueid; - /* FQDN of this site */ - macro_j = smfi_getsymval(ctx, const_cast("j")); - if (!macro_j) - { - macro_j = "localhost"; - warnmacro("j", "ENVRCPT"); - } + /* FQDN */ + macro_j = sctx->our_fqdn; + + /* Sender address */ + macro__ = sctx->sender_address; /* Protocol used to receive the message */ macro_r = smfi_getsymval(ctx, const_cast("r")); @@ -955,7 +1000,11 @@ mlfi_envrcpt(SMFICTX* ctx, char** envrcp macro_r = "SMTP"; warnmacro("r", "ENVRCPT"); } - + + /* SMTP AUTH details */ + macro_auth_authen = sctx->auth_authen; + macro_auth_ssf = sctx->auth_ssf; + /* 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 @@ -983,22 +1032,25 @@ mlfi_envrcpt(SMFICTX* ctx, char** envrcp warnmacro("Z", "ENVRCPT"); } - /* Validated sending site's address */ - macro__ = smfi_getsymval(ctx, const_cast("_")); - if (!macro__) + assassin->output((string)"X-Envelope-From: "+assassin->from()+"\r\n"); + assassin->output((string)"X-Envelope-To: "+envrcpt[0]+"\r\n"); + + string rec_header; + + rec_header = (string) "Received: from " + macro_s + " (" + macro__ + ")\r\n\t"; + + if (strlen(macro_auth_ssf)) { - macro__ = "unknown"; - warnmacro("_", "ENVRCPT"); + rec_header += (string) "(authenticated bits=" + macro_auth_ssf + ")\r\n\t"; } - assassin->output((string)"X-Envelope-From: "+assassin->from()+"\r\n"); - assassin->output((string)"X-Envelope-To: "+envrcpt[0]+"\r\n"); + 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"; - 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"+ - macro_b+"\r\n\t"+ - "(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"); @@ -1244,16 +1296,27 @@ mlfi_close(SMFICTX* ctx) { struct context *sctx; debug(D_FUNC, "mlfi_close"); - + sctx = (struct context*)smfi_getpriv(ctx); if (sctx == NULL) return SMFIS_ACCEPT; if (sctx->helo) free(sctx->helo); + if (sctx->our_fqdn) + free(sctx->our_fqdn); + if (sctx->sender_address) + free(sctx->sender_address); + if (sctx->queueid) + free(sctx->queueid); + if (sctx->auth_authen) + free(sctx->auth_authen); + if (sctx->auth_ssf) + free(sctx->auth_ssf); + free(sctx); smfi_setpriv(ctx, NULL); - + return SMFIS_ACCEPT; } --- spamass-milter.h +++ spamass-milter.h @@ -168,9 +168,6 @@ public: // List of recipients after alias/virtusertable expansion list expandedrcpt; - // the sendmail queue id for this message; used for logging - string queueid; - // Process handling variables pid_t pid; int pipe_io[2][2]; @@ -181,6 +178,11 @@ struct context { char connect_ip[64]; // remote IP address char *helo; + char *our_fqdn; + char *sender_address; + char *queueid; + char *auth_authen; + char *auth_ssf; SpamAssassin *assassin; // pointer to the SA object if we're processing a message };