There has been a recent (Feb 12-20th 2004) article posted by Microsoft that discusses their "Caller ID for E-Mail" which is basically their way of the usual branding of other people's ideas as their own.
In this particular case, the idea that they are branding as their own is the SPF (Sender Policy Framework) concept.
What these technologies both do are essentially try to fix the flaw in SMTP which allows anyone to send mail so that it looks as if it is from anyone. I could send mail out from the spamblogging.com domain but make it look in the from field that it is from whitehouse.gov if I was so inclined to be a jerk.
These two ideas both do a lookup to see if the domain that the mail was sent from is part of the same group as the from field claims to be from. If not, then action can be taken, based on how the mailserver is configured.
Microsoft Exchange, like many of their products, claims to make various maintenance processes easier - and perhaps in many cases it succeeds. But in cases like this, unless MS specifically writes in a tool to allow you to do something, you can't do it at all. So if you want to implement their Caller-ID system, you can't yet since they haven't written anything.
And at first glance you can't use the SPF either.
That said, these two pages help a little in explaining how to enable SPF on your server and setting up the DNS as well. The DNS bit is relatively straightfoward - quoting from this page:
You can create the reverse MX record by hand or through a wizard provided by the creators of the SPF RFC http://spf.pobox.com/wizard.html. The format of a typical reverse MX record is simple enough:domain-name. IN TXT "v=spf1 a mx ptr -all"
In this example,
is the domain name to publish a reverse MX record for. Note the trailing dot after the domain name; if you published the domain name foo.com, it would look like this: foo.com. IN TXT "v=spr1 a mx ptr -all"
To add the appropriate reverse MX record to a server running Microsoft's DNS, follow these steps:
1. Open the DNS snap-in via Microsoft Management Console
2. Right-click the domain that you want to add an SPF record for.
3. Choose Other New Records.
4. Choose a record type of TXT.
5. Enter the SPF record to specify valid mail senders.For other DNS servers, such as BIND or the free DNS server BIND-PE, you need to add the record manually.
To implement the SPF checking into Exchange is harder though. The same page lists out a way that you can use an Exchange event sink and some third party tools to do it.
So now this brings me to the idea that I had which spawned this blog post...
It occurred to me that I have already written a hack for Exchange that allows SpamAssassin to run on Exchange 2000/2003.
This hack also runs on an event sink that gets triggered every time a message is received by Exchange. The general process is:
1) A message comes in and triggers the event sink.
2) The event sink script holds the message in memory as an object.
3) The script writes the message out to a temp directory on the server as a file.
4) The script calls the SpamAssassin program to be executed on that file.
5) SpamAssassin does its thing and outputs a file with the headers in it that essentially say that it is either spam or not spam based on what the settings are.
6) The script scans that file that it outputs to for the X-Spam header and checks to see if it is "YES" or not.
7a) If it is spam, it then marks the subject of the message object in memory with and additional "*****SPAM****" (or whatever you have it configured to write).
7b) If it is not spam, then it does nothing at all and does so with a big smile on its face.
8) The message in memory is then left to continue on through the event triggers and if it passes all of those successfully and isn't kicked to badmail or killed, then it gets delivered to the end user.
9) The end user then has mail that was triggered marked as "******SPAM*****" (or whatever you chose to mark it as in the script settings) and can thereby sort it however they want with their own local filters.
This is all relevant because the new (as of the time of this writing) 2.70 (still in testing/cvs, but available to use) version of SpamAssassin will handle SPF lookups - so this is an "easy" way to get both spam filtering and SPF lookups into Exchange which would otherwise put up a fight.
A small zip file with the Exchange 2000/2003 event sink code can be found here.
A description of how to install SpamAssassin on your Win32 system can be found here. (that talks about how to install SA 2.60, but should still be applicable for 2.70 - although to setup the SPF part might take slightly more familiarity with the configuration settings - I haven't looked too hard yet)
Also note that this guy has written a Win32 version of the spamc/spamd as well - that is likely mentioned on his site.
You can find the latest SpamAssassin 2.70 releases at the bottom of this page. If you look in the "devel" directory you can find a 3.00 version - but I haven't look at anything at all regarding this - so I personally wouldn't use it just yet (at least not in the Exchange setup since I only know it works with the 2.x systems for sure).
Notes about the hack of mine:
1) This solution is serial - while it is working on one message, no other event sinks can fire during that time. So if you get a lot of messages in a day, then this might not be the best solution for your system. I have a network of about 25 users and we get maybe 1000 messages a day (largely spam too) and it runs fine for us (both on a PIII 1Ghz with 512MB RAM and on our newer P4 HT 3Ghz with 2G RAM).
2) This only gets triggered on incoming mail, it does nothing to outgoing mail - that said, you should already configure your system to not allow relays anyway.
3) I get about 1 e-mail a day from around the world asking me for the same feature to be added - everyone wants the headers that SpamAssassin creates to be passed on to the message that will be delivered to the end user. This is just a quick and dirty hack that I made - in an ideal world that would happen, it would behave just like SA - but as far as I know (someone please correct me if I'm wrong), the scripting API does give access to editing the subject of the message object before it is delivered to the end user, but it doesn't allow you to edit the headers.
It is either that or the way Exchange formats the headers is "wrong" compared the way SA assumes that they are formatted (Unix style).
I honestly don't recall since it has been nearly a year since I banged this hack out.
4) The code is no way guaranteed not do horrible things to your machine, your family, or the price of tea in Southeast Asia. That said, it has only ever done something weird on our server once - and that was that it just randomly stopped running. I reinstalled the sink and it was fine again - no clue what caused it (likely a server issue and not an issue with the code).
We briefly turned it off when we upgraded from Exchange 2000 to 2003 and planned on getting a commercial solution, but then when the higher-ups decided that my budget being $0 was too high as it is, we would be just fine without any commercial products.
5) This isn't a total no-brainer - it probably helps a bit if you know how to use SpamAssassin on other systems so that you can configure your user-prefs file.
6) The code currently will write out the spam/non-spam to folders that you specify - I need to add in the ability to turn that off. You can do that yourself if you so desire simply by commenting out code or cutting it out, but if that worries you, eventually I will get around to updating this code.
7) If you change something in the SA user-prefs file and expect it to show up in the message delivered to the end user... don't. The user-prefs file will change what the temporary file has done to it, but the message delivered to the end user with this hack is what the event sink controls - not SA directly. If you want to change the subject addition text to "*-*BEANS*-*" instead of "*****SPAM*****", then do so via the event sink script and not via your user-prefs config file.
If that didn't scare you off from it, then good luck with it and feel free to write me and I'll likely write back and whine about having too much other stuff to do right now to make modifications on it.
Also, a special thanks to Nugget who mentioned SPF/Caller-ID in his blog (I am "stenz" with the brain dead responses in that post).
I had been so busy with work lately that I had somehow not noticed the SPF/Caller-ID news.
Posted by Eric at March 11, 2004 11:29 AM
| TrackBack
Hi,
you might also have a look at www.aloaha.com to find a very elegant way on how to implement SPF, CallerID, greylisting and other features on Windows based Platforms. Aloaha is not limited to IIS/Exchange since it doenst use a sink either.
Thanks
Frank
Posted by: Frank Hellmann at July 31, 2004 10:50 AM
Same cat, different skin, except don't send SPAM to end user.
'
'
' install this sink event using:
'
' cscript smtpreg.vbs /add 1 onarrival SMTPScriptingHost CDO.SS_SMTPOnArrivalSink "mail from=*"
' cscript smtpreg.vbs /setprop 1 onarrival SMTPScriptingHost Sink ScriptName "d:\filter\filter.vbs"
'
' remove using:
'
' cscript smtpreg.vbs /remove 1 onarrival SMTPScriptingHost
' cscript smtpreg.vbs /delprop 1 onarrival SMTPScriptingHost Sink ScriptName "*:\path\to\filter.vbs"
'
' smtpreg.vbs is available from msdn.microsoft.com by downloading either the CDO platform SDK
' or any sample application using CDO event sinks.
'
' Declarations
Dim strPostmasterEmail ' email address of the SPA reject sender reply address (prevent loops)
Dim strPostmasterEmail1 ' email address of the SPA reject sender (prevent loops)
Dim strPostmasterEmail2 ' email address of the server postmaster (prevent loops)
Dim strPostmasterEmail3 ' email address of the Exchange postmaster
' Definitions
strPostmasterEmail = "your-domainname-Content-Filter@your-domainname.com" ' prevent AWL & Bayes D/B poisoning
strPostmasterEmail1 = "your-domainname-Content-Filter" ' prevent AWL & Bayes D/B poisoning
strPostmasterEmail2 = "postmaster@your-domainname.com" ' prevent AWL & Bayes D/B poisoning
strPostmasterEmail3 = "administrator@your-domainname.com"
Sub ISMTPOnArrival_OnArrival(ByVal oMsg, intEventStatus )
' only process the message if the message is not from the postmaster
if Instr(1, oMsg.To, strPostmasterEmail) = 0 then
if Instr(1, oMsg.To, strPostmasterEmail1) = 0 then
if Instr(1, oMsg.From, strPostmasterEmail1) = 0 then
if Instr(1, oMsg.From, strPostmasterEmail2) = 0 then
if Instr(1, oMsg.From, strPostmasterEmail3) = 0 then
' Send suspect to be scanned by SPA
Dim oStream ' Msg to be screened for SPAM
set oStream = oMsg.Getstream
oStream.SaveToFile "d:\filter\spam.txt",2
Dim oShell ' Call SpamAssassin
Set oShell = CreateObject("WSCript.shell")
oShell.run "d:\filter\spam.cmd",1,true
Set oShell = Nothing
' Read SpamAssassin results
Set fs = CreateObject("Scripting.FileSystemObject")
Set f = fs.GetFile("d:\filter\test.txt")
Set ts = f.OpenAsTextStream(1, -2)
strMessage = ts.ReadAll
ts.Close
' Is this oMsg SPAM?
If InStr(1, strMessage, "X-Spam-Flag: YES", 1) Then
' Clip out reason this is SPAM + message Header
Dim WordPos1
Dim WordPos2
Dim WordPos3
Dim MsgText
Dim MsgText1
dim MsgText2
WordPos1 = InStr(1, strmessage, "X-Spam-Report:", 0)
WordPos2 = InStr(1, strmessage, "X-Spam-Status:", 0)
WordPos3 = InStr(1, strmessage, "X-Spam-Level:", 0)
MsgText = Mid(strmessage, WordPos1, Wordpos2 - WordPos1 - 1) ' Report text for email
MsgText1 = Mid(strmessage, WordPos2, Wordpos3 - WordPos2 - 1) ' Status text for email
MsgText2 = left(strMessage, wordpos3 -1) 'Full "Header" text for log
Set WordPos1 = Nothing
Set WordPos2 = Nothing
Set WordPos3 = Nothing
If instr(1, strMessage, "USER_IN_BLACKLIST", 1) then
Blist = "yes"
elseif instr(1, strMessage, "RCVD_IN_OPM", 1) then
Blist = "yes"
elseIf instr(1, strMessage, "RCVD_IN_SORBS", 1) then
Blist = "yes"
elseif instr(1, strMessage, "RCVD_IN_SBL", 1) then
Blist = "yes"
elseif instr(1, strMessage, "RCVD_IN_DSBL", 1) then
Blist = "yes"
elseif instr(1, strMessage, "RCVD_IN_NJABL", 1) then
Blist = "yes"
elseif instr(1, strMessage, "RCVD_IN_RFCI", 1) then
Blist = "yes"
elseif instr(1, strMessage, "RCVD_IN_BL_SPAMCOP_NET", 1) then
Blist = "yes"
end if
' Log the SPAM Messages here!
Dim oFS1
Set oFS1 = CreateObject("Scripting.FileSystemObject")
Dim oLog1
Set oLog1 = oFS1.OpenTextFile("d:\filter\log\spam.log", 8, True )
oLog1.Write " oLog1.Write "SPAM Rule Violation" & vbCrLf
oLog1.Write msgtext2 & vbCrLf
oLog1.Write "Email Below>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" & vbCrLf
oLog1.Write "From: " & oMsg.From & vbCrLf
oLog1.Write "To: " & oMsg.To & vbCrLf
oLog1.Write "Subject: " & oMsg.Subject & vbCrLf & "Date: " & now() & vbCrLf
oLog1.Write oMsg.TextBody & vbCrLf & vbCrLf
' oLog1.Write strMessage & vbCrLf & vbCrLf ' for more detailed logging
oLog1.Close
' Send a rejection message back to the sender (optional, comment to remove)
If Blist > "yes" then ' Send reject msg to Spam senders if not on our blacklist or not Relayed
Dim oRejectionMessage
Set oRejectionMessage = CreateObject("CDO.Message")
oRejectionMessage.To = oMsg.From
oRejectionMessage.From = "your-domainname-Content-Filter"
oRejectionMessage.Subject = "Mail rejected"
oRejectionMessage.TextBody = "Your message with the subject: """ & oMsg.Subject & """ addressed to: " & oMsg.To &_
" was rejected by our content filter."
oRejectionMessage.Send
Set oRejectionMessage = Nothing 'release the object reference
Blist = "no"
end if
' Send status message to Administrator account
Dim oNotifyMessage
Set oNotifyMessage = CreateObject("CDO.Message")
oNotifyMessage.To = strPostmasterEmail3
oNotifyMessage.From = strPostmasterEmail1
oNotifyMessage.Subject = "Mail rejected"
oNotifyMessage.TextBody = "Subject: " & oMsg.Subject & vbCrLf & "Sent From: " & oMsg.from &_
vbCrLf & "Recipient: " & oMsg.To & vbcrlf & vbcrlf & msgtext1 & vbcrlf & msgtext
oNotifyMessage.Send
Set oNotifyMessage = Nothing 'release the object reference
' tag the message as bad, causing it to be rejected by CDO
Dim oFlds
Set oFlds = oMsg.EnvelopeFields
oFlds("http://schemas.microsoft.com/cdo/smtpenvelope/messagestatus") = 3 ' Send to bad mail queue, abort delivery
oFlds.Update ' must call update to commit changes to fields
oMsg.DataSource.Save() ' save the message to the message store
oMsg.Delete 'delete this message
intEventStatus = 1 ' skip remaining sinks since message rejected
'dim oFS3 'Temp -- Build pool of SPAM messages for SA-Learn (temporary)
'Set oFS3 = CreateObject("Scripting.FileSystemObject")
'name = Date & " " & Time
'name = Replace(name, "/", "-")
'name = Replace(name, ":", "-")
'name = Replace(name, " ", "-") & ".tmp"
'name = "d:\test\spam\" & name
'from = "d:\filter\spam.txt"
'ofs3.CopyFile from, name
else
Dim Reaspos1
Dim Reaspos2
Dim Reaslof
Dim Score
reaspos1 = Instr(1, strMessage, "X-Spam-Status:", 0)
reaspos2 = Instr(1, strMessage, "X-Spam-Level:", 0)
reaslof = reaspos2 - reaspos1
score = mid(strMessage,reaspos1,reaslof)
dim oFS2 ' This is logging nonspam messages processed (optional)
Set oFS2 = CreateObject("Scripting.FileSystemObject")
Dim oLog2
Set oLog2 = oFS2.OpenTextFile("d:\filter\log\nonspam.log", 8, True )
oLog2.Write "From: " & oMsg.From & vbCrLf
oLog2.Write "To: " & oMsg.To & vbCrLf
oLog2.Write "Subject: " & oMsg.Subject & vbCrLf & "Date: " & now() & vbCrLf
oLog2.Write "Spam Score: " & score & vbCrLf
oLog2.Close
'dim oFS4 'Temp -- Build pool of HAM messages for SA-Learn (temporary)
'Set oFS4 = CreateObject("Scripting.FileSystemObject")
'name = Date & " " & Time
'name = Replace(name, "/", "-")
'name = Replace(name, ":", "-")
'name = Replace(name, " ", "-") & ".tmp"
'name = "d:\test\Ham\" & name
'from = "d:\filter\spam.txt"
'ofs4.CopyFile from, name
intEventStatus = 0 ' run next event sink, no spam detected
end if
end if
end if
end if
end if
end if
End Sub
Posted by: Mark Mose at April 26, 2004 05:53 PM