Merge branch 'dkg/test-suite' into 1_0

This commit is contained in:
Scott Kitterman
2019-02-24 07:13:30 -05:00
12 changed files with 283 additions and 11 deletions
+1
View File
@@ -1,3 +1,4 @@
dist
dkimpy_milter.egg-info
*.pyc
*~
+9 -2
View File
@@ -61,7 +61,9 @@ class dkimMilter(Milter.Base):
self.external_connection = False
self.hello_name = None
# sometimes people put extra space in sendmail config, so we strip
self.receiver = self.getsymval('j').strip()
self.receiver = self.getsymval('j')
if self.receiver is not None:
self.receiver = self.receiver.strip()
try:
self.AuthservID = milterconfig['AuthservID']
except:
@@ -258,7 +260,12 @@ class dkimMilter(Milter.Base):
for y in range(self.has_dkim): # Verify _ALL_ the signatures
d = dkim.DKIM(txt)
try:
res = d.verify(idx=y)
dnsoverride = milterconfig.get('DNSOverride')
if isinstance(dnsoverride, str):
syslog.syslog("DNSOverride: {0}".format(dnsoverride))
res = d.verify(idx=y, dnsfunc=lambda _x: dnsoverride)
else:
res = d.verify(idx=y)
if res:
if d.signature_fields.get(b'a') == 'ed25519-sha256':
self.dkim_comment = ('Good {0} signature'
+6
View File
@@ -0,0 +1,6 @@
#!/usr/bin/python2
from dkimpy_milter import main
if __name__ == "__main__":
main()
+7 -2
View File
@@ -48,6 +48,7 @@ defaultConfigData = {
'DiagnosticDirectory': '',
'MacroList': '',
'MacroListVerify': '',
'DNSOverride': None,
'debugLevel': 0 # Undocumented config item for developer use
}
@@ -334,6 +335,7 @@ def _readConfigFile(path, configData=None, configGlobal={}):
'DiagnosticDirectory': 'str',
'MacroList': 'dataset',
'MacroListVerify': 'dataset',
'DNSOverride': 'str',
'debugLevel': 'int'
}
@@ -388,7 +390,10 @@ def _readConfigFile(path, configData=None, configGlobal={}):
if conversion == 'bool':
configData[name] = _find_boolean(value)
elif conversion == 'str':
configData[name] = str(value)
if isinstance(value, list):
configData[name] = line.split(None, 1)[1]
else:
configData[name] = str(value)
elif conversion == 'int':
configData[name] = int(value)
elif conversion == 'dataset':
@@ -399,7 +404,7 @@ def _readConfigFile(path, configData=None, configGlobal={}):
configData[name] = conversion(value)
fp.close()
try:
configData['AuthservID'] = _make_authserv_id(configData['AuthservID'])
configData['AuthservID'] = _make_authserv_id(configData.get('AuthservID', 'HOSTNAME'))
configData['IntHosts'] = HostsDataset(configData['InternalHosts'])
except:
pass
+12 -4
View File
@@ -150,10 +150,18 @@ def own_socketfile(milterconfig):
"""If socket is Unix socket, chown to UserID before dropping privileges"""
import os
user, group = user_group(milterconfig.get('UserID'))
if milterconfig.get('Socket')[:1] == '/':
os.chown(milterconfig.get('Socket')[1:], user, group)
if milterconfig.get('Socket')[:6] == "local:":
os.chown(milterconfig.get('Socket')[6:], user, group)
offset = None
sockname = milterconfig.get('Socket')
if sockname[:1] == '/':
offset = 0
elif sockname[:6] == "local:":
offset = 6
elif sockname[:5] == "unix:":
offset = 5
if offset is not None:
if os.path.exists(sockname[offset:]):
os.chown(sockname[offset:], user, group)
def read_keyfile(milterconfig, keytype):
+7
View File
@@ -311,6 +311,13 @@ be set:
(b) KeyTable, SigningTable, no Domain, no KeyFile, no Selector;
[fooTable options NOT IMPLEMENTED]
.TP
.I DNSOverride (string)
Provide a text string that a verifying milter should use instead of
consulting the DNS on each message. This is useful primarily for
testing purposes in environments where it is awkward to modify the
system DNS resolution. It should not be used in production.
.TP
.I PeerList (dataset)
Identifies a set of "peers" that identifies clients whose connections
+3 -3
View File
@@ -23,10 +23,10 @@ description = "Domain Keys Identified Mail (DKIM) signing/verifying milter for P
kw = {} # Work-around for lack of 'or' requires in setuptools.
try:
import DNS
kw['install_requires'] = ['dkimpy>=0.7', 'pymilter', 'authres>=1.1.0', 'PyNaCl', 'ipaddress', 'PyDNS']
except ImportError: # If PyDNS is not installed, prefer dnspython
import dns
kw['install_requires'] = ['dkimpy>=0.7', 'pymilter', 'authres>=1.1.0', 'PyNaCl', 'ipaddress', 'dnspython']
except ImportError: # If PyDNS is not installed, prefer dnspython
kw['install_requires'] = ['dkimpy>=0.7', 'pymilter', 'authres>=1.1.0', 'PyNaCl', 'ipaddress', 'PyDNS']
setup(
name='dkimpy-milter',
+12
View File
@@ -0,0 +1,12 @@
-- -*- lua -*-
for _, keytype in ipairs({"ed25519", "rsa"}) do
for _, func in ipairs({"signing", "verify"}) do
mt.echo("testing "..keytype.." "..func)
conn = mt.connect("unix:"..keytype.."."..func..".sock")
if conn == nil then
error("mt.connect() failed "..keytype.." "..func)
end
mt.disconnect(conn)
mt.echo(keytype.." "..func.." complete")
end
end
+40
View File
@@ -0,0 +1,40 @@
-- -*- lua -*-
for _, keytype in ipairs({"ed25519", "rsa"}) do
for _, func in ipairs({"signing", "verify"}) do
mt.echo("testing "..keytype.." "..func)
conn = mt.connect("unix:"..keytype.."."..func..".sock")
if conn == nil then
error("mt.connect() failed "..keytype.." "..func)
end
if mt.conninfo(conn, "localhost", "127.0.0.1") ~= nil then
error("mt.conninfo() failed "..keytype.." "..func)
end
if mt.getreply(conn) ~= SMFIR_CONTINUE then
error("mt.conninfo() unexpected reply "..keytype.." "..func)
end
if mt.test_action(conn, SMFIF_ADDHDRS) then
print("could add headers "..keytype.." "..func)
else
error("mt.test_action() says could not add headers "..keytype.." "..func)
end
if mt.test_action(conn, SMFIF_CHGHDRS) then
print("could change headers "..keytype.." "..func)
else
error("mt.test_action() says could not change headers "..keytype.." "..func)
end
-- -- FIXME: this part of the test fails, as apparently the
-- -- dkimpy-milter claims the right to change the body of a message,
-- -- even though it shouldn't. How can we fix the negotiation?
-- if mt.test_action(conn, SMFIF_CHGBODY) then
-- error("mt.test_action() says could change body "..keytype.." "..func)
-- else
-- print("could not change body "..keytype.." "..func)
-- end
mt.disconnect(conn)
mt.echo(keytype.." "..func.." test complete")
end
end
+100
View File
@@ -0,0 +1,100 @@
-- -*- lua -*-
msg = {
['headers'] = {
['From'] = 'Alice <alice@example.net>',
['Message-Id'] = '<dkimpy-milter-test-02@example.net>',
['To'] = 'Bob <bob@example.biz>',
['Date'] = 'Mon, 18 Feb 2019 08:32:50 -0500',
['Subject'] = 'Signing test',
['Content-Type'] = 'text/plain',
},
['body'] = "This is a test!\r\n",
}
-- returns miltertest connection object
function connect_and_send (sockname, headers, body)
conn = mt.connect(sockname)
if conn == nil then
error "mt.connect() failed"
end
if mt.conninfo(conn, "localhost", "127.0.0.1") ~= nil then
error "mt.conninfo() failed"
end
if mt.getreply(conn) ~= SMFIR_CONTINUE then
error "mt.conninfo() unexpected reply"
end
-- mt.macro(conn, SMFIC_MAIL, "i", "simple-message")
if mt.mailfrom(conn, "<alice@example.net>") ~= nil then
error "mt.mailfrom() failed"
end
if mt.getreply(conn) ~= SMFIR_CONTINUE then
error "mt.mailfrom() unexpected reply"
end
-- mt.rcptto() is called implicitly
-- send headers
for key,value in pairs(headers) do
if mt.header(conn, key, value) ~= nil then
error("mt.header(" .. key .. ") failed")
end
if mt.getreply(conn) ~= SMFIR_CONTINUE then
error("mt.header(" .. key .. ") unexpected reply")
end
end
-- send EOH
if mt.eoh(conn) ~= nil then
error "mt.eoh() failed"
end
if mt.getreply(conn) ~= SMFIR_CONTINUE then
error "mt.eoh() unexpected reply"
end
-- send body
if mt.bodystring(conn, body) ~= nil then
error "mt.bodystring() failed"
end
if mt.getreply(conn) ~= SMFIR_CONTINUE then
error "mt.bodystring() unexpected reply"
end
-- end of message; let the filter react
if mt.eom(conn) ~= nil then
error "mt.eom() failed"
end
reply = mt.getreply(conn)
if reply ~= SMFIR_CONTINUE then
error ("mt.eom() unexpected reply: " .. reply)
end
return conn
end
for _, keytype in ipairs({"ed25519", "rsa"}) do
mt.echo("testing "..keytype)
signing = connect_and_send("unix:"..keytype..".signing.sock", msg.headers, msg.body)
-- verify that a test header field got added
if not mt.eom_check(signing, MT_HDRINSERT) then
error "no header added by signer"
end
signature = mt.getheader(signing, "DKIM-Signature", 0)
mt.disconnect(signing)
mt.echo("DKIM-Signature: " .. signature)
msg.headers['DKIM-Signature'] = signature
verify = connect_and_send("unix:"..keytype..".verify.sock", msg.headers, msg.body)
if not mt.eom_check(verify, MT_HDRINSERT) then
error "no header added in verify"
end
authres = mt.getheader(verify, "Authentication-Results", 0)
mt.echo("Authentication-Results: "..authres)
mt.disconnect(verify)
mt.echo(keytype.." complete")
end
+2
View File
@@ -0,0 +1,2 @@
#!/bin/sh
python2 -m dkimpy_milter "$@"
Executable
+84
View File
@@ -0,0 +1,84 @@
#!/bin/bash
set -e
WORKDIR=$(mktemp -d)
TESTDIR=$(realpath "$(dirname "$0")")
DKIMPY_MILTER=${DKIMPY_MILTER:-"$TESTDIR/dkimpy-milter"}
KEY_TYPES=(ed25519 rsa)
cd "$WORKDIR"
printf "Testing %s from directory %s\n" "$DKIMPY_MILTER" "$WORKDIR"
for keytype in "${KEY_TYPES[@]}"; do
dknewkey --ktype "$keytype" "testkey.$keytype"
if [ "$keytype" = ed25519 ]; then
keyfile=KeyFileEd25519
selector=SelectorEd25519
else
keyfile=KeyFile
selector=Selector
fi
cat > "$keytype.signing.conf" <<EOF
Domain example.net
$keyfile testkey.$keytype.key
$selector testkey
Socket unix:$keytype.signing.sock
PidFile $keytype.signing.pid
Mode s
UserID $(id --name --user):$(id --name --group)
EOF
cat > "$keytype.verify.conf" <<EOF
Socket unix:$keytype.verify.sock
PidFile $keytype.verify.pid
Mode v
DNSOverride $(cat testkey.$keytype.dns)
UserID $(id --name --user):$(id --name --group)
EOF
done
cleanup() {
echo cleaning up jobs:
jobs
for keytype in "${KEY_TYPES[@]}"; do
for func in signing verify; do
if [ -s "$keytype.$func.pid" ] && kill -0 "$(cat "$keytype.$func.pid")"; then
kill "$(cat $keytype.$func.pid)"
fi
done
done
wait
for keytype in "${KEY_TYPES[@]}"; do
for func in signing verify; do
errdata="$keytype.$func.stderr"
if [ -s "$errdata" ]; then
printf -- "-> %s:\n" "$errdata"
cat "$errdata"
printf -- "-> end %s\n" "$errdata"
fi
done
done
rm -rf "$WORKDIR"
}
for keytype in "${KEY_TYPES[@]}"; do
for func in signing verify; do
PYTHONPATH="$(dirname "$TESTDIR")" "$DKIMPY_MILTER" "$keytype.$func.conf" 2>"$keytype.$func.stderr" &
done
done
trap cleanup EXIT
# ugly ugly (how are we supposed to know that the milters are all ready?):
sleep 2
# uses miltertest from opendkim:
for x in ${TESTS:-"$TESTDIR"/*.miltertest}; do
if ! [ -e "$x" ]; then
if [ -e "$TESTDIR/$x" ]; then
x="$TESTDIR/$x"
fi
fi
printf -- "-> running %s...\n" "$x"
miltertest -s "$x"
done