--- mimedefang.c 2009-05-24 07:40:40.000000000 +0200 +++ mimedefang.c 2009-05-24 06:27:35.000000000 +0200 @@ -233,6 +233,9 @@ /* Do recipient check? */ static int doRecipientCheck = 0; +/* Do precontent check */ +static int doPreContentCheck = 0; + /* Keep directories around if multiplexor fails? */ static int keepFailedDirectories = 0; @@ -978,12 +981,67 @@ *%RETURNS: * Standard milter reply code *%DESCRIPTION: -* Does a post-DATA callback +* Does a post-DATA callback before any content is submitted ***********************************************************************/ #ifdef MILTER_BUILDLIB_HAS_DATA static sfsistat mf_data(SMFICTX *ctx) { - return SMFIS_CONTINUE; + struct privdata *data = DATA; + char ans[SMALLBUF]; + sfsistat retcode = SMFIS_CONTINUE; + int i; + + DEBUG_ENTER("mf_data"); + if (!data) { + syslog(LOG_WARNING, "postdata: Unable to obtain private data from milter context"); + DEBUG_EXIT("mf_data", "SMFIS_TEMPFAIL"); + return SMFIS_TEMPFAIL; + } + + /* Post data check if enabled */ + if (doPreContentCheck) { + int n; + + n = MXDataOK(MultiplexorSocketName, ans, data->sender, data->hostip, + data->hostname, data->firstRecip, data->heloArg, + data->dir, data->qid); + + if (n == MD_REJECT) { + /* Reject this mail with all recipients */ + set_dsn(ctx, ans, 5); + + DEBUG_EXIT("mf_data", "SMFIS_REJECT"); + return SMFIS_REJECT; + } + if (n <= MD_TEMPFAIL) { + /* Tempfail this mail with all recipients */ + set_dsn(ctx, ans, 4); + + DEBUG_EXIT("mf_data", "SMFIS_TEMPFAIL"); + return SMFIS_TEMPFAIL; + } + if (n == MD_ACCEPT_AND_NO_MORE_FILTERING) { + /* Called in case we don't need content filtering */ + set_dsn(ctx, ans, 2); + cleanup(ctx); + DEBUG_EXIT("mf_data", "SMFIS_ACCEPT"); + return SMFIS_ACCEPT; + } + if (n == MD_DISCARD) { + set_dsn(ctx, ans, 2); + + cleanup(ctx); + DEBUG_EXIT("mf_data", "SMFIS_DISCARD"); + return SMFIS_DISCARD; + } + if (n == MD_CONTINUE) { + /* Called only in case we need to delay */ + set_dsn(ctx, ans, 2); + return SMFIS_CONTINUE; + } + } + DEBUG_EXIT("mf_data", "SMFIS_CONTINUE"); + return retcode; } #endif @@ -2092,6 +2150,7 @@ fprintf(stderr, " -r -- Do relay check before processing body\n"); fprintf(stderr, " -s -- Do sender check before processing body\n"); fprintf(stderr, " -t -- Do recipient checks before processing body\n"); + fprintf(stderr, " -A -- Do pre content check for processing body\n"); fprintf(stderr, " -q -- Allow new connections to be queued by multiplexor\n"); fprintf(stderr, " -P file -- Write process-ID of daemon to specified file\n"); fprintf(stderr, " -T -- Log filter times to syslog\n"); @@ -2189,7 +2248,7 @@ } /* Process command line options */ - while ((c = getopt(argc, argv, "NCDHL:MP:R:S:TU:Xa:b:cdhkm:p:qrstvx:z:")) != -1) { + while ((c = getopt(argc, argv, "ANCDHL:MP:R:S:TU:Xa:b:cdhkm:p:qrstvx:z:")) != -1) { switch (c) { case 'N': #ifdef MILTER_BUILDLIB_HAS_NEGOTIATE @@ -2346,6 +2405,9 @@ case 't': doRecipientCheck = 1; break; + case 'A': + doPreContentCheck = 1; + break; case 'h': usage(); break; --- mimedefang.h 2009-05-24 07:40:40.000000000 +0200 +++ mimedefang.h 2009-05-24 06:26:27.000000000 +0200 @@ -40,6 +40,10 @@ char const *dir, char const *qid, char const *rcpt_mailer, char const *rcpt_host, char const *rcpt_addr); +extern int MXDataOK(char const *sockname, char *msg, + char const *sender, char const *ip, char const *name, + char const *firstRecip, char const *helo, + char const *dir, char const *qid); extern int safeWriteHeader(int fd, char *str); extern void split_on_space(char *buf, char **first, char **rest); --- mimedefang.pl.in 2009-05-24 07:41:08.000000000 +0200 +++ mimedefang.pl.in 2009-05-24 07:10:42.000000000 +0200 @@ -5645,6 +5645,21 @@ chdir($Features{'Path:SPOOLDIR'}); next; } + if ($_ =~ /^dataok (\S*)\s+(\S*)\s+(\S*)\s+(\S*)\s+(\S*)\s+(\S*)\s+(\S*)/) { + $sender = percent_decode($1); + $ip = percent_decode($2); + $name = percent_decode($3); + $firstRecip = percent_decode($4); + $helo = percent_decode($5); + $CWD = percent_decode($6); + $QueueID = percent_decode($7); + $MsgID = $QueueID; + + chdir($CWD); + data_ok($sender, $ip, $name, $firstRecip, $helo); + chdir($Features{'Path:SPOOLDIR'}); + next; + } # If "filter_unknown_cmd" is defined, call it if (defined(&filter_unknown_cmd)) { @@ -7074,6 +7089,39 @@ } #*********************************************************************** +# %PROCEDURE: data_ok +# %ARGUMENTS: +# sender -- e-mail address of sender +# ip -- IP address of relay host +# name -- name of relay host +# firstRecip -- first recipient of message +# helo -- arg to SMTP HELO command +# %RETURNS: +# Nothing, but prints "ok 1" if we accept the message, +# "ok 0" if not. +#*********************************************************************** +sub data_ok ($$$$$) { + my($sender, $ip, $name, $firstRecip, $helo) = @_; + if (!defined(&filter_data)) { + send_filter_answer('CONTINUE', "ok", + "filter_data", "data"); + return; + } + + # Set up globals + $Sender = $sender; + $RelayAddr = $ip; + $RelayHostname = $name; + $Helo = $helo; + my($ok, $msg, $code, $dsn, $delay) = + filter_data($sender, $ip, $name, + $firstRecip, $helo); + send_filter_answer($ok, $msg, + "filter_data", "data", + $code, $dsn, $delay); +} + +#*********************************************************************** # %PROCEDURE: print_message_structure # %ARGUMENTS: # None --- utils.c 2009-05-24 07:40:40.000000000 +0200 +++ utils.c 2009-05-24 06:29:06.000000000 +0200 @@ -801,6 +801,69 @@ } /********************************************************************** +* %FUNCTION: MXDataOK +* %ARGUMENTS: +* sockname -- multiplexor socket name +* msg -- buffer of at least SMALLBUF size for error messages +* sender -- sender's e-mail address +* ip -- sending relay's IP address +* name -- sending relay's host name +* firstRecip -- first recipient of the message +* helo -- argument to "HELO/EHLO" (may be NULL) +* dir -- MIMEDefang working directory +* qid -- Sendmail queue identifier +* %RETURNS: +* 1 if it's OK to accept this message; 0 if not, -1 if error. +***********************************************************************/ +int +MXDataOK(char const *sockname, + char *msg, + char const *sender, + char const *ip, + char const *name, + char const *firstRecip, + char const *helo, + char const *dir, + char const *qid) +{ + char cmd[SMALLBUF]; + char ans[SMALLBUF]; + int i, l, l2; + + *msg = 0; + + if (!sender || !*sender) { + sender = "UNKNOWN"; + } + + if (!ip || !*ip) { + ip = "UNKNOWN"; + } + if (!name || !*name) { + name = ip; + } + + if (!firstRecip || !*firstRecip) { + firstRecip = "UNKNOWN"; + } + if (!helo) { + helo = "UNKNOWN"; + } + + if (percent_encode_command(0, cmd, sizeof(cmd), + "dataok", sender, ip, name, firstRecip, + helo, dir, qid, NULL) < 0) { + return MD_TEMPFAIL; + } + + /* Add newline */ + strcat(cmd, "\n"); + + if (MXCommand(sockname, cmd, ans, SMALLBUF-1) < 0) return MD_TEMPFAIL; + return munch_mx_return(ans, msg); +} + +/********************************************************************** * %FUNCTION: writen * %ARGUMENTS: * fd -- file to write to --- examples/init-script.in 2008-03-07 21:59:28.000000000 +0100 +++ examples/init-script.in 2009-05-24 08:06:00.000000000 +0200 @@ -61,6 +61,9 @@ # "yes" turns on the multiplexor recipient checking function # MX_RECIPIENT_CHECK=no +# "yes" turns on the multiplexor data checking function +# MX_DATA_CHECK=no + # Set to yes if you want the multiplexor to log events to syslog MX_LOG=yes @@ -268,6 +271,7 @@ `[ "$MX_HELO_CHECK" = "yes" ] && echo "-H"` \ `[ "$MX_SENDER_CHECK" = "yes" ] && echo "-s"` \ `[ "$MX_RECIPIENT_CHECK" = "yes" ] && echo "-t"` \ + `[ "$MX_DATA_CHECK" = "yes" ] && echo "-A"` \ `[ "$KEEP_FAILED_DIRECTORIES" = "yes" ] && echo "-k"` \ `[ "$MD_EXTRA" != "" ] && echo $MD_EXTRA` \ `[ "$MD_SKIP_BAD_RCPTS" = "yes" ] && echo "-N"` \ --- redhat/mimedefang-init.in 2008-08-15 20:03:41.000000000 +0200 +++ redhat/mimedefang-init.in 2009-05-24 08:06:31.000000000 +0200 @@ -141,6 +141,7 @@ $([ "$MX_HELO_CHECK" = "yes" ] && echo "-H") \ $([ "$MX_SENDER_CHECK" = "yes" ] && echo "-s") \ $([ "$MX_RECIPIENT_CHECK" = "yes" ] && echo "-t") \ + $([ "$MX_DATA_CHECK" = "yes" ] && echo "-A") \ $([ "$KEEP_FAILED_DIRECTORIES" = "yes" ] && echo "-k") \ $([ -n "$MD_EXTRA" ] && echo "$MD_EXTRA") \ $([ "$ALLOW_NEW_CONNECTIONS_TO_QUEUE" = "yes" ] && echo "-q") \ --- redhat/mimedefang-sysconfig.in 2008-03-07 21:59:28.000000000 +0100 +++ redhat/mimedefang-sysconfig.in 2009-05-24 08:07:22.000000000 +0200 @@ -145,6 +145,9 @@ # If "yes", turn on the multiplexor recipient checking function # MX_RECIPIENT_CHECK=no +# If "yes", turn on the multiplexor post data checking function +# MX_DATA_CHECK=no + # Ask for filter_tick to be called every 60 seconds # MX_TICK_REQUEST=60 --- mimedefang-filter.5.in 2009-01-05 16:06:45.000000000 +0100 +++ mimedefang-filter.5.in 2009-05-24 08:18:35.000000000 +0200 @@ -811,21 +811,21 @@ The host name of the relay. This is the name of the host that is attempting to send e-mail to your host. May be "undef" if the host name could not be determined. This variable is available in \fBfilter_relay\fR, -\fBfilter_sender\fR and \fBfilter_recipient\fR. +\fBfilter_sender\fR, \fBfilter_recipient\fR and \fBfilter_data\fR. .TP .B $RelayAddr The IP address of the sending relay (as a string consisting of four dot-separated decimal numbers.) One potential use of \fB$RelayAddr\fR is to limit mailing to certain lists to people within your organization. -This variable is available in \fBfilter_relay\fR, \fBfilter_sender\fR -and \fBfilter_recipient\fR. +This variable is available in \fBfilter_relay\fR, \fBfilter_sender\fR, +\fBfilter_recipient\fR and \fBfilter_data\fR. .TP .B $Helo The argument given to the SMTP "HELO" command. This variable is available in \fBfilter_sender\fR and \fBfilter_recipient\fR, -but \fInot\fR in \fBfilter_relay\fR. +\fBfilter_data\fR, but \fInot\fR in \fBfilter_relay\fR. .TP .B $Subject @@ -833,8 +833,8 @@ .TP .B $Sender -The sender of the e-mail. This variable is set in \fBfilter_sender\fR -and \fBfilter_recipient\fR. +The sender of the e-mail. This variable is set in \fBfilter_sender\fR, +\fBfilter_recipient\fR and \fBfilter_data\fR. .TP .B @Recipients @@ -856,16 +856,16 @@ .B $QueueID The Sendmail queue identifier if it could be determined. Otherwise, contains the string "NOQUEUE". This variable \fIis\fR set correctly -in \fBfilter_sender\fR and \fBfilter_recipient\fR, but it is \fInot\fR -available in \fBfilter_relay\fR. +in \fBfilter_sender\fR,\fBfilter_recipient\fR and \fBfilter_data\fR, +but it is \fInot\fR available in \fBfilter_relay\fR. .TP .B $MsgID Set to $QueueID if the queue ID could be determined; otherwise, set to $MessageID. This identifier should be used in logging, because it matches the identifier used by Sendmail to log messages. Note that this -variable \fIis\fR set correctly in \fBfilter_sender\fR -and \fBfilter_recipient\fR, but it is \fInot\fR available in +variable \fIis\fR set correctly in \fBfilter_sender\fR, +\fBfilter_recipient\fR and \fBfilter_data\fR, but it is \fInot\fR available in \fBfilter_relay\fR. .TP @@ -1396,16 +1396,16 @@ .TP .B read_commands_file() -This function should only be called from \fBfilter_sender\fR and -\fBfilter_recipient\fR. This will read the \fBCOMMANDS\fR file (as -described in mimedefang-protocol(7)), and will fill or update the -following global variables: $Sender, @Recipients, %RecipientMailers, -$RelayAddr, $RealRelayAddr, $RelayHostname, $RealRelayHostname, -$QueueID, $Helo, %SendmailMacros. +This function should only be called from \fBfilter_sender\fR, +\fBfilter_recipient\fR and \fBfilter_data\fR. This will read the +\fBCOMMANDS\fR file (as described in mimedefang-protocol(7)), +and will fill or update the following global variables: $Sender, +@Recipients, %RecipientMailers, $RelayAddr, $RealRelayAddr, +$RelayHostname, $RealRelayHostname, $QueueID, $Helo, %SendmailMacros. If you do not call \fBread_commands_file\fR, then the only information -available in \fBfilter_sender\fR and \fBfilter_recipient\fR is that -which is passed as an argument to the function. +available in \fBfilter_sender\fR, \fBfilter_recipient\fR or \fBfilter_data\fR +is that which is passed as an argument to the function. .TP .B stream_by_domain() @@ -2008,6 +2008,12 @@ defined a filter_recipient routine, it is called. .TP +.B 4. SMTP DATA: COMMAND +If you invoked \fBmimedefang\fR with the \fB\-A\fR option and have +defined a filter_data routine, it is called. At this point, no body +data have been received by the filter. + +.TP .B 5. END OF SMTP DATA filter_begin is called. For each MIME part, filter is called. Then filter_end is called. @@ -2087,7 +2093,7 @@ .PP \fINote\fR: The IP validation header works only in message-oriented functions. It (obviously) has no effect on \fBfilter_relay\fR, -\fBfilter_sender\fR and \fBfilter_recipient\fR, because no header +\fBfilter_sender\fR, \fBfilter_recipient\fR and \fBfilter_data\fR, because no header information is available yet. You must take this into account when writing your filter; you must defer relay-based decisions to the message filter for mail arriving from your other MX hosts. @@ -2104,7 +2110,7 @@ It is available to all functions, all the time. .TP -.B In filter_relay, filter_sender or filter_recipient +.B In filter_relay, filter_sender, filter_recipient or filter_data Not guaranteed to be available to any other function, not even from one filter_recipient call to the next, when receiving a multi-recipient email message. @@ -2124,8 +2130,8 @@ .PP The "built-in" globals like $Subject, $Sender, etc. are always available to filter_begin, filter and filter_end. Some are available to filter_relay, -filter_sender or filter_recipient, but you should check the documentation -of the variable above for details. +filter_sender, filter_recipient or filter_data, but you should check the +documentation of the variable above for details. .SH MAINTAINING STATE @@ -2146,6 +2152,10 @@ .TP .B 4 +filter_data + +.TP +.B 5 filter_begin, filter, filter_multipart, filter_end .PP