[Mimedefang] Validate users before scanning?

Jan Pieter Cornet johnpc at xs4all.nl
Sat May 14 20:07:39 EDT 2005


Sorry to reopen this conversation so late... holidays interfered.
This looked important enough however.

On Wed, May 04, 2005 at 01:00:02PM -0400, David F. Skoll wrote:
> Although nosuchuser at domain.net doesn't exist, the milter is still passed
> it, and it is NOT told by Sendmail that delivery will fail.  Normally,
> this isn't a big deal, but if you're streaming by recipient, it is
> a big deal.

Hm... this looks like a flaw in the milter API. The way to find out
which recipients are finally accepted by sendmail is very grotty
to say the least, and I just found out how to do it by inspection
of the sendmail source.

First, you could overcome this in stream_by_recipient by ignoring
permanent errors on the RCPT To: command... urm, that is, if you had
used an SMTP connection there, instead of a sendmail call, but it's
kinda hard to do deferred delivery (sendmail -odd) using an SMTP
connection.

The rationale would be that a (permanent) error return on RCPT TO:
has also been catched the previous time, and the sending server is
already aware of it... only MIMEDefang is kept in the dark by sendmail
here: the entry in the @Recipients array was really rejected but
MIMEDefang doesn't know it.

> (The rationale is that your milter might indeed want to know about
> all RCPT TO commands, even ones that fail.)

But that rationale is flawed in the first place, because not all
RCPT TO commands are passed to milters.

I just checked the sendmail 8.13.3 source to really confirm what I
know about recipient processing in sendmail. This is what actually
happens after RCPT To: (see sendmail/srvrsmtp.c, search for
"case CMDRCPT:" if you want to read along. I'll skip anything not
related to verifying the recipient).

1) lexical validation of the address (this actually happens inside
   parseaddr() in the parseaddr.c file).
2) call ruleset 3
3) call ruleset 0
4) resolve to (mailer, host, address) triple.
5) in case of the #$error mailer, PROCESS THE ERROR.
6) call check_rcpt
7) call any Milters (the xxfi_envrcpt callbacks).
8) check illegal mailer attempts (eg RCPT To:/etc/passwd)
   (this step and below are from recipient.c:recipient())
9) if the select mailer wants any of the following, do so:
    alias processing, userdb lookups, ruleset 5 (in that order, btw)
10) lookup the target in the passwd file if the selected mailer wants that.

In the above, in case of an error, all the following steps are skipped.
Resolving to the $#error mailer, or returning any reject code, counts
as an error, and will lead to an immediate error return at the SMTP level.

So, in short, Milters' xxfi_envrcpt (and thus mimedefang filter_recipient)
is only called if an address is syntactically correct, and is properly
parsed by rulesets 3,0, and passes check_rcpt.

After this Milter call, a recipient might still get rejected (and result
in an error returned from the "RCPT To" to the calling server) if a local
user is not found in the alias file or the passwd database (or userdb,
or via ruleset 5, but these are less common).

So, how can a Milter know if a recipient was really accepted by sendmail?
Looking at the list above, you won't know that when sendmail calls your
milter at xxfi_envrcpt time (and even if your milter decides to accept
the recipient, there might be multiple milters...)

The solution for a Milter to know if sendmail really accepted a recipient
is to check the value of the ${nrcpts} macro. As far as I can see that is
the only way to check. This means you must add `, {nrcpts}' to 
confMILTER_MACROS_ENVRCPT, which isn't the default. This value will only
get set after item 10 in the list above, so on the first xxfi_envrcpt
call it will be zero (or unset, I haven't checked), and on the next
xxfi_envrcpt OR the next xxfi_{header,eoh,eom} call, the value of ${nrcpts}
will have increased by one if the PREVIOUS recipient was really accepted
by sendmail after all.

And that assumes that alias expansion doesn't take place, but since MIMEDefang
always sets SMFIF_DELRCPT, aliases are only verified and not expanded at
RCPT To: time.

Note that since mimedefang's "-a" option is only consulted at
"xxfi_envfrom" time, it's impossible to put this check in the perl
filter. It has to be coded (or at least supported) in the mimedefang.c
milter part (in the rcptto() call).

Since I suspect that I lost some 98% of the readers of this list at
least seven paragraphs ago, I'll give an answer to the OP as a separate
message :)

Ugh, how messy... it would be so much nicer if sendmail could give you
the finalized list of recipients as soon as milter enters the xxfi_header
state... but I cannot find a way to extract that information.

-- 
#!perl -wpl # mmfppfmpmmpp mmpffm <pmmppfmfpppppfmmmf at fpffmm4mmmpmfpmf.ppppmf>
$p=3-2*/[^\W\dmpf_]/i;s.[a-z]{$p}.vec($f=join('',$p-1?chr(sub{$_[0]*9+$_[1]*3+
$_[2]}->(map{/p|f/i+/f/i}split//,$&)+97):qw(m p f)[map{((ord$&)%32-1)/$_%3}(9,
3,1)]),5,1)='`'lt$&;$f.eig;                                # Jan-Pieter Cornet



More information about the MIMEDefang mailing list