mimedefang-filter - Configuration file for MIMEDefang mail filter.
mimedefang-filter is a Perl fragment that controls how mimedefang.pl disposes of various parts of a MIME message. In addition, it contains some global variable settings that affect the operation of mimedefang.pl.
Incoming messages are scanned as follows:
1) A temporary working directory is created. It is made the current working directory and the e-mail message is split into parts in this directory. Each part is represented internally as an instance of MIME::Entity.
2) If the file /etc/mail/mimedefang-filter.pl defines a Perl function called filter_begin, it is called with a single argument consisting of a MIME::Entity representing the parsed e-mail message. Any return value is ignored.
3) For each leaf part of the mail message, filter is called with four arguments: entity, a MIME::Entity object; fname, the suggested filename taken from the MIME Content-Disposition header; ext, the file extension, and type, the MIME Content-Type value. For each non-leaf part of the mail message, filter_multipart is called with the same four arguments as filter. A non-leaf part of a message is a part that contains nested parts. Such a part has no useful body, but you should still perform filename checks to check for viruses that use malformed MIME to masquerade as non-leaf parts (like message/rfc822). In general, any action you perform in filter_multipart applies to the part itself and any contained parts.
Note that both filter and filter_multipart are optional. If you do not define them, a default function that simply accepts each part is used.
4) After all parts have been processed, the function filter_end is called if it has been defined. It is passed a single argument consisting of the (possibly modified) MIME::Entity object representing the message about to be delivered. Within filter_end, you can call functions that modify the message headers body.
5) After filter_end returns, the function filter_wrapup is called if it has been defined. It is passed a single argument consisting of the (possibly modified) MIME::Entity object representing the message about to be delivered, including any modifications made in filter_end. Within filter_wrapup, you can not call functions that modify the message body, but you can still add or modify message headers.
mimedefang.pl examines each part of the MIME message and chooses a disposition for that part. (A disposition is selected by calling one of the following functions from filter and then immediately returning.) Available dispositions are:
The part is passed through unchanged. If no disposition function is returned, this is the default.
The part is passed through unchanged, but a warning is added to the mail message.
The part is deleted without any notification to the recipients.
The part is deleted and a warning is added to the mail message.
The part is deleted and instead replaced with a text message.
The part is deleted and a warning is added to the mail message. In addition, a copy of the part is saved on the mail server in the directory /var/spool/MD-Quarantine and a notification is sent to the MIMEDefang administrator.
The entire e-mail message is rejected and an error returned to the sender. The intended recipients are not notified. Note that in spite of the name, MIMEDefang does not generate and e-mail a failure notification. Rather, it causes the SMTP server to return a 5XX SMTP failure code.
The entire e-mail message is discarded silently. Neither the sender nor the intended recipients are notified.
You can define a function called filter_relay in your filter. This lets you reject SMTP connection attempts early on in the SMTP dialog, rather than waiting until the whole message has been sent. Note that for this check to take place, you must use the -r flag with mimedefang.
filter_relay is passed six arguments: $hostip is the IP address of the relay host (for example, "127.0.0.1"), $hostname is the host name if known (for example, "localhost.localdomain"). If the host name could not be determined, $hostname is $hostip enclosed in square brackets. (That is, ("$hostname" eq "[$hostip]") will be true.)
The remaining four arguments to filter_relay are $port, $myip, $myport and $qid, which contain the client's TCP port, the Sendmail daemon's listening IP address, the Sendmail daemon's listening port, and the Sendmail Queue-ID, respectively. Note that the Queue-ID may not yet be available at this stage (for example, Postfix does not allocate a queue-ID this early.) If the Queue-ID is not available, the string NOQUEUE is passed instead.
filter_relay must return a two-element list: ($code, $msg). $msg specifies the text message to use for the SMTP reply, but because of limitations in the Milter API, this message is for documentation purposes only---you cannot set the text of the SMTP message returned to the SMTP client from filter_relay.
$code is a literal string, and can have one of the following values:
if the connection should be rejected.
if the connection should be accepted.
if a temporary failure code should be returned.
if the message should be accepted and silently discarded.
if the connection should be accepted and no further filtering done.
Earlier versions of MIMEDefang used -1 for TEMPFAIL, 0 for REJECT and 1 for CONTINUE. These values still work, but are deprecated.
In the case of REJECT or TEMPFAIL, $msg specifies the text part of the SMTP reply. $msg must not contain newlines.
For example, if you wish to reject connection attempts from any machine in the spammer.com domain, you could use this function:
sub filter_relay {
my ($ip, $name) = @_;
if ($name =~ /spammer\.com$/) {
return ('REJECT', "Sorry; spammer.com is blacklisted");
}
return ('CONTINUE', "ok");
}
You can define a function called filter_helo in your filter. This lets you reject connections after the HELO/EHLO SMTP command. Note that for this function to be called, you must use the -H flag with mimedefang.
filter_helo is passed seven arguments: $ip and $name are the IP address and name of the sending relay, as in filter_relay. The third argument, $helo, is the argument supplied in the HELO/EHLO command.
The remaining four arguments to filter_helo are $port, $myip, $myport and $qid, which contain the client's TCP port, the Sendmail daemon's listening IP address, the Sendmail daemon's listening port, and the Sendmail Queue-ID, respectively. Note that the Queue-ID may not yet be available at this stage (for example, Postfix does not allocate a queue-ID this early.) If the Queue-ID is not available, the string NOQUEUE is passed instead.
filter_helo must return a two-to-five element list: ($code, $msg, $smtp_code, $smtp_dsn, $delay). $code is a return code, with the same meaning as the $code return from filter_relay. $msg specifies the text message to use for the SMTP reply. If $smtp_code and $smtp_dsn are supplied, they become the SMTP numerical reply code and the enhanced status delivery code (DSN code). If they are not supplied, sensible defaults are used. $delay specifies a delay in seconds; the C milter code will sleep for $delay seconds before returning the reply to Sendmail. $delay defaults to zero.
(Note that the delay is implemented in the Milter C code; if you specify a delay of 30 seconds, that doesn't mean a Perl worker is tied up for the duration of the delay. The delay only costs one Milter thread.)
You can define a function called filter_sender in your filter. This lets you reject messages from certain senders, rather than waiting until the whole message has been sent. Note that for this check to take place, you must use the -s flag with mimedefang.
filter_sender is passed four arguments: $sender is the envelope e-mail address of the sender (for example, "dfs@roaringpenguin.com"). The address may or may not be surrounded by angle brackets. $ip and $name are the IP address and host name of the SMTP relay. Finally, $helo is the argument to the SMTP "HELO" command.
Inside filter_sender, you can access any ESMTP arguments (such as "SIZE=12345") in the array @ESMTPArgs. Each ESMTP argument occupies one array element.
filter_sender must return a two-to-five element list, with the same meaning as the return value from filter_helo.
For example, if you wish to reject messages from spammer@badguy.com, you could use this function:
sub filter_sender {
my ($sender, $ip, $hostname, $helo) = @_;
if ($sender =~ /^<?spammer\@badguy\.com>?$/i) {
return ('REJECT', 'Sorry; spammer@badguy.com is blacklisted.');
}
return ('CONTINUE', "ok");
}
As another example, some spammers identify their own machine as your machine in the SMTP "HELO" command. This function rejects a machine claiming to be in the "roaringpenguin.com" domain unless it really is a Roaring Penguin machine:
sub filter_sender {
my($sender, $ip, $hostname, $helo) = @_;
if ($helo =~ /roaringpenguin\.com/i) {
if ($ip ne "127.0.0.1" and
$ip ne "216.191.236.23" and
$ip ne "216.191.236.30") {
return('REJECT', "Go away... $ip is not in roaringpenguin.com");
}
}
return ('CONTINUE', "ok");
}
As a third example, you may wish to prevent spoofs by requiring SMTP authentication when email is sent from some email addresses. This function rejects mail from "king@example.com", unless the connecting user properly authenticated as "elvisp". Note that this needs access to the %SendmailMacros global, that is not available in filter_sender until after a call to read_commands_file.
sub filter_sender {
my($sender, $ip, $hostname, $helo) = @_;
read_commands_file();
### notice: This assumes The King uses authentication without realm!
if ($sender =~ /^<?king\@example\.com>?$/i and
$SendmailMacros{auth_authen} ne "elvisp") {
return('REJECT', "Faking mail from the king is not allowed.");
}
return ('CONTINUE', "ok");
}
You can define a function called filter_recipient in your filter. This lets you reject messages to certain recipients, rather than waiting until the whole message has been sent. Note that for this check to take place, you must use the -t flag with mimedefang.
filter_recipient is passed nine arguments: $recipient is the envelope address of the recipient and $sender is the envelope e-mail address of the sender (for example, "dfs@roaringpenguin.com"). The addresses may or may not be surrounded by angle brackets. $ip and $name are the IP address and host name of the SMTP relay. $first is the envelope address of the first recipient for this message, and $helo is the argument to the SMTP "HELO" command. The last three arguments, $rcpt_mailer, $rcpt_host and $rcpt_addr are the Sendmail mailer, host and address triple for the recipient address. For example, for local recipients, $rcpt_mailer is likely to be "local", while for remote recipients, it is likely to be "esmtp".
Inside filter_recipient, you can access any ESMTP arguments (such as "NOTIFY=never") in the array @ESMTPArgs. Each ESMTP argument occupies one array element.
filter_recipient must return a two-to-five element list whose interpretation is the same as for filter_sender. Note, however, that if filter_recipient returns 'DISCARD', then the entire message for all recipients is discarded. (It doesn't really make sense, but that's how Milter works.)
For example, if you wish to reject messages from spammer@badguy.com, unless they are to postmaster@mydomain.com, you could use this function:
sub filter_recipient {
my ($recipient, $sender, $ip, $hostname, $first, $helo,
$rcpt_mailer, $rcpt_host, $rcpt_addr) = @_;
if ($sender =~ /^<?spammer\@badguy\.com>?$/i) {
if ($recipient =~ /^<?postmaster\@mydomain\.com>?$/i) {
return ('CONTINUE', "ok");
}
return ('REJECT', 'Sorry; spammer@badguy.com is blacklisted.');
}
return ('CONTINUE', "ok");
}
Just before a worker begins processing messages, mimedefang.pl calls the functions filter_initialize (if it is defined) with no arguments. By the time filter_initialize is called, all the other initialization (such as setting up syslog facility and priority) has been done.
If you are not using an embedded Perl interpreter, then performing an action inside filter_initialize is practically the same as performing it directly in the filter file, outside any function definition. However, if you are using an embedded Perl interpreter, then anything you call directly from outside a function definition is executed once only in the parent process. Anything in filter_initialize is executed once per worker. If you use any code that opens a descriptor (for example, a connection to a database server), you must run that code inside filter_initialize and not directly from the filter, because the multiplexor closes all open descriptors when it activates a new worker. From within filter_initialize a configuration file could be loaded by calling read_config. read_config accepts a configuration file path and it can be used to overwrite global variables. Configuration file format is pure Perl code.
When a worker is about to exit, mimedefang.pl calls the function filter_cleanup (if it is defined) with no arguments. This function can do whatever cleanup you like, such as closing file descriptors and cleaning up long-lived worker resources. The return value from filter_cleanup becomes the worker's exit status. (You should therefore ensure that filter_cleanup returns an integer suitable for a process exit status.)
If filter_cleanup takes longer than 10 seconds to run, the worker is sent a SIGTERM signal. If that doesn't kill it (because you're catching signals, perhaps), then a further 10 seconds later, the worker is sent a SIGKILL signal.
If you define a function called filter_create_parser taking no arguments, then mimedefang.pl will call it to create a MIME::Parser object for parsing mail messages.
Filter_create_parser is expected to return a MIME::Parser object (or an instance of a class derived from MIME::Parser).
You can use filter_create_parser to change the behavior of the MIME::Parser used by mimedefang.pl.
If you do not define a filter_create_parser function, then a built-in version equivalent to this is used:
sub filter_create_parser () {
my $parser = MIME::Parser->new();
$parser->extract_nested_messages(1);
$parser->extract_uuencode(1);
$parser->output_to_core(0);
$parser->tmp_to_core(0);
return $parser;
}
The man page for mimedefang-protocol(7) lists commands that are passed to workers in server mode (see "SERVER COMMANDS".) You can define a function called filter_unknown_cmd to extend the set of commands your filter can handle.
If you define filter_unknown_cmd, it is passed the unknown command as a single argument. It should return a list of values as follows: The first element of the list must be either "ok" or "error:" (with the colon.) The remaining arguments are percent-encoded. All the resulting pieces are joined together with a single space between them, and the resulting string passed back as the reply to the multiplexor.
For example, the following function will make your filter reply to a "PING" command with "PONG":
sub filter_unknown_cmd ($) {
my($cmd) = @_;
if ($cmd eq "PING") {
return("ok", "PONG");
}
return("error:", "Unknown command");
}
You can test this filter by typing the following as root:
md-mx-ctrl PING
The response should be:
ok PONG
If you extend the set of commands using filter_unknown_cmd, you should make all your commands start with an upper-case letter to avoid clashes with future built-in commands.
A very common mail setup is to have a MIMEDefang machine act as an SMTP proxy, accepting and scanning mail and then relaying it to the real mail server. Unfortunately, this means that the MIMEDefang machine cannot know if a local address is valid or not, and will forward all mail for the appropriate domains. If a mail comes in for an unknown user, the MIMEDefang machine will be forced to generate a bounce message when it tries to relay the mail.
It's often desirable to have the MIMEDefang host reply with a "User unknown" SMTP response directly. While this can be done by copying the list of local users to the MIMEDefang machine, MIMEDefang has a built-in function called md_check_against_smtp_server for querying another relay host:
function connects to the SMTP server $server and pretends to send mail from $sender to $recip. The return value is always a two-element array. If the RCPT TO: command succeeds, the return value is ("CONTINUE", "OK"). If the RCPT fails with a permanent failure, the return value is ("REJECT", $msg), where $msg is the message from the SMTP server. Any temporary failures, connection errors, etc. result in a return value of ("TEMPFAIL", $msg).
The optional argument $port specifies the TCP port to connect to. If it is not supplied, then the default SMTP port of 25 is used.
If the server offers STARTTLS support, TLS step-up is attempted. If TLS step-up fails, the check will fall-back to using clear text and log the failure
Suppose the machine filter.domain.tld is filtering mail destined for the real mail server mail.domain.tld. You could have a filter_recipient function like this:
sub filter_recipient
{
my($recip, $sender, $ip, $host, $first, $helo,
$rcpt_mailer, $rcpt_host, $rcpt_addr) = @_;
return md_check_against_smtp_server($sender, $recip,
"filter.domain.tld",
"mail.domain.tld");
}
For each RCPT TO: command, MIMEDefang opens an SMTP connection to mail.domain.tld and checks if the command would succeed.
Please note that you should only use md_check_against_smtp_server if your mail server responds with a failure code for nonexistent users at the RCPT TO: level. Also, this function may impose too much overhead if you receive a lot of e-mail, and it will generate lots of useless log entries on the real mail server (because of all the RCPT TO: probes.) It may also significantly increase the load on the real mail server.
The following Perl global variables should be set in mimedefang-filter:
The e-mail address of the MIMEDefang administrator.
The e-mail address from which MIMEDefang-originated notifications come.
If this variable is set to 0, then all MIMEDefang warnings (such as created by action_quarantine or action_drop_with_warning) are collected together and added in a separate MIME part called WARNING.TXT. If the variable is set to 1, then the warnings are added directly in the first text/plain and text/html parts of the message. If the message does not contain any text/plain or text/html parts, then a WARNING.TXT MIME part is added as before.
A message containing many MIME parts can cause MIME::Tools to consume large amounts of memory and bring your system to its knees. If you set $MaxMIMEParts to a positive number, then MIME parsing is terminated for messages with more than that many parts, and the message is bounced. In this case, none of your filter functions is called.
By default, $MaxMIMEParts is set to -1, meaning there is no limit on the number of parts in a message. Note that in order to use this variable, you must install the Roaring Penguin patched version of MIME::Tools, version 5.411a-RP-Patched-02 or newer.
Set this to 1 if your e-mail is too stupid to display multiple MIME parts in-line. In this case, a nasty hack causes the first part of the original message to appear as an attachment if warning are issued. Mail clients that are not this stupid are Netscape Communicator and Pine. On the other hand, Microsoft Exchange and Microsoft Outlook are indeed this stupid. Perhaps users of those clients should switch.
The following global variables may optionally be set. If they are not set, sensible defaults are used:
By default, MIMEDefang tries to pass SpamAssassin a message that looks exactly like one it would receive via procmail. This means adding a Received: header, adding a Message-ID header if necessary, and adding a Return-Path: header. If you set $AddApparentlyToForSpamAssassin to 1, then MIMEDefang also adds an Apparently-To: header with all the envelope recipients before passing the message to SpamAssassin. This lets SpamAssassin detect possibly whitelisted recipient addresses.
The default value for $AddApparentlyToForSpamAssassin is 0.
This specifies the logging facility used by mimedefang.pl. By default, it is set to "mail", but you can set it to other possibilites. See the openlog(3) man page for details. You should name facilities as all-lowercase without the leading "LOG_". That is, use "local3", not "LOG_LOCAL3".
If set to 0 (the default), non-inline warnings are placed first. If you want the warning at the end of the e-mail, set $WarningLocation to -1.
The full name used when MIMEDefang sends out notifications.
The full name of the MIMEDefang administrator.
If set to 1, SpamAssassin calls will use only local tests. This is the default and recommended setting. This disables Received, RBL and Razor tests in an all or nothing fashion. To use Razor this MUST be set to 0. You can add 'skip_rbl_checks 1' to your SpamAssassin config file if you need to.
The subject used when e-mail is sent out by action_notify_sender(). If you set this, you should set it each time you call action_notify_sender() to ensure consistency.
The subject used when e-mail is sent out by action_notify_administrator(). If you set this, you should set it each time you call action_notify_administrator() to ensure consistency.
The subject used when a quarantine notice is sent to the administrator. If you set this, you should set it each time you call action_quarantine() or action_quarantine_entire_message().
Normally, notifications sent by action_notify_sender() have a preamble warning about message modifications. If you do not want this, set $NotifyNoPreamble to 1.
Host and port for the Symantec CarrierScan Server virus scanner. This takes the form ip_addr:port:local_or_nonlocal. The ip_addr and port are the host and port on which CarrierScan Server is listening. If you want to scan local files, append :local to force the use of the AVSCANLOCAL command. If the CarrierScan Server is on another host, append :nonlocal to force the file contents to be sent to the scanner over the socket.
Socket used for Sophie daemon calls within message_contains_virus_sophie and entity_contains_virus_sophie unless a socket is provided by the calling routine.
Socket used for clamd daemon calls within message_contains_virus_clamd and entity_contains_virus_clamd unless a socket is provided by the calling routine.
Socket used for Trophie daemon calls within message_contains_virus_trophie and entity_contains_virus_trophie unless a socket is provided by the calling routine.
The heart of mimedefang-filter is the filter procedure. See the examples that came with MIMEDefang to learn to write a filter. The filter is called with the following arguments:
The MIME::Entity object. (See the MIME::tools Perl module documentation.)
The suggested attachment filename, or "" if none was supplied.
The file extension (all characters from the rightmost period to the end of the filename.)
The MIME type (for example, "text/plain".)
The filename is derived as follows:
First, if the Content-Disposition header has a "filename" field, it is used.
Otherwise, if the Content-Type header has a "name" field, it is used.
Otherwise, the Content-Description header value is used.
Note that the truly paranoid will check all three fields for matches. The functions re_match and re_match_ext perform regular expression matches on all three of the fields named above, and return 1 if any field matches. See the sample filters for details. The calling sequence is:
re_match($entity, "regexp")
re_match_ext($entity, "regexp")
re_match returns true if any of the fields matches the regexp without regard to case. re_match_ext returns true if the extension in any field matches. An extension is defined as the last dot in a name and all remaining characters.
A third function called re_match_in_zip_directory will look inside zip files and return true if any of the file names inside the zip archive match the regular expression. Call it like this:
my $bh = $entity->bodyhandle();
my $path = (defined($bh)) ? $bh->path() : undef;
if (defined($path) and re_match_in_zip_directory($path, "regexp")) {
# Take action...
}
You should not call re_match_in_zip_directory unless you know that the entity is a zip file attachment.
Another function called re_match_in_rar_directory will look inside rar files and return true if any of the file names inside the rar archive match the regular expression. The function is very similar to re_match_in_zip_directory but the unrar binary is required and must be specified in $Features{"unrar"}.
Another function called re_match_in_7z_directory will look inside 7zip files and return true if any of the file names inside the 7zip archive match the regular expression. The function is very similar to re_match_in_zip_directory but the 7z binary is required and must be specified in $Features{"7zip"}.
The following global variables are set by mimedefang.pl and are available for use in your filter. All of these variables are always available to filter_begin, filter, filter_multipart and filter_end. In addition, some of them are available in filter_relay, filter_sender or filter_recipient. If this is the case, it will be noted below.
This hash lets you determine at run-time whether certain functionality is available. This hash is available at all times assuming the detect_and_load_perl_modules() function has been called. The defined features are:
$Features{"SpamAssassin"} is 1 if SpamAssassin 1.6 or better is installed; 0 otherwise.
$Features{"HTML::Parser"} is 1 if HTML::Parser is installed; 0 otherwise.
$Features{"Virus:FPROTD"} is currently always 0. Set it to 1 in your filter file if you have F-Risk's FPROTD scanner earlier than version 6.
$Features{"Virus:FPROTD6"} is currently always 0. Set it to 1 in your filter file if you have version 6 of F-Risk's FPROTD scanner.
$Features{"Virus:SymantecCSS"} is currently always 0. Set it to 1 in your filter file if you have the Symantec CarrierScan Server virus scanner.
$Features{"Virus:NAI"} is the full path to NAI uvscan if it is installed; 0 if it is not.
$Features{"Virus:BDC"} is the full path to Bitdefender bdc if it is installed; 0 if it is not.
$Features{"Virus:NVCC"} is the full path to Norman Virus Control nvcc if it is installed; 0 if it is not.
$Features{"Virus:HBEDV"} is the full path to H+BEDV AntiVir if it is installed; 0 if it is not.
$Features{"Virus:VEXIRA"} is the full path to Central Command Vexira if it is installed; 0 if it is not.
$Features{"Virus:SOPHOS"} is the full path to Sophos sweep if it is installed; 0 if it is not.
$Features{"Virus:SAVSCAN"} is the full path to Sophos savscan if it is installed; 0 if it is not.
$Features{"Virus:CLAMAV"} is the full path to Clam AV clamscan if it is installed; 0 if it is not.
$Features{"Virus:AVP"} is the full path to AVP AvpLinux if it is installed; 0 if it is not.
$Features{"Virus:AVP5"} is the full path to Kaspersky "aveclient" if it is installed; 0 if it is not.
$Features{"Virus:CSAV"} is the full path to Command csav if it is installed; 0 if it is not.
$Features{"Virus:FSAV"} is the full path to F-Secure fsav if it is installed; 0 if it is not.
$Features{"Virus:FPROT"} is the full path to F-Risk f-prot if it is installed; 0 if it is not.
$Features{"Virus:FPSCAN"} is the full path to F-Risk fpscan if it is installed; 0 if it is not.
$Features{"Virus:SOPHIE"} is the full path to Sophie if it is installed; 0 if it is not.
$Features{"Virus:CLAMD"} is the full path to clamd if it is installed; 0 if it is not.
$Features{"Virus:CLAMDSCAN"} is the full path to clamdscan if it is installed; 0 if it is not.
$Features{"Virus:TROPHIE"} is the full path to Trophie if it is installed; 0 if it is not.
$Features{"Virus:NOD32"} is the full path to ESET NOD32 nod32cli if it is installed; 0 if it is not.
$Features{"Path:RSPAMC"} is the full path to rspamc(1) if it is installed (deprecated); 0 if it is not.
NOTE: Perl-module based features such as SpamAssassin are determined at runtime and may change as these are added and removed. Most Virus features are predetermined at the time of configuration and do not adapt to runtime availability unless changed by the filter rules.
This variable holds the working directory for the current message. During filter processing, mimedefang.pl chdir's into this directory before calling any of the filter_ functions. Note that this variable is set correctly in filter_sender and filter_recipient, but not in filter_relay.
If this variable is true, then mimedefang has discovered suspicious characters in message headers. This might be an exploit for bugs in MIME-parsing routines in some badly-written mail user agents (e.g. Microsoft Outlook.) You should always drop such messages.
If this variable is true, then mimedefang has discovered suspicious characters in the message body. This might be an exploit for bugs in MIME-parsing routines in some badly-written mail user agents (e.g. Microsoft Outlook.) You should always drop such messages.
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 filter_relay, filter_sender and filter_recipient in addition to the body filtering functions.
The IP address of the sending relay (as a string consisting of four dot-separated decimal numbers.) One potential use of $RelayAddr is to limit mailing to certain lists to people within your organization. This variable is available in filter_relay, filter_sender and filter_recipient in addition to the body filtering functions.
$Helo The argument given to the SMTP "HELO" command. This variable is available in filter_sender and filter_recipient, but not in filter_relay.
The contents of the "Subject:" header.
The sender of the e-mail. This variable is set in filter_sender and filter_recipient in addition to the body filtering functions.
A list of the recipients. In filter_recipient, it is set to the single recipient currently under consideration. Or, after calling read_commands_file within filter_recipient, the current recipient under consideration is in the final position of the array, at $Recipients[-1], while any previous (and accepted) recipients are at the beginning of the array, that is, in @Recipients[0 .. $#Recipients-1].
The contents of the "Message-ID:" header if one is present. Otherwise, contains the string "NOQUEUE".
The Sendmail queue identifier if it could be determined. This variable is set correctly in filter_relay, filter_helo, filter_sender and filter_recipient. Note, however, that Postfix may not allocate a queue ID until filter_recipient time. If a Queue-ID has not yet been allocated, $QueueID is set to "NOQUEUE".
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 is set correctly in filter_sender and filter_recipient, but it is not available in filter_relay.
Each time a virus-scanning function is called, messages (if any) from the virus scanner are accumulated in this variable. You can use it in filter_end to formulate a notification (if you wish.)
If a virus-scanning function found a virus, this variable will hold the virus name (if it could be determined.)
If defined, this is the configured Mail::SpamAssassin object used for mail tests. It may be initialized with a call to spam_assassin_init which also returns it.
This hash contains the values of some Sendmail macros. The hash elements exist only for macros defined by Sendmail. See the Sendmail documentation for the meanings of the macros.
By default, mimedefang passes the values of the following macros: ${daemon_name}, ${daemon_port}, ${if_name}, ${if_addr}, $j, $_, $i, ${tls_version}, ${cipher}, ${cipher_bits}, ${cert_subject}, ${cert_issuer}, ${auth_type}, ${auth_authen}, ${auth_ssf}, ${auth_author}, ${mail_mailer}, ${mail_host} and ${mail_addr}. In addition, ${client_port} is set to the client's TCP port.
If any macro is not set or not passed to milter, it will be unavailable. To access the value of a macro, use:
$SendmailMacros{"macro_name"}
Do not place curly brackets around the macro name. This variable is available in filter_sender and filter_recipient after a call to read_commands_file.
This array contains all the ESMTP arguments supplied in the MAIL FROM: command. For example:
sub print_sender_esmtp_args { foreach (@SenderESMTPArgs) { print STDERR "Sender ESMTP arg: $_; } }
This hash contains all the ESMTP arguments supplied in each RCPT TO: command. For example:
sub print_recip_esmtp_args { foreach my $recip (@Recipients) { foreach(@{$RecipientESMTPArgs{$recip}}) { print STDERR "Recip ESMTP arg for $recip: $_; } } }
This hash contains the Sendmail "mailer-host-address" triple for each recipient. Here's an example of how to use it:
sub print_mailer_info { my($recip, $mailer, $host, $addr); foreach $recip (@Recipients) { $mailer = ${RecipientMailers{$recip}}[0]; $host = ${RecipientMailers{$recip}}[1]; $addr = ${RecipientMailers{$recip}}[2]; print STDERR "$recip: mailer=$mailer, host=$host, addr=$addrn"; } }
In filter_recipient, this variable by default only contains information on the recipient currently under investigation. Information on all recipients is available after calling read_commands_file.
When the filter procedure decides how to dispose of a part, it should call one or more action_ subroutines. The action subroutines are:
Accept the part.
Rebuild the mail body, even if mimedefang thinks no changes were made. Normally, mimedefang does not alter a message if no changes were made. action_rebuild may be used if you make changes to entities directly (by manipulating the MIME::Head, for example.) Unless you call action_rebuild, mimedefang will be unaware of the changes. Note that all the built-in action... routines that change a message implicitly call action_rebuild.
Add a header to the message. This can be used in filter_begin or filter_end. The $hdr component is the header name without the colon, and the $val is the header value. For example, to add the header:
X-MyHeader: A nice piece of text
use:
action_add_header("X-MyHeader", "A nice piece of text");
Changes an existing header in the message. This can be used in filter_begin or filter_end. The $hdr parameter is the header name without the colon, and $val is the header value. If the header does not exist, then a header with the given name and value is added.
The $index parameter is optional; it defaults to 1. If you supply it, then the $index'th occurrence of the header is changed, if there is more than one header with the same name. (This is common with the Received: header, for example.)
Add a header to the message int the specified position $index. A position of 0 specifies that the header should be prepended before existing headers. This can be used in filter_begin or filter_end. The $hdr component is the header name without the colon, and the $val is the header value.
Deletes an existing header in the message. This can be used in filter_begin or filter_end. The $hdr parameter is the header name without the colon.
The $index parameter is optional; it defaults to 1. If you supply it, then the $index'th occurrence of the header is deleted, if there is more than one header with the same name.
Deletes all headers with the specified name. This can be used in filter_begin or filter_end. The $hdr parameter is the header name without the colon.
Drop the part. If called from filter_multipart, drops all contained parts also.
Drop the part, but add the warning $msg to the e-mail message. If called from filter_multipart, drops all contained parts also.
Accept the part, but add the warning $msg to the e-mail message.
Drop the part and replace it with a text part $msg. If called from filter_multipart, drops all contained parts also.
Drop the part, but save it in a unique location under $doc_root. The part is replaced with the text message $msg. The string "URL" in $msg is replaced with $base_url/something, that can be used to retrieve the message.
You should not use this function in filter_multipart.
This action is intended for stripping large parts out of the message and replacing them to a link on a Web server. Here's how you would use it in filter():
$size = (stat($entity->bodyhandle->path))[7];
if ($size > 1000000) {
return action_replace_with_url($entity,
"/home/httpd/html/mail_parts",
"http://mailserver.company.com/mail_parts",
"The attachment was larger than 1,000,000 bytes.\n" .
"It was removed, but may be accessed at this URL:\n\n" .
"t_URL_n");
}
This example moves attachments greater than 1,000,000 bytes into /home/httpd/html/mail_parts and replaces them with a link. The directory should be accessible via a Web server at http://mailserver.company.com/mail_parts.
The generated name is created by performing a SHA1 hash of the part and adding the extension to the ASCII-HEX representation of the hash. If many different e-mails are sent containing an identical large part, only one copy of the part is stored, regardless of the number of senders or recipients.
For privacy reasons, you must turn off Web server indexing in the directory in which you place mail parts, or anyone will be able to read them. If indexing is disabled, an attacker would have to guess the SHA1 hash of a part in order to read it.
Optionally, a fifth argument can supply data to be saved into a hidden dot filename based on the generated name. This data can then be read in on the fly by a CGI script or mod_perl module before serving the file to a web client, and used to add information to the response, such as Content-Disposition data.
A sixth optional argument, $salt, is mixed in to the SHA1 hash. This salt can be any string and should be kept confidential. The salt is designed to prevent people from guessing whether or not a particular attachment has been received on your server by altering the SHA1 hash calculation.
Accept the part, but change its name to $name, its suggested filename to $fname and its MIME type to $type. If $name or $fname are "", then mimedefang.pl generates generic names. Do not use this action in filter_multipart.
If you use action_defang, you must define a subroutine called defang_warning in your filter. This routine takes two arguments: $oldfname (the original name of an attachment) and $fname (the defanged version.) It should return a message telling the user what happened. For example:
sub defang_warning {
my($oldfname, $fname) = @_;
return "The attachment '$oldfname' was renamed to '$fname'n";
}
Run an external UNIX command $cmd. This command must read the part from the file ./FILTERINPUT and leave the result in ./FILTEROUTPUT. If the command executes successfully, returns 1, otherwise 0. You can test the return value and call another action_ if the filter failed. Do not use this action in filter_multipart.
Drop and quarantine the part, but add the warning $msg to the e-mail message.
Quarantines the entire message in a quarantine directory on the mail server, but does not otherwise affect disposition of the message. If "$msg" is non-empty, it is included in any administrator notification.
Quarantines a message in the Sendmail mail queue using the new QUARANTINE facility of Sendmail 8.13. Consult the Sendmail documentation for details about this facility. If you use action_sm_quarantine with a version of Sendmail that lacks the QUARANTINE facility, mimedefang will log an error message and not quarantine the message.
Reject the entire e-mail message with an SMTP failure code, and the one-line error message $reply. If the optional $code and $dsn arguments are supplied, they specify the numerical SMTP reply code and the extended status code (DSN code). If the codes you supply do not make sense for a bounce, they are replaced with "554" and "5.7.1" respectively.
action_bounce merely makes a note that the message is to be bounced; remaining parts are still processed. If action_bounce is called for more than one part, the mail is bounced with the message in the final call to action_bounce. You can profitably call action_quarantine followed by action_bounce if you want to keep a copy of the offending part. Note that the message is not bounced immediately; rather, remaining parts are processed and the message is bounced after all parts have been processed.
Note that despite its name, action_bounce does not generate a "bounce message". It merely rejects the message with an SMTP failure code.
WARNING: action_bounce() may cause the sending relay to generate spurious bounce messages if the sender address is faked. This is a particular problem with viruses. However, we believe that on balance, it's better to bounce a virus than to silently discard it. It's almost never a good idea to hide a problem.
Cause an SMTP "temporary failure" code to be returned, so the sending mail relay requeues the message and tries again later. The message $msg is included with the temporary failure code. If the optional $code and $dsn arguments are supplied, they specify the numerical SMTP reply code and the extended status code (DSN code). If the codes you supply do not make sense for a temporary failure, they are replaced with "450" and "4.7.1" respectively.
Silently discard the message, notifying nobody. You can profitably call action_quarantine followed by action_discard if you want to keep a copy of the offending part. Note that the message is not discarded immediately; rather, remaining parts are processed and the message is discarded after all parts have been processed.
This action sends an e-mail back to the original sender with the indicated message. You may call another action after this one. If action_notify_sender is called more than once, the messages are accumulated into a single e-mail message -- at most one notification message is sent per incoming message. The message should be terminated with a newline.
The notification is delivered in deferred mode; you should run a client-queue runner if you are using Sendmail 8.12.
NOTE: Viruses often fake the sender address. For that reason, if a virus-scanner has detected a virus, action_notify_sender is disabled and will simply log an error message if you try to use it.
This action e-mails the MIMEDefang administrator the supplied message. You may call another action after this one; action_notify_administrator does not affect mail processing. If action_notify_administrator is called more than once, the messages are accumulated into a single e-mail message -- at most one notification message is sent per incoming message. The message should be terminated with a newline.
The notification is delivered in deferred mode; you should run a client-queue runner if you are using Sendmail 8.12.
This action should only be called from filter_end. It appends the text "n$boilerplaten" to the first text/plain part (if $all is 0) or to all text/plain parts (if $all is 1).
This action should only be called from filter_end. It adds the text "n$boilerplaten" to the first text/html part (if $all is 0) or to all text/html parts (if $all is 1). This function tries to be smart about inserting the boilerplate; it uses HTML::Parser to detect closing tags and inserts the boilerplate before the
tag if there is one, or before the