1
0
Fork 0

Makes qmail-smtpd with AUTH support plus SPF and DKIM.

This is the qmail that we're currently running at antartida.xyz.  We
took the notqmail repository and essentially applied patches for AUTH,
SPF and DKIM.

The DKIM patch does not come from the notqmail repository.  We took it
from the excellent notes at

  https://www.memoryhole.net/qmail/

We also applied the any-to-cname patch.  But we did not take the
oversize-dns patch.  Any mistakes here are mine---I had to merge it as
best we could and resolve various conflicts.

This is it. -- MAILER DAEMON :)

(*) Commit message from the notqmail branch

Remove FILES.auth, add new files to FILES.

Realphabetize Makefile targets.  Remove trailing whitespace variously.

Remove duplication declaration of auth in qmail-smtpd.c

Modification to Erwin Hoffmann's SMTP authentication for qmail-smtpd,
qmail-remote.

Apply Jana Saout's qmail-spf-rc5.patch

From https://www.saout.de/misc/spf/

This is an SPF implementation for qmail.

SPF is something like a DNS based "reverse MX" system to designate
permitted senders for mails depending on the domain name. The goal
is to disallow sender address forgery.

The patch is entirely written using the qmail string and DNS
functions, and thus completely integrated into qmail without
external dependencies.

It can check incoming mails inside the SMTP daemon, add Received-SPF
lines and optionally block undesired transfers. The check is
performed at the envelope level.
This commit is contained in:
MAILER-DAEMON 2024-11-23 20:13:12 -03:00 committed by MAILER DAEMON
parent 36d7678ec9
commit 69cea49034
35 changed files with 1952 additions and 227 deletions

18
FILES
View file

@ -3,6 +3,7 @@ BLURB2
BLURB3 BLURB3
BLURB4 BLURB4
README README
README.auth
FAQ FAQ
INSTALL INSTALL
INSTALL.alias INSTALL.alias
@ -40,6 +41,7 @@ TARGETS
Makefile Makefile
BIN.README BIN.README
BIN.Makefile BIN.Makefile
LICENSE.authentication
idedit.c idedit.c
conf-break conf-break
auto_break.h auto_break.h
@ -136,6 +138,8 @@ dnsfq.c
dnsip.c dnsip.c
dnsmxip.c dnsmxip.c
dnsptr.c dnsptr.c
dnstxt.c
spfquery.c
hostname.c hostname.c
ipmeprint.c ipmeprint.c
tcp-env.c tcp-env.c
@ -336,13 +340,16 @@ envread.c
byte.h byte.h
byte_chr.c byte_chr.c
byte_copy.c byte_copy.c
byte_cspn.c
byte_cr.c byte_cr.c
byte_diff.c byte_diff.c
byte_rchr.c byte_rchr.c
byte_rcspn.c
byte_zero.c byte_zero.c
str.h str.h
str_chr.c str_chr.c
str_cpy.c str_cpy.c
str_cpyb.c
str_diff.c str_diff.c
str_diffn.c str_diffn.c
str_len.c str_len.c
@ -402,6 +409,8 @@ date822fmt.h
date822fmt.c date822fmt.c
dns.h dns.h
dns.c dns.c
spf.h
spf.c
trylsock.c trylsock.c
tryrsolv.c tryrsolv.c
ip.h ip.h
@ -432,3 +441,12 @@ maildir.c
tcp-environ.5 tcp-environ.5
constmap.h constmap.h
constmap.c constmap.c
base64.c
base64.h
case_startb.c
global.h
hmac_md5.h
hmac_md5.c
install_authentication.sh
md5.h
md5c.c

View file

@ -1,27 +0,0 @@
The qmail-smtpd Auth patch modifies the following QMAIL 1.03 files:
= TARGETS
= Makefile
= qmail-smtpd.c
= qmail-smtpd.8
= qmail-remote.c
= qmail-remote.8
= qmail-showctl.c
= qmail-control.9
Added files:
+ base64.c
+ base64.h
+ case_startb.c
+ hmac_md5.h
+ hmac_md5.c
+ global.h
+ md5.h
+ md5c.c
Informational files:
% install_auth.sh (Installation shell script)
% LICENSE.authentication (some sort of ...)
% README.auth

View file

@ -3,9 +3,9 @@ AUTHOR
Author: Author:
Dr. Erwin Hoffmann - FEHCom Germany Dr. Erwin Hoffmann - FEHCom Germany
Web-Site: Web-Site:
http://www.fehcom.de/qmail.html http://www.fehcom.de/qmail.html
E-Mail: E-Mail:
feh@fehcom.de feh@fehcom.de

197
Makefile
View file

