Passing macros to MIMEDefang

Setup md_tag | Early %SendmailMacros

Custom Sendmail macros may be set in Sendmail rulesets, and the data passed to MILTER agents. Applications of this method include aborting MILTER processing early for trusted hosts, or to alter the behavior of the agent based on the ruleset outcome.

In the following example, Sendmail is configured to pass the custom md_tag macro to MIMEDefang; the macro will contain certified or authenticated if the connection in question was made using a verified Transport Layer Security (TLS) certificate or was authenticated via SMTP AUTH. This allows custom code in the MIMEDefang /etc/mail/mimedefang-filter file to check $SendmailMacros{md_tag} and alter the processing of mail based on the macro.

Additionally, a patch for MIMEDefang and documentation is provided on configuring MIMEDefang to make the macros available early in the non-default filter_recipient subroutine.

Setup md_tag

  1. Configure sendmail.mc.
  2. The md_tag macro will need to be registered as a value to pass to MILTER, along with the usual macros passed by sendmail if they are to be used.

    define(`confMILTER_MACROS_ENVFROM', ``i, {auth_type}, {auth_authen},?
    {auth_ssf}, {auth_author}, {mail_mailer}, {mail_host}, {mail_addr},?
    {md_tag}'')

    Also, disable the FEATURE(`delay_checks') feature in sendmail.mc if present. The delay_checks feature prevents md_tag from being passed to MIMEDefang by altering the order in which rulesets are invoked by sendmail.

  3. Configure mimedefang to pass md_tag.
  4. Upgrade MIMEDefang to version 2.36 or higher, and supply the -a md_tag argument to mimedefang. This configuration should ideally be done in /etc/mail/mimedefang.conf with a MD_EXTRA="-a md_tag" entry, assuming the MIMEDefang examples/init-script is being used as the startup script.

    Older versions of MIMEDefang will require manual patching of mimedefang.c prior to running ./configure to pass custom macro values. Search the code for write_macro_value statements to see how this is done, then rebuild and reinstall MIMEDefang.

  5. Create rulesets to populate md_tag as needed.
  6. The following ruleset sets md_tag to either be certified or authenticated depending on whether the client TLS certificate could be verified or SASL authentication succeeded. Additional rules could set md_tag if the host is listed in the access map, or other conditions are met to avoid having to duplicate such checks in MIMEDefang. The following data should be appended to sendmail.mc and sendmail.cf rebuilt.

    LOCAL_CONFIG
    Kmacro macro

    LOCAL_RULESETS
    SLocal_check_mail
    R$* $: <$&{verify}> $1
    R<OK> $* $: $(macro {md_tag} $@ certified $) <> $1
    R<$*> $* $: <$&{auth_type}> $2
    R<$={TrustAuthMech}> $* $: $(macro {md_tag} $@ authenticated $) <> $2
    R<$*> $* $@ OK

    Sendmail expects tabs between the left and right hand sides of the above ruleset, not spaces.

  7. Configure mimedefang-filter.
  8. To make use of md_tag in /etc/mail/mimedefang-filter, one can perform different actions depending on whether md_tag exists, or contains a particular value. md_tag will only be set by the ruleset, so messages that fail the ruleset tests will leave md_tag undefined in mimedefang-filter. For example, time consuming spam checks in filter_end can be avoided for messages that set a md_tag value.

    if ($Features{"SpamAssassin"} and not exists $SendmailMacros{md_tag}) {
    ?
    }

Early %SendmailMacros

In MIMEDefang, %SendmailMacros is not populated when the non-default filter_relay, filter_sender, and filter_recipient subroutines are called. To configure MIMEDefang to read the macro values early for use in filter_recipient, add the following code to the mimedefang-filter file and then call the load_sendmail_macros() subroutine in the required filter_* routines.

Adding early macro reading imposes a slight performance penalty, as the COMMANDS file will be read twice for each message: once for filter_recipient, and again before filter_begin.

  1. load_sendmail_macros() function to add to mimedefang_filter
  2. sub load_sendmail_macros {
    open COMMANDS, '< ./COMMANDS' or return 0;
    while (<COMMANDS>) {
    chomp;
    my $rawcmd = $_;
    my $cmd = percent_decode($rawcmd);
    my $arg = substr($cmd, 1);
    $cmd = substr($cmd, 0, 1);
    my $rawarg = substr($rawcmd, 1);
    if ($cmd eq "=") {
    my ($macro, $value);
    ($macro, $value) = split(' ', $rawarg);
    $value = "" unless defined($value);
    $macro = "" unless defined($macro);
    if ($macro ne "") {
    $macro = percent_decode($macro);
    $value = percent_decode($value);
    $SendmailMacros{$macro} = $value;
    }
    }
    }
    return 1;
    }

  3. Enable filter_recipient.
  4. The filter_recipient function is not called by default. Enabling it is done in the system startup script for MIMEDefang, e.g. via MX_RECIPIENT_CHECK=yes in the sample examples/init-script file under the MIMEDefang source directory.

  5. Call load_sendmail_macros() from filter_recipient.
  6. sub filter_recipient {
    my ($recipient, $sender, $ip, $hostname, $first, $helo,
    $rcpt_mailer, $rcpt_host, $rcpt_addr) = @_;

    load_sendmail_macros();
    ?

Now code in filter_recipient should have access to the macro data in %SendmailMacros. The following will log the macros to syslogd for debugging purposes, and abort all subsequent MILTER filtering for the message if md_tag is set to certified.

sub filter_recipient {
load_sendmail_macros();
md_syslog(
'info',
'macros: ' . join ' ',
map { $_ . '=' . $SendmailMacros{$_} } sort keys %SendmailMacros
);

if (exists $SendmailMacros{md_tag}
and $SendmailMacros{md_tag} eq 'certified') {
return 'ACCEPT_AND_NO_MORE_FILTERING', 'ok';
}

return 'CONTINUE', 'ok';
}