pep8 and a few other cleanups
This commit is contained in:
+237
-215
@@ -30,7 +30,7 @@ import os
|
|||||||
import tempfile
|
import tempfile
|
||||||
import StringIO
|
import StringIO
|
||||||
import re
|
import re
|
||||||
from Milter.utils import parse_addr,parseaddr
|
from Milter.utils import parse_addr, parseaddr
|
||||||
import dkimpy_milter.config as config
|
import dkimpy_milter.config as config
|
||||||
from dkimpy_milter.util import drop_privileges
|
from dkimpy_milter.util import drop_privileges
|
||||||
from dkimpy_milter.util import setExceptHook
|
from dkimpy_milter.util import setExceptHook
|
||||||
@@ -42,226 +42,246 @@ from dkimpy_milter.util import fold
|
|||||||
__version__ = "0.9.5"
|
__version__ = "0.9.5"
|
||||||
FWS = re.compile(r'\r?\n[ \t]+')
|
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):
|
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
|
||||||
def connect(self,hostname,unused,hostaddr):
|
def connect(self, hostname, unused, hostaddr):
|
||||||
self.internal_connection = False
|
self.internal_connection = False
|
||||||
self.external_connection = False
|
self.external_connection = False
|
||||||
self.hello_name = None
|
self.hello_name = None
|
||||||
# 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()
|
||||||
try:
|
try:
|
||||||
self.AuthservID = milterconfig['AuthservID']
|
self.AuthservID = milterconfig['AuthservID']
|
||||||
except:
|
except:
|
||||||
self.AuthservID = self.receiver
|
self.AuthservID = self.receiver
|
||||||
if hostaddr and len(hostaddr) > 0:
|
if hostaddr and len(hostaddr) > 0:
|
||||||
ipaddr = hostaddr[0]
|
ipaddr = hostaddr[0]
|
||||||
if milterconfig['InternalHostsObj']:
|
if milterconfig['InternalHostsObj']:
|
||||||
if milterconfig['InternalHostsObj'].match(ipaddr):
|
if milterconfig['InternalHostsObj'].match(ipaddr):
|
||||||
self.internal_connection = True
|
self.internal_connection = True
|
||||||
else: ipaddr = ''
|
else:
|
||||||
self.connectip = ipaddr
|
ipaddr = ''
|
||||||
if milterconfig.get('MacroList') and not self.internal_connection:
|
self.connectip = ipaddr
|
||||||
macrolist = milterconfig.get('MacroList')
|
if milterconfig.get('MacroList') and not self.internal_connection:
|
||||||
for macro in macrolist:
|
macrolist = milterconfig.get('MacroList')
|
||||||
macroname = macro.split('|')[0]
|
for macro in macrolist:
|
||||||
macroname = '{' + macroname + '}'
|
macroname = macro.split('|')[0]
|
||||||
macroresult = self.getsymval(macroname)
|
macroname = '{' + macroname + '}'
|
||||||
if (len(macro.split('|')) == 1 and macroresult) or macroresult in \
|
macroresult = self.getsymval(macroname)
|
||||||
macro.split('|')[1:]:
|
if ((len(macro.split('|')) == 1 and macroresult) or macroresult
|
||||||
self.internal_connection = True
|
in macro.split('|')[1:]):
|
||||||
if milterconfig.get('MacroListVerify'):
|
self.internal_connection = True
|
||||||
macrolist = milterconfig.get('MacroListVerify')
|
if milterconfig.get('MacroListVerify'):
|
||||||
for macro in macrolist:
|
macrolist = milterconfig.get('MacroListVerify')
|
||||||
macroname = macro.split('|')[0]
|
for macro in macrolist:
|
||||||
macroname = '{' + macroname + '}'
|
macroname = macro.split('|')[0]
|
||||||
macroresult = self.getsymval(macroname)
|
macroname = '{' + macroname + '}'
|
||||||
if (len(macro.split('|')) == 1 and macroresult) or macroresult in \
|
macroresult = self.getsymval(macroname)
|
||||||
macro.split('|')[1:]:
|
if ((len(macro.split('|')) == 1 and macroresult) or macroresult
|
||||||
self.external_connection = True
|
in macro.split('|')[1:]):
|
||||||
if self.internal_connection:
|
self.external_connection = True
|
||||||
connecttype = 'INTERNAL'
|
if self.internal_connection:
|
||||||
else:
|
connecttype = 'INTERNAL'
|
||||||
connecttype = 'EXTERNAL'
|
else:
|
||||||
if milterconfig.get('Syslog'):
|
connecttype = 'EXTERNAL'
|
||||||
syslog.syslog("connect from {0} at {1} {2}".format(hostname,hostaddr,connecttype))
|
|
||||||
return Milter.CONTINUE
|
|
||||||
|
|
||||||
# multiple messages can be received on a single connection
|
|
||||||
# envfrom (MAIL FROM in the SMTP protocol) seems to mark the start
|
|
||||||
# of each message.
|
|
||||||
@Milter.noreply
|
|
||||||
def envfrom(self,f,*str):
|
|
||||||
if milterconfig.get('Syslog'):
|
|
||||||
syslog.syslog("mail from: {0} {1}".format(f,str))
|
|
||||||
self.fp = StringIO.StringIO()
|
|
||||||
self.mailfrom = f
|
|
||||||
t = parse_addr(f)
|
|
||||||
if len(t) == 2: t[1] = t[1].lower()
|
|
||||||
self.canon_from = '@'.join(t)
|
|
||||||
self.user = self.getsymval('{auth_authen}')
|
|
||||||
self.has_dkim = 0
|
|
||||||
self.author = None
|
|
||||||
self.arheaders = []
|
|
||||||
self.arresults = []
|
|
||||||
return Milter.CONTINUE
|
|
||||||
|
|
||||||
@Milter.noreply
|
|
||||||
def header(self,name,val):
|
|
||||||
lname = name.lower()
|
|
||||||
if lname == 'dkim-signature':
|
|
||||||
if milterconfig.get('Syslog'):
|
if milterconfig.get('Syslog'):
|
||||||
syslog.syslog("{0}: {1}".format(name,val))
|
syslog.syslog("connect from {0} at {1} {2}"
|
||||||
self.has_dkim += 1
|
.format(hostname, hostaddr, connecttype))
|
||||||
if lname == 'from':
|
return Milter.CONTINUE
|
||||||
fname,self.author = parseaddr(val)
|
|
||||||
self.fdomain = self.author.split('@')[1]
|
# multiple messages can be received on a single connection
|
||||||
|
# envfrom (MAIL FROM in the SMTP protocol) seems to mark the start
|
||||||
|
# of each message.
|
||||||
|
@Milter.noreply
|
||||||
|
def envfrom(self, f, *str):
|
||||||
if milterconfig.get('Syslog'):
|
if milterconfig.get('Syslog'):
|
||||||
syslog.syslog("{0}: {1}".format(name,val))
|
syslog.syslog("mail from: {0} {1}".format(f, str))
|
||||||
elif lname == 'authentication-results':
|
self.fp = StringIO.StringIO()
|
||||||
self.arheaders.append(val)
|
self.mailfrom = f
|
||||||
if self.fp:
|
t = parse_addr(f)
|
||||||
self.fp.write("%s: %s\n" % (name,val))
|
if len(t) == 2:
|
||||||
return Milter.CONTINUE
|
t[1] = t[1].lower()
|
||||||
|
self.canon_from = '@'.join(t)
|
||||||
|
self.has_dkim = 0
|
||||||
|
self.author = None
|
||||||
|
self.arheaders = []
|
||||||
|
self.arresults = []
|
||||||
|
return Milter.CONTINUE
|
||||||
|
|
||||||
@Milter.noreply
|
@Milter.noreply
|
||||||
def eoh(self):
|
def header(self, name, val):
|
||||||
if self.fp:
|
lname = name.lower()
|
||||||
self.fp.write("\n") # terminate headers
|
if lname == 'dkim-signature':
|
||||||
self.bodysize = 0
|
if milterconfig.get('Syslog'):
|
||||||
return Milter.CONTINUE
|
syslog.syslog("{0}: {1}".format(name, val))
|
||||||
|
self.has_dkim += 1
|
||||||
|
if lname == 'from':
|
||||||
|
fname, self.author = parseaddr(val)
|
||||||
|
self.fdomain = self.author.split('@')[1]
|
||||||
|
if milterconfig.get('Syslog'):
|
||||||
|
syslog.syslog("{0}: {1}".format(name, val))
|
||||||
|
elif lname == 'authentication-results':
|
||||||
|
self.arheaders.append(val)
|
||||||
|
if self.fp:
|
||||||
|
self.fp.write("%s: %s\n" % (name, val))
|
||||||
|
return Milter.CONTINUE
|
||||||
|
|
||||||
@Milter.noreply
|
@Milter.noreply
|
||||||
def body(self,chunk): # copy body to temp file
|
def eoh(self):
|
||||||
if self.fp:
|
if self.fp:
|
||||||
self.fp.write(chunk) # IOError causes TEMPFAIL in milter
|
self.fp.write("\n") # terminate headers
|
||||||
self.bodysize += len(chunk)
|
self.bodysize = 0
|
||||||
return Milter.CONTINUE
|
return Milter.CONTINUE
|
||||||
|
|
||||||
def eom(self):
|
@Milter.noreply
|
||||||
if not self.fp:
|
def body(self, chunk): # copy body to temp file
|
||||||
return Milter.ACCEPT # no message collected - so no eom processing
|
if self.fp:
|
||||||
# Remove existing Authentication-Results headers for our authserv_id
|
self.fp.write(chunk) # IOError causes TEMPFAIL in milter
|
||||||
for i,val in enumerate(self.arheaders,1):
|
self.bodysize += len(chunk)
|
||||||
# FIXME: don't delete A-R headers from trusted MTAs
|
return Milter.CONTINUE
|
||||||
try:
|
|
||||||
ar = authres.AuthenticationResultsHeader.parse_value(FWS.sub('',val))
|
|
||||||
if ar.authserv_id == self.AuthservID:
|
|
||||||
self.chgheader('authentication-results',i,'')
|
|
||||||
if milterconfig.get('Syslog'):
|
|
||||||
syslog.syslog('REMOVE: {0}'.format(val))
|
|
||||||
except:
|
|
||||||
# Don't error out on unparseable AR header fiels
|
|
||||||
pass
|
|
||||||
# Check or sign DKIM
|
|
||||||
self.fp.seek(0)
|
|
||||||
if milterconfig.get('Domain'):
|
|
||||||
domain = milterconfig.get('Domain')
|
|
||||||
else:
|
|
||||||
domain = ''
|
|
||||||
if (self.fdomain in domain) and not milterconfig.get('Mode') == 'v' and not self.external_connection:
|
|
||||||
txt = self.fp.read()
|
|
||||||
self.sign_dkim(txt)
|
|
||||||
if (self.has_dkim) and (not self.internal_connection) and (milterconfig.get('Mode') == 'v' or milterconfig.get('Mode') == 'sv'):
|
|
||||||
txt = self.fp.read()
|
|
||||||
self.check_dkim(txt)
|
|
||||||
if self.arresults:
|
|
||||||
h = authres.AuthenticationResultsHeader(authserv_id = self.AuthservID,
|
|
||||||
results=self.arresults)
|
|
||||||
h = fold(str(h))
|
|
||||||
if milterconfig.get('Syslog'):
|
|
||||||
syslog.syslog(str(h))
|
|
||||||
name,val = str(h).split(': ',1)
|
|
||||||
self.addheader(name,val,0)
|
|
||||||
return Milter.CONTINUE
|
|
||||||
|
|
||||||
def sign_dkim(self,txt):
|
def eom(self):
|
||||||
canon = milterconfig.get('Canonicalization')
|
if not self.fp:
|
||||||
canonicalize = []
|
return Milter.ACCEPT # no message collected - so no eom processing
|
||||||
if len(canon.split('/')) == 2:
|
# Remove existing Authentication-Results headers for our authserv_id
|
||||||
canonicalize.append(canon.split('/')[0])
|
for i, val in enumerate(self.arheaders, 1):
|
||||||
canonicalize.append(canon.split('/')[1])
|
# FIXME: don't delete A-R headers from trusted MTAs
|
||||||
else:
|
try:
|
||||||
canonicalize.append(canon)
|
ar = (authres.AuthenticationResultsHeader
|
||||||
canonicalize.append(canon)
|
.parse_value(FWS.sub('', val)))
|
||||||
syslog.syslog('canonicalize: {0}'.format(canonicalize))
|
if ar.authserv_id == self.AuthservID:
|
||||||
try:
|
self.chgheader('authentication-results', i, '')
|
||||||
if privateRSA:
|
if milterconfig.get('Syslog'):
|
||||||
|
syslog.syslog('REMOVE: {0}'.format(val))
|
||||||
|
except:
|
||||||
|
# Don't error out on unparseable AR header fiels
|
||||||
|
pass
|
||||||
|
# Check or sign DKIM
|
||||||
|
self.fp.seek(0)
|
||||||
|
if milterconfig.get('Domain'):
|
||||||
|
domain = milterconfig.get('Domain')
|
||||||
|
else:
|
||||||
|
domain = ''
|
||||||
|
if ((self.fdomain in domain) and not milterconfig.get('Mode') == 'v'
|
||||||
|
and not self.external_connection):
|
||||||
|
txt = self.fp.read()
|
||||||
|
self.sign_dkim(txt)
|
||||||
|
if ((self.has_dkim) and (not self.internal_connection) and
|
||||||
|
(milterconfig.get('Mode') == 'v' or
|
||||||
|
milterconfig.get('Mode') == 'sv')):
|
||||||
|
txt = self.fp.read()
|
||||||
|
self.check_dkim(txt)
|
||||||
|
if self.arresults:
|
||||||
|
h = authres.AuthenticationResultsHeader(authserv_id=
|
||||||
|
self.AuthservID,
|
||||||
|
results=self.arresults)
|
||||||
|
h = fold(str(h))
|
||||||
|
if milterconfig.get('Syslog'):
|
||||||
|
syslog.syslog(str(h))
|
||||||
|
name, val = str(h).split(': ', 1)
|
||||||
|
self.addheader(name, val, 0)
|
||||||
|
return Milter.CONTINUE
|
||||||
|
|
||||||
|
def sign_dkim(self, txt):
|
||||||
|
canon = milterconfig.get('Canonicalization')
|
||||||
|
canonicalize = []
|
||||||
|
if len(canon.split('/')) == 2:
|
||||||
|
canonicalize.append(canon.split('/')[0])
|
||||||
|
canonicalize.append(canon.split('/')[1])
|
||||||
|
else:
|
||||||
|
canonicalize.append(canon)
|
||||||
|
canonicalize.append(canon)
|
||||||
|
syslog.syslog('canonicalize: {0}'.format(canonicalize))
|
||||||
|
try:
|
||||||
|
if privateRSA:
|
||||||
|
d = dkim.DKIM(txt)
|
||||||
|
h = d.sign(milterconfig.get('Selector'), self.fdomain,
|
||||||
|
privateRSA, canonicalize=(canonicalize[0],
|
||||||
|
canonicalize[1]))
|
||||||
|
name, val = h.split(': ', 1)
|
||||||
|
self.addheader(name, val.strip().replace('\r\n', '\n'), 0)
|
||||||
|
if privateEd25519:
|
||||||
|
d = dkim.DKIM(txt)
|
||||||
|
h = d.sign(milterconfig.get('SelectorEd25519'), self.fdomain,
|
||||||
|
privateEd25519, canonicalize=(canonicalize[0],
|
||||||
|
canonicalize[1]),
|
||||||
|
signature_algorithm='ed25519-sha256')
|
||||||
|
name, val = h.split(': ', 1)
|
||||||
|
self.addheader(name, val.strip().replace('\r\n', '\n'), 0)
|
||||||
|
except dkim.DKIMException as x:
|
||||||
|
if milterconfig.get('Syslog'):
|
||||||
|
syslog.syslog('DKIM: {0}'.format(x))
|
||||||
|
except Exception as x:
|
||||||
|
if milterconfig.get('Syslog'):
|
||||||
|
syslog.syslog("sign_dkim: {0}".format(x))
|
||||||
|
raise
|
||||||
|
|
||||||
|
def check_dkim(self, txt):
|
||||||
|
res = False
|
||||||
|
for y in range(self.has_dkim): # Verify _ALL_ the signatures
|
||||||
d = dkim.DKIM(txt)
|
d = dkim.DKIM(txt)
|
||||||
h = d.sign(milterconfig.get('Selector'), self.fdomain, privateRSA,
|
try:
|
||||||
canonicalize=(canonicalize[0], canonicalize[1]))
|
res = d.verify(idx=y)
|
||||||
name,val = h.split(': ',1)
|
if res:
|
||||||
self.addheader(name,val.strip().replace('\r\n','\n'),0)
|
self.dkim_comment = ('Good {0} bit {1} signature.'
|
||||||
if privateEd25519:
|
.format(d.keysize,
|
||||||
d = dkim.DKIM(txt)
|
d.signature_fields.get(b'a')))
|
||||||
h = d.sign(milterconfig.get('SelectorEd25519'), self.fdomain, privateEd25519,
|
else:
|
||||||
canonicalize=(canonicalize[0], canonicalize[1]), signature_algorithm='ed25519-sha256')
|
self.dkim_comment = ('Bad {0} bit {1} signature.'
|
||||||
name,val = h.split(': ',1)
|
.format(d.keysize,
|
||||||
self.addheader(name,val.strip().replace('\r\n','\n'),0)
|
d.signature_fields.get(b'a')))
|
||||||
except dkim.DKIMException as x:
|
except dkim.DKIMException as x:
|
||||||
if milterconfig.get('Syslog'):
|
self.dkim_comment = str(x)
|
||||||
syslog.syslog('DKIM: {0}'.format(x))
|
if milterconfig.get('Syslog'):
|
||||||
except Exception as x:
|
syslog.syslog('DKIM: {0}'.format(x))
|
||||||
if milterconfig.get('Syslog'):
|
except Exception as x:
|
||||||
syslog.syslog("sign_dkim: {0}".format(x))
|
self.dkim_comment = str(x)
|
||||||
raise
|
if milterconfig.get('Syslog'):
|
||||||
|
syslog.syslog("check_dkim: {0}".format(x))
|
||||||
|
self.header_i = d.signature_fields.get(b'i')
|
||||||
|
self.header_d = d.signature_fields.get(b'd')
|
||||||
|
self.header_a = d.signature_fields.get(b'a')
|
||||||
|
if res:
|
||||||
|
if milterconfig.get('Syslog'):
|
||||||
|
syslog.syslog('DKIM: Pass ({0})'.format(d.domain))
|
||||||
|
self.dkim_domain = d.domain
|
||||||
|
else:
|
||||||
|
if milterconfig.get('DiagnosticDirectory'):
|
||||||
|
fd, fname = tempfile.mkstemp(".dkim")
|
||||||
|
with os.fdopen(fd, "w+b") as fp:
|
||||||
|
fp.write(txt)
|
||||||
|
if milterconfig.get('Syslog'):
|
||||||
|
syslog.syslog('DKIM: Fail (saved as {0})'
|
||||||
|
.format(fname))
|
||||||
|
else:
|
||||||
|
syslog.syslog('DKIM: Fail ({0})'.format(d.domain))
|
||||||
|
if res:
|
||||||
|
result = 'pass'
|
||||||
|
else:
|
||||||
|
result = 'fail'
|
||||||
|
res = False
|
||||||
|
self.arresults.append(
|
||||||
|
authres.DKIMAuthenticationResult(result=result,
|
||||||
|
header_i=self.header_i,
|
||||||
|
header_d=self.header_d,
|
||||||
|
header_a=self.header_a,
|
||||||
|
result_comment=
|
||||||
|
self.dkim_comment)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
def check_dkim(self,txt):
|
|
||||||
res = False
|
|
||||||
for y in range(self.has_dkim): # Verify _ALL_ the signatures
|
|
||||||
d = dkim.DKIM(txt)
|
|
||||||
try:
|
|
||||||
res = d.verify(idx=y)
|
|
||||||
if res:
|
|
||||||
self.dkim_comment = 'Good {0} bit {1} signature.'.format(d.keysize, d.signature_fields.get(b'a'))
|
|
||||||
else:
|
|
||||||
self.dkim_comment = 'Bad {0} bit {1} signature.'.format(d.keysize, d.signature_fields.get(b'a'))
|
|
||||||
except dkim.DKIMException as x:
|
|
||||||
self.dkim_comment = str(x)
|
|
||||||
if milterconfig.get('Syslog'):
|
|
||||||
syslog.syslog('DKIM: {0}'.format(x))
|
|
||||||
except Exception as x:
|
|
||||||
self.dkim_comment = str(x)
|
|
||||||
if milterconfig.get('Syslog'):
|
|
||||||
syslog.syslog("check_dkim: {0}".format(x))
|
|
||||||
self.header_i = d.signature_fields.get(b'i')
|
|
||||||
self.header_d = d.signature_fields.get(b'd')
|
|
||||||
self.header_a = d.signature_fields.get(b'a')
|
|
||||||
if res:
|
|
||||||
if milterconfig.get('Syslog'):
|
|
||||||
syslog.syslog('DKIM: Pass ({0})'.format(d.domain))
|
|
||||||
self.dkim_domain = d.domain
|
|
||||||
else:
|
|
||||||
if milterconfig.get('DiagnosticDirectory'):
|
|
||||||
fd,fname = tempfile.mkstemp(".dkim")
|
|
||||||
with os.fdopen(fd,"w+b") as fp:
|
|
||||||
fp.write(txt)
|
|
||||||
if milterconfig.get('Syslog'):
|
|
||||||
syslog.syslog('DKIM: Fail (saved as {0})'.format(fname))
|
|
||||||
else:
|
|
||||||
syslog.syslog('DKIM: Fail ({0})'.format(d.domain))
|
|
||||||
if res:
|
|
||||||
result = 'pass'
|
|
||||||
else:
|
|
||||||
result = 'fail'
|
|
||||||
res = False
|
|
||||||
self.arresults.append(
|
|
||||||
authres.DKIMAuthenticationResult(result=result,
|
|
||||||
header_i = self.header_i, header_d = self.header_d, header_a = self.header_a,
|
|
||||||
result_comment = self.dkim_comment)
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
# Ugh, but there's no easy way around this.
|
# Ugh, but there's no easy way around this.
|
||||||
@@ -272,13 +292,14 @@ def main():
|
|||||||
privateEd25519 = False
|
privateEd25519 = False
|
||||||
configFile = '/etc/dkimpy-milter.conf'
|
configFile = '/etc/dkimpy-milter.conf'
|
||||||
if len(sys.argv) > 1:
|
if len(sys.argv) > 1:
|
||||||
if sys.argv[1] in ( '-?', '--help', '-h' ):
|
if sys.argv[1] in ('-?', '--help', '-h'):
|
||||||
print('usage: dkimpy-milter [<configfilename>]')
|
print('usage: dkimpy-milter [<configfilename>]')
|
||||||
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'):
|
if milterconfig.get('Syslog'):
|
||||||
facility = eval("syslog.LOG_{0}".format(milterconfig.get('SyslogFacility').upper()))
|
facility = eval("syslog.LOG_{0}"
|
||||||
|
.format(milterconfig.get('SyslogFacility').upper()))
|
||||||
syslog.openlog(os.path.basename(sys.argv[0]), syslog.LOG_PID, facility)
|
syslog.openlog(os.path.basename(sys.argv[0]), syslog.LOG_PID, facility)
|
||||||
setExceptHook()
|
setExceptHook()
|
||||||
pid = write_pid(milterconfig)
|
pid = write_pid(milterconfig)
|
||||||
@@ -291,9 +312,10 @@ def main():
|
|||||||
miltername = 'dkimpy-filter'
|
miltername = 'dkimpy-filter'
|
||||||
socketname = milterconfig.get('Socket')
|
socketname = milterconfig.get('Socket')
|
||||||
if milterconfig.get('Syslog'):
|
if milterconfig.get('Syslog'):
|
||||||
syslog.syslog('dkimpy-milter started:{0} user:{1}'.format(pid,milterconfig.get('UserID')))
|
syslog.syslog('dkimpy-milter started:{0} user:{1}'
|
||||||
|
.format(pid, milterconfig.get('UserID')))
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
Milter.runmilter(miltername,socketname,240)
|
Milter.runmilter(miltername, socketname, 240)
|
||||||
own_socketfile(milterconfig)
|
own_socketfile(milterconfig)
|
||||||
drop_privileges(milterconfig)
|
drop_privileges(milterconfig)
|
||||||
|
|
||||||
|
|||||||
+102
-86
@@ -36,28 +36,27 @@ from dnsplug import Session
|
|||||||
|
|
||||||
# default values
|
# default values
|
||||||
defaultConfigData = {
|
defaultConfigData = {
|
||||||
'Syslog' : 'yes',
|
'Syslog': 'yes',
|
||||||
'SyslogFacility' : 'mail',
|
'SyslogFacility': 'mail',
|
||||||
'UMask' : 007,
|
'UMask': 007,
|
||||||
'Mode' : 'sv',
|
'Mode': 'sv',
|
||||||
'Socket' : 'local:/var/run/dkimpy-milter/dkimpy-milter.sock',
|
'Socket': 'local:/var/run/dkimpy-milter/dkimpy-milter.sock',
|
||||||
'PidFile' : '/var/run/dkimpy-milter/dkimpy-milter.pid',
|
'PidFile': '/var/run/dkimpy-milter/dkimpy-milter.pid',
|
||||||
'UserID' : 'dkimpy-milter',
|
'UserID': 'dkimpy-milter',
|
||||||
'Canonicalization' : 'relaxed/simple',
|
'Canonicalization': 'relaxed/simple',
|
||||||
'InternalHosts' : '127.0.0.1',
|
'InternalHosts': '127.0.0.1',
|
||||||
'InternalHostsObj' : False,
|
'InternalHostsObj': False,
|
||||||
'DiagnosticDirectory' : '',
|
'DiagnosticDirectory': '',
|
||||||
'MacroList' : '',
|
'MacroList': '',
|
||||||
'MacroListVerify' : ''
|
'MacroListVerify': ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#################################
|
|
||||||
class ConfigException(Exception):
|
class ConfigException(Exception):
|
||||||
'''Exception raised when there's a configuration file error.'''
|
'''Exception raised when there's a configuration file error.'''
|
||||||
pass
|
pass
|
||||||
|
|
||||||
#################################
|
|
||||||
class HostsDataset(object):
|
class HostsDataset(object):
|
||||||
'''Hold a group of host related dataset objects'''
|
'''Hold a group of host related dataset objects'''
|
||||||
|
|
||||||
@@ -86,34 +85,41 @@ class HostsDataset(object):
|
|||||||
self.negative = True
|
self.negative = True
|
||||||
try:
|
try:
|
||||||
self.item = ipaddress.ip_address(unicode(self.item, "utf-8"))
|
self.item = ipaddress.ip_address(unicode(self.item, "utf-8"))
|
||||||
if isinstance(self.item, ipaddress.IPv4Address): self.isipv4 = True
|
if isinstance(self.item, ipaddress.IPv4Address):
|
||||||
elif isinstance(self.item, ipaddress.IPv6Address): self.isipv6 = True
|
self.isipv4 = True
|
||||||
|
elif isinstance(self.item, ipaddress.IPv6Address):
|
||||||
|
self.isipv6 = True
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
try:
|
try:
|
||||||
self.item = ipaddress.ip_network(unicode(self.item, "utf-8"), strict=False)
|
self.item = ipaddress.ip_network(unicode
|
||||||
if isinstance(self.item, ipaddress.IPv4Network): self.isipv4cidr = True
|
(self.item, "utf-8"),
|
||||||
elif isinstance(self.item, ipaddress.IPv6Network): self.isipv6cidr = True
|
strict=False)
|
||||||
|
if isinstance(self.item, ipaddress.IPv4Network):
|
||||||
|
self.isipv4cidr = True
|
||||||
|
elif isinstance(self.item, ipaddress.IPv6Network):
|
||||||
|
self.isipv6cidr = True
|
||||||
except ValueError as e2:
|
except ValueError as e2:
|
||||||
if self.item[0] == '.' and len(self.item.split('.')) > 2:
|
if self.item[0] == '.' and len(self.item.split('.')) > 2:
|
||||||
self.isdomain = True
|
self.isdomain = True
|
||||||
elif len(self.item.split('.')) > 1: # It has a '.' in it
|
elif len(self.item.split('.')) > 1: # It has a '.' in it
|
||||||
self.ishostname = True
|
self.ishostname = True
|
||||||
else:
|
else:
|
||||||
raise ConfigException('Unknown dataset item: {0}'.format(item))
|
raise ConfigException('Unknown dataset item: {0}'
|
||||||
|
.format(item))
|
||||||
|
|
||||||
def match(self, connectip):
|
def match(self, connectip):
|
||||||
'''Check if the connect IP is part of the dataset'''
|
'''Check if the connect IP is part of the dataset'''
|
||||||
source = ipaddress.ip_address(unicode(connectip, "utf-8"))
|
source = ipaddress.ip_address(unicode(connectip, "utf-8"))
|
||||||
for item in self.dataset:
|
for item in self.dataset:
|
||||||
if item.isdomain or item.ishostname:
|
if item.isdomain or item.ishostname:
|
||||||
result = self.matchname(source) # Match host/domain names first
|
result = self.matchname(source) # Match host/domains first
|
||||||
if result:
|
if result:
|
||||||
return(result)
|
return(result)
|
||||||
elif item.isipv4 or item.isipv4cidr:
|
elif item.isipv4 or item.isipv4cidr: # Then IPv4/6 addresses or
|
||||||
if isinstance(source, ipaddress.IPv4Address): # Then IPv4/6 addresses
|
if isinstance(source, ipaddress.IPv4Address): # networks
|
||||||
return(self.match4(source)) # or networks depending
|
return(self.match4(source)) # depending on the item type
|
||||||
elif item.isipv6 or item.isipv6cidr: # on the item type and
|
elif item.isipv6 or item.isipv6cidr: # and connect type
|
||||||
if isinstance(source, ipaddress.IPv6Address): # connection type
|
if isinstance(source, ipaddress.IPv6Address):
|
||||||
return(self.match6(source))
|
return(self.match6(source))
|
||||||
|
|
||||||
def matchname(self, source):
|
def matchname(self, source):
|
||||||
@@ -127,7 +133,7 @@ class HostsDataset(object):
|
|||||||
for item in self.dataset:
|
for item in self.dataset:
|
||||||
if item.isdomain:
|
if item.isdomain:
|
||||||
for ptr in ptrlist:
|
for ptr in ptrlist:
|
||||||
# Strip the leading '.' off the domain name so exact match works.
|
# Strip the leading '.' off the domain name for exact match
|
||||||
if item.item[1:] == ptr[-len(item.item)+1:]:
|
if item.item[1:] == ptr[-len(item.item)+1:]:
|
||||||
matchdomain = True
|
matchdomain = True
|
||||||
negativedomain = item.negative
|
negativedomain = item.negative
|
||||||
@@ -212,21 +218,16 @@ class HostsDataset(object):
|
|||||||
match = False
|
match = False
|
||||||
return(match)
|
return(match)
|
||||||
|
|
||||||
def dump(self):
|
|
||||||
for item in self.dataset:
|
|
||||||
print 'name: {0} ip4: {1} cidr4: {2} ip6: {3} cidr6: {4} host: {5} domain: {6} negat: {7} type: {8}'.format(item.item,
|
|
||||||
item.isipv4, item.isipv4cidr, item.isipv6, item.isipv6cidr, item.ishostname, item.isdomain,
|
|
||||||
item.negative, type(item.item))
|
|
||||||
|
|
||||||
####################################################################
|
def _processConfigFile(filename=None, configdata=None, useSyslog=1,
|
||||||
def _processConfigFile(filename = None, configdata = None, useSyslog = 1,
|
useStderr=0):
|
||||||
useStderr = 0):
|
|
||||||
'''Load the specified config file, exit and log errors if it fails,
|
'''Load the specified config file, exit and log errors if it fails,
|
||||||
otherwise return a config dictionary.'''
|
otherwise return a config dictionary.'''
|
||||||
|
|
||||||
import config
|
import config
|
||||||
if configdata == None: configdata = config.defaultConfigData
|
if configdata is None:
|
||||||
if filename != None:
|
configdata = config.defaultConfigData
|
||||||
|
if filename is not None:
|
||||||
try:
|
try:
|
||||||
_readConfigFile(filename, configdata)
|
_readConfigFile(filename, configdata)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
@@ -238,7 +239,7 @@ def _processConfigFile(filename = None, configdata = None, useSyslog = 1,
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
return(configdata)
|
return(configdata)
|
||||||
|
|
||||||
####################
|
|
||||||
def _find_boolean(item):
|
def _find_boolean(item):
|
||||||
if type(item) == int:
|
if type(item) == int:
|
||||||
item = str(item)
|
item = str(item)
|
||||||
@@ -249,14 +250,15 @@ def _find_boolean(item):
|
|||||||
else:
|
else:
|
||||||
raise dkim.ParameterError()
|
raise dkim.ParameterError()
|
||||||
return item
|
return item
|
||||||
####################
|
|
||||||
|
|
||||||
def _calculate_authserv_id(as_id):
|
def _calculate_authserv_id(as_id):
|
||||||
"""Determine AuthservID if needed"""
|
"""Determine AuthservID if needed"""
|
||||||
if as_id == 'HOSTNAME':
|
if as_id == 'HOSTNAME':
|
||||||
as_id = socket.gethostname()
|
as_id = socket.gethostname()
|
||||||
return as_id
|
return as_id
|
||||||
|
|
||||||
####################
|
|
||||||
def _dataset_to_list(dataset):
|
def _dataset_to_list(dataset):
|
||||||
"""Convert a dataset (as defined in dkimpymilter.8) and return a python
|
"""Convert a dataset (as defined in dkimpymilter.8) and return a python
|
||||||
list of values."""
|
list of values."""
|
||||||
@@ -292,80 +294,89 @@ def _dataset_to_list(dataset):
|
|||||||
else:
|
else:
|
||||||
return [dataset.strip().strip(',')]
|
return [dataset.strip().strip(',')]
|
||||||
if dataset[-3:] == '.db' or dataset[:3] == 'db:':
|
if dataset[-3:] == '.db' or dataset[:3] == 'db:':
|
||||||
# This is a Sleepycat (Oracle) DB dataset
|
# This is a Sleepycat (Oracle) DB dataset
|
||||||
import whichdb # Will need rewriting someday for python3
|
import whichdb # Will need rewriting someday for python3
|
||||||
if dataset[-3:] == '.db':
|
if dataset[-3:] == '.db':
|
||||||
dbname = dataset
|
dbname = dataset
|
||||||
elif dataset[:3] == 'db:':
|
elif dataset[:3] == 'db:':
|
||||||
dbname = dataset[3:]
|
dbname = dataset[3:]
|
||||||
else:
|
else:
|
||||||
raise dkim.ParameterError('Unimplmented dataset type: {0}'.format(type(dataset)))
|
raise dkim.ParameterError('Unimplmented dataset type: {0}'
|
||||||
|
.format(type(dataset)))
|
||||||
if whichdb.whichdb(dbname) != 'dbhash':
|
if whichdb.whichdb(dbname) != 'dbhash':
|
||||||
raise dkim.ParameterError('Unimplmented dataset type: {0}'.format(type(dataset)))
|
raise dkim.ParameterError('Unimplmented dataset type: {0}'
|
||||||
|
.format(type(dataset)))
|
||||||
#TODO replace this with code to use db maps
|
#TODO replace this with code to use db maps
|
||||||
raise dkim.ParameterError('Unsupported dataset db dataset not yet used: {0}'.format(type(dataset)))
|
raise dkim.ParameterError('Unsupported dataset db datase: {0}'
|
||||||
|
.format(type(dataset)))
|
||||||
|
|
||||||
raise dkim.ParameterError('Unimplmented dataset type: {0}'.format(type(dataset)))
|
raise dkim.ParameterError('Unimplmented dataset type: {0}'
|
||||||
|
.format(type(dataset)))
|
||||||
|
|
||||||
###############################################################
|
|
||||||
commentRx = re.compile(r'^(.*)#.*$')
|
def _readConfigFile(path, configData=None, configGlobal={}):
|
||||||
def _readConfigFile(path, configData = None, configGlobal = {}):
|
|
||||||
'''Reads a configuration file from the specified path, merging it
|
'''Reads a configuration file from the specified path, merging it
|
||||||
with the configuration data specified in configData. Returns a
|
with the configuration data specified in configData. Returns a
|
||||||
dictionary of name/value pairs based on configData and the values
|
dictionary of name/value pairs based on configData and the values
|
||||||
read from path.'''
|
read from path.'''
|
||||||
|
|
||||||
debugLevel = configGlobal.get('debugLevel', 0)
|
debugLevel = configGlobal.get('debugLevel', 0)
|
||||||
if debugLevel >= 5: syslog.syslog('readConfigFile: Loading "%s"' % path)
|
if debugLevel >= 5:
|
||||||
if configData == None: configData = {}
|
syslog.syslog('readConfigFile: Loading "%s"' % path)
|
||||||
|
if configData is None:
|
||||||
|
configData = {}
|
||||||
nameConversion = {
|
nameConversion = {
|
||||||
'AuthservID' : 'str',
|
'AuthservID': 'str',
|
||||||
'Syslog' : 'bool',
|
'Syslog': 'bool',
|
||||||
'SyslogFacility' : 'str',
|
'SyslogFacility': 'str',
|
||||||
'SyslogSuccess' : 'bool',
|
'SyslogSuccess': 'bool',
|
||||||
'UMask' : 'int',
|
'UMask': 'int',
|
||||||
'Mode' : 'str',
|
'Mode': 'str',
|
||||||
'Socket' : 'str',
|
'Socket': 'str',
|
||||||
'PidFile' : 'str',
|
'PidFile': 'str',
|
||||||
'UserID' : 'str',
|
'UserID': 'str',
|
||||||
'Domain' : 'dataset',
|
'Domain': 'dataset',
|
||||||
'KeyFile' : 'str',
|
'KeyFile': 'str',
|
||||||
'KeyFileEd25519' : 'str',
|
'KeyFileEd25519': 'str',
|
||||||
'Selector' : 'str',
|
'Selector': 'str',
|
||||||
'SelectorEd25519': 'str',
|
'SelectorEd25519': 'str',
|
||||||
'Canonicalization' : 'str',
|
'Canonicalization': 'str',
|
||||||
'InternalHosts' : 'dataset',
|
'InternalHosts': 'dataset',
|
||||||
'InternalHostsObj': 'bool',
|
'InternalHostsObj': 'bool',
|
||||||
'DiagnosticDirectory' : 'str',
|
'DiagnosticDirectory': 'str',
|
||||||
'MacroList' : 'dataset',
|
'MacroList': 'dataset',
|
||||||
'MacroListVerify' : 'dataset'
|
'MacroListVerify': 'dataset'
|
||||||
}
|
}
|
||||||
|
|
||||||
# check to see if it's a file
|
# check to see if it's a file
|
||||||
try:
|
try:
|
||||||
mode = os.stat(path)[0]
|
mode = os.stat(path)[0]
|
||||||
except OSError, e:
|
except OSError, e:
|
||||||
syslog.syslog(syslog.LOG_ERR,'ERROR stating "%s": %s' % ( path, e.strerror ))
|
syslog.syslog(syslog.LOG_ERR, 'ERROR stating "%s": %s'
|
||||||
|
% (path, e.strerror))
|
||||||
return(configData)
|
return(configData)
|
||||||
if not stat.S_ISREG(mode):
|
if not stat.S_ISREG(mode):
|
||||||
syslog.syslog(syslog.LOG_ERR,'ERROR: is not a file: "%s", mode=%s' % ( path, oct(mode) ))
|
syslog.syslog(syslog.LOG_ERR, 'ERROR: is not a file: "%s", mode=%s'
|
||||||
|
% (path, oct(mode)))
|
||||||
return(configData)
|
return(configData)
|
||||||
|
|
||||||
# load file
|
# load file
|
||||||
fp = open(path, 'r')
|
fp = open(path, 'r')
|
||||||
while 1:
|
while 1:
|
||||||
line = fp.readline()
|
line = fp.readline()
|
||||||
if not line: break
|
if not line:
|
||||||
|
break
|
||||||
|
|
||||||
# parse line
|
# parse line
|
||||||
line = line.split('#', 1)[0].strip()
|
line = line.split('#', 1)[0].strip()
|
||||||
if not line: continue
|
if not line:
|
||||||
|
continue
|
||||||
data = line.split()
|
data = line.split()
|
||||||
if len(data) != 2:
|
if len(data) != 2:
|
||||||
if len(data) == 1:
|
if len(data) == 1:
|
||||||
if debugLevel >= 1:
|
if debugLevel >= 1:
|
||||||
syslog.syslog('Configuration item "%s" not defined in file "%s"'
|
syslog.syslog('Config item "%s" not defined in file "%s"'
|
||||||
% ( line, path ))
|
% (line, path))
|
||||||
if len(data) == 1:
|
if len(data) == 1:
|
||||||
name = data
|
name = data
|
||||||
value = ''
|
value = ''
|
||||||
@@ -377,12 +388,14 @@ def _readConfigFile(path, configData = None, configGlobal = {}):
|
|||||||
|
|
||||||
# check validity of name
|
# check validity of name
|
||||||
conversion = nameConversion.get(name)
|
conversion = nameConversion.get(name)
|
||||||
if conversion == None:
|
if conversion is None:
|
||||||
syslog.syslog('ERROR: Unknown name "%s" in file "%s"' % ( name, path ))
|
syslog.syslog('ERROR: Unknown name "%s" in file "%s"'
|
||||||
|
% (name, path))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if debugLevel >= 5: syslog.syslog('readConfigFile: Found entry "%s=%s"'
|
if debugLevel >= 5:
|
||||||
% ( name, value ))
|
syslog.syslog('readConfigFile: Found entry "%s=%s"'
|
||||||
|
% (name, value))
|
||||||
if conversion == 'bool':
|
if conversion == 'bool':
|
||||||
configData[name] = _find_boolean(value)
|
configData[name] = _find_boolean(value)
|
||||||
elif conversion == 'str':
|
elif conversion == 'str':
|
||||||
@@ -392,12 +405,15 @@ def _readConfigFile(path, configData = None, configGlobal = {}):
|
|||||||
elif conversion == 'dataset':
|
elif conversion == 'dataset':
|
||||||
configData[name] = _dataset_to_list(value)
|
configData[name] = _dataset_to_list(value)
|
||||||
else:
|
else:
|
||||||
syslog.syslog(str('name: ' + name + ' value: ' + value + ' conversion: ' + conversion))
|
syslog.syslog(str('name: ' + name + ' value: ' + value +
|
||||||
|
' conversion: ' + conversion))
|
||||||
configData[name] = conversion(value)
|
configData[name] = conversion(value)
|
||||||
fp.close()
|
fp.close()
|
||||||
try:
|
try:
|
||||||
configData['AuthservID'] = _calculate_authserv_id(configData['AuthservID'])
|
configData['AuthservID'] = _calculate_authserv_id(configData
|
||||||
configData['InternalHostsObj'] = HostsDataset(configData['InternalHosts'])
|
['AuthservID'])
|
||||||
|
configData['InternalHostsObj'] = HostsDataset(configData
|
||||||
|
['InternalHosts'])
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
+19
-12
@@ -16,6 +16,7 @@
|
|||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
|
||||||
def fold(header):
|
def fold(header):
|
||||||
"""Fold a header line into multiple crlf-separated lines at column 72.
|
"""Fold a header line into multiple crlf-separated lines at column 72.
|
||||||
Borrowed from dkimpy and updated to only add \n instead of \r\n because
|
Borrowed from dkimpy and updated to only add \n instead of \r\n because
|
||||||
@@ -48,6 +49,7 @@ def fold(header):
|
|||||||
header = header[j:]
|
header = header[j:]
|
||||||
return pre + header
|
return pre + header
|
||||||
|
|
||||||
|
|
||||||
def user_group(userid):
|
def user_group(userid):
|
||||||
"""Return user and group from UserID"""
|
"""Return user and group from UserID"""
|
||||||
import grp
|
import grp
|
||||||
@@ -63,13 +65,14 @@ def user_group(userid):
|
|||||||
running_gid = grp.getgrnam(gidname).gr_gid
|
running_gid = grp.getgrnam(gidname).gr_gid
|
||||||
return running_uid, running_gid
|
return running_uid, running_gid
|
||||||
|
|
||||||
|
|
||||||
def drop_privileges(milterconfig):
|
def drop_privileges(milterconfig):
|
||||||
import os
|
import os
|
||||||
import syslog
|
import syslog
|
||||||
|
|
||||||
if os.getuid() != 0:
|
if os.getuid() != 0:
|
||||||
if milterconfig.get('Syslog'):
|
if milterconfig.get('Syslog'):
|
||||||
syslog.syslog('drop_privileges: Not running as root. Cannot drop permissions.')
|
syslog.syslog('drop_privileges: Not root. No action taken.')
|
||||||
return
|
return
|
||||||
|
|
||||||
# Get user and group
|
# Get user and group
|
||||||
@@ -85,9 +88,9 @@ def drop_privileges(milterconfig):
|
|||||||
# Set umask
|
# Set umask
|
||||||
old_umask = os.umask(milterconfig.get('UMask'))
|
old_umask = os.umask(milterconfig.get('UMask'))
|
||||||
|
|
||||||
#################
|
|
||||||
class ExceptHook:
|
class ExceptHook:
|
||||||
def __init__(self, useSyslog = 1, useStderr = 0):
|
def __init__(self, useSyslog=1, useStderr=0):
|
||||||
self.useSyslog = useSyslog
|
self.useSyslog = useSyslog
|
||||||
self.useStderr = useStderr
|
self.useStderr = useStderr
|
||||||
|
|
||||||
@@ -103,12 +106,11 @@ class ExceptHook:
|
|||||||
sys.stderr.write(line)
|
sys.stderr.write(line)
|
||||||
|
|
||||||
|
|
||||||
####################
|
|
||||||
def setExceptHook():
|
def setExceptHook():
|
||||||
import sys
|
import sys
|
||||||
sys.excepthook = ExceptHook(useSyslog = 1, useStderr = 1)
|
sys.excepthook = ExceptHook(useSyslog=1, useStderr=1)
|
||||||
|
|
||||||
|
|
||||||
####################
|
|
||||||
def write_pid(milterconfig):
|
def write_pid(milterconfig):
|
||||||
"""Write PID in pidfile. Will not overwrite an existing file."""
|
"""Write PID in pidfile. Will not overwrite an existing file."""
|
||||||
import os
|
import os
|
||||||
@@ -125,10 +127,11 @@ def write_pid(milterconfig):
|
|||||||
os.chown(piddir, user, group)
|
os.chown(piddir, user, group)
|
||||||
f = open(milterconfig.get('PidFile'), 'w')
|
f = open(milterconfig.get('PidFile'), 'w')
|
||||||
if milterconfig.get('Syslog'):
|
if milterconfig.get('Syslog'):
|
||||||
syslog.syslog('Missing pid dir created: {0}'.format(piddir))
|
syslog.syslog('PID dir created: {0}'.format(piddir))
|
||||||
else:
|
else:
|
||||||
if milterconfig.get('Syslog'):
|
if milterconfig.get('Syslog'):
|
||||||
syslog.syslog('Unable to write pidfle {0}. IOError: {1}'.format(milterconfig.get('PidFile'), e))
|
syslog.syslog('Unable to write pidfle {0}. IOError: {1}'
|
||||||
|
.format(milterconfig.get('PidFile'), e))
|
||||||
raise
|
raise
|
||||||
f.write(pid)
|
f.write(pid)
|
||||||
f.close()
|
f.close()
|
||||||
@@ -136,10 +139,13 @@ def write_pid(milterconfig):
|
|||||||
os.chown(milterconfig.get('PidFile'), user, group)
|
os.chown(milterconfig.get('PidFile'), user, group)
|
||||||
else:
|
else:
|
||||||
if milterconfig.get('Syslog'):
|
if milterconfig.get('Syslog'):
|
||||||
syslog.syslog('Unable to write pidfle {0}. File exists.'.format(milterconfig.get('PidFile')))
|
syslog.syslog('Unable to write pidfle {0}. File exists.'
|
||||||
raise RuntimeError('Unable to write pidfle {0}. File exists.'.format(milterconfig.get('PidFile')))
|
.format(milterconfig.get('PidFile')))
|
||||||
|
raise RuntimeError('Unable to write pidfle {0}. File exists.'
|
||||||
|
.format(milterconfig.get('PidFile')))
|
||||||
return pid
|
return pid
|
||||||
|
|
||||||
|
|
||||||
def own_socketfile(milterconfig):
|
def own_socketfile(milterconfig):
|
||||||
"""If socket is Unix socket, chown to UserID before dropping privileges"""
|
"""If socket is Unix socket, chown to UserID before dropping privileges"""
|
||||||
import os
|
import os
|
||||||
@@ -149,7 +155,7 @@ def own_socketfile(milterconfig):
|
|||||||
if milterconfig.get('Socket')[:6] == "local:":
|
if milterconfig.get('Socket')[:6] == "local:":
|
||||||
os.chown(milterconfig.get('Socket')[6:], user, group)
|
os.chown(milterconfig.get('Socket')[6:], user, group)
|
||||||
|
|
||||||
####################
|
|
||||||
def read_keyfile(milterconfig, keytype):
|
def read_keyfile(milterconfig, keytype):
|
||||||
"""Read private key from file."""
|
"""Read private key from file."""
|
||||||
import syslog
|
import syslog
|
||||||
@@ -162,7 +168,8 @@ def read_keyfile(milterconfig, keytype):
|
|||||||
keylist = 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 = ''
|
key = ''
|
||||||
|
|||||||
Reference in New Issue
Block a user