This commit was manufactured by cvs2svn to create tag 'milter-0_8_2'.
Sprout from bmsi 2005-05-31 18:23:49 UTC Stuart Gathman <stuart@gathman.org> 'Development changes since 0.7.2' Cherrypick from master 2005-07-20 14:56:38 UTC Stuart Gathman <stuart@gathman.org> 'Handle corrupt ZIP attachments': COPYING CREDITS MANIFEST.in Milter/__init__.py Milter/dsn.py Milter/dynip.py NEWS TODO bms.py faq.html milter.cfg milter.html milter.spec miltermodule.c mime.py setup.cfg setup.py softfail.txt spf.py spfquery.py strike3.txt test/zip1 test/zip2 test/zip3 test/ziploop testmime.py
This commit is contained in:
@@ -1,4 +1,16 @@
|
||||
# $Log$
|
||||
# Revision 1.4 2005/06/17 01:49:39 customdesigned
|
||||
# Handle zip within zip.
|
||||
#
|
||||
# Revision 1.3 2005/06/02 15:00:17 customdesigned
|
||||
# Configure banned extensions. Scan zipfile option with test case.
|
||||
#
|
||||
# Revision 1.2 2005/06/02 04:18:55 customdesigned
|
||||
# Update copyright notices after reading article on /.
|
||||
#
|
||||
# Revision 1.1.1.4 2005/05/31 18:23:49 customdesigned
|
||||
# Development changes since 0.7.2
|
||||
#
|
||||
# Revision 1.62 2005/02/14 22:31:17 stuart
|
||||
# _parseparam replacement not needed for python2.4
|
||||
#
|
||||
@@ -62,12 +74,13 @@
|
||||
# with a warning message.
|
||||
|
||||
# Author: Stuart D. Gathman <stuart@bmsi.com>
|
||||
# Copyright 2001 Business Management Systems, Inc.
|
||||
# This code is under GPL. See COPYING for details.
|
||||
# Copyright 2001,2002,2003,2004,2005 Business Management Systems, Inc.
|
||||
# This code is under the GNU General Public License. See COPYING for details.
|
||||
|
||||
import StringIO
|
||||
import socket
|
||||
import Milter
|
||||
import zipfile
|
||||
|
||||
import email
|
||||
import email.Message
|
||||
@@ -80,6 +93,16 @@ from email import Errors
|
||||
|
||||
from types import ListType,StringType
|
||||
|
||||
def zipnames(txt):
|
||||
fp = StringIO.StringIO(txt)
|
||||
zipf = zipfile.ZipFile(fp,'r')
|
||||
names = []
|
||||
for nm in zipf.namelist():
|
||||
names.append(('zipname',nm))
|
||||
if nm.lower().endswith('.zip'):
|
||||
names += zipnames(zipf.read(nm))
|
||||
return names
|
||||
|
||||
class MimeGenerator(Generator):
|
||||
def _dispatch(self, msg):
|
||||
# Get the Content-Type: for the message, then try to dispatch to
|
||||
@@ -153,7 +176,7 @@ class MimeMessage(Message):
|
||||
def getname(self):
|
||||
return self.get_param('name')
|
||||
|
||||
def getnames(self):
|
||||
def getnames(self,scan_zip=False):
|
||||
"""Return a list of (attr,name) pairs of attributes that IE might
|
||||
interpret as a name - and hence decide to execute this message."""
|
||||
names = []
|
||||
@@ -168,7 +191,14 @@ class MimeMessage(Message):
|
||||
else:
|
||||
val = _unquotevalue(val.strip())
|
||||
names.append((attr,val))
|
||||
return names + [("filename",self.get_filename())]
|
||||
names += [("filename",self.get_filename())]
|
||||
if scan_zip:
|
||||
for key,name in tuple(names): # copy by converting to tuple
|
||||
if name and name.lower().endswith('.zip'):
|
||||
txt = self.get_payload(decode=True)
|
||||
if txt.strip():
|
||||
names += zipnames(txt)
|
||||
return names
|
||||
|
||||
def ismodified(self):
|
||||
"True if this message or a subpart has been modified."
|
||||
@@ -276,19 +306,27 @@ A copy of your original message was saved as '%s:%s'.
|
||||
See your administrator.
|
||||
"""
|
||||
|
||||
def check_name(msg,savname=None,ckname=check_ext):
|
||||
def check_name(msg,savname=None,ckname=check_ext,scan_zip=False):
|
||||
"Replace attachment with a warning if its name is suspicious."
|
||||
for key,name in msg.getnames():
|
||||
badname = ckname(name)
|
||||
if badname:
|
||||
hostname = socket.gethostname()
|
||||
msg.set_payload(virus_msg % (badname,hostname,savname))
|
||||
del msg["content-type"]
|
||||
del msg["content-disposition"]
|
||||
del msg["content-transfer-encoding"]
|
||||
name = "WARNING.TXT"
|
||||
msg["Content-Type"] = "text/plain; name="+name
|
||||
break
|
||||
try:
|
||||
for key,name in msg.getnames(scan_zip):
|
||||
badname = ckname(name)
|
||||
if badname:
|
||||
if key == 'zipname':
|
||||
badname = msg.get_filename()
|
||||
break
|
||||
else:
|
||||
return Milter.CONTINUE
|
||||
except zipfile.BadZipfile:
|
||||
# a ZIP that is not a zip is very suspicious
|
||||
badname = msg.get_filename()
|
||||
hostname = socket.gethostname()
|
||||
msg.set_payload(virus_msg % (badname,hostname,savname))
|
||||
del msg["content-type"]
|
||||
del msg["content-disposition"]
|
||||
del msg["content-transfer-encoding"]
|
||||
name = "WARNING.TXT"
|
||||
msg["Content-Type"] = "text/plain; name="+name
|
||||
return Milter.CONTINUE
|
||||
|
||||
import email.Iterators
|
||||
@@ -309,11 +347,11 @@ check function(MimeMessage): int
|
||||
# save call context for Python without nested_scopes
|
||||
class _defang:
|
||||
|
||||
def __init__(self):
|
||||
self.scan_html = True
|
||||
def __init__(self,scan_html=True):
|
||||
self.scan_html = scan_html
|
||||
|
||||
def _chk_name(self,msg):
|
||||
rc = check_name(msg,self._savname,self._check)
|
||||
rc = check_name(msg,self._savname,self._check,self.scan_zip)
|
||||
if self.scan_html:
|
||||
check_html(msg,self._savname) # remove scripts from HTML
|
||||
if self.scan_rfc822:
|
||||
@@ -322,12 +360,14 @@ class _defang:
|
||||
return check_attachments(msg,self._chk_name)
|
||||
return rc
|
||||
|
||||
def __call__(self,msg,savname=None,check=check_ext,scan_rfc822=True):
|
||||
def __call__(self,msg,savname=None,check=check_ext,scan_rfc822=True,
|
||||
scan_zip=False):
|
||||
"""Compatible entry point.
|
||||
Replace all attachments with dangerous names."""
|
||||
self._savname = savname
|
||||
self._check = check
|
||||
self.scan_rfc822 = scan_rfc822
|
||||
self.scan_zip = scan_zip
|
||||
check_attachments(msg,self._chk_name)
|
||||
if msg.ismodified():
|
||||
return True
|
||||
|
||||
Reference in New Issue
Block a user