@ -140,14 +140,6 @@ base64.o: \
compile base64.c base64.h stralloc.h substdio.h str.h compile base64.c base64.h stralloc.h substdio.h str.h
./compile base64.c ./compile base64.c
md5c.o : \
compile md5c.c md5.h
./compile md5c.c
hmac_md5.o : \
compile hmac_md5.c hmac_md5.h global.h
./compile hmac_md5.c
binm1: \ binm1: \
binm1.sh conf-qmail binm1.sh conf-qmail
cat binm1.sh \ cat binm1.sh \
@ -215,6 +207,10 @@ byte_cr.o: \
compile byte_cr.c byte.h compile byte_cr.c byte.h
./compile byte_cr.c ./compile byte_cr.c
byte_cspn.o: \
compile byte_cspn.c byte.h
./compile byte_cspn.c
byte_diff.o: \ byte_diff.o: \
compile byte_diff.c byte.h compile byte_diff.c byte.h
./compile byte_diff.c ./compile byte_diff.c
@ -223,6 +219,10 @@ byte_rchr.o: \
compile byte_rchr.c byte.h compile byte_rchr.c byte.h
./compile byte_rchr.c ./compile byte_rchr.c
byte_rcspn.o: \
compile byte_rcspn.c byte.h
./compile byte_rcspn.c
byte_zero.o: \ byte_zero.o: \
compile byte_zero.c byte.h compile byte_zero.c byte.h
./compile byte_zero.c ./compile byte_zero.c
@ -405,84 +405,96 @@ compile trydrent.c direntry.h1 direntry.h2
rm -f trydrent.o rm -f trydrent.o
dns.lib: \ dns.lib: \
tryrsolv.c compile load socket.lib dns.o ipalloc.o ip.o stralloc.a \ tryrsolv.c compile load socket.lib dns.o ipalloc.o strsalloc.o ip.o \
alloc.a error.a fs.a str.a stralloc.a alloc.a error.a fs.a str.a
( ( ./compile tryrsolv.c && ./load tryrsolv dns.o \ ( ( ./compile tryrsolv.c && ./load tryrsolv dns.o \
ipalloc.o ip.o stralloc.a alloc.a error.a fs.a str.a \ ipalloc.o strsalloc.o ip.o stralloc.a alloc.a error.a fs.a str.a \
-lresolv `cat socket.lib` ) >/dev/null 2>&1 \ -lresolv `cat socket.lib` ) >/dev/null 2>&1 \
&& echo -lresolv || exit 0 ) > dns.lib && echo -lresolv || exit 0 ) > dns.lib
rm -f tryrsolv.o tryrsolv rm -f tryrsolv.o tryrsolv
dns.o: \ dns.o: \
compile dns.c ip.h ipalloc.h ip.h gen_alloc.h fmt.h alloc.h str.h \ compile dns.c ip.h ipalloc.h strsalloc.h gen_alloc.h fmt.h alloc.h \
stralloc.h gen_alloc.h dns.h case.h str.h stralloc.h dns.h case.h
./compile dns.c ./compile dns.c
dnscname: \ dnscname: \
load dnscname.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \ load dnscname.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \
substdio.a error.a str.a fs.a dns.lib socket.lib substdio.a error.a str.a fs.a dns.lib socket.lib
./load dnscname dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \ ./load dnscname dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \
alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \ alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \
socket.lib` socket.lib`
dnscname.o: \ dnscname.o: \
compile dnscname.c substdio.h subfd.h substdio.h stralloc.h \ compile dnscname.c substdio.h subfd.h stralloc.h \
gen_alloc.h dns.h dnsdoe.h readwrite.h exit.h gen_alloc.h dns.h dnsdoe.h readwrite.h exit.h
./compile dnscname.c ./compile dnscname.c
dnsdoe.o: \ dnsdoe.o: \
compile dnsdoe.c substdio.h subfd.h substdio.h exit.h dns.h dnsdoe.h compile dnsdoe.c substdio.h subfd.h exit.h dns.h dnsdoe.h
./compile dnsdoe.c ./compile dnsdoe.c
dnsfq: \ dnsfq: \
load dnsfq.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \ load dnsfq.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \
substdio.a error.a str.a fs.a dns.lib socket.lib substdio.a error.a str.a fs.a dns.lib socket.lib
./load dnsfq dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \ ./load dnsfq dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \
alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \ alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \
socket.lib` socket.lib`
dnsfq.o: \ dnsfq.o: \
compile dnsfq.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \ compile dnsfq.c substdio.h subfd.h stralloc.h gen_alloc.h \
dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h exit.h dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h exit.h
./compile dnsfq.c ./compile dnsfq.c
dnsip: \ dnsip: \
load dnsip.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \ load dnsip.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \
substdio.a error.a str.a fs.a dns.lib socket.lib substdio.a error.a str.a fs.a dns.lib socket.lib
./load dnsip dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \ ./load dnsip dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \
alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \ alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \
socket.lib` socket.lib`
dnsip.o: \ dnsip.o: \
compile dnsip.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \ compile dnsip.c substdio.h subfd.h stralloc.h gen_alloc.h \
dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h exit.h dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h exit.h
./compile dnsip.c ./compile dnsip.c
dnsmxip: \ dnsmxip: \
load dnsmxip.o dns.o dnsdoe.o ip.o ipalloc.o now.o stralloc.a alloc.a \ load dnsmxip.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o now.o stralloc.a \
substdio.a error.a str.a fs.a dns.lib socket.lib alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib
./load dnsmxip dns.o dnsdoe.o ip.o ipalloc.o now.o \ ./load dnsmxip dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o now.o \
stralloc.a alloc.a substdio.a error.a str.a fs.a `cat \ stralloc.a alloc.a substdio.a error.a str.a fs.a `cat \
dns.lib` `cat socket.lib` dns.lib` `cat socket.lib`
dnsmxip.o: \ dnsmxip.o: \
compile dnsmxip.c substdio.h subfd.h substdio.h stralloc.h \ compile dnsmxip.c substdio.h subfd.h stralloc.h \
gen_alloc.h fmt.h dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h \ gen_alloc.h fmt.h dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h \
now.h datetime.h exit.h now.h datetime.h exit.h
./compile dnsmxip.c ./compile dnsmxip.c
dnsptr: \ dnsptr: \
load dnsptr.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \ load dnsptr.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \
substdio.a error.a str.a fs.a dns.lib socket.lib substdio.a error.a str.a fs.a dns.lib socket.lib
./load dnsptr dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \ ./load dnsptr dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \
alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \ alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \
socket.lib` socket.lib`
dnsptr.o: \ dnsptr.o: \
compile dnsptr.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \ compile dnsptr.c substdio.h subfd.h stralloc.h gen_alloc.h \
str.h scan.h dns.h dnsdoe.h ip.h exit.h str.h scan.h dns.h dnsdoe.h ip.h exit.h
./compile dnsptr.c ./compile dnsptr.c
dnstxt: \
load dnstxt.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \
substdio.a error.a str.a fs.a dns.lib socket.lib
./load dnstxt dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \
alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \
socket.lib`
dnstxt.o: \
compile dnstxt.c substdio.h subfd.h stralloc.h gen_alloc.h \
str.h scan.h dns.h dnsdoe.h ip.h exit.h
./compile dnstxt.c
dot-qmail.0: \ dot-qmail.0: \
dot-qmail.5 dot-qmail.5
nroff -man dot-qmail.5 > dot-qmail.0 nroff -man dot-qmail.5 > dot-qmail.0
@ -717,6 +729,10 @@ hier.o: \
compile hier.c auto_qmail.h auto_split.h auto_uids.h fmt.h fifo.h compile hier.c auto_qmail.h auto_split.h auto_uids.h fmt.h fifo.h
./compile hier.c ./compile hier.c
hmac_md5.o : \
compile hmac_md5.c hmac_md5.h global.h
./compile hmac_md5.c
home: \ home: \
home.sh conf-qmail home.sh conf-qmail
cat home.sh \ cat home.sh \
@ -789,24 +805,24 @@ compile ip.c fmt.h scan.h ip.h
./compile ip.c ./compile ip.c
ipalloc.o: \ ipalloc.o: \
compile ipalloc.c alloc.h gen_allocdefs.h ip.h ipalloc.h ip.h \ compile ipalloc.c alloc.h gen_allocdefs.h ip.h ipalloc.h \
gen_alloc.h gen_alloc.h
./compile ipalloc.c ./compile ipalloc.c
ipme.o: \ ipme.o: \
compile ipme.c hassalen.h byte.h ip.h ipalloc.h ip.h gen_alloc.h \ compile ipme.c hassalen.h byte.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.h \
stralloc.h gen_alloc.h ipme.h ip.h ipalloc.h stralloc.h gen_alloc.h ipme.h ip.h ipalloc.h strsalloc.h
./compile ipme.c ./compile ipme.c
ipmeprint: \ ipmeprint: \
load ipmeprint.o ipme.o ip.o ipalloc.o stralloc.a alloc.a substdio.a \ load ipmeprint.o ipme.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \
error.a str.a fs.a socket.lib substdio.a error.a str.a fs.a socket.lib
./load ipmeprint ipme.o ip.o ipalloc.o stralloc.a alloc.a \ ./load ipmeprint ipme.o ip.o ipalloc.o strsalloc.o stralloc.a \
substdio.a error.a str.a fs.a `cat socket.lib` alloc.a substdio.a error.a str.a fs.a `cat socket.lib`
ipmeprint.o: \ ipmeprint.o: \
compile ipmeprint.c subfd.h substdio.h substdio.h ip.h ipme.h ip.h \ compile ipmeprint.c subfd.h substdio.h substdio.h ip.h ipme.h ip.h \
ipalloc.h ip.h gen_alloc.h exit.h ipalloc.h strsalloc.h ip.h gen_alloc.h exit.h
./compile ipmeprint.c ./compile ipmeprint.c
it: \ it: \
@ -816,11 +832,11 @@ predate datemail mailsubj qmail-upq qmail-showctl qmail-newu \
qmail-pw2u qmail-qread qmail-qstat qmail-tcpto qmail-tcpok \ qmail-pw2u qmail-qread qmail-qstat qmail-tcpto qmail-tcpok \
qmail-pop3d qmail-popup qmail-qmqpc qmail-qmqpd qmail-qmtpd \ qmail-pop3d qmail-popup qmail-qmqpc qmail-qmqpd qmail-qmtpd \
qmail-smtpd sendmail tcp-env qmail-newmrh config config-fast dnscname \ qmail-smtpd sendmail tcp-env qmail-newmrh config config-fast dnscname \
dnsptr dnsip dnsmxip dnsfq hostname ipmeprint qreceipt qsmhook qbiff \ dnsptr dnsip dnsmxip dnsfq dnstxt hostname ipmeprint qreceipt qsmhook \
forward preline condredirect bouncesaying except maildirmake \ forward preline condredirect bouncesaying except maildirmake \
maildir2mbox maildirwatch qail elq pinq idedit install-big install \ maildir2mbox maildirwatch qail elq pinq idedit install-big install \
instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \ instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \
binm3 binm3+df binm3 binm3+df spfquery
load: \ load: \
make-load warn-auto.sh systype make-load warn-auto.sh systype
@ -953,6 +969,10 @@ mbox.0: \
mbox.5 mbox.5
nroff -man mbox.5 > mbox.0 nroff -man mbox.5 > mbox.0
md5c.o : \
compile md5c.c md5.h
./compile md5c.c
myctime.o: \ myctime.o: \
compile myctime.c datetime.h fmt.h myctime.h compile myctime.c datetime.h fmt.h myctime.h
./compile myctime.c ./compile myctime.c
@ -1451,14 +1471,14 @@ auto_qmail.h auto_uids.h date822fmt.h fmtqfn.h
qmail-remote: \ qmail-remote: \
load qmail-remote.o control.o constmap.o timeoutread.o timeoutwrite.o \ load qmail-remote.o control.o constmap.o timeoutread.o timeoutwrite.o \
timeoutconn.o tcpto.o now.o dns.o ip.o ipalloc.o ipme.o quote.o \ timeoutconn.o tcpto.o now.o dns.o ip.o ipalloc.o strsalloc.o ipme.o quote.o \
ndelay.a case.a sig.a open.a lock.a seek.a getln.a stralloc.a alloc.a \ ndelay.a case.a sig.a open.a lock.a seek.a getln.a stralloc.a alloc.a \
substdio.a error.a str.a fs.a auto_qmail.o \ substdio.a error.a str.a fs.a auto_qmail.o \
base64.o md5c.o hmac_md5.o \ base64.o md5c.o hmac_md5.o \
dns.lib socket.lib dns.lib socket.lib
./load qmail-remote control.o constmap.o timeoutread.o \ ./load qmail-remote control.o constmap.o timeoutread.o \
timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \ timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \
ipalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \ ipalloc.o strsalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \
lock.a seek.a getln.a stralloc.a alloc.a substdio.a error.a \ lock.a seek.a getln.a stralloc.a alloc.a substdio.a error.a \
base64.o md5c.o hmac_md5.o \ base64.o md5c.o hmac_md5.o \
str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib` str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib`
@ -1470,7 +1490,7 @@ qmail-remote.8
qmail-remote.o: \ qmail-remote.o: \
compile qmail-remote.c sig.h stralloc.h gen_alloc.h substdio.h \ compile qmail-remote.c sig.h stralloc.h gen_alloc.h substdio.h \
subfd.h substdio.h scan.h case.h error.h auto_qmail.h control.h dns.h \ subfd.h substdio.h scan.h case.h error.h auto_qmail.h control.h dns.h \
alloc.h quote.h ip.h ipalloc.h ip.h gen_alloc.h ipme.h ip.h ipalloc.h \ alloc.h quote.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.h ipme.h ip.h ipalloc.h strsalloc.h \
gen_alloc.h gen_allocdefs.h str.h now.h datetime.h exit.h constmap.h \ gen_alloc.h gen_allocdefs.h str.h now.h datetime.h exit.h constmap.h \
tcpto.h readwrite.h timeoutconn.h timeoutread.h timeoutwrite.h tcpto.h readwrite.h timeoutconn.h timeoutread.h timeoutwrite.h
./compile qmail-remote.c ./compile qmail-remote.c
@ -1543,21 +1563,21 @@ qmail-showctl.o: \
compile qmail-showctl.c substdio.h subfd.h substdio.h exit.h fmt.h \ compile qmail-showctl.c substdio.h subfd.h substdio.h exit.h fmt.h \
str.h control.h constmap.h stralloc.h gen_alloc.h direntry.h \ str.h control.h constmap.h stralloc.h gen_alloc.h direntry.h \
auto_uids.h auto_qmail.h auto_break.h auto_patrn.h auto_spawn.h \ auto_uids.h auto_qmail.h auto_break.h auto_patrn.h auto_spawn.h \
auto_split.h auto_split.h spf.h
./compile qmail-showctl.c ./compile qmail-showctl.c
qmail-smtpd: \ qmail-smtpd: \
load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \ load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \
timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ timeoutwrite.o ip.o ipme.o ipalloc.o strsalloc.o control.o constmap.o \
date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \ received.o date822fmt.o now.o qmail.o spf.o dns.o cdb.a fd.a wait.a \
open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \ datetime.a getln.a open.a sig.a case.a env.a stralloc.a alloc.a substdio.a \
fs.a auto_qmail.o base64.o socket.lib error.a str.a fs.a auto_qmail.o base64.o socket.lib dns.lib
./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \
timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ timeoutwrite.o ip.o ipme.o ipalloc.o strsalloc.o control.o \
received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ constmap.o received.o date822fmt.o now.o qmail.o spf.o dns.o cdb.a \
datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ fd.a wait.a datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
alloc.a substdio.a error.a str.a fs.a auto_qmail.o base64.o `cat \ alloc.a substdio.a error.a str.a fs.a auto_qmail.o base64.o \
socket.lib` str.a `cat socket.lib` `cat dns.lib`
qmail-smtpd.0: \ qmail-smtpd.0: \
qmail-smtpd.8 qmail-smtpd.8
@ -1566,9 +1586,9 @@ qmail-smtpd.8
qmail-smtpd.o: \ qmail-smtpd.o: \
compile qmail-smtpd.c sig.h readwrite.h stralloc.h gen_alloc.h \ compile qmail-smtpd.c sig.h readwrite.h stralloc.h gen_alloc.h \
substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \ substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \
error.h ipme.h ip.h ipalloc.h ip.h gen_alloc.h ip.h qmail.h \ error.h ipme.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.h ip.h qmail.h \
substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \ substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \
exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h base64.h exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h spf.h base64.h
./compile qmail-smtpd.c ./compile qmail-smtpd.c
qmail-start: \ qmail-start: \
@ -1760,6 +1780,12 @@ substdio.h alloc.h auto_qmail.h exit.h env.h str.h
setup: \ setup: \
it man it man
./install ./install
mv "`head -1 conf-qmail`/bin/qmail-remote" "`head -1 conf-qmail`/bin/qmail-remote-orig"
cp ./qmail-remote-dkim "`head -1 conf-qmail`/bin/qmail-remote"
## It seems with qmail-remote being a Perl program, it cannot
## have the same permissions as qmail-remote-orig. I could
## get it work with 0755 and did not investigate further.
chmod 0755 "`head -1 conf-qmail`/bin/qmail-remote"
sgetopt.o: \ sgetopt.o: \
compile sgetopt.c substdio.h subfd.h substdio.h sgetopt.h subgetopt.h \ compile sgetopt.c substdio.h subfd.h substdio.h sgetopt.h subgetopt.h \
@ -1794,7 +1820,7 @@ qmail-popup.c qmail-pw2u.c qmail-qmqpc.c qmail-qmqpd.c qmail-qmtpd.c \
qmail-qread.c qmail-qstat.sh qmail-queue.c qmail-remote.c \ qmail-qread.c qmail-qstat.sh qmail-queue.c qmail-remote.c \
qmail-rspawn.c qmail-send.c qmail-showctl.c qmail-smtpd.c \ qmail-rspawn.c qmail-send.c qmail-showctl.c qmail-smtpd.c \
qmail-start.c qmail-tcpok.c qmail-tcpto.c spawn.c dnscname.c dnsfq.c \ qmail-start.c qmail-tcpok.c qmail-tcpto.c spawn.c dnscname.c dnsfq.c \
dnsip.c dnsmxip.c dnsptr.c hostname.c ipmeprint.c tcp-env.c \ dnsip.c dnsmxip.c dnsptr.c dnstxt.c hostname.c ipmeprint.c tcp-env.c \
sendmail.c qreceipt.c qsmhook.c qbiff.c forward.c preline.c predate.c \ sendmail.c qreceipt.c qsmhook.c qbiff.c forward.c preline.c predate.c \
except.c bouncesaying.c condredirect.c maildirmake.c maildir2mbox.c \ except.c bouncesaying.c condredirect.c maildirmake.c maildir2mbox.c \
maildirwatch.c splogger.c qail.sh elq.sh pinq.sh qmail-upq.sh \ maildirwatch.c splogger.c qail.sh elq.sh pinq.sh qmail-upq.sh \
@ -1828,8 +1854,9 @@ uint32.h2 tryulong32.c wait.3 wait.h wait_pid.c wait_nohang.c \
trywaitp.c sig.h sig_alarm.c sig_block.c sig_catch.c sig_pause.c \ trywaitp.c sig.h sig_alarm.c sig_block.c sig_catch.c sig_pause.c \
sig_pipe.c sig_child.c sig_term.c sig_hup.c sig_misc.c sig_bug.c \ sig_pipe.c sig_child.c sig_term.c sig_hup.c sig_misc.c sig_bug.c \
trysgact.c trysgprm.c env.3 env.h env.c envread.c byte.h byte_chr.c \ trysgact.c trysgprm.c env.3 env.h env.c envread.c byte.h byte_chr.c \
byte_copy.c byte_cr.c byte_diff.c byte_rchr.c byte_zero.c str.h \ byte_copy.c byte_cr.c byte_cspn.c byte_diff.c byte_rchr.c byte_rcspn.c \
str_chr.c str_cpy.c str_diff.c str_diffn.c str_len.c str_rchr.c \ byte_zero.c str.h spf.c spf.h spfquery.c \
str_chr.c str_cpy.c str_cpyb.c str_diff.c str_diffn.c str_len.c str_rchr.c \
str_start.c lock.h lock_ex.c lock_exnb.c lock_un.c tryflock.c getln.3 \ str_start.c lock.h lock_ex.c lock_exnb.c lock_un.c tryflock.c getln.3 \
getln.h getln.c getln2.3 getln2.c sgetopt.3 sgetopt.h sgetopt.c \ getln.h getln.c getln2.3 getln2.c sgetopt.3 sgetopt.h sgetopt.c \
subgetopt.3 subgetopt.h subgetopt.c error.3 error_str.3 error_temp.3 \ subgetopt.3 subgetopt.h subgetopt.c error.3 error_str.3 error_temp.3 \
@ -1839,7 +1866,7 @@ slurpclose.h slurpclose.c quote.h quote.c hfield.h hfield.c \
headerbody.h headerbody.c token822.h token822.c control.h control.c \ headerbody.h headerbody.c token822.h token822.c control.h control.c \
datetime.3 datetime.h datetime.c datetime_un.c prioq.h prioq.c \ datetime.3 datetime.h datetime.c datetime_un.c prioq.h prioq.c \
date822fmt.h date822fmt.c dns.h dns.c trylsock.c tryrsolv.c ip.h ip.c \ date822fmt.h date822fmt.c dns.h dns.c trylsock.c tryrsolv.c ip.h ip.c \
ipalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \ ipalloc.h strsalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \
ndelay_off.c direntry.3 direntry.h1 direntry.h2 trydrent.c prot.h \ ndelay_off.c direntry.3 direntry.h1 direntry.h2 trydrent.c prot.h \
prot.c chkshsgr.c warn-shsgr tryshsgr.c ipme.h ipme.c trysalen.c \ prot.c chkshsgr.c warn-shsgr tryshsgr.c ipme.h ipme.c trysalen.c \
maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c
@ -1912,6 +1939,23 @@ auto_qmail.h auto_uids.h auto_spawn.h
./chkspawn ./chkspawn
./compile spawn.c ./compile spawn.c
spf.o: \
compile spf.c stralloc.h gen_alloc.h alloc.h ipme.h ip.h ipalloc.h \
strsalloc.h str.h fmt.h scan.h byte.h now.h case.h
./compile spf.c
spfquery: \
load spfquery.o spf.o ip.o ipme.o ipalloc.o strsalloc.o now.o dns.o \
datetime.a stralloc.a alloc.a str.a substdio.a error.a fs.a case.a dns.lib
./load spfquery spf.o ip.o ipme.o ipalloc.o strsalloc.o \
now.o dns.o datetime.a stralloc.a alloc.a str.a substdio.a \
case.a error.a fs.a `cat dns.lib` `cat socket.lib`
spfquery.o: \
compile spfquery.c substdio.h subfd.h stralloc.h gen_alloc.h alloc.h \
spf.h exit.h
./compile spfquery.c
splogger: \ splogger: \
load splogger.o substdio.a error.a str.a fs.a syslog.lib socket.lib load splogger.o substdio.a error.a str.a fs.a syslog.lib socket.lib
./load splogger substdio.a error.a str.a fs.a `cat \ ./load splogger substdio.a error.a str.a fs.a `cat \
@ -1927,12 +1971,12 @@ scan.h fmt.h
./compile splogger.c ./compile splogger.c
str.a: \ str.a: \
makelib str_len.o str_diff.o str_diffn.o str_cpy.o str_chr.o \ makelib str_len.o str_diff.o str_diffn.o str_cpy.o str_cpyb.o str_chr.o \
str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_diff.o byte_copy.o \ str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_cspn.o byte_rcspn.o \
byte_cr.o byte_zero.o byte_diff.o byte_copy.o byte_cr.o byte_zero.o
./makelib str.a str_len.o str_diff.o str_diffn.o str_cpy.o \ ./makelib str.a str_len.o str_diff.o str_diffn.o str_cpy.o str_cpyb.o \
str_chr.o str_rchr.o str_start.o byte_chr.o byte_rchr.o \ str_chr.o str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_cspn.o \
byte_diff.o byte_copy.o byte_cr.o byte_zero.o byte_rcspn.o byte_diff.o byte_copy.o byte_cr.o byte_zero.o
str_chr.o: \ str_chr.o: \
compile str_chr.c str.h compile str_chr.c str.h
@ -1942,6 +1986,10 @@ str_cpy.o: \
compile str_cpy.c str.h compile str_cpy.c str.h
./compile str_cpy.c ./compile str_cpy.c
str_cpyb.o: \
compile str_cpyb.c str.h
./compile str_cpyb.c
str_diff.o: \ str_diff.o: \
compile str_diff.c str.h compile str_diff.c str.h
./compile str_diff.c ./compile str_diff.c
@ -2021,6 +2069,11 @@ strerr_sys.o: \
compile strerr_sys.c error.h strerr.h compile strerr_sys.c error.h strerr.h
./compile strerr_sys.c ./compile strerr_sys.c
strsalloc.o: \
compile strsalloc.c alloc.h gen_allocdefs.h stralloc.h strsalloc.h \
gen_alloc.h
./compile strsalloc.c
subfderr.o: \ subfderr.o: \
compile subfderr.c readwrite.h substdio.h subfd.h substdio.h compile subfderr.c readwrite.h substdio.h subfd.h substdio.h
./compile subfderr.c ./compile subfderr.c
@ -2081,11 +2134,11 @@ find-systype trycpp.c
tcp-env: \ tcp-env: \
load tcp-env.o dns.o remoteinfo.o timeoutread.o timeoutwrite.o \ load tcp-env.o dns.o remoteinfo.o timeoutread.o timeoutwrite.o \
timeoutconn.o ip.o ipalloc.o case.a ndelay.a sig.a env.a getopt.a \ timeoutconn.o ip.o ipalloc.o strsalloc.o case.a ndelay.a sig.a env.a \
stralloc.a alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib getopt.a stralloc.a alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib
./load tcp-env dns.o remoteinfo.o timeoutread.o \ ./load tcp-env dns.o remoteinfo.o timeoutread.o \
timeoutwrite.o timeoutconn.o ip.o ipalloc.o case.a ndelay.a \ timeoutwrite.o timeoutconn.o ip.o ipalloc.o strsalloc.o case.a \
sig.a env.a getopt.a stralloc.a alloc.a substdio.a error.a \ ndelay.a sig.a env.a getopt.a stralloc.a alloc.a substdio.a error.a \
str.a fs.a `cat dns.lib` `cat socket.lib` str.a fs.a `cat dns.lib` `cat socket.lib`
tcp-env.0: \ tcp-env.0: \

View file

@ -4,8 +4,8 @@ README qmail SMTP Authentication
Scope: Scope:
------ ------
This patch supports RFC 2554 "SMTP Service Extension for Authentication" and This patch supports RFC 2554 "SMTP Service Extension for Authentication" and
RFC 4409 "Message Submission for Mail" for RFC 4409 "Message Submission for Mail" for
* qmail-smtpd and * qmail-smtpd and
* qmail-remote * qmail-remote
@ -48,7 +48,7 @@ In order activate SMTP authentication, you need to provide the environment
variable 'SMTPAUTH' to qmail-smtpd. variable 'SMTPAUTH' to qmail-smtpd.
Possible choices: Possible choices:
a) SMTPAUTH=""; qmail-smtpd supports auth of type PLAIN and/or LOGIN. a) SMTPAUTH=""; qmail-smtpd supports auth of type PLAIN and/or LOGIN.
b) SMTPAUTH="+cram"; qmail-smtpd will additionally annonce CRAM-MD5, b) SMTPAUTH="+cram"; qmail-smtpd will additionally annonce CRAM-MD5,
this requires a CRAM-MD5 supporting PAM. this requires a CRAM-MD5 supporting PAM.
@ -74,7 +74,7 @@ but with additional authentication information (username + password):
Note: The choice of the AUTH method depends on the capabilities of the server. Note: The choice of the AUTH method depends on the capabilities of the server.
b) SMTP Authentication by recipient domain: b) SMTP Authentication by recipient domain:
The control file 'smtproutes' is enhanced with the authentication information: The control file 'smtproutes' is enhanced with the authentication information:
@ -125,7 +125,7 @@ Version: Notes: Date:
0.7.1 cosmetics for qmail-remote 5.8.2010 0.7.1 cosmetics for qmail-remote 5.8.2010
0.7.2 authorization-id = authentication-id 0.7.2 authorization-id = authentication-id
for qmail-remote; for qmail-remote;
added SMTPAUTH environment variable added SMTPAUTH environment variable
for qmail-smtpd; backport from SC 2.7 29.4.2012 for qmail-smtpd; backport from SC 2.7 29.4.2012
0.7.3 Fixed missing AUTH for qmai-smtpd announcement. 0.7.3 Fixed missing AUTH for qmai-smtpd announcement.
Improved SUBMISSION port handling. 2.5.2012 Improved SUBMISSION port handling. 2.5.2012
@ -134,7 +134,7 @@ Version: Notes: Date:
0.7.5 Fixed bug in qmail-remote not respecting 0.7.5 Fixed bug in qmail-remote not respecting
announced AUTH types. Tx. Callum Gibson. announced AUTH types. Tx. Callum Gibson.
Added '432' server code evaluation for Added '432' server code evaluation for
AUTH password expired in qmail-remote. 23.10.2012 AUTH password expired in qmail-remote. 23.10.2012
0.7.6 Fixed order of SMTP commands (tx roberto). 0.7.6 Fixed order of SMTP commands (tx roberto).
02.02.2013 02.02.2013
0.8.0 Added authentication by recipient domain 0.8.0 Added authentication by recipient domain

10
TARGETS
View file

@ -101,11 +101,14 @@ str_len.o
str_diff.o str_diff.o
str_diffn.o str_diffn.o
str_cpy.o str_cpy.o
str_cpyb.o
str_chr.o str_chr.o
str_rchr.o str_rchr.o
str_start.o str_start.o
byte_chr.o byte_chr.o
byte_rchr.o byte_rchr.o
byte_cspn.o
byte_rcspn.o
byte_diff.o byte_diff.o
byte_copy.o byte_copy.o
byte_cr.o byte_cr.o
@ -172,8 +175,10 @@ timeoutwrite.o
timeoutconn.o timeoutconn.o
tcpto.o tcpto.o
dns.o dns.o
spf.o
ip.o ip.o
ipalloc.o ipalloc.o
strsalloc.o
hassalen.h hassalen.h
ipme.o ipme.o
ndelay.o ndelay.o
@ -215,6 +220,9 @@ qmail-inject.o
headerbody.o headerbody.o
hfield.o hfield.o
token822.o token822.o
spf.o
spfquery.o
spfquery
qmail-inject qmail-inject
predate.o predate.o
predate predate
@ -273,6 +281,8 @@ dnsip.o
dnsip dnsip
dnsmxip.o dnsmxip.o
dnsmxip dnsmxip
dnstxt.o
dnstxt
dnsfq.o dnsfq.o
dnsfq dnsfq
hostname.o hostname.o

View file

@ -101,7 +101,7 @@ stralloc *out; /* not null terminated */
return 0; return 0;
} }
i = in->len / 3 * 4 + 4; i = in->len / 3 * 4 + 4;
if (!stralloc_ready(out,i)) return -1; if (!stralloc_ready(out,i)) return -1;
s = out->s; s = out->s;

2
byte.h
View file

@ -3,6 +3,8 @@
extern unsigned int byte_chr(); extern unsigned int byte_chr();
extern unsigned int byte_rchr(); extern unsigned int byte_rchr();
extern unsigned int byte_cspn();
extern unsigned int byte_rcspn();
extern void byte_copy(); extern void byte_copy();
extern void byte_copyr(); extern void byte_copyr();
extern int byte_diff(); extern int byte_diff();

11
byte_cspn.c Normal file
View file

@ -0,0 +1,11 @@
#include "byte.h"
unsigned int byte_cspn(s,n,c)
register char *s;
register unsigned int n;
register char *c;
{
while(*c)
n = byte_chr(s,n,*c++);
return n;
}

17
byte_rcspn.c Normal file
View file

@ -0,0 +1,17 @@
#include "byte.h"
unsigned int byte_rcspn(s,n,c)
register char *s;
register unsigned int n;
register char *c;
{
unsigned int ret,pos,i;
for(ret = n,pos = 0;*c;++c) {
i = byte_rchr(s + pos,n - pos,*c) + pos;
if (i < n) ret = pos = i;
}
return ret;
}

132
dns.c
View file

@ -9,6 +9,7 @@ extern int res_query();
extern int res_search(); extern int res_search();
#include "ip.h" #include "ip.h"
#include "ipalloc.h" #include "ipalloc.h"
#include "strsalloc.h"
#include "fmt.h" #include "fmt.h"
#include "alloc.h" #include "alloc.h"
#include "str.h" #include "str.h"
@ -27,6 +28,7 @@ static unsigned char *responsepos;
static int numanswers; static int numanswers;
static char name[MAXDNAME]; static char name[MAXDNAME];
static struct ip_address ip; static struct ip_address ip;
static stralloc txt = {0};
unsigned short pref; unsigned short pref;
static stralloc glue = {0}; static stralloc glue = {0};
@ -177,6 +179,49 @@ int wanttype;
return 0; return 0;
} }
static int findtxt(wanttype)
int wanttype;
{
unsigned short rrtype;
unsigned short rrdlen;
int i;
if (numanswers <= 0) return 2;
--numanswers;
if (responsepos == responseend) return DNS_SOFT;
i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
if (i < 0) return DNS_SOFT;
responsepos += i;
i = responseend - responsepos;
if (i < 4 + 3 * 2) return DNS_SOFT;
rrtype = getshort(responsepos);
rrdlen = getshort(responsepos + 8);
responsepos += 10;
if (rrtype == wanttype)
{
unsigned short txtpos;
unsigned char txtlen;
txt.len = 0;
for (txtpos = 0;txtpos < rrdlen;txtpos += txtlen)
{
txtlen = responsepos[txtpos++];
if (txtlen > rrdlen-txtpos) txtlen = rrdlen-txtpos;
if (!stralloc_catb(&txt,&responsepos[txtpos],txtlen)) return DNS_MEM;
}
responsepos += rrdlen;
return 1;
}
responsepos += rrdlen;
return 0;
}
void dns_init(flagsearch) void dns_init(flagsearch)
int flagsearch; int flagsearch;
{ {
@ -194,7 +239,7 @@ stralloc *sa;
if (!sa->len) return loop; if (!sa->len) return loop;
if (sa->s[sa->len - 1] == ']') return loop; if (sa->s[sa->len - 1] == ']') return loop;
if (sa->s[sa->len - 1] == '.') { --sa->len; continue; } if (sa->s[sa->len - 1] == '.') { --sa->len; continue; }
switch(resolve(sa,T_ANY)) switch(resolve(sa,T_CNAME))
{ {
case DNS_MEM: return DNS_MEM; case DNS_MEM: return DNS_MEM;
case DNS_SOFT: return DNS_SOFT; case DNS_SOFT: return DNS_SOFT;
@ -235,15 +280,18 @@ struct ip_address *ip;
return len; return len;
} }
int dns_ptr(sa,ip) static int dns_ptrplus(ssa,ip)
stralloc *sa; strsalloc *ssa;
struct ip_address *ip; struct ip_address *ip;
{ {
stralloc sa = {0};
int r; int r;
if (!stralloc_ready(sa,iaafmt((char *) 0,ip))) return DNS_MEM; if (!stralloc_ready(&sa,iaafmt((char *) 0,ip))) return DNS_MEM;
sa->len = iaafmt(sa->s,ip); sa.len = iaafmt(sa.s,ip);
switch(resolve(sa,T_PTR)) r = resolve(&sa,T_PTR);
alloc_free(sa.s);
switch(r)
{ {
case DNS_MEM: return DNS_MEM; case DNS_MEM: return DNS_MEM;
case DNS_SOFT: return DNS_SOFT; case DNS_SOFT: return DNS_SOFT;
@ -254,13 +302,35 @@ struct ip_address *ip;
if (r == DNS_SOFT) return DNS_SOFT; if (r == DNS_SOFT) return DNS_SOFT;
if (r == 1) if (r == 1)
{ {
if (!stralloc_copys(sa,name)) return DNS_MEM; stralloc sa2 = {0};
return 0; if (!stralloc_copys(&sa2,name)) return DNS_MEM;
if (!strsalloc_append(ssa,&sa2)) return DNS_MEM;
} }
} }
if (ssa->len) return 0;
return DNS_HARD; return DNS_HARD;
} }
int dns_ptr(ssa,ip)
strsalloc *ssa;
struct ip_address *ip;
{
int r;
int j;
if (!strsalloc_readyplus(ssa,0)) return DNS_MEM;
ssa->len = 0;
r = dns_ptrplus(ssa,ip);
if (r < 0)
{
for (j = 0;j < ssa->len;++j)
alloc_free(ssa->sa[j].s);
ssa->len = 0;
}
return r;
}
static int dns_ipplus(ia,sa,pref) static int dns_ipplus(ia,sa,pref)
ipalloc *ia; ipalloc *ia;
stralloc *sa; stralloc *sa;
@ -396,3 +466,49 @@ unsigned long random;
alloc_free(mx); alloc_free(mx);
return flagsoft; return flagsoft;
} }
static int dns_txtplus(ssa,sa)
strsalloc *ssa;
stralloc *sa;
{
int r;
switch(resolve(sa,T_TXT))
{
case DNS_MEM: return DNS_MEM;
case DNS_SOFT: return DNS_SOFT;
case DNS_HARD: return DNS_HARD;
}
while ((r = findtxt(T_TXT)) != 2)
{
if (r == DNS_SOFT) return DNS_SOFT;
if (r == 1)
{
stralloc sa = {0};
if (!stralloc_copy(&sa,&txt)) return DNS_MEM;
if (!strsalloc_append(ssa,&sa)) return DNS_MEM;
}
}
if (ssa->len) return 0;
return DNS_HARD;
}
int dns_txt(ssa,sa)
strsalloc *ssa;
stralloc *sa;
{
int r;
int j;
if (!strsalloc_readyplus(ssa,0)) return DNS_MEM;
ssa->len = 0;
r = dns_txtplus(ssa,sa);
if (r < 0)
{
for (j = 0;j < ssa->len;++j)
alloc_free(ssa->sa[j].s);
ssa->len = 0;
}
return r;
}

1
dns.h
View file

@ -10,5 +10,6 @@ int dns_cname();
int dns_mxip(); int dns_mxip();
int dns_ip(); int dns_ip();
int dns_ptr(); int dns_ptr();
int dns_txt();
#endif #endif

13
dnsfq.c
View file

@ -5,15 +5,19 @@
#include "dnsdoe.h" #include "dnsdoe.h"
#include "ip.h" #include "ip.h"
#include "ipalloc.h" #include "ipalloc.h"
#include "strsalloc.h"
#include "exit.h" #include "exit.h"
stralloc sa = {0}; stralloc sa = {0};
strsalloc ssa = {0};
ipalloc ia = {0}; ipalloc ia = {0};
void main(argc,argv) void main(argc,argv)
int argc; int argc;
char **argv; char **argv;
{ {
int j;
if (!argv[1]) _exit(100); if (!argv[1]) _exit(100);
if (!stralloc_copys(&sa,argv[1])) if (!stralloc_copys(&sa,argv[1]))
@ -25,8 +29,11 @@ char **argv;
{ {
substdio_putsflush(subfderr,"no IP addresses\n"); _exit(100); substdio_putsflush(subfderr,"no IP addresses\n"); _exit(100);
} }
dnsdoe(dns_ptr(&sa,&ia.ix[0].ip)); dnsdoe(dns_ptr(&ssa,&ia.ix[0].ip));
substdio_putflush(subfdout,sa.s,sa.len); for(j = 0;j < ssa.len;++j)
substdio_putsflush(subfdout,"\n"); {
substdio_putflush(subfdout,ssa.sa[j].s,ssa.sa[j].len);
substdio_putsflush(subfdout,"\n");
}
_exit(0); _exit(0);
} }

View file

@ -6,22 +6,28 @@
#include "dns.h" #include "dns.h"
#include "dnsdoe.h" #include "dnsdoe.h"
#include "ip.h" #include "ip.h"
#include "strsalloc.h"
#include "exit.h" #include "exit.h"
stralloc sa = {0}; strsalloc ssa = {0};
struct ip_address ip; struct ip_address ip;
void main(argc,argv) void main(argc,argv)
int argc; int argc;
char **argv; char **argv;
{ {
int j;
if (!argv[1]) _exit(100); if (!argv[1]) _exit(100);
ip_scan(argv[1],&ip); ip_scan(argv[1],&ip);
dns_init(0); dns_init(0);
dnsdoe(dns_ptr(&sa,&ip)); dnsdoe(dns_ptr(&ssa,&ip));
substdio_putflush(subfdout,sa.s,sa.len); for(j = 0;j < ssa.len;++j)
substdio_putsflush(subfdout,"\n"); {
substdio_putflush(subfdout,ssa.sa[j].s,ssa.sa[j].len);
substdio_putsflush(subfdout,"\n");
}
_exit(0); _exit(0);
} }

32
dnstxt.c Normal file
View file

@ -0,0 +1,32 @@
#include "substdio.h"
#include "subfd.h"
#include "stralloc.h"
#include "str.h"
#include "scan.h"
#include "dns.h"
#include "dnsdoe.h"
#include "strsalloc.h"
#include "exit.h"
strsalloc ssa = {0};
stralloc sa = {0};
void main(argc,argv)
int argc;
char **argv;
{
int j;
if (!argv[1]) _exit(100);
if (!stralloc_copys(&sa, argv[1]))
{ substdio_putsflush(subfderr,"out of memory\n"); _exit(111); }
dns_init(0);
dnsdoe(dns_txt(&ssa,&sa));
for (j = 0;j < ssa.len;++j)
{
substdio_put(subfdout,ssa.sa[j].s,ssa.sa[j].len);
substdio_putsflush(subfdout,"\n");
}
_exit(0);
}

View file

@ -40,7 +40,7 @@ typedef unsigned short int UINT2;
/* PROTO_LIST is defined depending on how PROTOTYPES is defined above. /* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
returns an empty list. returns an empty list.
*/ */
#if PROTOTYPES #if PROTOTYPES
#define PROTO_LIST(list) list #define PROTO_LIST(list) list

4
hier.c
View file

@ -104,7 +104,7 @@ void hier()
c(auto_qmail,"bin","qmail-start",auto_uido,auto_gidq,0700); c(auto_qmail,"bin","qmail-start",auto_uido,auto_gidq,0700);
c(auto_qmail,"bin","qmail-getpw",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-getpw",auto_uido,auto_gidq,0711);
c(auto_qmail,"bin","qmail-local",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-local",auto_uido,auto_gidq,0711);
c(auto_qmail,"bin","qmail-remote",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-remote",auto_uido,auto_gidq,0755);
c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711);
c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711);
c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711);
@ -131,7 +131,7 @@ void hier()
c(auto_qmail,"bin","tcp-env",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","tcp-env",auto_uido,auto_gidq,0755);
c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755);
c(auto_qmail,"bin","qsmhook",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","qsmhook",auto_uido,auto_gidq,0755);
c(auto_qmail,"bin","qbiff",auto_uido,auto_gidq,0755); /* c(auto_qmail,"bin","qbiff",auto_uido,auto_gidq,0755); */
c(auto_qmail,"bin","forward",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","forward",auto_uido,auto_gidq,0755);
c(auto_qmail,"bin","preline",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","preline",auto_uido,auto_gidq,0755);
c(auto_qmail,"bin","condredirect",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","condredirect",auto_uido,auto_gidq,0755);

View file

@ -15,7 +15,7 @@
# #
# Return Codes: 0 - Patches applied successfully # Return Codes: 0 - Patches applied successfully
# 1 - Original QMAIL files not found (Patch not extracted in QMAIL source directory) # 1 - Original QMAIL files not found (Patch not extracted in QMAIL source directory)
# 2 - Patch files not found # 2 - Patch files not found
# #
# Output: install_auth.log # Output: install_auth.log
# #
@ -40,7 +40,7 @@ BUILD=20150823180830
if [ $# -eq 0 ] ; then if [ $# -eq 0 ] ; then
echo "Installing qmail AUTH $REL (Build $BUILD) at $DATE <<<" | tee -a $LOGFILE 2>&1 echo "Installing qmail AUTH $REL (Build $BUILD) at $DATE <<<" | tee -a $LOGFILE 2>&1
for FILE in $(grep "^= " ${TARGETS} | awk '{print $2}'); do for FILE in $(grep "^= " ${TARGETS} | awk '{print $2}'); do
echo "Targeting file $FILE ..." | tee -a $LOGFILE 2>&1 echo "Targeting file $FILE ..." | tee -a $LOGFILE 2>&1
@ -63,27 +63,27 @@ if [ $# -eq 0 ] ; then
echo "!! ${FILE}.patch not found !" echo "!! ${FILE}.patch not found !"
exit 2 exit 2
fi fi
done done
echo "Copying documentation and samples to ${QMAILHOME}/doc/ ..." | tee -a $LOGFILE 2>&1 echo "Copying documentation and samples to ${QMAILHOME}/doc/ ..." | tee -a $LOGFILE 2>&1
cp README.auth* ${QMAILHOME}/doc/ | tee -a $LOGFILE 2>&1 cp README.auth* ${QMAILHOME}/doc/ | tee -a $LOGFILE 2>&1
echo "" echo ""
echo "If you dont wont CRAM-MD5 suport disable '#define CRAM_MD5' in qmail-smtpd !" echo "If you dont wont CRAM-MD5 suport disable '#define CRAM_MD5' in qmail-smtpd !"
echo "Installation of qmail authentication $REL (Build $BUILD) finished at $DATE <<<" | tee -a $LOGFILE 2>&1 echo "Installation of qmail authentication $REL (Build $BUILD) finished at $DATE <<<" | tee -a $LOGFILE 2>&1
# Now go for the uninstallation.... # Now go for the uninstallation....
elif [ "$1" = "-u" ] ; then elif [ "$1" = "-u" ] ; then
# Get the Version Number from INPUT # Get the Version Number from INPUT
if [ $# -eq 2 ] ; then if [ $# -eq 2 ] ; then
REL=$2 REL=$2
fi fi
echo "De-installing qmail authentication $REL (Build $BUILD) at $DATE <<<" | tee -a $LOGFILE 2>&1 echo "De-installing qmail authentication $REL (Build $BUILD) at $DATE <<<" | tee -a $LOGFILE 2>&1
for FILE in $(grep "^= " ${TARGETS} | awk '{print $2}'); do for FILE in $(grep "^= " ${TARGETS} | awk '{print $2}'); do
echo "Targeting file $FILE ..." | tee -a $LOGFILE 2>&1 echo "Targeting file $FILE ..." | tee -a $LOGFILE 2>&1
@ -95,7 +95,7 @@ elif [ "$1" = "-u" ] ; then
echo "!! ${FILE}.$REL not found !" echo "!! ${FILE}.$REL not found !"
fi fi
done done
echo "De-installation of qmail authentication $REL (Build $BUILD) finished at $DATE <<<" | tee -a $LOGFILE 2>&1 echo "De-installation of qmail authentication $REL (Build $BUILD) finished at $DATE <<<" | tee -a $LOGFILE 2>&1
fi fi
exit 0 exit 0

10
md5.h
View file

@ -12,15 +12,15 @@
License is also granted to make and use derivative works provided License is also granted to make and use derivative works provided
that such works are identified as "derived from the RSA Data that such works are identified as "derived from the RSA Data
Security, Inc. MD5 Message-Digest Algorithm" in all material Security, Inc. MD5 Message-Digest Algorithm" in all material
mentioning or referencing the derived work. mentioning or referencing the derived work.
RSA Data Security, Inc. makes no representations concerning either RSA Data Security, Inc. makes no representations concerning either
the merchantability of this software or the suitability of this the merchantability of this software or the suitability of this
software for any particular purpose. It is provided "as is" software for any particular purpose. It is provided "as is"
without express or implied warranty of any kind. without express or implied warranty of any kind.
These notices must be retained in any copies of any part of this These notices must be retained in any copies of any part of this
documentation and/or software. documentation and/or software.
*/ */
#ifndef _MD5_H_ #ifndef _MD5_H_

34
md5c.c
View file

@ -12,15 +12,15 @@
License is also granted to make and use derivative works provided License is also granted to make and use derivative works provided
that such works are identified as "derived from the RSA Data that such works are identified as "derived from the RSA Data
Security, Inc. MD5 Message-Digest Algorithm" in all material Security, Inc. MD5 Message-Digest Algorithm" in all material
mentioning or referencing the derived work. mentioning or referencing the derived work.
RSA Data Security, Inc. makes no representations concerning either RSA Data Security, Inc. makes no representations concerning either
the merchantability of this software or the suitability of this the merchantability of this software or the suitability of this
software for any particular purpose. It is provided "as is" software for any particular purpose. It is provided "as is"
without express or implied warranty of any kind. without express or implied warranty of any kind.
These notices must be retained in any copies of any part of this These notices must be retained in any copies of any part of this
documentation and/or software. documentation and/or software.
*/ */
#include "global.h" #include "global.h"
@ -128,26 +128,26 @@ unsigned int inputLen; /* length of input block */
< ((UINT4)inputLen << 3)) < ((UINT4)inputLen << 3))
context->count[1]++; context->count[1]++;
context->count[1] += ((UINT4)inputLen >> 29); context->count[1] += ((UINT4)inputLen >> 29);
partLen = 64 - index; partLen = 64 - index;
/* Transform as many times as possible. /* Transform as many times as possible.
*/ */
if (inputLen >= partLen) { if (inputLen >= partLen) {
MD5_memcpy MD5_memcpy
((POINTER)&context->buffer[index], (POINTER)input, partLen); ((POINTER)&context->buffer[index], (POINTER)input, partLen);
MD5Transform (context->state, context->buffer); MD5Transform (context->state, context->buffer);
for (i = partLen; i + 63 < inputLen; i += 64) for (i = partLen; i + 63 < inputLen; i += 64)
MD5Transform (context->state, &input[i]); MD5Transform (context->state, &input[i]);
index = 0; index = 0;
} }
else else
i = 0; i = 0;
/* Buffer remaining input */ /* Buffer remaining input */
MD5_memcpy MD5_memcpy
((POINTER)&context->buffer[index], (POINTER)&input[i], ((POINTER)&context->buffer[index], (POINTER)&input[i],
inputLen-i); inputLen-i);
} }
@ -170,13 +170,13 @@ MD5_CTX *context; /* context */
index = (unsigned int)((context->count[0] >> 3) & 0x3f); index = (unsigned int)((context->count[0] >> 3) & 0x3f);
padLen = (index < 56) ? (56 - index) : (120 - index); padLen = (index < 56) ? (56 - index) : (120 - index);
MD5Update (context, PADDING, padLen); MD5Update (context, PADDING, padLen);
/* Append length (before padding) */ /* Append length (before padding) */
MD5Update (context, bits, 8); MD5Update (context, bits, 8);
/* Store state in digest */ /* Store state in digest */
Encode (digest, context->state, 16); Encode (digest, context->state, 16);
/* Zeroize sensitive information. /* Zeroize sensitive information.
*/ */
MD5_memset ((POINTER)context, 0, sizeof (*context)); MD5_memset ((POINTER)context, 0, sizeof (*context));
@ -189,7 +189,7 @@ UINT4 state[4];
unsigned char block[64]; unsigned char block[64];
{ {
UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
Decode (x, block, 64); Decode (x, block, 64);
/* Round 1 */ /* Round 1 */
@ -268,7 +268,7 @@ unsigned char block[64];
state[1] += b; state[1] += b;
state[2] += c; state[2] += c;
state[3] += d; state[3] += d;
/* Zeroize sensitive information. /* Zeroize sensitive information.
*/ */
MD5_memset ((POINTER)x, 0, sizeof (x)); MD5_memset ((POINTER)x, 0, sizeof (x));
@ -315,7 +315,7 @@ POINTER input;
unsigned int len; unsigned int len;
{ {
unsigned int i; unsigned int i;
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
output[i] = input[i]; output[i] = input[i];
} }
@ -328,7 +328,7 @@ int value;
unsigned int len; unsigned int len;
{ {
unsigned int i; unsigned int i;
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
((char *)output)[i] = (char)value; ((char *)output)[i] = (char)value;
} }

View file

@ -64,6 +64,10 @@ control default used by
.I rcpthosts \fR(none) \fRqmail-smtpd .I rcpthosts \fR(none) \fRqmail-smtpd
.I smtpgreeting \fIme \fRqmail-smtpd .I smtpgreeting \fIme \fRqmail-smtpd
.I smtproutes \fR(none) \fRqmail-remote .I smtproutes \fR(none) \fRqmail-remote
.I spfbehavior \fR0 \fRqmail-smtpd
.I spfexp \fR(default) \fRqmail-smtpd
.I spfguess \fR(none) \fRqmail-smtpd
.I spfrules \fR(none) \fRqmail-smtpd
.I timeoutconnect \fR60 \fRqmail-remote .I timeoutconnect \fR60 \fRqmail-remote
.I timeoutremote \fR1200 \fRqmail-remote .I timeoutremote \fR1200 \fRqmail-remote
.I timeoutsmtpd \fR1200 \fRqmail-smtpd .I timeoutsmtpd \fR1200 \fRqmail-smtpd

308
qmail-remote-dkim Executable file
View file

@ -0,0 +1,308 @@
#!/usr/local/bin/perl
#
# Copyright (C) 2007 Manuel Mausz (manuel@mausz.at)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
use strict;
use warnings;
our $VERSION = '0.3';
use Mail::DKIM 0.29;
use Mail::DKIM::Signer;
# enable support for "pretty" signatures, if available
eval 'require Mail::DKIM::TextWrap';
=head
config file structure
- missing settings will be merged from the global-node
- domain-entry will also match its subdomains
- create empty domain-node to omit signing (or specify "none" as id)
<dkimsign>
<!-- per default sign all mails using dkim -->
<global algorithm="rsa-sha256" domain="/var/qmail/control/me" keyfile="/var/qmail/control/dkim/global.key" method="simple" selector="beta">
<types id="dkim" />
</global>
<!-- use dkim + domainkey for example.com -->
<example.com selector="beta2">
<types id="dkim" />
<types id="domainkey" method="nofws" />
</example.com>
<!-- no signing for example2.com -->
<example2.com />
</dkimsign>
=cut
my $configfile = undef;
$configfile = '/var/qmail/control/dkim/signconf.xml';
my $debugfile = undef;
#$debugfile = '/tmp/dkim.debug';
my $qremote = '/var/qmail/bin/qmail-remote-orig';
my $rcpthosts = '/var/qmail/control/rcpthosts';
my $binary = 0;
our $config;
$config->{'global'} = {
types => { dkim => {} },
keyfile => '/var/qmail/control/dkim/global.key',
algorithm => 'rsa-sha256',
method => 'simple',
selector => 'beta',
# either undefined (=sender), string or file (first line of file will be used)
#domain => '/var/qmail/control/me'
};
#-------------------------------------------------------------------------------
# read config file. safely
if (defined($configfile) && -r $configfile)
{
eval 'use XML::Simple';
if (!$@)
{
my $xmlconf;
eval { $xmlconf = XMLin($configfile, ForceArray => ['types'], KeyAttr => ['id']); };
qexit_deferral('Unable to read config file: ', $@)
if ($@);
ConfigMerge::merge($config, $xmlconf);
}
}
# open debug file
my $debugfh = undef;
if (defined($debugfile))
{
open($debugfh, '>', $debugfile)
or qexit_deferral('Unable to open ', $debugfile, ' to writing: ', $!);
}
# generate signatures
my $dkim;
my $mailbuf = '';
eval
{
my $conf = $config->{'global'};
$dkim = Mail::DKIM::Signer->new(
Policy => 'MySignerPolicy',
Debug_Canonicalization => $debugfh
);
if ($binary)
{
binmode STDIN;
}
while (<STDIN>)
{
$mailbuf .= $_;
unless ($binary)
{
chomp $_;
s/\015?$/\015\012/s;
}
$dkim->PRINT($_);
}
$dkim->CLOSE();
};
qexit_deferral('Error while signing: ', $@)
if ($@);
# close debug file
close($debugfh)
if (defined($debugfh));
# execute qmail-remote
unshift(@ARGV, $qremote);
open(QR, '|-') || exec { $ARGV[0] } @ARGV
or qexit_deferral('Unable to run qmail-remote: ', $!);
foreach my $dkim_signature ($dkim->signatures)
{
my $sig = $dkim_signature->as_string;
$sig =~ s/\015\012\t/\012\t/g;
print QR $sig."\012";
}
print QR $mailbuf;
close(QR);
#-------------------------------------------------------------------------------
sub qexit
{
print @_, "\0";
exit(0);
}
sub qexit_deferral
{
return qexit('Z', @_);
}
sub qexit_failure
{
return qexit('D', @_);
}
sub qexit_success
{
return qexit('K', @_);
}
#-------------------------------------------------------------------------------
package ConfigMerge;
# merge config hashes. arrays and scalars will be copied.
sub merge
{
my ($left, $right) = @_;
foreach my $rkey (keys(%$right))
{
my $rtype = ref($right->{$rkey}) eq 'HASH' ? 'HASH'
: ref($right->{$rkey}) eq 'ARRAY' ? 'ARRAY'
: defined($right->{$rkey}) ? 'SCALAR'
: '';
my $ltype = ref($left->{$rkey}) eq 'HASH' ? 'HASH'
: ref($left->{$rkey}) eq 'ARRAY' ? 'ARRAY'
: defined($left->{$rkey}) ? 'SCALAR'
: '';
if ($rtype ne 'HASH' || $ltype ne 'HASH')
{
$left->{$rkey} = $right->{$rkey};
}
else
{
merge($left->{$rkey}, $right->{$rkey});
}
}
return;
}
#-------------------------------------------------------------------------------
package MySignerPolicy;
use Mail::DKIM::SignerPolicy;
use base 'Mail::DKIM::SignerPolicy';
use Mail::DKIM::Signature;
use Mail::DKIM::DkSignature;
use Carp;
use strict;
use warnings;
sub apply
{
my ($self, $signer) = @_;
my $domain = undef;
$domain = lc($signer->message_sender->host)
if (defined($signer) && defined($signer->message_sender)
&& defined($signer->message_sender->host));
my $sender = $domain;
# merge configs
while($domain)
{
if (defined($config->{$domain}))
{
$config->{'global'}->{'types'} = undef;
ConfigMerge::merge($config->{'global'}, $config->{$domain});
last;
}
(undef, $domain) = split(/\./, $domain, 2);
}
my $conf = $config->{'global'};
return 0
if (!defined($conf->{'types'}) || defined($conf->{'types'}->{'none'}));
# set key file
$signer->key_file($conf->{'keyfile'});
# parse (signature) domain
if (!defined($conf->{'domain'}) || $conf->{'domain'} eq 'sender')
{
return 0
if (!defined($sender));
$conf->{'domain'} = undef;
open(FH, '<', $rcpthosts)
or croak('Unable to open rcpthosts: '.$!);
while (my $row = <FH>)
{
chomp($row);
if ($row eq $sender)
{
$conf->{'domain'} = $sender;
last;
}
}
close(FH);
return 0
if (!defined($conf->{'domain'}));
}
elsif (substr($conf->{'domain'}, 0, 1) eq '/')
{
open(FH, '<', $conf->{'domain'})
or croak('Unable to open domain-file: '.$!);
my $newdom = (split(/ /, <FH>))[0];
close(FH);
croak("Unable to read domain-file. Maybe empty file.")
if (!$newdom);
chomp($newdom);
$conf->{'domain'} = $newdom;
}
# generate signatures
my $sigdone = 0;
foreach my $type (keys(%{$conf->{'types'}}))
{
my $sigconf = $conf->{'types'}->{$type};
if ($type eq 'dkim')
{
$signer->add_signature(
new Mail::DKIM::Signature(
Algorithm => $sigconf->{'algorithm'} || $conf->{'algorithm'} || $signer->algorithm,
Method => $sigconf->{'method'} || $conf->{'method'} || $signer->method,
Headers => $sigconf->{'headers'} || $conf->{'headers'} || $signer->headers,
Domain => $sigconf->{'domain'} || $conf->{'domain'} || $signer->domain,
Selector => $sigconf->{'selector'} || $conf->{'selector'} || $signer->selector,
Query => $sigconf->{'query'} || $conf->{'query'},
Identity => $sigconf->{'identity'} || $conf->{'identity'},
Expiration => $sigconf->{'expiration'} || $conf->{'expiration'}
)
);
$sigdone = 1;
}
elsif ($type eq 'domainkey')
{
$signer->add_signature(
new Mail::DKIM::DkSignature(
Algorithm => 'rsa-sha1', # only rsa-sha1 supported
Method => $sigconf->{'method'} || $conf->{'method'} || $signer->method,
Headers => $sigconf->{'selector'} || $conf->{'headers'} || $signer->headers,
Domain => $sigconf->{'domain'} || $conf->{'domain'} || $signer->domain,
Selector => $sigconf->{'selector'} || $conf->{'selector'} || $signer->selector,
Query => $sigconf->{'query'} || $conf->{'query'}
)
);
$sigdone = 1;
}
}
return $sigdone;
}

View file

@ -53,7 +53,6 @@ does not take options
and does not follow the and does not follow the
.B getopt .B getopt
standard. standard.
.SH TRANSPARENCY .SH TRANSPARENCY
End-of-file in SMTP is encoded as dot CR LF. End-of-file in SMTP is encoded as dot CR LF.
A dot at the beginning of a line is encoded as dot dot. A dot at the beginning of a line is encoded as dot dot.
@ -110,17 +109,17 @@ K
no supported AUTH method found, continuing without authentication. no supported AUTH method found, continuing without authentication.
.TP 5 .TP 5
Z Z
Connected to Connected to
.I host .I host
but authentication was rejected (AUTH PLAIN). but authentication was rejected (AUTH PLAIN).
.TP 5 .TP 5
Z Z
Connected to Connected to
.I host .I host
but unable to base64encode (plain). but unable to base64encode (plain).
.TP 5 .TP 5
Z Z
Connected to Connected to
.I host .I host
but authentication was rejected (plain)." but authentication was rejected (plain)."
.TP 5 .TP 5
@ -130,18 +129,18 @@ Connected to
but authentication was rejected (AUTH LOGIN). but authentication was rejected (AUTH LOGIN).
.TP 5 .TP 5
Z Z
Connected to Connected to
.I host .I host
but unable to base64encode user. but unable to base64encode user.
.TP 5 .TP 5
Z Z
Connected to Connected to
.I host .I host
but authentication was rejected (username). but authentication was rejected (username).
.TP 5 .TP 5
Z Z
Connected to Connected to
.I host .I host
but unable to base64encode pass. but unable to base64encode pass.
.TP 5 .TP 5
Z Z
@ -149,23 +148,23 @@ Connected to
.I host .I host
but authentication was rejected (AUTH CRAM-MD5). but authentication was rejected (AUTH CRAM-MD5).
Z Z
Connected to Connected to
.I host .I host
but unable to base64decode challenge. but unable to base64decode challenge.
.TP 5 .TP 5
Z Z
Connected to Connected to
.I host .I host
but unable to base64encode username+digest. but unable to base64encode username+digest.
.TP 5 .TP 5
Z Z
Connected to Connected to
.I host .I host
but password expired. but password expired.
.TP 5 .TP 5
Z Z
Connected to Connected to
.I host .I host
but authentication was rejected (username+digest). but authentication was rejected (username+digest).
.PP .PP
The recipient reports will always be printed in the same order as The recipient reports will always be printed in the same order as
@ -185,36 +184,36 @@ always exits zero.
.I authsenders .I authsenders
Authenticated sender. Authenticated sender.
For each For each
.I sender .I sender
included in included in
.IR authsenders : .IR authsenders :
.I sender\fB:\fIrelay\fB:\fIport\fB|\fIuser\fB|\fIpassword .I sender\fB:\fIrelay\fB:\fIport\fB|\fIuser\fB|\fIpassword
.B qmail-remote .B qmail-remote
will try SMTP Authentication will try SMTP Authentication
of type CRAM-MD5, LOGIN, or PLAIN of type CRAM-MD5, LOGIN, or PLAIN
with the provided user name with the provided user name
.I user .I user
and password and password
.I password .I password
(the authentication information) (the authentication information)
and eventually relay the and eventually relay the
mail through mail through
.I relay .I relay
on port on port
.IR port . .IR port .
The use of The use of
.I relay .I relay
and and
.I port .I port
follows the same rules as for follows the same rules as for
.IR smtproutes .IR smtproutes
Note: In case Note: In case
.I sender .I sender
is empty, is empty,
.B qmail-remote .B qmail-remote
will try to deliver each outgoing mail will try to deliver each outgoing mail
SMTP authenticated. If the authentication SMTP authenticated. If the authentication
information is missing, the mail is information is missing, the mail is
delivered none-authenticated. delivered none-authenticated.
.I authsenders .I authsenders
can be constructed as follows: can be constructed as follows:
@ -241,7 +240,7 @@ refuses to run.
Artificial SMTP routes. Artificial SMTP routes.
Each route has the form Each route has the form
.IR domain\fB:\fIrelay , .IR domain\fB:\fIrelay ,
or or
.IR domain\fB:\fIrelay\fB|\fIuser\fB|\fIpassword .IR domain\fB:\fIrelay\fB|\fIuser\fB|\fIpassword
in case of authenticated routes without any extra spaces. in case of authenticated routes without any extra spaces.
If If
@ -293,13 +292,13 @@ any other address is artificially routed to
The The
.B qmail .B qmail
system does not protect you if you create an artificial system does not protect you if you create an artificial
mail loop between machines. mail loop between machines.
However, However,
you are always safe using you are always safe using
.I smtproutes .I smtproutes
if you do not accept mail from the network. if you do not accept mail from the network.
Note: Note:
.I authsender .I authsender
routes have precedence over routes have precedence over
.IR smtproutes . .IR smtproutes .
.TP 5 .TP 5

View file

@ -251,12 +251,12 @@ int len;
int i; int i;
if(!stralloc_copys(sa,"")) temp_nomem(); if(!stralloc_copys(sa,"")) temp_nomem();
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
if (s[i] == '=') { if (s[i] == '=') {
if (!stralloc_cats(sa,"+3D")) temp_nomem(); if (!stralloc_cats(sa,"+3D")) temp_nomem();
} else if (s[i] == '+') { } else if (s[i] == '+') {
if (!stralloc_cats(sa,"+2B")) temp_nomem(); if (!stralloc_cats(sa,"+2B")) temp_nomem();
} else if ((int) s[i] < 33 || (int) s[i] > 126) { } else if ((int) s[i] < 33 || (int) s[i] > 126) {
if (!stralloc_cats(sa,"+3F")) temp_nomem(); /* ok. not correct */ if (!stralloc_cats(sa,"+3F")) temp_nomem(); /* ok. not correct */
} else if (!stralloc_catb(sa,s+i,1)) { } else if (!stralloc_catb(sa,s+i,1)) {
@ -296,7 +296,7 @@ int mailfrom_plain()
if (smtpcode() == 235) { mailfrom_xtext(); return 0; } if (smtpcode() == 235) { mailfrom_xtext(); return 0; }
else if (smtpcode() == 432) { quit("ZConnected to "," but password expired."); return 1; } else if (smtpcode() == 432) { quit("ZConnected to "," but password expired."); return 1; }
else { quit("ZConnected to "," but authentication was rejected (plain)."); return 1; } else { quit("ZConnected to "," but authentication was rejected (plain)."); return 1; }
return 0; return 0;
} }
@ -340,13 +340,13 @@ int mailfrom_cram()
if (!stralloc_copyb(&slop,smtptext.s+4,smtptext.len-5)) temp_nomem(); if (!stralloc_copyb(&slop,smtptext.s+4,smtptext.len-5)) temp_nomem();
if (b64decode(slop.s,slop.len,&chal)) quit("ZConnected to "," but unable to base64decode challenge."); if (b64decode(slop.s,slop.len,&chal)) quit("ZConnected to "," but unable to base64decode challenge.");
} }
hmac_md5(chal.s,chal.len,pass.s,pass.len,digest); hmac_md5(chal.s,chal.len,pass.s,pass.len,digest);
for (j = 0;j < 16;j++) /* HEX => ASCII */ for (j = 0;j < 16;j++) /* HEX => ASCII */
{ {
digascii[2*j] = hextab[digest[j] >> 4]; digascii[2*j] = hextab[digest[j] >> 4];
digascii[2*j+1] = hextab[digest[j] & 0xf]; digascii[2*j+1] = hextab[digest[j] & 0xf];
} }
digascii[32]=0; digascii[32]=0;
@ -354,7 +354,7 @@ int mailfrom_cram()
if (!stralloc_copys(&slop,"")) temp_nomem(); if (!stralloc_copys(&slop,"")) temp_nomem();
if (!stralloc_cat(&slop,&user)) temp_nomem(); /* user-id */ if (!stralloc_cat(&slop,&user)) temp_nomem(); /* user-id */
if (!stralloc_cats(&slop," ")) temp_nomem(); if (!stralloc_cats(&slop," ")) temp_nomem();
if (!stralloc_catb(&slop,digascii,32)) temp_nomem(); /* digest */ if (!stralloc_catb(&slop,digascii,32)) temp_nomem(); /* digest */
if (!stralloc_copys(&auth,"")) temp_nomem(); if (!stralloc_copys(&auth,"")) temp_nomem();
if (b64encode(&slop,&auth)) quit("ZConnected to "," but unable to base64encode username+digest."); if (b64encode(&slop,&auth)) quit("ZConnected to "," but unable to base64encode username+digest.");
@ -363,25 +363,25 @@ int mailfrom_cram()
substdio_flush(&smtpto); substdio_flush(&smtpto);
if (smtpcode() == 235) { mailfrom_xtext(); return 0; } if (smtpcode() == 235) { mailfrom_xtext(); return 0; }
else if (smtpcode() == 432) { quit("ZConnected to "," but password expired."); return 1; } else if (smtpcode() == 432) { quit("ZConnected to "," but password expired."); return 1; }
else { quit("ZConnected to "," but authentication was rejected (username+digest)."); return 1; } else { quit("ZConnected to "," but authentication was rejected (username+digest)."); return 1; }
} }
void smtp_auth() void smtp_auth()
{ {
int i, j; int i, j;
for (i = 0; i + 8 < smtptext.len; i += str_chr(smtptext.s+i,'\n')+1) for (i = 0; i + 8 < smtptext.len; i += str_chr(smtptext.s+i,'\n')+1)
if (!str_diffn(smtptext.s+i+4,"AUTH",4)) { if (!str_diffn(smtptext.s+i+4,"AUTH",4)) {
if (j = str_chr(smtptext.s+i+8,'C') > 0) if (j = str_chr(smtptext.s+i+8,'C') > 0)
if (case_starts(smtptext.s+i+8+j,"CRAM")) if (case_starts(smtptext.s+i+8+j,"CRAM"))
if (mailfrom_cram() >= 0) return; if (mailfrom_cram() >= 0) return;
if (j = str_chr(smtptext.s+i+8,'P') > 0) if (j = str_chr(smtptext.s+i+8,'P') > 0)
if (case_starts(smtptext.s+i+8+j,"PLAIN")) if (case_starts(smtptext.s+i+8+j,"PLAIN"))
if (mailfrom_plain() >= 0) return; if (mailfrom_plain() >= 0) return;
if (j = str_chr(smtptext.s+i+8,'L') > 0) if (j = str_chr(smtptext.s+i+8,'L') > 0)
if (case_starts(smtptext.s+i+8+j,"LOGIN")) if (case_starts(smtptext.s+i+8+j,"LOGIN"))
if (mailfrom_login() >= 0) return; if (mailfrom_login() >= 0) return;
err_authprot(); err_authprot();
@ -416,7 +416,7 @@ void smtp()
if (user.len && pass.len) if (user.len && pass.len)
smtp_auth(); smtp_auth();
else else
mailfrom(); mailfrom();
code = smtpcode(); code = smtpcode();
@ -509,7 +509,7 @@ void getcontrols()
case 1: case 1:
if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break; if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break;
} }
switch(control_readfile(&authsenders,"control/authsenders",0)) { switch(control_readfile(&authsenders,"control/authsenders",0)) {
case -1: case -1:
temp_control(); temp_control();
@ -532,7 +532,7 @@ char **argv;
int flagallaliases; int flagallaliases;
int flagalias; int flagalias;
char *relayhost; char *relayhost;
sig_pipeignore(); sig_pipeignore();
if (argc < 4) perm_usage(); if (argc < 4) perm_usage();
if (chdir(auto_qmail) == -1) temp_chdir(); if (chdir(auto_qmail) == -1) temp_chdir();

View file

@ -15,6 +15,7 @@
#include "auto_patrn.h" #include "auto_patrn.h"
#include "auto_spawn.h" #include "auto_spawn.h"
#include "auto_split.h" #include "auto_split.h"
#include "spf.h"
stralloc me = {0}; stralloc me = {0};
int meok; int meok;
@ -258,6 +259,10 @@ void main()
do_str("smtpgreeting",1,"smtpgreeting","SMTP greeting: 220 "); do_str("smtpgreeting",1,"smtpgreeting","SMTP greeting: 220 ");
do_lst("smtproutes","No artificial SMTP routes.","SMTP route: ",""); do_lst("smtproutes","No artificial SMTP routes.","SMTP route: ","");
do_int("spfbehavior","0","The SPF behavior is ","");
do_str("spfexp",0,SPF_DEFEXP,"The SPF default explanation is: 550 ");
do_str("spfguess",0,"","The guess SPF rules are: ");
do_str("spfrules",0,"","The local SPF rules are: ");
do_int("timeoutconnect","60","SMTP client connection timeout is "," seconds"); do_int("timeoutconnect","60","SMTP client connection timeout is "," seconds");
do_int("timeoutremote","1200","SMTP client data timeout is "," seconds"); do_int("timeoutremote","1200","SMTP client data timeout is "," seconds");
do_int("timeoutsmtpd","1200","SMTP server data timeout is "," seconds"); do_int("timeoutsmtpd","1200","SMTP server data timeout is "," seconds");
@ -292,6 +297,10 @@ void main()
if (str_equal(d->d_name,"rcpthosts")) continue; if (str_equal(d->d_name,"rcpthosts")) continue;
if (str_equal(d->d_name,"smtpgreeting")) continue; if (str_equal(d->d_name,"smtpgreeting")) continue;
if (str_equal(d->d_name,"smtproutes")) continue; if (str_equal(d->d_name,"smtproutes")) continue;
if (str_equal(d->d_name,"spfbehavior")) continue;
if (str_equal(d->d_name,"spfexp")) continue;
if (str_equal(d->d_name,"spfguess")) continue;
if (str_equal(d->d_name,"spfrules")) continue;
if (str_equal(d->d_name,"timeoutconnect")) continue; if (str_equal(d->d_name,"timeoutconnect")) continue;
if (str_equal(d->d_name,"timeoutremote")) continue; if (str_equal(d->d_name,"timeoutremote")) continue;
if (str_equal(d->d_name,"timeoutsmtpd")) continue; if (str_equal(d->d_name,"timeoutsmtpd")) continue;

View file

@ -229,15 +229,15 @@ In case
is defined, is defined,
.B qmail-smtpd .B qmail-smtpd
honors LOGIN, PLAIN, and additionally CRAM-MD5 authentication. honors LOGIN, PLAIN, and additionally CRAM-MD5 authentication.
Simply Simply
.TP 5 .TP 5
.IR SMTPAUTH='cram' .IR SMTPAUTH='cram'
restricts authentication just to CRAM-MD5. restricts authentication just to CRAM-MD5.
If however If however
.TP 5 .TP 5
.IR SMTPAUTH='!' .IR SMTPAUTH='!'
starts with an exclamation mark, AUTH is required. starts with an exclamation mark, AUTH is required.
You can enforce 'Submission' using this option You can enforce 'Submission' using this option
and binding and binding
.B qmail-smtpd .B qmail-smtpd
to the SUBMISSION port \'587'\. to the SUBMISSION port \'587'\.
@ -253,6 +253,42 @@ connections.
Note: The use of 'cram' requires a CRAM-MD5 enabled PAM. Note: The use of 'cram' requires a CRAM-MD5 enabled PAM.
.TP 5
.I spfbehavior
Set to a value between 1 and 6 to enable SPF checks; 0 to disable.
1 selects 'annotate-only' mode, where
.B qmail-smtpd
will annotate incoming email with
.B Received-SPF
fields, but will not reject any messages. 2 will produce temporary
failures on DNS lookup problems so you can make sure you always have
meaningful Received-SPF headers. 3 selects 'reject' mode,
where incoming mail will be rejected if the SPF record says 'fail'. 4
selects a more stricter rejection mode, which is like 'reject' mode,
except that incoming mail will also be rejected when the SPF record
says 'softfail'. 5 will also reject when the SPF record says 'neutral',
and 6 if no SPF records are available at all (or a syntax error was
encountered). The contents of this file are overridden by the value of
the
.B SPFBEHAVIOR
environment variable, if set.
Default: 0.
.TP 5
.I spfexp
You can add a line with a an SPF explanation that will be shown to the
sender in case of a reject. It will override the default one. You can
use SPF macro expansion.
.TP 5
.I spfguess
You can add a line with SPF rules that will be checked if a sender
domain doesn't have a SPF record. The local rules will also be used
in this case.
.TP 5
.I spfrules
You can add a line with SPF rules that will be checked before other SPF
rules would fail. This can be used to always allow certain machines to
send certain mails.
.SH "SEE ALSO" .SH "SEE ALSO"
tcp-env(1), tcp-env(1),
tcp-environ(5), tcp-environ(5),

View file

@ -24,12 +24,13 @@
#include "timeoutwrite.h" #include "timeoutwrite.h"
#include "commands.h" #include "commands.h"
#include "wait.h" #include "wait.h"
#include "spf.h"
#define AUTHSLEEP 5 #define AUTHSLEEP 5
#define MAXHOPS 100 #define MAXHOPS 100
unsigned int databytes = 0; unsigned int databytes = 0;
int timeout = 1200; int timeout = 1200;
unsigned int spfbehavior = 0;
int safewrite(fd,buf,len) int fd; char *buf; int len; int safewrite(fd,buf,len) int fd; char *buf; int len;
{ {
@ -50,7 +51,7 @@ void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); }
void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); } void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); }
void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); } void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); }
void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); } void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); }
void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); } void straynewline() { out("451 See https://cr.yp.to/docs/smtplf.html.\r\n"); flush(); _exit(1); }
void err_size() { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); } void err_size() { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); }
void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); } void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
@ -76,6 +77,9 @@ void err_authfail() { out("535 authentication failed (#5.7.1)\r\n"); }
void err_submission() { out("530 Authorization required (#5.7.1) \r\n"); } void err_submission() { out("530 Authorization required (#5.7.1) \r\n"); }
stralloc greeting = {0}; stralloc greeting = {0};
stralloc spflocal = {0};
stralloc spfguess = {0};
stralloc spfexp = {0};
void smtp_greet(code) char *code; void smtp_greet(code) char *code;
{ {
@ -84,7 +88,7 @@ void smtp_greet(code) char *code;
} }
void smtp_help(arg) char *arg; void smtp_help(arg) char *arg;
{ {
out("214 netqmail home page: http://qmail.org/netqmail\r\n"); out("214 Omg, you didn't take ``SMTP 101'' in college.\r\n");
} }
void smtp_quit(arg) char *arg; void smtp_quit(arg) char *arg;
{ {
@ -128,6 +132,7 @@ void setup()
if (liphostok == -1) die_control(); if (liphostok == -1) die_control();
if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control(); if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control();
if (timeout <= 0) timeout = 1; if (timeout <= 0) timeout = 1;
if (rcpthosts_init() == -1) die_control(); if (rcpthosts_init() == -1) die_control();
bmfok = control_readfile(&bmf,"control/badmailfrom",0); bmfok = control_readfile(&bmf,"control/badmailfrom",0);
@ -141,6 +146,19 @@ void setup()
if (!(databytes + 1)) --databytes; if (!(databytes + 1)) --databytes;
protocol = "SMTP"; protocol = "SMTP";
if (control_readint(&spfbehavior,"control/spfbehavior") == -1)
die_control();
x = env_get("SPFBEHAVIOR");
if (x) { scan_ulong(x,&u); spfbehavior = u; }
if (control_readline(&spflocal,"control/spfrules") == -1) die_control();
if (spflocal.len && !stralloc_0(&spflocal)) die_nomem();
if (control_readline(&spfguess,"control/spfguess") == -1) die_control();
if (spfguess.len && !stralloc_0(&spfguess)) die_nomem();
if (control_rldef(&spfexp,"control/spfexp",0,SPF_DEFEXP) == -1)
die_control();
if (!stralloc_0(&spfexp)) die_nomem();
remoteip = env_get("TCPREMOTEIP"); remoteip = env_get("TCPREMOTEIP");
if (!remoteip) remoteip = "unknown"; if (!remoteip) remoteip = "unknown";
local = env_get("TCPLOCALHOST"); local = env_get("TCPLOCALHOST");
@ -166,6 +184,7 @@ void setup()
dohelo(remotehost); dohelo(remotehost);
} }
stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */ stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */
int addrparse(arg) int addrparse(arg)
@ -247,11 +266,13 @@ int addrallowed()
return r; return r;
} }
char *auth;
int seenauth = 0; int seenauth = 0;
int seenmail = 0; int seenmail = 0;
int flagbarf; /* defined if seenmail */ int flagbarf; /* defined if seenmail */
int flagsize; int flagsize;
int flagbarfspf;
stralloc spfbarfmsg = {0};
stralloc mailfrom = {0}; stralloc mailfrom = {0};
stralloc rcptto = {0}; stralloc rcptto = {0};
stralloc fuser = {0}; stralloc fuser = {0};
@ -268,13 +289,13 @@ int mailfrom_size(arg) char *arg;
return 0; return 0;
} }
void mailfrom_auth(arg,len) void mailfrom_auth(arg,len)
char *arg; char *arg;
int len; int len;
{ {
if (!stralloc_copys(&fuser,"")) die_nomem(); if (!stralloc_copys(&fuser,"")) die_nomem();
if (case_starts(arg,"<>")) { if (!stralloc_cats(&fuser,"unknown")) die_nomem(); } if (case_starts(arg,"<>")) { if (!stralloc_cats(&fuser,"unknown")) die_nomem(); }
else else
while (len) { while (len) {
if (*arg == '+') { if (*arg == '+') {
if (case_starts(arg,"+3D")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"=")) die_nomem(); } if (case_starts(arg,"+3D")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"=")) die_nomem(); }
@ -302,14 +323,14 @@ void mailfrom_parms(arg) char *arg;
i = byte_chr(arg,len,'>'); i = byte_chr(arg,len,'>');
if (i > 4 && i < len) { if (i > 4 && i < len) {
while (len) { while (len) {
arg++; len--; arg++; len--;
if (*arg == ' ' || *arg == '\0' ) { if (*arg == ' ' || *arg == '\0' ) {
if (case_starts(mfparms.s,"SIZE=")) if (mailfrom_size(mfparms.s+5)) { flagsize = 1; return; } if (case_starts(mfparms.s,"SIZE=")) if (mailfrom_size(mfparms.s+5)) { flagsize = 1; return; }
if (case_starts(mfparms.s,"AUTH=")) mailfrom_auth(mfparms.s+5,mfparms.len-5); if (case_starts(mfparms.s,"AUTH=")) mailfrom_auth(mfparms.s+5,mfparms.len-5);
if (!stralloc_copys(&mfparms,"")) die_nomem(); if (!stralloc_copys(&mfparms,"")) die_nomem();
} }
else else
if (!stralloc_catb(&mfparms,arg,1)) die_nomem(); if (!stralloc_catb(&mfparms,arg,1)) die_nomem();
} }
} }
} }
@ -323,8 +344,7 @@ void smtp_ehlo(arg) char *arg;
{ {
char size[FMT_ULONG]; char size[FMT_ULONG];
size[fmt_ulong(size,(unsigned int) databytes)] = 0; size[fmt_ulong(size,(unsigned int) databytes)] = 0;
smtp_greet("250-"); smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250-8BITMIME\r\n");
out("\r\n250-PIPELINING\r\n250-8BITMIME\r\n");
if (smtpauth == 1 || smtpauth == 11) out("250-AUTH LOGIN PLAIN\r\n"); if (smtpauth == 1 || smtpauth == 11) out("250-AUTH LOGIN PLAIN\r\n");
if (smtpauth == 2 || smtpauth == 12) out("250-AUTH CRAM-MD5\r\n"); if (smtpauth == 2 || smtpauth == 12) out("250-AUTH CRAM-MD5\r\n");
if (smtpauth == 3 || smtpauth == 13) out("250-AUTH LOGIN PLAIN CRAM-MD5\r\n"); if (smtpauth == 3 || smtpauth == 13) out("250-AUTH LOGIN PLAIN CRAM-MD5\r\n");
@ -333,29 +353,88 @@ void smtp_ehlo(arg) char *arg;
} }
void smtp_rset(arg) char *arg; void smtp_rset(arg) char *arg;
{ {
seenmail = 0; seenauth = 0; seenmail = 0; seenauth = 0;
mailfrom.len = 0; rcptto.len = 0; mailfrom.len = 0; rcptto.len = 0;
out("250 flushed\r\n"); out("250 flushed\r\n");
} }
void smtp_mail(arg) char *arg; void smtp_mail(arg) char *arg;
{ {
int r;
if (smtpauth) if (smtpauth)
if (smtpauth > 10 && !seenauth) { err_submission(); return; } if (smtpauth > 10 && !seenauth) { err_submission(); return; }
if (!addrparse(arg)) { err_syntax(); return; } if (!addrparse(arg)) { err_syntax(); return; }
flagsize = 0; flagsize = 0;
mailfrom_parms(arg); mailfrom_parms(arg);
if (flagsize) { err_size(); return; } if (flagsize) { err_size(); return; }
flagbarf = bmfcheck(); flagbarf = bmfcheck();
flagbarfspf = 0;
if (spfbehavior && !relayclient)
{
switch(r = spfcheck()) {
case SPF_OK: env_put2("SPFRESULT","pass"); break;
case SPF_NONE: env_put2("SPFRESULT","none"); break;
case SPF_UNKNOWN: env_put2("SPFRESULT","unknown"); break;
case SPF_NEUTRAL: env_put2("SPFRESULT","neutral"); break;
case SPF_SOFTFAIL: env_put2("SPFRESULT","softfail"); break;
case SPF_FAIL: env_put2("SPFRESULT","fail"); break;
case SPF_ERROR: env_put2("SPFRESULT","error"); break;
}
switch (r) {
case SPF_NOMEM:
die_nomem();
case SPF_ERROR:
if (spfbehavior < 2) break;
out("451 SPF lookup failure (#4.3.0)\r\n");
return;
case SPF_NONE:
case SPF_UNKNOWN:
if (spfbehavior < 6) break;
case SPF_NEUTRAL:
if (spfbehavior < 5) break;
case SPF_SOFTFAIL:
if (spfbehavior < 4) break;
case SPF_FAIL:
if (spfbehavior < 3) break;
if (!spfexplanation(&spfbarfmsg)) die_nomem();
if (!stralloc_0(&spfbarfmsg)) die_nomem();
flagbarfspf = 1;
}
}
else
env_unset("SPFRESULT");
seenmail = 1; seenmail = 1;
if (!stralloc_copys(&rcptto,"")) die_nomem(); if (!stralloc_copys(&rcptto,"")) die_nomem();
if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); if (!stralloc_copys(&mailfrom,addr.s)) die_nomem();
if (!stralloc_0(&mailfrom)) die_nomem(); if (!stralloc_0(&mailfrom)) die_nomem();
out("250 ok\r\n"); out("250 ok\r\n");
} }
void err_spf() {
int i,j;
for(i = 0; i < spfbarfmsg.len; i = j + 1) {
j = byte_chr(spfbarfmsg.s + i, spfbarfmsg.len - i, '\n') + i;
if (j < spfbarfmsg.len) {
out("550-");
spfbarfmsg.s[j] = 0;
out(spfbarfmsg.s);
spfbarfmsg.s[j] = '\n';
out("\r\n");
} else {
out("550 ");
out(spfbarfmsg.s);
out(" (#5.7.1)\r\n");
}
}
}
void smtp_rcpt(arg) char *arg; { void smtp_rcpt(arg) char *arg; {
if (!seenmail) { err_wantmail(); return; } if (!seenmail) { err_wantmail(); return; }
if (!addrparse(arg)) { err_syntax(); return; } if (!addrparse(arg)) { err_syntax(); return; }
if (flagbarf) { err_bmf(); return; } if (flagbarf) { err_bmf(); return; }
if (flagbarfspf) { err_spf(); return; }
if (relayclient) { if (relayclient) {
--addr.len; --addr.len;
if (!stralloc_cats(&addr,relayclient)) die_nomem(); if (!stralloc_cats(&addr,relayclient)) die_nomem();
@ -456,6 +535,25 @@ int *hops;
} }
} }
void spfreceived()
{
stralloc sa = {0};
stralloc rcvd_spf = {0};
if (!spfbehavior || relayclient) return;
if (!stralloc_copys(&rcvd_spf, "Received-SPF: ")) die_nomem();
if (!spfinfo(&sa)) die_nomem();
if (!stralloc_cat(&rcvd_spf, &sa)) die_nomem();
if (!stralloc_append(&rcvd_spf, "\n")) die_nomem();
if (bytestooverflow) {
bytestooverflow -= rcvd_spf.len;
if (bytestooverflow <= 0) qmail_fail(&qqt);
}
qmail_put(&qqt,rcvd_spf.s,rcvd_spf.len);
}
char accept_buf[FMT_ULONG]; char accept_buf[FMT_ULONG];
void acceptmessage(qp) unsigned long qp; void acceptmessage(qp) unsigned long qp;
{ {
@ -484,6 +582,7 @@ void smtp_data(arg) char *arg; {
out("354 go ahead\r\n"); out("354 go ahead\r\n");
received(&qqt,protocol,local,remoteip,remotehost,remoteinfo,fakehelo); received(&qqt,protocol,local,remoteip,remotehost,remoteinfo,fakehelo);
spfreceived();
blast(&hops); blast(&hops);
hops = (hops >= MAXHOPS); hops = (hops >= MAXHOPS);
if (hops) qmail_fail(&qqt); if (hops) qmail_fail(&qqt);
@ -558,7 +657,7 @@ int authenticate(void)
substdio_fdbuf(&ssauth,write,pi[1],ssauthbuf,sizeof ssauthbuf); substdio_fdbuf(&ssauth,write,pi[1],ssauthbuf,sizeof ssauthbuf);
if (substdio_put(&ssauth,user.s,user.len) == -1) return err_write(); if (substdio_put(&ssauth,user.s,user.len) == -1) return err_write();
if (substdio_put(&ssauth,pass.s,pass.len) == -1) return err_write(); if (substdio_put(&ssauth,pass.s,pass.len) == -1) return err_write();
if (smtpauth == 2 || smtpauth == 3 || smtpauth == 12 || smtpauth == 13) if (smtpauth == 2 || smtpauth == 3 || smtpauth == 12 || smtpauth == 13)
if (substdio_put(&ssauth,chal.s,chal.len) == -1) return err_write(); if (substdio_put(&ssauth,chal.s,chal.len) == -1) return err_write();
if (substdio_flush(&ssauth) == -1) return err_write(); if (substdio_flush(&ssauth) == -1) return err_write();

878
spf.c Normal file
View file

@ -0,0 +1,878 @@
#include "stralloc.h"
#include "strsalloc.h"
#include "alloc.h"
#include "ip.h"
#include "ipalloc.h"
#include "ipme.h"
#include "str.h"
#include "fmt.h"
#include "scan.h"
#include "byte.h"
#include "now.h"
#include "dns.h"
#include "case.h"
#include "spf.h"
#define SPF_EXT -1
#define SPF_SYNTAX -2
#define WSPACE(x) ((x) == ' ' || (x) == '\t' || (x) == '\r' || (x) == '\n')
#define NXTOK(b, p, a) do { (b) = (p); \
while((p) < (a)->len && !WSPACE((a)->s[(p)])) ++(p); \
while((p) < (a)->len && WSPACE((a)->s[(p)])) (a)->s[(p)++] = 0; \
} while(0)
/* this table and macro came from wget more or less */
/* and was in turn stolen by me from libspf as is :) */
const static unsigned char urlchr_table[256] =
{
1, 1, 1, 1, 1, 1, 1, 1, /* NUL SOH STX ETX EOT ENQ ACK BEL */
1, 1, 1, 1, 1, 1, 1, 1, /* BS HT LF VT FF CR SO SI */
1, 1, 1, 1, 1, 1, 1, 1, /* DLE DC1 DC2 DC3 DC4 NAK SYN ETB */
1, 1, 1, 1, 1, 1, 1, 1, /* CAN EM SUB ESC FS GS RS US */
1, 0, 1, 1, 0, 1, 1, 0, /* SP ! " # $ % & ' */
0, 0, 0, 1, 0, 0, 0, 1, /* ( ) * + , - . / */
0, 0, 0, 0, 0, 0, 0, 0, /* 0 1 2 3 4 5 6 7 */
0, 0, 1, 1, 1, 1, 1, 1, /* 8 9 : ; < = > ? */
1, 0, 0, 0, 0, 0, 0, 0, /* @ A B C D E F G */
0, 0, 0, 0, 0, 0, 0, 0, /* H I J K L M N O */
0, 0, 0, 0, 0, 0, 0, 0, /* P Q R S T U V W */
0, 0, 0, 1, 1, 1, 1, 0, /* X Y Z [ \ ] ^ _ */
1, 0, 0, 0, 0, 0, 0, 0, /* ` a b c d e f g */
0, 0, 0, 0, 0, 0, 0, 0, /* h i j k l m n o */
0, 0, 0, 0, 0, 0, 0, 0, /* p q r s t u v w */
0, 0, 0, 1, 1, 1, 1, 1, /* x y z { | } ~ DEL */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
};
extern stralloc addr;
extern stralloc helohost;
extern char *remoteip;
extern char *local;
extern stralloc spflocal;
extern stralloc spfguess;
extern stralloc spfexp;
static stralloc sender_fqdn = {0};
static stralloc explanation = {0};
static stralloc expdomain = {0};
static stralloc errormsg = {0};
static char *received;
static int recursion;
static struct ip_address ip;
static void hdr_pass() { received = "pass (%{xr}: %{xs} designates %{i} as permitted sender)"; };
static void hdr_softfail() { received = "softfail (%{xr}: transitioning %{xs} does not designate %{i} as permitted sender)"; };
static void hdr_fail() { received = "fail (%{xr}: %{xs} does not designate %{i} as permitted sender)"; };
static void hdr_unknown() { received = "unknown (%{xr}: domain at %{d} does not designate permitted sender hosts)"; };
static void hdr_neutral() { received = "neutral (%{xr}: %{i} is neither permitted nor denied by %{xs})"; };
static void hdr_none() { received = "none (%{xr}: domain at %{d} does not designate permitted sender hosts)"; };
static void hdr_unknown_msg(e) char *e; { stralloc_copys(&errormsg, e); received = "unknown (%{xr}: %{xe})"; };
static void hdr_ext(e) char *e; { stralloc_copys(&errormsg, e); received = "unknown %{xe} (%{xr}: %{xs} uses mechanism not recognized by this client)"; };
static void hdr_syntax() { received = "unknown (%{xr}: parse error in %{xs})"; };
static void hdr_error(e) char *e; { stralloc_copys(&errormsg, e); received = "error (%{xr}: error in processing during lookup of %{d}: %{xe})"; };
static void hdr_dns() { hdr_error("DNS problem"); }
static int matchip(struct ip_address *net, int mask, struct ip_address *ip)
{
int j;
int bytemask;
for (j = 0; j < 4 && mask > 0; ++j) {
if (mask > 8) bytemask = 8; else bytemask = mask;
mask -= bytemask;
if ((net->d[j] ^ ip->d[j]) & (0x100 - (1 << (8 - bytemask))))
return 0;
}
return 1;
}
static int getipmask(char *mask, int ipv6) {
unsigned long r;
int pos;
if (!mask) return 32;
pos = scan_ulong(mask, &r);
if (!pos || (mask[pos] && !(mask[pos] == '/' && ipv6))) return -1;
if (r > 32) return -1;
return r;
}
int spfget(stralloc *spf, stralloc *domain)
{
strsalloc ssa = {0};
int j;
int begin, pos, i;
int r = SPF_NONE;
spf->len = 0;
switch(dns_txt(&ssa, domain)) {
case DNS_MEM: return SPF_NOMEM;
case DNS_SOFT: hdr_dns(); return SPF_ERROR;
case DNS_HARD: return SPF_NONE;
}
for (j = 0;j < ssa.len;++j) {
pos = 0;
NXTOK(begin, pos, &ssa.sa[j]);
if (str_len(ssa.sa[j].s + begin) < 6) continue;
if (!byte_equal(ssa.sa[j].s + begin,6,"v=spf1")) continue;
if (ssa.sa[j].s[begin + 6]) {
/* check for subversion */
if (ssa.sa[j].s[begin + 6] != '.') continue;
for(i = begin + 7;;++i)
if (!(ssa.sa[j].s[i] >= '0' && ssa.sa[j].s[i] <= '9')) break;
if (i == (begin + 7)) continue;
if (ssa.sa[j].s[i]) continue;
}
if (spf->len > 0) {
spf->len = 0;
hdr_unknown_msg("Multiple SPF records returned");
r = SPF_UNKNOWN;
break;
}
if (!stralloc_0(&ssa.sa[j])) return SPF_NOMEM;
if (!stralloc_copys(spf,ssa.sa[j].s + pos)) return SPF_NOMEM;
r = SPF_OK;
}
for (j = 0;j < ssa.len;++j)
alloc_free(ssa.sa[j].s);
alloc_free(ssa.sa);
return r;
}
static int spf_ptr(char *spec, char *mask);
int spfsubst(stralloc *expand, char *spec, char *domain)
{
static char hexdigits[] = "0123456789abcdef";
stralloc sa = {0};
char ch;
int digits = -1;
int urlencode = 0;
int reverse = 0;
int start = expand->len;
int i, pos;
char *split = ".";
if (!stralloc_readyplus(&sa,0)) return 0;
if (*spec == 'x') { i = 1; ++spec; } else i = 0;
ch = *spec++;
if (!ch) { alloc_free(sa.s); return 1; }
if (ch >= 'A' && ch <= 'Z') { ch += 32; urlencode = 1; }
if (i) ch -= 32;
while(*spec >= '0' && *spec <= '9') {
if (digits < 0) digits = 0;
if (digits >= 1000000) { digits = 10000000; continue; }
digits = (digits * 10) + (*spec - '0');
spec++;
}
while((*spec >= 'a' && *spec <= 'z') || (*spec >= 'A' && *spec <= 'Z')) {
if (*spec == 'r') reverse = 1;
spec++;
}
if (*spec) split = spec;
switch(ch) {
case 'l':
pos = byte_rchr(addr.s, addr.len, '@');
if (pos < addr.len) {
if (!stralloc_copyb(&sa, addr.s, pos)) return 0;
} else
if (!stralloc_copys(&sa, "postmaster")) return 0;
break;
case 's':
if (!stralloc_copys(&sa, addr.s)) return 0;
break;
case 'o':
pos = byte_rchr(addr.s, addr.len, '@') + 1;
if (pos > addr.len) break;
if (!stralloc_copys(&sa, addr.s + pos)) return 0;
break;
case 'd':
if (!stralloc_copys(&sa, domain)) return 0;
break;
case 'i':
if (!stralloc_ready(&sa, IPFMT)) return 0;
sa.len = ip_fmt(sa.s, &ip);
break;
case 't':
if (!stralloc_ready(&sa, FMT_ULONG)) return 0;
sa.len = fmt_ulong(sa.s, (unsigned long)now());
break;
case 'p':
if (!sender_fqdn.len)
spf_ptr(domain, 0);
if (sender_fqdn.len) {
if (!stralloc_copy(&sa, &sender_fqdn)) return 0;
} else
if (!stralloc_copys(&sa, "unknown")) return 0;
break;
case 'v':
if (!stralloc_copys(&sa, "in-addr")) return 0;
break;
case 'h':
if (!stralloc_copys(&sa, helohost.s)) return 0; /* FIXME: FQDN? */
break;
case 'E':
if (errormsg.len && !stralloc_copy(&sa, &errormsg)) return 0;
break;
case 'R':
if (!stralloc_copys(&sa, local)) return 0;
break;
case 'S':
if (expdomain.len > 0) {
if (!stralloc_copys(&sa, "SPF record at ")) return 0;
if (!stralloc_cats(&sa, expdomain.s)) return 0;
} else {
if (!stralloc_copys(&sa, "local policy")) return 0;
}
break;
}
if (reverse) {
for(pos = 0; digits; ++pos) {
pos += byte_cspn(sa.s + pos, sa.len - pos, split);
if (pos >= sa.len) break;
if (!--digits) break;
}
for(; pos > 0; pos = i - 1) {
i = byte_rcspn(sa.s, pos, split) + 1;
if (i > pos) i = 0;
if (!stralloc_catb(expand, sa.s + i, pos - i)) return 0;
if (i > 0 && !stralloc_append(expand, ".")) return 0;
}
} else {
for(pos = sa.len; digits; --pos) {
i = byte_rcspn(sa.s, pos, split) + 1;
if (i > pos) { pos = 0; break; }
pos = i;
if (!--digits) break;
}
if (!stralloc_catb(expand, sa.s + pos, sa.len - pos)) return 0;
if (split[0] != '.' || split[1])
for(pos = 0; pos < expand->len; pos++) {
pos += byte_cspn(expand->s + pos, expand->len - pos, split);
if (pos < expand->len)
expand->s[pos] = '.';
}
}
if (urlencode) {
stralloc_copyb(&sa, expand->s + start, expand->len - start);
expand->len = start;
for(pos = 0; pos < sa.len; ++pos) {
ch = sa.s[pos];
if (urlchr_table[(unsigned char)ch]) {
if (!stralloc_readyplus(expand, 3)) return 0;
expand->s[expand->len++] = '%';
expand->s[expand->len++] = hexdigits[(unsigned char)ch >> 4];
expand->s[expand->len++] = hexdigits[(unsigned char)ch & 15];
} else
if (!stralloc_append(expand, &ch)) return 0;
}
}
alloc_free(sa.s);
return 1;
}
int spfexpand(stralloc *sa, char *spec, char *domain)
{
char *p;
char append;
int pos;
if (!stralloc_readyplus(sa, 0)) return 0;
sa->len = 0;
for(p = spec; *p; p++) {
append = *p;
if (*p == '%') {
p++;
switch(*p) {
case '%': break;
case '_': append = ' '; break;
case '-': if (!stralloc_cats(sa, "%20")) return 0; continue;
case '{':
pos = str_chr(p, '}');
if (p[pos] != '}') { p--; break; }
p[pos] = 0;
if (!spfsubst(sa, p + 1, domain)) return 0;
p += pos;
continue;
default: p--;
}
}
if (!stralloc_append(sa, &append)) return 0;
}
return 1;
}
static int spflookup(stralloc *domain);
static int spf_include(char *spec, char *mask)
{
stralloc sa = {0};
int r;
if (!stralloc_copys(&sa, spec)) return SPF_NOMEM;
r = spflookup(&sa);
alloc_free(sa.s);
switch(r) {
case SPF_NONE:
hdr_unknown();
r = SPF_UNKNOWN;
break;
case SPF_SYNTAX:
r = SPF_UNKNOWN;
break;
case SPF_NEUTRAL:
case SPF_SOFTFAIL:
case SPF_FAIL:
r = SPF_NONE;
break;
}
return r;
}
static int spf_a(char *spec, char *mask)
{
stralloc sa = {0};
ipalloc ia = {0};
int ipmask = getipmask(mask, 1);
int r;
int j;
if (ipmask < 0) return SPF_SYNTAX;
if (!stralloc_copys(&sa, spec)) return SPF_NOMEM;
if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM;
switch(dns_ip(&ia, &sa)) {
case DNS_MEM: return SPF_NOMEM;
case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break;
case DNS_HARD: r = SPF_NONE; break;
default:
r = SPF_NONE;
for(j = 0; j < ia.len; ++j)
if (matchip(&ia.ix[j].ip, ipmask, &ip)) {
r = SPF_OK;
break;
}
}
alloc_free(sa.s);
alloc_free(ia.ix);
return r;
}
static int spf_mx(char *spec, char *mask)
{
stralloc sa = {0};
ipalloc ia = {0};
int ipmask = getipmask(mask, 1);
int random = now() + (getpid() << 16);
int r;
int j;
if (ipmask < 0) return SPF_SYNTAX;
if (!stralloc_copys(&sa, spec)) return SPF_NOMEM;
if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM;
switch(dns_mxip(&ia, &sa, random)) {
case DNS_MEM: return SPF_NOMEM;
case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break;
case DNS_HARD: r = SPF_NONE; break;
default:
r = SPF_NONE;
for(j = 0; j < ia.len; ++j)
if (matchip(&ia.ix[j].ip, ipmask, &ip)) {
r = SPF_OK;
break;
}
}
alloc_free(sa.s);
alloc_free(ia.ix);
return r;
}
static int spf_ptr(char *spec, char *mask)
{
strsalloc ssa = {0};
ipalloc ia = {0};
int len = str_len(spec);
int r;
int j, k;
int pos;
/* we didn't find host with the matching ip before */
if (sender_fqdn.len == 7 && str_equal(sender_fqdn.s, "unknown"))
return SPF_NONE;
/* the hostname found will probably be the same as before */
while (sender_fqdn.len) {
pos = sender_fqdn.len - len;
if (pos < 0) break;
if (pos > 0 && sender_fqdn.s[pos - 1] != '.') break;
if (case_diffb(sender_fqdn.s + pos, len, spec)) break;
return SPF_OK;
}
/* ok, either it's the first test or it's a very weird setup */
if (!stralloc_readyplus(&ssa, 0)) return SPF_NOMEM;
if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM;
switch(dns_ptr(&ssa, &ip)) {
case DNS_MEM: return SPF_NOMEM;
case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break;
case DNS_HARD: r = SPF_NONE; break;
default:
r = SPF_NONE;
for(j = 0; j < ssa.len; ++j) {
switch(dns_ip(&ia, &ssa.sa[j])) {
case DNS_MEM: return SPF_NOMEM;
case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break;
case DNS_HARD: break;
default:
for(k = 0; k < ia.len; ++k)
if (matchip(&ia.ix[k].ip, 32, &ip)) {
if (!sender_fqdn.len)
if (!stralloc_copy(&sender_fqdn, &ssa.sa[j])) return SPF_NOMEM;
pos = ssa.sa[j].len - len;
if (pos < 0) continue;
if (pos > 0 && ssa.sa[j].s[pos - 1] != '.') continue;
if (case_diffb(ssa.sa[j].s + pos, len, spec)) continue;
stralloc_copy(&sender_fqdn, &ssa.sa[j]);
r = SPF_OK;
break;
}
}
if (r == SPF_ERROR) break;
}
}
for(j = 0;j < ssa.len;++j)
alloc_free(ssa.sa[j].s);
alloc_free(ssa.sa);
alloc_free(ia.ix);
if (!sender_fqdn.len)
if (!stralloc_copys(&sender_fqdn, "unknown")) return SPF_NOMEM;
return r;
}
static int spf_ip(char *spec, char *mask)
{
struct ip_address net;
int ipmask = getipmask(mask, 0);
if (ipmask < 0) return SPF_SYNTAX;
if (!ip_scan(spec, &net)) return SPF_SYNTAX;
if (matchip(&net, ipmask, &ip)) return SPF_OK;
return SPF_NONE;
}
static int spf_exists(char *spec, char *mask)
{
stralloc sa = {0};
ipalloc ia = {0};
int r;
if (!stralloc_copys(&sa, spec)) return SPF_NOMEM;
if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM;
switch(dns_ip(&ia, &sa)) {
case DNS_MEM: return SPF_NOMEM;
case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break;
case DNS_HARD: r = SPF_NONE; break;
default: r = SPF_OK;
}
alloc_free(sa.s);
alloc_free(ia.ix);
return r;
}
static struct mechanisms {
char *mechanism;
int (*func)(char *spec, char *mask);
unsigned int takes_spec : 1;
unsigned int takes_mask : 1;
unsigned int expands : 1;
unsigned int filldomain : 1;
int defresult : 4;
} mechanisms[] = {
{ "all", 0, 0,0,0,0,SPF_OK }
, { "include", spf_include,1,0,1,0,0 }
, { "a", spf_a, 1,1,1,1,0 }
, { "mx", spf_mx, 1,1,1,1,0 }
, { "ptr", spf_ptr, 1,0,1,1,0 }
, { "ip4", spf_ip, 1,1,0,0,0 }
, { "ip6", 0, 1,1,0,0,SPF_NONE }
, { "exists", spf_exists, 1,0,1,0,0 }
, { "extension",0, 1,1,0,0,SPF_EXT }
, { 0, 0, 1,1,0,0,SPF_EXT }
};
static int spfmech(char *mechanism, char *spec, char *mask, char *domain)
{
struct mechanisms *mech;
stralloc sa = {0};
int r;
int pos;
for(mech = mechanisms; mech->mechanism; mech++)
if (str_equal(mech->mechanism, mechanism)) break;
if (mech->takes_spec && !spec && mech->filldomain) spec = domain;
if (!mech->takes_spec != !spec) return SPF_SYNTAX;
if (!mech->takes_mask && mask) return SPF_SYNTAX;
if (!mech->func) return mech->defresult;
if (!stralloc_readyplus(&sa, 0)) return SPF_NOMEM;
if (mech->expands && spec != domain) {
if (!spfexpand(&sa, spec, domain)) return SPF_NOMEM;
for (pos = 0; (sa.len - pos) > 255;) {
pos += byte_chr(sa.s + pos, sa.len - pos, '.');
if (pos < sa.len) pos++;
}
sa.len -= pos;
if (pos > 0) byte_copy(sa.s, sa.len, sa.s + pos);
stralloc_0(&sa);
spec = sa.s;
}
r = mech->func(spec, mask);
alloc_free(sa.s);
return r;
}
static struct default_aliases {
char *alias;
int defret;
} default_aliases[] = {
{ "allow", SPF_OK }
, { "pass", SPF_OK }
, { "deny", SPF_FAIL }
, { "softdeny",SPF_SOFTFAIL }
, { "fail", SPF_FAIL }
, { "softfail",SPF_SOFTFAIL }
, { "unknown", SPF_NEUTRAL }
, { 0, SPF_UNKNOWN }
};
static int spflookup(stralloc *domain)
{
stralloc spf = {0};
stralloc sa = {0};
struct default_aliases *da;
int main = !recursion;
int local_pos = -1;
int r, q;
int begin, pos;
int i;
int prefix;
int done;
int guessing = 0;
char *p;
if (!stralloc_readyplus(&spf, 0)) return SPF_NOMEM;
if (!stralloc_readyplus(&sa, 0)) return SPF_NOMEM;
/* fallthrough result */
if (main) hdr_none();
redirect:
if (++recursion > 20) {
alloc_free(spf.s);
alloc_free(sa.s);
hdr_unknown_msg("Maximum nesting level exceeded, possible loop");
return SPF_SYNTAX;
}
if (!stralloc_0(domain)) return SPF_NOMEM;
if (!stralloc_copy(&expdomain, domain)) return SPF_NOMEM;
r = spfget(&spf, domain);
if (r == SPF_NONE) {
if (!main) { alloc_free(spf.s); return r; }
if (spfguess.len) {
/* try to guess */
guessing = 1;
if (!stralloc_copys(&spf, spfguess.s)) return SPF_NOMEM;
if (!stralloc_append(&spf, " ")) return SPF_NOMEM;
} else
spf.len = 0;
/* append local rulest */
if (spflocal.len) {
local_pos = spf.len;
if (!stralloc_cats(&spf, spflocal.s)) return SPF_NOMEM;
}
if (!stralloc_0(&spf)) return SPF_NOMEM;
expdomain.len = 0;
} else if (r == SPF_OK) {
if (!stralloc_0(&spf)) return SPF_NOMEM;
if (main) hdr_neutral();
r = SPF_NEUTRAL;
/* try to add local rules before fail all mechs */
if (main && spflocal.len) {
pos = 0;
p = (char *) 0;
while(pos < spf.len) {
NXTOK(begin, pos, &spf);
if (!spf.s[begin]) continue;
if (p && spf.s[begin] != *p) p = (char *) 0;
if (!p && (spf.s[begin] == '-' || spf.s[begin] == '~' ||
spf.s[begin] == '?')) p = &spf.s[begin];
if (p && p > spf.s && str_equal(spf.s + begin + 1, "all")) {
/* ok, we can insert the local rules at p */
local_pos = p - spf.s;
stralloc_readyplus(&spf, spflocal.len);
p = spf.s + local_pos;
byte_copyr(p + spflocal.len, spf.len - local_pos, p);
byte_copy(p, spflocal.len, spflocal.s);
spf.len += spflocal.len;
pos += spflocal.len;
break;
}
}
if (pos >= spf.len) pos = spf.len - 1;
for(i = 0; i < pos; i++)
if (!spf.s[i]) spf.s[i] = ' ';
}
} else {
alloc_free(spf.s);
return r;
}
pos = 0;
done = 0;
while(pos < spf.len) {
NXTOK(begin, pos, &spf);
if (!spf.s[begin]) continue;
/* in local ruleset? */
if (!done && local_pos >= 0 && begin >= local_pos) {
if (begin < (local_pos + spflocal.len))
expdomain.len = 0;
else
if (!stralloc_copy(&expdomain, domain))
return SPF_NOMEM;
}
for (p = spf.s + begin;*p;++p)
if (*p == ':' || *p == '/' || *p == '=') break;
if (*p == '=') {
*p++ = 0;
/* modifiers are simply handled here */
if (str_equal(spf.s + begin, "redirect")) {
if (done) continue;
if (!spfexpand(&sa, p, domain->s)) return SPF_NOMEM;
stralloc_copy(domain, &sa);
hdr_unknown();
r = SPF_UNKNOWN;
goto redirect;
} else if (str_equal(spf.s + begin, "default")) {
if (done) continue;
for(da = default_aliases; da->alias; ++da)
if (str_equal(da->alias, p)) break;
r = da->defret;
} else if (str_equal(spf.s + begin, "exp")) {
strsalloc ssa = {0};
if (!main) continue;
if (!stralloc_copys(&sa, p)) return SPF_NOMEM;
switch(dns_txt(&ssa, &sa)) {
case DNS_MEM: return SPF_NOMEM;
case DNS_SOFT: continue; /* FIXME... */
case DNS_HARD: continue;
}
explanation.len = 0;
for(i = 0; i < ssa.len; i++) {
if (!stralloc_cat(&explanation, &ssa.sa[i])) return SPF_NOMEM;
if (i < (ssa.len - 1))
if (!stralloc_append(&explanation, "\n")) return SPF_NOMEM;
alloc_free(ssa.sa[i].s);
}
if (!stralloc_0(&explanation)) return SPF_NOMEM;
} /* and unknown modifiers are ignored */
} else if (!done) {
if (!stralloc_copys(&sa, spf.s + begin)) return SPF_NOMEM;
if (!stralloc_0(&sa)) return SPF_NOMEM;
switch(spf.s[begin]) {
case '-': begin++; prefix = SPF_FAIL; break;
case '~': begin++; prefix = SPF_SOFTFAIL; break;
case '+': begin++; prefix = SPF_OK; break;
case '?': begin++; prefix = SPF_NEUTRAL; break;
default: prefix = SPF_OK;
}
if (*p == '/') {
*p++ = 0;
q = spfmech(spf.s + begin, 0, p, domain->s);
} else {
if (*p) *p++ = 0;
i = str_chr(p, '/');
if (p[i] == '/') {
p[i++] = 0;
q = spfmech(spf.s + begin, p, p + i, domain->s);
} else if (i > 0)
q = spfmech(spf.s + begin, p, 0, domain->s);
else
q = spfmech(spf.s + begin, 0, 0, domain->s);
}
if (q == SPF_OK) q = prefix;
switch(q) {
case SPF_OK: hdr_pass(); break;
case SPF_NEUTRAL: hdr_neutral(); break;
case SPF_SYNTAX: hdr_syntax(); break;
case SPF_SOFTFAIL: hdr_softfail(); break;
case SPF_FAIL: hdr_fail(); break;
case SPF_EXT: hdr_ext(sa.s); break;
case SPF_ERROR:
if (!guessing)
break;
if (local_pos >= 0 && begin >= local_pos)
break;
hdr_none();
q = SPF_NONE;
break;
case SPF_NONE: continue;
}
r = q;
done = 1; /* we're done, no more mechanisms */
}
}
/* we fell through, no local rule applied */
if (!done && !stralloc_copy(&expdomain, domain)) return SPF_NOMEM;
alloc_free(spf.s);
alloc_free(sa.s);
return r;
}
int spfcheck()
{
stralloc domain = {0};
int pos;
int r;
pos = byte_rchr(addr.s, addr.len, '@') + 1;
if (pos < addr.len) {
if (!stralloc_copys(&domain, addr.s + pos)) return SPF_NOMEM;
} else {
pos = str_rchr(helohost.s, '@');
if (helohost.s[pos]) {
if (!stralloc_copys(&domain, helohost.s + pos + 1)) return SPF_NOMEM;
} else
if (!stralloc_copys(&domain, helohost.s)) return SPF_NOMEM;
}
if (!stralloc_copys(&explanation, spfexp.s)) return SPF_NOMEM;
if (!stralloc_0(&explanation)) return SPF_NOMEM;
recursion = 0;
if (!remoteip || !ip_scan(remoteip, &ip)) {
hdr_unknown_msg("No IP address in conversation");
return SPF_UNKNOWN;
}
if (!stralloc_readyplus(&expdomain, 0)) return SPF_NOMEM;
if (!stralloc_readyplus(&errormsg, 0)) return SPF_NOMEM;
expdomain.len = 0;
errormsg.len = 0;
sender_fqdn.len = 0;
received = (char *) 0;
if ((ip.d[0] == 127 && ip.d[1] == 0 && ip.d[2] == 0 && ip.d[3] == 1) || ipme_is(&ip))
{ hdr_pass(); r = SPF_OK; }
else
r = spflookup(&domain);
if (r < 0) r = SPF_UNKNOWN;
alloc_free(domain.s);
return r;
}
int spfexplanation(sa)
stralloc *sa;
{
return spfexpand(sa, explanation.s, expdomain.s);
}
int spfinfo(sa)
stralloc *sa;
{
stralloc tmp = {0};
if (!stralloc_copys(&tmp, received)) return 0;
if (!stralloc_0(&tmp)) return 0;
if (!spfexpand(sa, tmp.s, expdomain.s)) return 0;
alloc_free(tmp.s);
return 1;
}

