procmail
[Top] [All Lists]

Re: Local domain forgery detection?

2002-08-28 03:11:38
From: Paul Chvostek <paul(_at_)it(_dot_)ca>

On Wed, Aug 28, 2002 at 12:48:14AM -0000, John Conover wrote:

Is there any way of detecting if *_ANY_* "Received: " record does
*_NOT_* have a local FQDN following the " from " tag in a sendmail(1)
produced e-mail header?


But I think what you'd want would be something more like:

 COUNTReceived=`sed '/^$/q' | grep -c '^Received:'`

Why so fast to leave procmail's internal egrep to do this?
There's no need.

Moreover, the default procmail egrep action considers the
headers only, so the sed statement is superfluous.

If you really want to count the Received: lines, how about:

        :0
        * 1^1 ^Received:
        { countRCVD = $= }



 #  Don't bother analysing local mail (adjust for your LDA)
 :0
 * COUNTReceived ?? ^1$

Perhaps you meant the more canonical

   * COUNTReceived ?? ^^1^^

The double-caret anchors the start and/or end of the expression
being considered when using the `??' syntax.


        
 *       ^From:
 *       ^To:
 *       ^Received: (from [a-z0-9]+(_at_)localhost
 { SWITCHRC=/dev/null }


I understand that you're quitting if the only Received: line is
local and appears valid; but this seems a cumbersome method.
I'll leave it, for now, though, with only this comment: it
seems like a lot of work used on every mail merely to save
us from a small-likelihood possible spoof case.  (If we're
going to look so hard for spoofs, we might as well ID them
as such right away, too, btw.  Anyway, this kind of reminds me of
a doctor who'd do a blood check for smallpox vaccinations before 
dispensing aspirin for the flu.)  :-)


 #  Catch anything that tries to appear local but isn't
 :0
 *       ^From:
 * !     ^(From|Return-Path):.*@
 *       ^Received:.*[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+
 {
         # From address is atless
         :0 fhw
         * !$    ^Received:.*($LOGNAME|root)@localhost
         | formail -A "X-spam: invalid return address (1-no @ symbol, but 
came from offsite)"
 
         # From address is badly formatted
         :0 fhw
         * !$    ^Received:.*($LOGNAME|root)@localhost
         * !     
^From:.*[a-z][a-z0-9_.:-]+@([a-z0-9][a-z0-9.-]*\.)+(com|net|org|edu|int|mil|gov|biz|info|name|[a-z][a-z])\>
         | formail -A "X-spam: invalid return address (2-address format 
error)"
 }


Here's how I check for local mail, fwiw.  "$WS" has been defined somewhere
above as a space and a tab; and "$INFINITY" has been defined as the max
score from "man procmailsc". I munged the dotted-quad stuff for this post
to stop tyro smartasses. :)

 :0  # if it's local mail (including via our mailhost), deliver it
     * $ $INFINITY^0 ^Received:.*\<myispname.com \[566\.684\.
     * $         2^0 ^Message-ID:[$WS]*<[^$WS]+(_at_)localhost>$
     *          -1^2 ^Received:
   $DEFAULT

This counts the Received: headers at the same time that it's
conducting the reasonable secure test of a valid Received: line.
If there are too many, it won't consider the mail local.




 :0 fhw
 * ! COUNTReceived ?? ^[01]$
 *       ^From:[^(_at_)]+$
 *       ^To:[^(_at_)]+$
 | formail -A "X-spam: too many Received lines for local mail"

 MYDOMAIN=`hostname`


Perhaps $HOST already works without forking a shell.  Try it; I'm not sure.
Or in any case, I'd probably just hardcode the name in to stop an extra
process on every piece of mail.



 :0
 * MYDOMAIN ?? 
[a-z]+[a-z0-9-]+\.\/[a-z]+[a-z0-9-]+\.(com?|net?|org?|biz|info)(\.([a-z][a-z]))?$
 { MYDOMAIN=$MATCH }

 :0 E
 * MYDOMAIN ?? 
[a-z]+[a-z0-9-]+\.\/[a-z]+[a-z0-9-]+(\.(bc|ab|sk|mb|on|qc|pe|ns|nf))?\.ca$
 { MYDOMAIN=$MATCH }

 # Adjust the range to suite your expectations.
 :0 fhw
 * MYDOMAIN ?? .+
 * COUNTReceived ?? ^[3-9]
 * $    ^Message-ID:.+@(.+\.)?$MYDOMAIN>
 | formail -A "X-spam: too many Received lines for local Message-ID (probably 
forged)"

It ain't perfect, but it may serve your needs, or at least act as a
starting point for your own rules.  If you come up with anything better,
please share.  :)

Okay. :)  Here's my "ATCOUNT" thingee:


        :0  # count @'s in To:
            * 1^1 TO ?? @
          { ATCOUNT = $= }
        :0  # count @'s in Cc:
            * $ $=^0
            * 1^1 CC ?? @
          { ATCOUNT = $= }
        :0  # add the subtotals, subtract 4 "gimmes"
            * $ $=^0
            * -4^0
          { TOO_MANY = $ATCOUNT }

In later recipes, I use either $ATCOUNT or $TOO_MANY, depending on
what I want to test.  ($TOO_MANY will only be defined if 
we exceeded my personal choice for "gimmes.")

Now back to the original question from John Connover, looking for
any Received: header without a local FQDN following the "from"
string: John, the more I read your little question, which seemed
simply enough stated, the more I confuse myself.  Would my
local tester shown above suffice?  I find on my ISP's hosts
that any local mail conforms to my test, including mail arriving
from the dial-up modem pool.  If that isn't sufficient for what
you had in mind, please write back.  :)

-- 
dman

_______________________________________________
procmail mailing list
procmail(_at_)lists(_dot_)RWTH-Aachen(_dot_)DE
http://MailMan.RWTH-Aachen.DE/mailman/listinfo/procmail