Bug fixes for RSA/Ed25519 signing (now works), syslog fixes, update TODO verification status

This commit is contained in:
Scott Kitterman
2018-02-17 02:10:21 -05:00
parent 246bdba859
commit 7b8fccf801
5 changed files with 40 additions and 30 deletions
+5 -5
View File
@@ -1,14 +1,14 @@
TODO TODO
0.9.1 (Alpha) 0.9.1 (Alpha)
Sign rsa/ed25519 implemented Sign rsa/ed25519 implemented verified
Verify rsa/ed25519 implemented Verify rsa/ed25519 implemented
Domain implemented Domain implemented verified
KeyFile implemented KeyFile implemented verified
KeyFileEd25519 implemented KeyFileEd25519 implemented verified
Mode implemented Mode implemented
PidFile implemented verified PidFile implemented verified
Selector implemented Selector implemented verified
Socket implemented verified Socket implemented verified
Syslog implemented partly tested Syslog implemented partly tested
UMask implemented UMask implemented
+26 -21
View File
@@ -45,13 +45,13 @@ FWS = re.compile(r'\r?\n[ \t]+')
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 __init__(self, milterconfig, privatersa=False, privateed25519=False): def __init__(self):
self.mailfrom = None self.mailfrom = None
self.id = Milter.uniqueID() self.id = Milter.uniqueID()
# we don't want config used to change during a connection # we don't want config used to change during a connection
self.conf = milterconfig self.conf = milterconfig
self.privatersa = privatersa self.privatersa = privateRSA
self.privateed25519 = privateed25519 self.privateed25519 = privateEd25519
self.fp = None self.fp = None
@Milter.noreply @Milter.noreply
@@ -71,7 +71,7 @@ class dkimMilter(Milter.Base):
else: else:
connecttype = 'EXTERNAL' connecttype = 'EXTERNAL'
if milterconfig.get('Syslog'): if milterconfig.get('Syslog'):
syslog.syslog("connect from %s at %s %s" % (hostname,hostaddr,connecttype)) syslog.syslog("connect from {0} at {1} {2}".format(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
@@ -80,7 +80,7 @@ class dkimMilter(Milter.Base):
@Milter.noreply @Milter.noreply
def envfrom(self,f,*str): def envfrom(self,f,*str):
if milterconfig.get('Syslog'): if milterconfig.get('Syslog'):
syslog.syslog("mail from",f,str) syslog.syslog("mail from: {0} {1}".format(f,str))
self.fp = StringIO.StringIO() self.fp = StringIO.StringIO()
self.mailfrom = f self.mailfrom = f
t = parse_addr(f) t = parse_addr(f)
@@ -114,12 +114,12 @@ class dkimMilter(Milter.Base):
lname = name.lower() lname = name.lower()
if lname == 'dkim-signature': if lname == 'dkim-signature':
if milterconfig.get('Syslog'): if milterconfig.get('Syslog'):
syslog.syslog("%s: %s" % (name,val)) syslog.syslog("{0}: {1}".format(name,val))
self.has_dkim += 1 self.has_dkim += 1
if lname == 'from': if lname == 'from':
fname,self.author = parseaddr(val) fname,self.author = parseaddr(val)
if milterconfig.get('Syslog'): if milterconfig.get('Syslog'):
syslog.syslog("%s: %s" % (name,val)) syslog.syslog("{0}: {1}".format(name,val))
elif lname == 'authentication-results': elif lname == 'authentication-results':
self.arheaders.append(val) self.arheaders.append(val)
if self.fp: if self.fp:
@@ -150,14 +150,14 @@ class dkimMilter(Milter.Base):
if ar.authserv_id == self.receiver: if ar.authserv_id == self.receiver:
self.chgheader('authentication-results',i,'') self.chgheader('authentication-results',i,'')
if milterconfig.get('Syslog'): if milterconfig.get('Syslog'):
syslog.syslog('REMOVE: ',val) syslog.syslog('REMOVE: {0}'.format(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 milterconfig.get('Mode') == 's' or milterconfig.get('Mode') == 'sv':
txt = self.fp.read() txt = self.fp.read()
self.sign_dkim(txt) self.sign_dkim(txt)
result = None result = None
if self.has_dkim and (conf.get('Mode') == 'v' or conf.get('Mode') == 'sv'): if self.has_dkim and (milterconfig.get('Mode') == 'v' or milterconfig.get('Mode') == 'sv'):
txt = self.fp.read() txt = self.fp.read()
self.check_dkim(txt) self.check_dkim(txt)
else: else:
@@ -175,22 +175,23 @@ class dkimMilter(Milter.Base):
conf = self.conf conf = self.conf
try: try:
d = dkim.DKIM(txt) d = dkim.DKIM(txt)
h = d.sign(conf.get('Selector'),conf.get('Domain'), self.privatersa, h = d.sign(milterconfig.get('Selector'),milterconfig.get('Domain'), privateRSA,
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)
if self.privateed25519: if privateEd25519:
d = dkim.DKIM(txt) d = dkim.DKIM(txt)
h = d.sign(conf.get('SelectorEd25519'),conf.get('Domain'), self.privateed25519, h = d.sign(milterconfig.get('SelectorEd25519'),milterconfig.get('Domain'), privateEd25519,
canonicalize=('relaxed','simple')) canonicalize=('relaxed','simple'), signature_algorithm='ed25519-sha256')
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:
if milterconfig.get('Syslog'): if milterconfig.get('Syslog'):
syslog.syslog('DKIM: %s'%x) syslog.syslog('DKIM: {0}'.format(x))
except Exception as x: except Exception as x:
if milterconfig.get('Syslog'): if milterconfig.get('Syslog'):
syslog.syslog("sign_dkim: %s",x,exc_info=True) syslog.syslog("sign_dkim: {0}".format(x))
raise
def check_dkim(self,txt): def check_dkim(self,txt):
res = False res = False
@@ -206,23 +207,23 @@ class dkimMilter(Milter.Base):
except dkim.DKIMException as x: except dkim.DKIMException as x:
self.dkim_comment = str(x) self.dkim_comment = str(x)
if milterconfig.get('Syslog'): if milterconfig.get('Syslog'):
syslog.syslog('DKIM: %s'%x) syslog.syslog('DKIM: {0}'.format(x))
except Exception as x: except Exception as x:
self.dkim_comment = str(x) self.dkim_comment = str(x)
if milterconfig.get('Syslog'): if milterconfig.get('Syslog'):
syslog.syslog("check_dkim: %s",x,exc_info=True) syslog.syslog("check_dkim: {0}".format(x))
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:
if milterconfig.get('Syslog'): if milterconfig.get('Syslog'):
syslog.syslog('DKIM: Pass (%s)'%d.domain) syslog.syslog('DKIM: Pass ({0})'.format(d.domain))
self.dkim_domain = 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)
if milterconfig.get('Syslog'): if milterconfig.get('Syslog'):
syslog.syslog('DKIM: Fail (saved as %s)'%fname) syslog.syslog('DKIM: Fail (saved as {0})'.format(fname))
if res: if res:
result = 'pass' result = 'pass'
else: else:
@@ -235,6 +236,10 @@ class dkimMilter(Milter.Base):
return return
def main(): def main():
# Ugh, but there's no easy way around this.
global milterconfig
global privateRSA
global privateEd25519
privateRSA = False privateRSA = False
privateEd25519 = False privateEd25519 = False
configFile = '/etc/dkimpy-milter.conf' configFile = '/etc/dkimpy-milter.conf'
@@ -255,7 +260,7 @@ def main():
drop_privileges(milterconfig) drop_privileges(milterconfig)
if milterconfig.get('Syslog'): if milterconfig.get('Syslog'):
syslog.syslog('dkimpy-milter started. user: {0}'.format(milterconfig.get('UserID'))) syslog.syslog('dkimpy-milter started. user: {0}'.format(milterconfig.get('UserID')))
Milter.factory = dkimMilter(milterconfig, privatersa=privateRSA, privateed25519=privateEd25519) Milter.factory = dkimMilter
Milter.set_flags(Milter.CHGHDRS + Milter.ADDHDRS) Milter.set_flags(Milter.CHGHDRS + Milter.ADDHDRS)
miltername = 'dkimpy-filter' miltername = 'dkimpy-filter'
socketname = milterconfig.get('Socket') socketname = milterconfig.get('Socket')
+4 -2
View File
@@ -107,9 +107,11 @@ def _readConfigFile(path, configData = None, configGlobal = {}):
'UserID' : 'str', 'UserID' : 'str',
'Domain' : 'str', 'Domain' : 'str',
'KeyFile' : 'str', 'KeyFile' : 'str',
'KeyFileEd25119' : 'str', 'KeyFileEd25519' : 'str',
'Selector' : 'str', 'Selector' : 'str',
'Canonicalization' : 'str' 'SelectorEd25519': 'str',
'Canonicalization' : 'str',
'CanonicalizationEd25519' : 'str'
} }
# check to see if it's a file # check to see if it's a file
+4 -1
View File
@@ -103,10 +103,13 @@ def read_keyfile(milterconfig, keytype):
keyfile = milterconfig.get('KeyFileEd25519') keyfile = milterconfig.get('KeyFileEd25519')
try: try:
f = open(keyfile, 'r') f = open(keyfile, 'r')
key = f.readlines keylist = f.readlines()
except IOError as e: except IOError as e:
if milterconfig.get('Syslog'): if milterconfig.get('Syslog'):
syslog.syslog('Unable to read keyfile {0}. IOError: {1}'.format(keyfile, e)) syslog.syslog('Unable to read keyfile {0}. IOError: {1}'.format(keyfile, e))
raise raise
f.close() f.close()
key = ''
for line in keylist:
key += line
return key return key
+1 -1
View File
@@ -133,7 +133,7 @@
dkimpy-milter \- Python milter for DKIM signing and validation dkimpy-milter \- Python milter for DKIM signing and validation
.SH "VERSION" .SH "VERSION"
.IX Header "VERSION" .IX Header "VERSION"
0\. 0\.9\.1
.SH "DESCRIPTION" .SH "DESCRIPTION"
.IX Header "DESCRIPTION" .IX Header "DESCRIPTION"