20
spf.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef SPF_H
#define SPF_H
#define SPF_OK 0
#define SPF_NONE 1
#define SPF_UNKNOWN 2
#define SPF_NEUTRAL 3
#define SPF_SOFTFAIL 4
#define SPF_FAIL 5
#define SPF_ERROR 6
#define SPF_NOMEM 7
#define SPF_DEFEXP "See http://spf.pobox.com/" \
"why.html?sender=%{S}&ip=%{I}&receiver=%{xR}"
extern int spfcheck();
extern int spfexplanation();
extern int spfinfo();
#endif

84
spfquery.c Normal file
View file

@ -0,0 +1,84 @@
#include "substdio.h"
#include "subfd.h"
#include "stralloc.h"
#include "alloc.h"
#include "spf.h"
#include "exit.h"
void die(e,s) int e; char *s; { substdio_putsflush(subfderr,s); _exit(e); }
void die_usage() { die(100,"fatal: invalid usage\nusage: spfquery <sender-ip> <sender-helo/ehlo> <envelope-from> [<local rules>] [<best guess rules>]\n"); }
void die_nomem() { die(111,"fatal: out of memory\n"); }
stralloc addr = {0};
stralloc helohost = {0};
char *remoteip;
char *local;
stralloc spflocal = {0};
stralloc spfguess = {0};
stralloc spfexp = {0};
void main(argc,argv)
int argc;
char **argv;
{
stralloc sa = {0};
int r;
if (argc < 4) die_usage();
remoteip = (char *)strdup(argv[1]);
local = "localhost";
if (!stralloc_copys(&helohost, argv[2])) die_nomem();
if (!stralloc_0(&helohost)) die_nomem();
if (!stralloc_copys(&addr, argv[3])) die_nomem();
if (!stralloc_0(&addr)) die_nomem();
if (argc > 4) {
if (!stralloc_copys(&spflocal, argv[4])) die_nomem();
if (spflocal.len && !stralloc_0(&spflocal)) die_nomem();
}
if (argc > 5) {
if (!stralloc_copys(&spfguess, argv[5])) die_nomem();
if (spfguess.len && !stralloc_0(&spfguess)) die_nomem();
}
if (argc > 6) {
if (!stralloc_copys(&spfexp, argv[6])) die_nomem();
} else
if (!stralloc_copys(&spfexp, SPF_DEFEXP)) die_nomem();
if (spfexp.len && !stralloc_0(&spfexp)) die_nomem();
dns_init(0);
r = spfcheck();
if (r == SPF_NOMEM) die_nomem();
substdio_puts(subfdout,"result=");
switch(r) {
case SPF_OK: substdio_puts(subfdout,"pass"); break;
case SPF_NONE: substdio_puts(subfdout,"none"); break;
case SPF_UNKNOWN: substdio_puts(subfdout,"unknown"); break;
case SPF_NEUTRAL: substdio_puts(subfdout,"neutral"); break;
case SPF_SOFTFAIL: substdio_puts(subfdout,"softfail"); break;
case SPF_FAIL: substdio_puts(subfdout,"fail"); break;
case SPF_ERROR: substdio_puts(subfdout,"error"); break;
}
if (r == SPF_FAIL) {
substdio_puts(subfdout,": ");
if (!spfexplanation(&sa)) die_nomem();
substdio_put(subfdout,sa.s,sa.len);
}
substdio_putsflush(subfdout,"\n");
substdio_puts(subfdout,"Received-SPF: ");
if (!spfinfo(&sa)) die_nomem();
substdio_put(subfdout,sa.s,sa.len);
substdio_putsflush(subfdout,"\n");
_exit(0);
}

