Add initial support for Syslog option

This commit is contained in:
Scott Kitterman
2018-02-16 14:11:48 -05:00
parent 1980266666
commit 3e032ad6ad
2 changed files with 51 additions and 42 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ Mode implemented
PidFile PidFile
Selector implemented Selector implemented
Socket implemented verified Socket implemented verified
Syslog Syslog implemented partly tested
UMask UMask
UserID implemented verified UserID implemented verified
+49 -40
View File
@@ -28,8 +28,6 @@ import dkim
from dkim.dnsplug import get_txt from dkim.dnsplug import get_txt
from dkim.util import parse_tag_value from dkim.util import parse_tag_value
import authres import authres
import logging
import logging.config
import os import os
import tempfile import tempfile
import StringIO import StringIO
@@ -41,15 +39,10 @@ from dkimpy_milter.util import drop_privileges
from dkimpy_milter.util import setExceptHook from dkimpy_milter.util import setExceptHook
FWS = re.compile(r'\r?\n[ \t]+') FWS = re.compile(r'\r?\n[ \t]+')
syslog.openlog(os.path.basename(sys.argv[0]), syslog.LOG_PID, syslog.LOG_MAIL)
setExceptHook()
class dkimMilter(Milter.Base): class dkimMilter(Milter.Base):
"Milter to check and sign DKIM. Each connection gets its own instance." "Milter to check and sign DKIM. Each connection gets its own instance."
def log(self,*msg):
self.conf.log.info('[%d] %s' % (self.id,' '.join([str(m) for m in msg])))
def __init__(self, milterconfig): def __init__(self, milterconfig):
self.mailfrom = None self.mailfrom = None
self.id = Milter.uniqueID() self.id = Milter.uniqueID()
@@ -64,16 +57,17 @@ class dkimMilter(Milter.Base):
# sometimes people put extra space in sendmail config, so we strip # sometimes people put extra space in sendmail config, so we strip
self.receiver = self.getsymval('j').strip() self.receiver = self.getsymval('j').strip()
if hostaddr and len(hostaddr) > 0: if hostaddr and len(hostaddr) > 0:
ipaddr = hostaddr[0] ipaddr = hostaddr[0]
"""if iniplist(ipaddr,self.conf.internal_connect): FIXME """if iniplist(ipaddr,self.conf.internal_connect): FIXME
self.internal_connection = True""" self.internal_connection = True"""
else: ipaddr = '' else: ipaddr = ''
self.connectip = ipaddr self.connectip = ipaddr
if self.internal_connection: if self.internal_connection:
connecttype = 'INTERNAL' connecttype = 'INTERNAL'
else: else:
connecttype = 'EXTERNAL' connecttype = 'EXTERNAL'
self.log("connect from %s at %s %s" % (hostname,hostaddr,connecttype)) if milterconfig.get('Syslog'):
syslog.syslog("connect from %s at %s %s" % (hostname,hostaddr,connecttype))
return Milter.CONTINUE return Milter.CONTINUE
# multiple messages can be received on a single connection # multiple messages can be received on a single connection
@@ -81,7 +75,8 @@ class dkimMilter(Milter.Base):
# of each message. # of each message.
@Milter.noreply @Milter.noreply
def envfrom(self,f,*str): def envfrom(self,f,*str):
self.log("mail from",f,str) if milterconfig.get('Syslog'):
syslog.syslog("mail from",f,str)
self.fp = StringIO.StringIO() self.fp = StringIO.StringIO()
self.mailfrom = f self.mailfrom = f
t = parse_addr(f) t = parse_addr(f)
@@ -98,9 +93,10 @@ class dkimMilter(Milter.Base):
self.internal_connection = True self.internal_connection = True
auth_type = self.getsymval('{auth_type}') auth_type = self.getsymval('{auth_type}')
ssl_bits = self.getsymval('{cipher_bits}') ssl_bits = self.getsymval('{cipher_bits}')
self.log( if milterconfig.get('Syslog'):
"SMTP AUTH:",self.user,"sslbits =",ssl_bits, auth_type, syslog.syslog(
"ssf =",self.getsymval('{auth_ssf}'), "INTERNAL" "SMTP AUTH:",self.user,"sslbits =",ssl_bits, auth_type,
"ssf =",self.getsymval('{auth_ssf}'), "INTERNAL"
) )
# Detailed authorization policy is configured in the access file below. # Detailed authorization policy is configured in the access file below.
self.arresults.append( self.arresults.append(
@@ -113,15 +109,17 @@ class dkimMilter(Milter.Base):
def header(self,name,val): def header(self,name,val):
lname = name.lower() lname = name.lower()
if lname == 'dkim-signature': if lname == 'dkim-signature':
self.log("%s: %s" % (name,val)) if milterconfig.get('Syslog'):
self.has_dkim += 1 syslog.syslog("%s: %s" % (name,val))
self.has_dkim += 1
if lname == 'from': if lname == 'from':
fname,self.author = parseaddr(val) fname,self.author = parseaddr(val)
self.log("%s: %s" % (name,val)) if milterconfig.get('Syslog'):
syslog.syslog("%s: %s" % (name,val))
elif lname == 'authentication-results': elif lname == 'authentication-results':
self.arheaders.append(val) self.arheaders.append(val)
if self.fp: if self.fp:
self.fp.write("%s: %s\n" % (name,val)) self.fp.write("%s: %s\n" % (name,val))
return Milter.CONTINUE return Milter.CONTINUE
@Milter.noreply @Milter.noreply
@@ -147,7 +145,8 @@ class dkimMilter(Milter.Base):
ar = authres.AuthenticationResultsHeader.parse_value(FWS.sub('',val)) ar = authres.AuthenticationResultsHeader.parse_value(FWS.sub('',val))
if ar.authserv_id == self.receiver: if ar.authserv_id == self.receiver:
self.chgheader('authentication-results',i,'') self.chgheader('authentication-results',i,'')
self.log('REMOVE: ',val) if milterconfig.get('Syslog'):
syslog.syslog('REMOVE: ',val)
# Check or sign DKIM # Check or sign DKIM
self.fp.seek(0) self.fp.seek(0)
if self.internal_connection or conf.get('Mode') == 's' or conf.get('Mode') == 'sv': if self.internal_connection or conf.get('Mode') == 's' or conf.get('Mode') == 'sv':
@@ -160,30 +159,33 @@ class dkimMilter(Milter.Base):
else: else:
result = 'none' result = 'none'
if self.arresults: if self.arresults:
h = authres.AuthenticationResultsHeader(authserv_id = self.receiver, h = authres.AuthenticationResultsHeader(authserv_id = self.receiver,
results=self.arresults) results=self.arresults)
self.log(h) if milterconfig.get('Syslog'):
name,val = str(h).split(': ',1) syslog.syslog(h)
self.addheader(name,val,0) name,val = str(h).split(': ',1)
self.addheader(name,val,0)
return Milter.CONTINUE return Milter.CONTINUE
def sign_dkim(self,txt): def sign_dkim(self,txt):
conf = self.conf conf = self.conf
try: try:
d = dkim.DKIM(txt,logger=conf.log) d = dkim.DKIM(txt)
h = d.sign(conf.selector,conf.domain,conf.key, h = d.sign(conf.get('Selector'),conf.get('Domain'),conf.get('KeyFile'),
canonicalize=('relaxed','simple')) canonicalize=('relaxed','simple'))
name,val = h.split(': ',1) name,val = h.split(': ',1)
self.addheader(name,val.strip().replace('\r\n','\n'),0) self.addheader(name,val.strip().replace('\r\n','\n'),0)
except dkim.DKIMException as x: except dkim.DKIMException as x:
self.log('DKIM: %s'%x) if milterconfig.get('Syslog'):
syslog.syslog('DKIM: %s'%x)
except Exception as x: except Exception as x:
conf.log.error("sign_dkim: %s",x,exc_info=True) if milterconfig.get('Syslog'):
syslog.syslog("sign_dkim: %s",x,exc_info=True)
def check_dkim(self,txt): def check_dkim(self,txt):
res = False res = False
conf = self.conf conf = self.conf
d = dkim.DKIM(txt,logger=conf.log) d = dkim.DKIM(txt)
for y in range(self.has_dkim): # Verify _ALL_ the signatures for y in range(self.has_dkim): # Verify _ALL_ the signatures
try: try:
res = d.verify(idx=y) res = d.verify(idx=y)
@@ -193,20 +195,24 @@ class dkimMilter(Milter.Base):
self.dkim_comment = 'Bad %d bit signature.' % d.keysize self.dkim_comment = 'Bad %d bit signature.' % d.keysize
except dkim.DKIMException as x: except dkim.DKIMException as x:
self.dkim_comment = str(x) self.dkim_comment = str(x)
#self.log('DKIM: %s'%x) if milterconfig.get('Syslog'):
syslog.syslog('DKIM: %s'%x)
except Exception as x: except Exception as x:
self.dkim_comment = str(x) self.dkim_comment = str(x)
conf.log.error("check_dkim: %s",x,exc_info=True) if milterconfig.get('Syslog'):
syslog.syslog("check_dkim: %s",x,exc_info=True)
self.header_i = d.signature_fields.get(b'i') self.header_i = d.signature_fields.get(b'i')
self.header_d = d.signature_fields.get(b'd') self.header_d = d.signature_fields.get(b'd')
if res: if res:
#self.log('DKIM: Pass (%s)'%d.domain) if milterconfig.get('Syslog'):
self.dkim_domain = d.domain syslog.syslog('DKIM: Pass (%s)'%d.domain)
self.dkim_domain = d.domain
else: else:
fd,fname = tempfile.mkstemp(".dkim") fd,fname = tempfile.mkstemp(".dkim")
with os.fdopen(fd,"w+b") as fp: with os.fdopen(fd,"w+b") as fp:
fp.write(txt) fp.write(txt)
self.log('DKIM: Fail (saved as %s)'%fname) if milterconfig.get('Syslog'):
syslog.syslog('DKIM: Fail (saved as %s)'%fname)
if res: if res:
result = 'pass' result = 'pass'
else: else:
@@ -226,6 +232,9 @@ def main():
sys.exit(1) sys.exit(1)
configFile = sys.argv[1] configFile = sys.argv[1]
milterconfig = config._processConfigFile(filename = configFile) milterconfig = config._processConfigFile(filename = configFile)
if milterconfig.get('Syslog'):
syslog.openlog(os.path.basename(sys.argv[0]), syslog.LOG_PID, syslog.LOG_MAIL)
setExceptHook()
drop_privileges(milterconfig) drop_privileges(milterconfig)
Milter.factory = dkimMilter(milterconfig) Milter.factory = dkimMilter(milterconfig)
Milter.set_flags(Milter.CHGHDRS + Milter.ADDHDRS) Milter.set_flags(Milter.CHGHDRS + Milter.ADDHDRS)