1
str.h
View file

@ -2,6 +2,7 @@
#define STR_H #define STR_H
extern unsigned int str_copy(); extern unsigned int str_copy();
extern unsigned int str_copyb();
extern int str_diff(); extern int str_diff();
extern int str_diffn(); extern int str_diffn();
extern unsigned int str_len(); extern unsigned int str_len();

18
str_cpyb.c Normal file
View file

@ -0,0 +1,18 @@
#include "str.h"
unsigned int str_copyb(s,t,max)
register char *s;
register char *t;
unsigned int max;
{
register int len;
len = 0;
while (max-- > 0) {
if (!(*s = *t)) return len; ++s; ++t; ++len;
if (!(*s = *t)) return len; ++s; ++t; ++len;
if (!(*s = *t)) return len; ++s; ++t; ++len;
if (!(*s = *t)) return len; ++s; ++t; ++len;
}
return len;
}

7
strsalloc.c Normal file
View file

@ -0,0 +1,7 @@
#include "alloc.h"
#include "gen_allocdefs.h"
#include "stralloc.h"
#include "strsalloc.h"
GEN_ALLOC_readyplus(strsalloc,stralloc,sa,len,a,i,n,x,10,strsalloc_readyplus)
GEN_ALLOC_append(strsalloc,stralloc,sa,len,a,i,n,x,10,strsalloc_readyplus,strsalloc_append)

12
strsalloc.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef STRSALLOC_H
#define STRSALLOC_H
#include "stralloc.h"
#include "gen_alloc.h"
GEN_ALLOC_typedef(strsalloc,stralloc,sa,len,a)
extern int strsalloc_readyplus();
extern int strsalloc_append();
#endif

View file

@ -10,6 +10,7 @@
#include "scan.h" #include "scan.h"
#include "subgetopt.h" #include "subgetopt.h"
#include "ip.h" #include "ip.h"
#include "strsalloc.h"
#include "dns.h" #include "dns.h"
#include "byte.h" #include "byte.h"
#include "remoteinfo.h" #include "remoteinfo.h"
@ -34,6 +35,7 @@ void main(argc,argv)
int argc; int argc;
char *argv[]; char *argv[];
{ {
strsalloc ssa = {0};
int dummy; int dummy;
char *proto; char *proto;
int opt; int opt;
@ -74,12 +76,13 @@ char *argv[];
temp[ip_fmt(temp,&iplocal)] = 0; temp[ip_fmt(temp,&iplocal)] = 0;
if (!env_put2("TCPLOCALIP",temp)) die(); if (!env_put2("TCPLOCALIP",temp)) die();
switch(dns_ptr(&localname,&iplocal)) switch(dns_ptr(&ssa,&iplocal))
{ {
case DNS_MEM: die(); case DNS_MEM: die();
case DNS_SOFT: case DNS_SOFT:
if (!stralloc_copys(&localname,"softdnserror")) die(); if (!stralloc_copys(&localname,"softdnserror")) die();
case 0: case 0:
if (!stralloc_copy(&localname,&ssa.sa[0])) die();
if (!stralloc_0(&localname)) die(); if (!stralloc_0(&localname)) die();
case_lowers(localname.s); case_lowers(localname.s);
if (!env_put2("TCPLOCALHOST",localname.s)) die(); if (!env_put2("TCPLOCALHOST",localname.s)) die();
@ -99,12 +102,13 @@ char *argv[];
temp[ip_fmt(temp,&ipremote)] = 0; temp[ip_fmt(temp,&ipremote)] = 0;
if (!env_put2("TCPREMOTEIP",temp)) die(); if (!env_put2("TCPREMOTEIP",temp)) die();
switch(dns_ptr(&remotename,&ipremote)) switch(dns_ptr(&ssa,&ipremote))
{ {
case DNS_MEM: die(); case DNS_MEM: die();
case DNS_SOFT: case DNS_SOFT:
if (!stralloc_copys(&remotename,"softdnserror")) die(); if (!stralloc_copys(&remotename,"softdnserror")) die();
case 0: case 0:
if (!stralloc_copy(&remotename,&ssa.sa[0])) die();
if (!stralloc_0(&remotename)) die(); if (!stralloc_0(&remotename)) die();
case_lowers(remotename.s); case_lowers(remotename.s);
if (!env_put2("TCPREMOTEHOST",remotename.s)) die(); if (!env_put2("TCPREMOTEHOST",remotename.s)) die();