From 0d0a30fed9fb80f1637dd187277ec3ff721b4461 Mon Sep 17 00:00:00 2001 From: Josuah Demangeon Date: Mon, 27 Sep 2021 11:40:52 +0200 Subject: [PATCH] add patches from the wiki --- conf-branches | 1 + patches/badmailfrom-wildcard.patch | 35 + patches/badmailfrom-x-relayclient.patch | 25 + patches/big-concurrency.patch | 262 +++ patches/big-todo.patch | 187 ++ patches/netqmail-spp.patch | 413 +++++ patches/rcptcheck.patch | 126 ++ patches/smtp-auth.patch | Bin 0 -> 30720 bytes patches/smtp-tls.patch | 1676 +++++++++++++++++ patches/smtpd-logging.patch | 421 +++++ patches/smtpd-spf.patch | 2173 +++++++++++++++++++++++ patches/spp-smtpauth-eh.patch | 431 +++++ patches/spp-smtpauth-tls-20060105.patch | 441 +++++ patches/spp-smtpauth.patch | 431 +++++ patches/spp.patch | 413 +++++ 15 files changed, 7035 insertions(+) create mode 100644 conf-branches create mode 100644 patches/badmailfrom-wildcard.patch create mode 100644 patches/badmailfrom-x-relayclient.patch create mode 100644 patches/big-concurrency.patch create mode 100644 patches/big-todo.patch create mode 100644 patches/netqmail-spp.patch create mode 100644 patches/rcptcheck.patch create mode 100644 patches/smtp-auth.patch create mode 100644 patches/smtp-tls.patch create mode 100644 patches/smtpd-logging.patch create mode 100644 patches/smtpd-spf.patch create mode 100644 patches/spp-smtpauth-eh.patch create mode 100644 patches/spp-smtpauth-tls-20060105.patch create mode 100644 patches/spp-smtpauth.patch create mode 100644 patches/spp.patch diff --git a/conf-branches b/conf-branches new file mode 100644 index 0000000..1f7391f --- /dev/null +++ b/conf-branches @@ -0,0 +1 @@ +master diff --git a/patches/badmailfrom-wildcard.patch b/patches/badmailfrom-wildcard.patch new file mode 100644 index 0000000..752d8ed --- /dev/null +++ b/patches/badmailfrom-wildcard.patch @@ -0,0 +1,35 @@ +If badmailfrom contains ".example.com" then reject mail from +"anything@any.thing.example.com" + +http://tomclegg.net/qmail/qmail-badmailfrom-wildcard.patch + +--- qmail-smtpd.c~ Thu Apr 17 16:08:41 2003 ++++ qmail-smtpd.c Thu Apr 17 20:04:16 2003 +@@ -215,8 +215,13 @@ + if (!bmfok) return 0; + if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1; + j = byte_rchr(addr.s,addr.len,'@'); +- if (j < addr.len) ++ if (j < addr.len) { + if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1; ++ for (j++; j < addr.len; j++) ++ if (addr.s[j] == '.') { ++ if(constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1; ++ } ++ } + return 0; + } + +--- /tmp/qmail-1.03/qmail-smtpd.8 Mon Jun 15 03:53:16 1998 ++++ qmail-smtpd.8 Sun Aug 1 15:19:19 2004 +@@ -49,6 +49,10 @@ + .BR @\fIhost , + meaning every address at + .IR host . ++A line of the form ++.BR .\fIdomain ++means every address at every host ending with ++.IR .domain . + .TP 5 + .I databytes + Maximum number of bytes allowed in a message, diff --git a/patches/badmailfrom-x-relayclient.patch b/patches/badmailfrom-x-relayclient.patch new file mode 100644 index 0000000..cf9bcc9 --- /dev/null +++ b/patches/badmailfrom-x-relayclient.patch @@ -0,0 +1,25 @@ +This patch disables the badmailfrom check where RELAYCLIENT is set. + +This allows you to put your own domain in badmailfrom, but still allow your +customers to relay through your mail server. + +Also Works with the qmail-smtpd-auth patch from http://members.elysium.pl/brush/qmail-smtpd-auth/ +so your smtp authenticated users are also permitted to relay. + +The basic idea behind the need for this concept is so you can put your own domain in badmailfrom, +and still allow your trusted users to be able to send mail through your mail server. + +Jeremy Kitchen -- kitchen@scriptkitchen.com 12/23/2003 + +diff -urN qmail-1.03.orig/qmail-smtpd.c qmail-1.03/qmail-smtpd.c +--- qmail-1.03.orig/qmail-smtpd.c 1998-06-15 05:53:16.000000000 -0500 ++++ qmail-1.03/qmail-smtpd.c 2003-12-23 16:41:44.386786384 -0600 +@@ -240,7 +240,7 @@ + void smtp_mail(arg) char *arg; + { + if (!addrparse(arg)) { err_syntax(); return; } +- flagbarf = bmfcheck(); ++ if (!relayclient) flagbarf = bmfcheck(); + seenmail = 1; + if (!stralloc_copys(&rcptto,"")) die_nomem(); + if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); diff --git a/patches/big-concurrency.patch b/patches/big-concurrency.patch new file mode 100644 index 0000000..cb78065 --- /dev/null +++ b/patches/big-concurrency.patch @@ -0,0 +1,262 @@ +From: "Johannes Erdfelt" +To: qmail@list.cr.yp.to +Subject: Re: mail volume +Date: 4 Aug 1999 20:41:00 -0700 +Mime-Version: 1.0 +Content-Type: multipart/mixed; boundary=7AUc2qLy4jB3hD7Z + +--7AUc2qLy4jB3hD7Z +Content-Type: text/plain; charset=us-ascii + +On Thu, Aug 05, 1999, richard@illuin.demon.co.uk wrote: +> On Wed, 4 Aug 1999, Daemeon Reiydelle wrote: +> +> > (2.6 or later). There may be limitations within e.g. qmail-[lr]spawn +> > about how many children it can manage. I am not working with that code +> > right know so I don't know. Anyone? +> +> This is what people have been trying to say -- the protocol between +> qmail-Xspawn and qmail-send only passes a single byte for the delivery +> attempt back in the status messages. if you want to increase the maximum +> number above 256 one has to modify qmail-send and the common code in +> qmail-Xspawn. making it a short should allow up to 2**16 concurrency +> remotes. +> +> **CAUTION** if you do this one should realise that qmail-send might try to +> open 64K connections to the /same/ host because it doesn't maintain a +> per-domain concurrency. this is distinctly Unfriendly. I produced some +> code for qmail to do this, but when I asked my ISP if i could open >>1024 +> connections to one of their mail relays for testing they were less than +> enthusiastic... (the code is on my desktop system somewhere between here +> and Austin where I'm moving to next week, so I can't email it, and without +> testing it I won't email it. the changes to up the concurrency are fairly +> straightforward, the once for a per-domain concurrency are non-trivial) + +This is the patch that I use at suse.com. We do almost 1 million +messages a day with this patch and concurrencyremote set to 400. + +This patch comes with the standard disclaimer. No warranty, it may not +work, etc. But it works for me :) + +It's also not pretty. It's against qmail-1.03+verh-0.02 (the ezmlm patch +l and h patch). So the offsets may be off a little bit. + +JE + + +--7AUc2qLy4jB3hD7Z +Content-Type: text/plain; charset=us-ascii +Content-Disposition: attachment; filename="qmail-bigrem.patch" + +diff -u qmail-1.03.orig/chkspawn.c qmail-1.03/chkspawn.c +--- qmail-1.03.orig/chkspawn.c Mon Jun 15 03:53:16 1998 ++++ qmail-1.03/chkspawn.c Wed Aug 4 20:33:22 1999 +@@ -22,8 +22,8 @@ + _exit(1); + } + +- if (auto_spawn > 255) { +- substdio_puts(subfderr,"Oops. You have set conf-spawn higher than 255.\n"); ++ if (auto_spawn > 65000) { ++ substdio_puts(subfderr,"Oops. You have set conf-spawn higher than 65000.\n"); + substdio_flush(subfderr); + _exit(1); + } +diff -u qmail-1.03.orig/conf-spawn qmail-1.03/conf-spawn +--- qmail-1.03.orig/conf-spawn Mon Jun 15 03:53:16 1998 ++++ qmail-1.03/conf-spawn Tue Jul 27 13:32:30 1999 +@@ -1,4 +1,4 @@ +-120 ++1000 + + This is a silent concurrency limit. You can't set it above 255. On some + systems you can't set it above 125. qmail will refuse to compile if the +diff -u qmail-1.03.orig/qmail-send.c qmail-1.03/qmail-send.c +--- qmail-1.03.orig/qmail-send.c Mon Jun 15 03:53:16 1998 ++++ qmail-1.03/qmail-send.c Wed Aug 4 20:37:23 1999 +@@ -262,6 +262,8 @@ + while (!stralloc_copys(&comm_buf[c],"")) nomem(); + ch = delnum; + while (!stralloc_append(&comm_buf[c],&ch)) nomem(); ++ ch = delnum >> 8; ++ while (!stralloc_append(&comm_buf[c],&ch)) nomem(); + fnmake_split(id); + while (!stralloc_cats(&comm_buf[c],fn.s)) nomem(); + while (!stralloc_0(&comm_buf[c])) nomem(); +@@ -906,41 +908,42 @@ + dline[c].len = REPORTMAX; + /* qmail-lspawn and qmail-rspawn are responsible for keeping it short */ + /* but from a security point of view, we don't trust rspawn */ +- if (!ch && (dline[c].len > 1)) ++ if (!ch && (dline[c].len > 2)) + { + delnum = (unsigned int) (unsigned char) dline[c].s[0]; ++ delnum += (unsigned int) ((unsigned int) dline[c].s[1]) << 8; + if ((delnum < 0) || (delnum >= concurrency[c]) || !d[c][delnum].used) + log1("warning: internal error: delivery report out of range\n"); + else + { + strnum3[fmt_ulong(strnum3,d[c][delnum].delid)] = 0; +- if (dline[c].s[1] == 'Z') ++ if (dline[c].s[2] == 'Z') + if (jo[d[c][delnum].j].flagdying) + { +- dline[c].s[1] = 'D'; ++ dline[c].s[2] = 'D'; + --dline[c].len; + while (!stralloc_cats(&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem(); + while (!stralloc_0(&dline[c])) nomem(); + } +- switch(dline[c].s[1]) ++ switch(dline[c].s[2]) + { + case 'K': + log3("delivery ",strnum3,": success: "); +- logsafe(dline[c].s + 2); ++ logsafe(dline[c].s + 3); + log1("\n"); + markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos); + --jo[d[c][delnum].j].numtodo; + break; + case 'Z': + log3("delivery ",strnum3,": deferral: "); +- logsafe(dline[c].s + 2); ++ logsafe(dline[c].s + 3); + log1("\n"); + break; + case 'D': + log3("delivery ",strnum3,": failure: "); +- logsafe(dline[c].s + 2); ++ logsafe(dline[c].s + 3); + log1("\n"); +- addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 2); ++ addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 3); + markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos); + --jo[d[c][delnum].j].numtodo; + break; +@@ -1544,7 +1547,7 @@ + numjobs = 0; + for (c = 0;c < CHANNELS;++c) + { +- char ch; ++ char ch, ch1; + int u; + int r; + do +@@ -1552,7 +1555,13 @@ + while ((r == -1) && (errno == error_intr)); + if (r < 1) + { log1("alert: cannot start: hath the daemon spawn no fire?\n"); _exit(111); } ++ do ++ r = read(chanfdin[c],&ch1,1); ++ while ((r == -1) && (errno == error_intr)); ++ if (r < 1) ++ { log1("alert: cannot start: hath the daemon spawn no fire?\n"); _exit(111); } + u = (unsigned int) (unsigned char) ch; ++ u += (unsigned int) ((unsigned char) ch1) << 8; + if (concurrency[c] > u) concurrency[c] = u; + numjobs += concurrency[c]; + } +diff -u qmail-1.03.orig/spawn.c qmail-1.03/spawn.c +--- qmail-1.03.orig/spawn.c Mon Jun 15 03:53:16 1998 ++++ qmail-1.03/spawn.c Tue Jul 27 12:25:14 1999 +@@ -63,7 +63,7 @@ + int flagreading = 1; + char outbuf[1024]; substdio ssout; + +-int stage = 0; /* reading 0:delnum 1:messid 2:sender 3:recip */ ++int stage = 0; /* reading 0:delnum 1:delnum2 2:messid 3:sender 4:recip */ + int flagabort = 0; /* if 1, everything except delnum is garbage */ + int delnum; + stralloc messid = {0}; +@@ -73,6 +73,7 @@ + void err(s) char *s; + { + char ch; ch = delnum; substdio_put(&ssout,&ch,1); ++ ch = delnum >> 8; substdio_put(&ssout,&ch,1); + substdio_puts(&ssout,s); substdio_putflush(&ssout,"",1); + } + +@@ -155,16 +156,19 @@ + { + case 0: + delnum = (unsigned int) (unsigned char) ch; +- messid.len = 0; stage = 1; break; ++ stage = 1; break; + case 1: ++ delnum += (unsigned int) ((unsigned int) ch) << 8; ++ messid.len = 0; stage = 2; break; ++ case 2: + if (!stralloc_append(&messid,&ch)) flagabort = 1; + if (ch) break; +- sender.len = 0; stage = 2; break; +- case 2: ++ sender.len = 0; stage = 3; break; ++ case 3: + if (!stralloc_append(&sender,&ch)) flagabort = 1; + if (ch) break; +- recip.len = 0; stage = 3; break; +- case 3: ++ recip.len = 0; stage = 4; break; ++ case 4: + if (!stralloc_append(&recip,&ch)) flagabort = 1; + if (ch) break; + docmd(); +@@ -201,7 +205,8 @@ + + initialize(argc,argv); + +- ch = auto_spawn; substdio_putflush(&ssout,&ch,1); ++ ch = auto_spawn; substdio_put(&ssout,&ch,1); ++ ch = auto_spawn >> 8; substdio_putflush(&ssout,&ch,1); + + for (i = 0;i < auto_spawn;++i) { d[i].used = 0; d[i].output.s = 0; } + +@@ -236,7 +241,8 @@ + continue; /* read error on a readable pipe? be serious */ + if (r == 0) + { +- ch = i; substdio_put(&ssout,&ch,1); ++ char ch; ch = i; substdio_put(&ssout,&ch,1); ++ ch = i >> 8; substdio_put(&ssout,&ch,1); + report(&ssout,d[i].wstat,d[i].output.s,d[i].output.len); + substdio_put(&ssout,"",1); + substdio_flush(&ssout); + +--7AUc2qLy4jB3hD7Z-- + + +From: "Fred Lindberg" +To: "nelson@crynwr.com" +Cc: "qmail@list.cr.yp.to" +Subject: Solved: qmail-bigrem limits linux +Date: Thu, 12 Aug 1999 13:41:18 -0500 + +Dear Russell, + +Thanks for your input. The limiting factor for my redhat linux 2.2.5 +installation was a per user process limit of 256. The reason I +sometimes got 256 or 257 concurrency is that reporting is not exactly +synchronous with forking. + +Thus, in addition to increasing the number of file handles, you need to +rebuild the kernel after editing: + +/usr/src/linux/include/linux/tasks.h NR_TASKS from 512 to e.g. 2048. +Per-user limit defined to half this on the line below. + +There also appears to be a bug that limits the number of per process +file handles to 1024. There are "unofficial" patches for this, but +AFAIK, not integrated into the official kernel. + +If anyone has more insight into this, I'd love to hear it. With the +patch, our P100/64MB does very well at a concurrencyremote of 400. +Since performance was limited by concurrency (many slow/dead clients) +we got considerably better throughput. + + +-Sincerely, Fred + +(Frederik Lindberg, Infectious Diseases, WashU, St. Louis, MO, USA) + + diff --git a/patches/big-todo.patch b/patches/big-todo.patch new file mode 100644 index 0000000..ee2e2d8 --- /dev/null +++ b/patches/big-todo.patch @@ -0,0 +1,187 @@ +Note to readers: After you apply this patch, your queue will no longer +be readable in its current state. So, either install this into +/var/qmail2 and run both qmails until the queue is emptied on the old +one, or else accept that your current queue will have to be destroyed +and ``rm -rf /var/qmail/queue; make setup''. + + +From: Bruce Guenter +To: Russell Nelson +Subject: Missing piece of your big-todo patch +Date: Tue, 3 Aug 1999 08:12:11 -0600 + +Greetings. + +While testing the performance of a big todo directory, I observed that +qmail-qstat miscounted the number of messages in the todo directory. +I've appended the necessary patch to yours. +-- +Bruce Guenter http://em.ca/~bruceg/ + + +diff -u orig/qmail-clean.c ./qmail-clean.c +--- orig/qmail-clean.c Tue Apr 15 01:05:23 1997 ++++ ./qmail-clean.c Thu Jul 10 16:20:33 1997 +@@ -73,22 +73,26 @@ + if (line.len < 7) { respond("x"); continue; } + if (line.len > 100) { respond("x"); continue; } + if (line.s[line.len - 1]) { respond("x"); continue; } /* impossible */ +- for (i = 5;i < line.len - 1;++i) ++ for (i = line.len - 2;i > 4;--i) ++ { ++ if (line.s[i] == '/') break; + if ((unsigned char) (line.s[i] - '0') > 9) + { respond("x"); continue; } +- if (!scan_ulong(line.s + 5,&id)) { respond("x"); continue; } ++ } ++ if (line.s[i] == '/') ++ if (!scan_ulong(line.s + i + 1,&id)) { respond("x"); continue; } + if (byte_equal(line.s,5,"foop/")) + { + #define U(prefix,flag) fmtqfn(fnbuf,prefix,id,flag); \ + if (unlink(fnbuf) == -1) if (errno != error_noent) { respond("!"); continue; } +- U("intd/",0) ++ U("intd/",1) + U("mess/",1) + respond("+"); + } + else if (byte_equal(line.s,4,"todo/")) + { +- U("intd/",0) +- U("todo/",0) ++ U("intd/",1) ++ U("todo/",1) + respond("+"); + } + else +diff -u orig/hier.c ./hier.c +--- orig/hier.c Tue Apr 15 01:05:23 1997 ++++ ./hier.c Tue Jul 8 13:40:50 1997 +@@ -100,6 +100,8 @@ + substdio_puts(subfdout,"622:/queue/lock/:trigger:\n"); + + dsplit("queue/mess",auto_uidq,0750); ++ dsplit("queue/todo",auto_uidq,0750); ++ dsplit("queue/intd",auto_uidq,0700); + dsplit("queue/info",auto_uids,0700); + dsplit("queue/local",auto_uids,0700); + dsplit("queue/remote",auto_uids,0700); +diff -u orig/qmail-queue.c ./qmail-queue.c +--- orig/qmail-queue.c Tue Apr 15 01:05:23 1997 ++++ ./qmail-queue.c Tue Jul 8 13:33:17 1997 +@@ -180,8 +180,8 @@ + + messnum = pidst.st_ino; + messfn = fnnum("mess/",1); +- todofn = fnnum("todo/",0); +- intdfn = fnnum("intd/",0); ++ todofn = fnnum("todo/",1); ++ intdfn = fnnum("intd/",1); + + if (link(pidfn,messfn) == -1) die(105); + if (unlink(pidfn) == -1) die(105); +diff -u orig/qmail-send.c ./qmail-send.c +--- orig/qmail-send.c Tue Apr 15 01:05:23 1997 ++++ ./qmail-send.c Wed Jul 9 02:04:09 1997 +@@ -101,7 +101,7 @@ + } + + void fnmake_info(id) unsigned long id; { fn.len = fmtqfn(fn.s,"info/",id,1); } +-void fnmake_todo(id) unsigned long id; { fn.len = fmtqfn(fn.s,"todo/",id,0); } ++void fnmake_todo(id) unsigned long id; { fn.len = fmtqfn(fn.s,"todo/",id,1); } + void fnmake_mess(id) unsigned long id; { fn.len = fmtqfn(fn.s,"mess/",id,1); } + void fnmake_foop(id) unsigned long id; { fn.len = fmtqfn(fn.s,"foop/",id,0); } + void fnmake_split(id) unsigned long id; { fn.len = fmtqfn(fn.s,"",id,1); } +@@ -1242,7 +1242,8 @@ + /* this file is too long ---------------------------------------------- TODO */ + + datetime_sec nexttodorun; +-DIR *tododir; /* if 0, have to opendir again */ ++int flagtododir = 0; /* if 0, have to readsubdir_init again */ ++readsubdir todosubdir; + stralloc todoline = {0}; + char todobuf[SUBSTDIO_INSIZE]; + char todobufinfo[512]; +@@ -1250,7 +1251,7 @@ + + void todo_init() + { +- tododir = 0; ++ flagtododir = 0; + nexttodorun = now(); + trigger_set(); + } +@@ -1262,7 +1263,7 @@ + { + if (flagexitasap) return; + trigger_selprep(nfds,rfds); +- if (tododir) *wakeup = 0; ++ if (flagtododir) *wakeup = 0; + if (*wakeup > nexttodorun) *wakeup = nexttodorun; + } + +@@ -1279,8 +1280,7 @@ + char ch; + int match; + unsigned long id; +- unsigned int len; +- direntry *d; ++ int z; + int c; + unsigned long uid; + unsigned long pid; +@@ -1291,32 +1291,26 @@ + + if (flagexitasap) return; + +- if (!tododir) ++ if (!flagtododir) + { + if (!trigger_pulled(rfds)) + if (recent < nexttodorun) + return; + trigger_set(); +- tododir = opendir("todo"); +- if (!tododir) +- { +- pausedir("todo"); +- return; +- } ++ readsubdir_init(&todosubdir, "todo", pausedir); ++ flagtododir = 1; + nexttodorun = recent + SLEEP_TODO; + } + +- d = readdir(tododir); +- if (!d) ++ switch(readsubdir_next(&todosubdir, &id)) + { +- closedir(tododir); +- tododir = 0; +- return; ++ case 1: ++ break; ++ case 0: ++ flagtododir = 0; ++ default: ++ return; + } +- if (str_equal(d->d_name,".")) return; +- if (str_equal(d->d_name,"..")) return; +- len = scan_ulong(d->d_name,&id); +- if (!len || d->d_name[len]) return; + + fnmake_todo(id); + +--- qmail-1.03/qmail-qstat.sh.orig Tue Aug 3 08:06:47 1999 ++++ qmail-1.03/qmail-qstat.sh Tue Aug 3 08:06:38 1999 +@@ -3,7 +3,7 @@ + cd /var/qmail + messdirs=`echo queue/mess/* | wc -w` + messfiles=`find queue/mess/* -print | wc -w` +-tododirs=`echo queue/todo | wc -w` +-todofiles=`find queue/todo -print | wc -w` ++tododirs=`echo queue/todo/* | wc -w` ++todofiles=`find queue/todo/* -print | wc -w` + echo messages in queue: `expr $messfiles - $messdirs` + echo messages in queue but not yet preprocessed: `expr $todofiles - $tododirs` diff --git a/patches/netqmail-spp.patch b/patches/netqmail-spp.patch new file mode 100644 index 0000000..e1bec2d --- /dev/null +++ b/patches/netqmail-spp.patch @@ -0,0 +1,413 @@ +--- Makefile 2004-08-10 11:45:55.000000000 +0200 ++++ Makefile 2005-01-19 12:58:40.000000000 +0100 +@@ -1531,18 +1531,23 @@ + auto_split.h + ./compile qmail-showctl.c + ++qmail-spp.o: \ ++compile qmail-spp.c readwrite.h stralloc.h substdio.h control.h str.h \ ++byte.h env.h exit.h wait.h fork.h fd.h fmt.h getln.h ++ ./compile qmail-spp.c ++ + qmail-smtpd: \ + load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \ + timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ + date822fmt.o now.o qmail.o cdb.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 socket.lib ++fs.a auto_qmail.o str.a qmail-spp.o socket.lib + ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ + timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ + received.o date822fmt.o now.o qmail.o cdb.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 `cat \ +- socket.lib` ++ datetime.a getln.a open.a sig.a case.a qmail-spp.o env.a stralloc.a \ ++ alloc.a substdio.a error.a str.a fs.a auto_qmail.o \ ++ str.a `cat socket.lib` + + qmail-smtpd.0: \ + qmail-smtpd.8 +@@ -1553,7 +1558,7 @@ + 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 \ + 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 ++exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h qmail-spp.h + ./compile qmail-smtpd.c + + qmail-start: \ +--- qmail-smtpd.c 2004-08-10 11:45:55.000000000 +0200 ++++ qmail-smtpd.c 2006-04-01 22:22:59.000000000 +0200 +@@ -23,6 +23,9 @@ + #include "timeoutread.h" + #include "timeoutwrite.h" + #include "commands.h" ++#include "qmail-spp.h" ++ ++int spp_val; + + #define MAXHOPS 100 + unsigned int databytes = 0; +@@ -111,6 +114,7 @@ + if (timeout <= 0) timeout = 1; + + if (rcpthosts_init() == -1) die_control(); ++ if (spp_init() == -1) die_control(); + + bmfok = control_readfile(&bmf,"control/badmailfrom",0); + if (bmfok == -1) die_control(); +@@ -219,27 +223,33 @@ + + int seenmail = 0; + int flagbarf; /* defined if seenmail */ ++int allowed; + stralloc mailfrom = {0}; + stralloc rcptto = {0}; + + void smtp_helo(arg) char *arg; + { ++ if(!spp_helo(arg)) return; + smtp_greet("250 "); out("\r\n"); + seenmail = 0; dohelo(arg); + } + void smtp_ehlo(arg) char *arg; + { ++ if(!spp_helo(arg)) return; + smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); + seenmail = 0; dohelo(arg); + } + void smtp_rset(arg) char *arg; + { ++ spp_rset(); + seenmail = 0; + out("250 flushed\r\n"); + } + void smtp_mail(arg) char *arg; + { + if (!addrparse(arg)) { err_syntax(); return; } ++ if (!(spp_val = spp_mail())) return; ++ if (spp_val == 1) + flagbarf = bmfcheck(); + seenmail = 1; + if (!stralloc_copys(&rcptto,"")) die_nomem(); +@@ -251,13 +261,18 @@ + if (!seenmail) { err_wantmail(); return; } + if (!addrparse(arg)) { err_syntax(); return; } + if (flagbarf) { err_bmf(); return; } ++ if (!relayclient) allowed = addrallowed(); ++ else allowed = 1; ++ if (!(spp_val = spp_rcpt(allowed))) return; + if (relayclient) { + --addr.len; + if (!stralloc_cats(&addr,relayclient)) die_nomem(); + if (!stralloc_0(&addr)) die_nomem(); + } +- else +- if (!addrallowed()) { err_nogateway(); return; } ++ else if (spp_val == 1) { ++ if (!allowed) { err_nogateway(); return; } ++ } ++ spp_rcpt_accepted(); + if (!stralloc_cats(&rcptto,"T")) die_nomem(); + if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); +@@ -372,6 +387,7 @@ + + if (!seenmail) { err_wantmail(); return; } + if (!rcptto.len) { err_wantrcpt(); return; } ++ if (!spp_data()) return; + seenmail = 0; + if (databytes) bytestooverflow = databytes + 1; + if (qmail_open(&qqt) == -1) { err_qqt(); return; } +@@ -379,6 +395,8 @@ + out("354 go ahead\r\n"); + + received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); ++ qmail_put(&qqt,sppheaders.s,sppheaders.len); /* set in qmail-spp.c */ ++ spp_rset(); + blast(&hops); + hops = (hops >= MAXHOPS); + if (hops) qmail_fail(&qqt); +@@ -414,8 +432,10 @@ + if (chdir(auto_qmail) == -1) die_control(); + setup(); + if (ipme_init() != 1) die_ipme(); ++ if (spp_connect()) { + smtp_greet("220 "); + out(" ESMTP\r\n"); ++ } + if (commands(&ssin,&smtpcommands) == 0) die_read(); + die_nomem(); + } +--- qmail-spp.c 1970-01-01 01:00:00.000000000 +0100 ++++ qmail-spp.c 2006-04-01 22:46:52.000000000 +0200 +@@ -0,0 +1,251 @@ ++/* ++ * Copyright (C) 2004-2005 Pawel Foremski ++ * ++ * 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. ++ * ++ *** Note ++ * ++ * This is the core of qmail-spp patch for qmail ++ * ++ * Why I made it a separate file? Because I wanted qmail-spp to apply more ++ * cleanly on heavily patched qmail sources and to make it bit simpler to ++ * maintain, so don't treat it as a library. ++ * ++ * "..." comments marks places where code for other SMTP commands should be ++ * added, if needed. ++ * ++ */ ++ ++#include "readwrite.h" ++#include "stralloc.h" ++#include "substdio.h" ++#include "control.h" ++#include "str.h" ++#include "byte.h" ++#include "env.h" ++#include "exit.h" ++#include "wait.h" ++#include "fork.h" ++#include "fd.h" ++#include "fmt.h" ++#include "getln.h" ++ ++/* stuff needed from qmail-smtpd */ ++extern void flush(); ++extern void out(); ++extern void die_nomem(); ++extern stralloc addr; ++/* *** */ ++ ++stralloc sppheaders = {0}; ++static int spprun = 0; ++static int sppfok = 0; ++static int sppret; ++static stralloc sppf = {0}; ++static stralloc plugins_dummy = {0}, plugins_connect = {0}, plugins_helo = {0}, plugins_mail = {0}, ++ plugins_rcpt = {0}, plugins_data = {0}; /* ... */ ++static stralloc error_mail = {0}, error_rcpt = {0}, error_data = {0}; /* ... */ ++static stralloc sppmsg = {0}; ++static char rcptcountstr[FMT_ULONG]; ++static unsigned long rcptcount; ++static unsigned long rcptcountall; ++static substdio ssdown; ++static char downbuf[128]; ++ ++static void err_spp(s1, s2) char *s1, *s2; { out("451 qmail-spp failure: "); out(s1); out(": "); out(s2); out(" (#4.3.0)\r\n"); } ++ ++int spp_init() ++{ ++ int i, len = 0; ++ stralloc *plugins_to; ++ char *x, *conffile = "control/smtpplugins"; ++ ++ if (!env_get("NOSPP")) { ++ spprun = 1; ++ plugins_to = &plugins_dummy; ++ x = env_get("SPPCONFFILE"); ++ if (x && *x) conffile = x; ++ sppfok = control_readfile(&sppf, conffile, 0); ++ if (sppfok != 1) return -1; ++ for (i = 0; i < sppf.len; i += len) { ++ len = str_len(sppf.s + i) + 1; ++ if (sppf.s[i] == '[') ++ switch (sppf.s[i + 1]) { ++ case 'c': plugins_to = &plugins_connect; break; ++ case 'h': plugins_to = &plugins_helo; break; ++ case 'm': plugins_to = &plugins_mail; break; ++ case 'r': plugins_to = &plugins_rcpt; break; ++ case 'd': plugins_to = &plugins_data; break; ++ /* ... */ ++ default: plugins_to = &plugins_dummy; ++ } ++ else ++ if (!stralloc_catb(plugins_to, sppf.s + i, len)) die_nomem(); ++ } ++ } ++ ++ return 0; ++} ++ ++void sppout() { if (sppmsg.len) out(sppmsg.s); out("\r\n"); } ++ ++int spp(plugins, addrenv) stralloc *plugins; char *addrenv; ++{ ++ static int pipes[2]; ++ static int i, pid, wstat, match, last; ++ static stralloc data = {0}; ++ static char *(args[4]); ++ static stralloc *errors_to; ++ ++ if (!spprun) return 1; ++ if (addrenv) if (!env_put2(addrenv, addr.s)) die_nomem(); ++ last = 0; ++ ++ for (i = 0; i < plugins->len; i += str_len(plugins->s + i) + 1) { ++ if (plugins->s[i] == ':') ++ { args[0] = "/bin/sh"; args[1] = "-c"; args[2] = plugins->s + i + 1; args[3] = 0; } ++ else ++ { args[0] = plugins->s + i; args[1] = 0; } ++ ++ if (pipe(pipes) == -1) ++ { err_spp(plugins->s + i, "can't pipe()"); return 0; } ++ ++ switch (pid = vfork()) { ++ case -1: ++ err_spp(plugins->s + i, "vfork() failed"); ++ return 0; ++ case 0: ++ close(0); close(pipes[0]); fd_move(1, pipes[1]); ++ execv(*args, args); ++ _exit(120); ++ } ++ ++ close(pipes[1]); ++ substdio_fdbuf(&ssdown, read, pipes[0], downbuf, sizeof(downbuf)); ++ do { ++ if (getln(&ssdown, &data, &match, '\n') == -1) die_nomem(); ++ if (data.len > 1) { ++ data.s[data.len - 1] = 0; ++ switch (data.s[0]) { ++ case 'H': ++ if (!stralloc_catb(&sppheaders, data.s + 1, data.len - 2)) die_nomem(); ++ if (!stralloc_append(&sppheaders, "\n")) die_nomem(); ++ break; ++ case 'C': ++ if (addrenv) { ++ if (!stralloc_copyb(&addr, data.s + 1, data.len - 1)) die_nomem(); ++ if (!env_put2(addrenv, addr.s)) die_nomem(); ++ } ++ break; ++ case 'S': if (!env_put(data.s + 1)) die_nomem(); break; ++ case 'U': if (!env_unset(data.s + 1)) die_nomem(); break; ++ case 'A': spprun = 0; ++ case 'O': ++ case 'N': ++ case 'D': last = 1; match = 0; break; ++ case 'E': ++ case 'R': last = 1; match = 0; ++ case 'P': out(data.s + 1); out("\r\n"); break; ++ case 'L': ++ switch (data.s[1]) { ++ case 'M': errors_to = &error_mail; break; ++ case 'R': errors_to = &error_rcpt; break; ++ case 'D': errors_to = &error_data; break; ++ /* ... */ ++ default: errors_to = 0; ++ } ++ if (errors_to) { ++ if (!stralloc_catb(errors_to, data.s + 2, data.len - 3)) die_nomem(); ++ if (!stralloc_catb(errors_to, "\r\n", 2)) die_nomem(); ++ } ++ break; ++ } ++ } ++ } while (match); ++ ++ close(pipes[0]); ++ if (wait_pid(&wstat,pid) == -1) { err_spp(plugins->s + i, "wait_pid() failed"); return 0; } ++ if (wait_crashed(wstat)) { err_spp(plugins->s + i, "child crashed"); return 0; } ++ if (wait_exitcode(wstat) == 120) { err_spp(plugins->s + i, "can't execute"); return 0; } ++ ++ if (last) ++ switch (*data.s) { ++ case 'E': return 0; ++ case 'A': ++ case 'N': return 1; ++ case 'O': return 2; ++ case 'R': ++ case 'D': flush(); _exit(0); ++ } ++ } ++ ++ return 1; ++} ++ ++int spp_errors(errors) stralloc *errors; ++{ ++ if (!errors->len) return 1; ++ if (!stralloc_0(errors)) die_nomem(); ++ out(errors->s); ++ return 0; ++} ++ ++int spp_connect() { return spp(&plugins_connect, 0); } ++ ++int spp_helo(arg) char *arg; ++{ ++ if (!env_put2("SMTPHELOHOST", arg)) die_nomem(); ++ return spp(&plugins_helo, 0); ++} ++ ++void spp_rset() ++{ ++ if (!stralloc_copys(&sppheaders, "")) die_nomem(); ++ if (!stralloc_copys(&error_mail, "")) die_nomem(); ++ if (!stralloc_copys(&error_rcpt, "")) die_nomem(); ++ if (!stralloc_copys(&error_data, "")) die_nomem(); ++ /* ... */ ++ rcptcount = rcptcountall = 0; ++} ++ ++int spp_mail() ++{ ++ if (!spp_errors(&error_mail)) return 0; ++ rcptcount = rcptcountall = 0; ++ return spp(&plugins_mail, "SMTPMAILFROM"); ++} ++ ++int spp_rcpt(allowed) int allowed; ++{ ++ if (!spp_errors(&error_rcpt)) return 0; ++ rcptcountstr[fmt_ulong(rcptcountstr, rcptcount)] = 0; ++ if (!env_put2("SMTPRCPTCOUNT", rcptcountstr)) die_nomem(); ++ rcptcountstr[fmt_ulong(rcptcountstr, ++rcptcountall)] = 0; ++ if (!env_put2("SMTPRCPTCOUNTALL", rcptcountstr)) die_nomem(); ++ if (!env_put2("SMTPRCPTHOSTSOK", allowed ? "1" : "0")) die_nomem(); ++ sppret = spp(&plugins_rcpt, "SMTPRCPTTO"); ++ return sppret; ++} ++ ++void spp_rcpt_accepted() { rcptcount++; } ++ ++int spp_data() ++{ ++ if (!spp_errors(&error_data)) return 0; ++ return spp(&plugins_data, 0); ++} ++ ++/* ... */ +--- qmail-spp.h 1970-01-01 01:00:00.000000000 +0100 ++++ qmail-spp.h 2005-01-19 12:53:44.000000000 +0100 +@@ -0,0 +1,14 @@ ++#ifndef QMAIL_SPP_H ++#define QMAIL_SPP_H ++ ++extern stralloc sppheaders; ++extern int spp_init(); ++extern int spp_connect(); ++extern int spp_helo(); ++extern void spp_rset(); ++extern int spp_mail(); ++extern int spp_rcpt(); ++extern int spp_rcpt_accepted(); ++extern int spp_data(); ++ ++#endif diff --git a/patches/rcptcheck.patch b/patches/rcptcheck.patch new file mode 100644 index 0000000..9e9ba33 --- /dev/null +++ b/patches/rcptcheck.patch @@ -0,0 +1,126 @@ +*** qmail-1.03.orig/qmail-smtpd.c Mon Jun 15 06:53:16 1998 +--- qmail-1.03/qmail-smtpd.c Wed Jul 14 13:48:24 2004 +*************** +*** 23,28 **** +--- 23,29 ---- + #include "timeoutread.h" + #include "timeoutwrite.h" + #include "commands.h" ++ #include "wait.h" + + #define MAXHOPS 100 + unsigned int databytes = 0; +*************** +*** 47,52 **** +--- 48,56 ---- + 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_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); } ++ void die_fork() { out("421 unable to fork (#4.3.0)\r\n"); flush(); _exit(1); } ++ void die_rcpt() { out("421 unable to verify recipient (#4.3.0)\r\n"); flush(); _exit(1); } ++ void die_rcpt2() { out("421 unable to execute recipient check (#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 err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); } +*************** +*** 58,63 **** +--- 62,68 ---- + void err_noop() { out("250 ok\r\n"); } + void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); } + void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); } ++ void err_badrcpt() { out("553 sorry, no mailbox here by that name. (#5.1.1)\r\n"); } + + + stralloc greeting = {0}; +*************** +*** 81,86 **** +--- 86,92 ---- + char *remoteinfo; + char *local; + char *relayclient; ++ static char *rcptcheck[2] = { 0, 0 }; + + stralloc helohost = {0}; + char *fakehelo; /* pointer into helohost, or 0 */ +*************** +*** 131,136 **** +--- 137,143 ---- + if (!remotehost) remotehost = "unknown"; + remoteinfo = env_get("TCPREMOTEINFO"); + relayclient = env_get("RELAYCLIENT"); ++ rcptcheck[0] = env_get("RCPTCHECK"); + dohelo(remotehost); + } + +*************** +*** 216,227 **** + return r; + } + +- + int seenmail = 0; + int flagbarf; /* defined if seenmail */ + stralloc mailfrom = {0}; + stralloc rcptto = {0}; + + void smtp_helo(arg) char *arg; + { + smtp_greet("250 "); out("\r\n"); +--- 223,258 ---- + return r; + } + + int seenmail = 0; + int flagbarf; /* defined if seenmail */ + stralloc mailfrom = {0}; + stralloc rcptto = {0}; + ++ int addrvalid() ++ { ++ int pid; ++ int wstat; ++ ++ if (!rcptcheck[0]) return 1; ++ ++ switch(pid = fork()) { ++ case -1: die_fork(); ++ case 0: ++ if (!env_put2("SENDER",mailfrom.s)) die_nomem(); ++ if (!env_put2("RECIPIENT",addr.s)) die_nomem(); ++ execv(*rcptcheck,rcptcheck); ++ _exit(120); ++ } ++ if (wait_pid(&wstat,pid) == -1) die_rcpt2(); ++ if (wait_crashed(wstat)) die_rcpt2(); ++ switch(wait_exitcode(wstat)) { ++ case 100: return 0; ++ case 111: die_rcpt(); ++ case 120: die_rcpt2(); ++ } ++ return 1; ++ } ++ + void smtp_helo(arg) char *arg; + { + smtp_greet("250 "); out("\r\n"); +*************** +*** 256,263 **** + if (!stralloc_cats(&addr,relayclient)) die_nomem(); + if (!stralloc_0(&addr)) die_nomem(); + } +! else + if (!addrallowed()) { err_nogateway(); return; } + if (!stralloc_cats(&rcptto,"T")) die_nomem(); + if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); +--- 287,296 ---- + if (!stralloc_cats(&addr,relayclient)) die_nomem(); + if (!stralloc_0(&addr)) die_nomem(); + } +! else { + if (!addrallowed()) { err_nogateway(); return; } ++ if (!addrvalid()) { err_badrcpt(); return; } ++ } + if (!stralloc_cats(&rcptto,"T")) die_nomem(); + if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); diff --git a/patches/smtp-auth.patch b/patches/smtp-auth.patch new file mode 100644 index 0000000000000000000000000000000000000000..f1e97f2754dab5c4357a702eb70934de79919af5 GIT binary patch literal 30720 zcmeHPeP0tvw%&i~PfJ9c+Kbo}o3c}`Vz zcS1mLbnf2!!`}?)bXA=?b?Us=scdb(*gUAYlVtFh@Aj|WsMlB8?Z4FV@4_#}Bc=ZGe&qVY-22(bN$f?l@BSZrKOA^U zmR^lV$+)N1!3k>YCf$J=g+0ITd$CH;rymZ7;h7(tsK4*5Z@*BDTD_(E{?Ln89cN7) zuJ1qFJUqbDj{DBzdpzdg*19v_A3Dx@ujlnr828n&8+$8l^h_TIjnY* zPj|;mD(tI+ox?q$M#=equ+5h)!ERSg_WaUrf2iRL^51;WSjpsnWd-uzY~$lr{{Ilw zuQ#|_t)?Pd3nTwTsm7y653BW+YNO+n%jL9H>3>b=>ni_iIUHN7E!UQ{pi?C*TYg&D zff)U55~xN;HJYoP)@ox#APY>4%Er{{?cBJn$X6R)S1F8ZuXY};)>jnfY&k#uq^gb9 zN@Ycr`O&DWpMG*wHyn+jZ4~rCctjoTLV>7-+VcDcr);!LxT@YbW!uTB9;<@}Do!G( zm#|yI69IH><6O;;p!)w5mF*TW;Q=(&ma zu-WX75_}KBGyD})@Y(Gh*Id=_;m?`tC-`7zPK?JBFBt~-1c-3#@hSFC@V5&EjSnw4 z@&=BmQc$PIdYG&GDn*AQdj>!}JlEaWJABeHVr9MWEkt{~gO7ioKubn4$^ zG;sIV3{L58p{Rvy2mb9SCr|uZo!l;h+3B<@56H%y2lylxr`#|w41!MzsosAP4PoFx z#@U6L;hH~l&>dVa5VM8g2(V21|qnMk^mv@x&c0p&mmoykxV@h zrXGlp3^Gw55>@X5zy07rqlGx30XAyg+W&9536}uV`=1x;%=7hv#D^+@ zL+q?w>!0H|&;2+Fqw`fd2lCHx4hMd0LJB-k!SocIgP)_1=kdn`uJ(p|9EE4`JAX0G zBT}VrU#-_#4Kvy~8~BJz{3P~}+2|}v z!~ttUF!Hu4Z;K}$!jnL~^UjY$H|mvCRV5!(5~>~lou{6;5pWBGS{{`q;ut&mV!PQ; zlfXTO4}(FpS2XC8u zZKxIud<+dt$mgm%3`u^$aUaA4#_Gj69Ed3d8QEC`kKL7a&m%pHCDliaJc&HeLiDDx z)KS>jAWWb|xex>Ym!aG9M}DOK<@emE2Ie^E!tPntm|~20G9HIfLOzL-;knx1da9b8 zPFocu4mJs1_tJw2a)gVd6 ztINx0XJ@s(H-Mt9^*rr}F89LjWSJo_nXWcSMnfW;k0~wQDPJ$CR{?Ve43Fb*5&=KZ zh2ONAmJoRmjy%=#;XZ^(bY63o)IOZz+VWKVinluqw$Ej>bxP^5zyYKDslweJqUyj) zDGpW*)j4KWf#<>FAz-gMLh!9;7jk>aWNgvqVpJIQJRlrWc;?ADA*z9UD!j-(u3Fq1 zPEJmQKi5H;jNU|fGV~Umz4aY}Jodm7L+<|Zd0vbkQ>eHX!*j=3SUzE3ZegFdJ44~?T5MXj$1d4*kqcBV=Y8t=*?=@~C zw})+lLVJf@I2?H7Vh$Jju@B%6C+Oq$de9+EH-HbhoAfRN7kgoLmrJQlQIk71NkBC8 zrbtjV01;9nIPoe|Sc}?X$Hk;OMu6cC37_gEO>}VYo(^0Ljn!F{)SxQoXi5p#?d}?; zyyC+FhpeB5K8VW6?V>ob<~m@jRivk|qhzS32wwbXMB;2f#Xa#N1R9buw<@6-#BD~eJw5Y7fRidsvQ4|P%#JTaK)&GUnSe++^nvos7(PWXxW4h}aq_xCw? z3z`TlH93iisSUT-A|m6X5xP1I?53Gnpru9mVNA;OF>`%*K~>0ksL7446(yQ_c+jDX z+=R^t887ub?DwJK18B{sA#j8gW%l6#wk-Ekm()+)gqJ$b z3lRcMLZ(R^3Esji!Q-ftegqSbmeF}vQX6=T;lGSU5uxGFAnUj*}F8!H~rS;`KI48H(GEU*jRX3bblhXbiC4b zhvR{()*Ph@>rb9;Y;HY!zWukKU+nC@-240f!Qrc4{_*QS|K%Qc;ozPO{NLUUM?pA# zAH~V!^z6g=M;r#U+MSgL4l& zt0i=QtS}=T&tL+d39iq`50E|WafVM=Cnp)JHC2CXz5tHOfV267ZSpJXH$LkbioYXDoqw@>(Azq?~`J3@4I9v6XNrBEo93{5}*0#){_AoZC55ls)h zq942-RvT{#Skts*V2@R~tVa&3RX)g|0P6x8Wi>9TW!2_;Oj!L9o-SJgwWL~%fyZni z${BhRfeLzd#4M6AKu8f-+#dOe=U-v`X50SKd2vmP%9-YvzYP(wyk7fR0TvC zJD~}rls81MG&P4iGsnl9&jAo+)m*t^lKRXfk8VB*K$KOddC4T;ipBEfKv$ac3BX~? zR}5=iK5XrhVS0q{pi=ScCO9^?ITV!=w(UN%2BpX9gZdF0{ZJ2zUz5MD-@AU3-m1K& zT42h64KJJl(u*_sd`PVuU&1K(fw36h z&YE3jYwT3Z$b>GbHk4ia%Vq}{f_(MIG&=?Wo+;_p^1!2!7E)SknPa{I_(nEH{g>^- z_ixpzb_qZa)3!lxk&KJ*j{(2Y5%^7k-zEH&3-FsXwKs;}1@skqJe0O6 z-y#N&TjwC}&3wKM<5F+_<`35Ezxl}PkN86TS8ue;`5){5ZsWh}6ZRYQyzBP^L|*F2 z`oZQ(`{;QVVA&UrheMYrfGFofC-{rg?~OUZd*`l)Y;6BlEWg3_e0LC2=TPzYDFUYa zKj?HWtN-nEPz#*a|LXd`2c6sczwf^HoZ#*qId+Q&&Rys3)IrtytHbB2_-ePby?b!D z{^Er?kZM{wZ&RhWC5*e`-0AWrnQ;&4SR6^O>frm3$8Z82hwwbuvEO)Rl|TENIf~q4 z!Lo+uMmVa2!iov9I$_jjfB?}26XdI3_P12gRtMm`35S($KMwoVyf7ZzSwj7+x+y@)p|;RTC*$!y`|3H8bF z3E>3EVsSL+j>khDf5nq-*Nfvml2PZxp@BXxQOSv<)X-FGOQE);too>IK*6)XkQdP6 zk=teV3L&hn?KX$ma%#*|a|2@hVCc(>t z0%v%(g{u~8QeHs1m@|(xzSS2nob9cHpEo!6fCipEJNuh2)=(O&?y7@9I6;LvnAGp- z19+qzO);mf+o!Y+ar`=Tp1j(Av9ZP@%K9Tzm$X)zI6ij}AH7!hAgbPbaM4@j(I8U+ zPUKN45vqb=Tpo)~$r-`k1175YWa0yddjh?rP+@qFBt)3;Ljg-pJWo|!b_$l&xsXTjih=td|>C2Hhj zbx+{cYPHMe#XwnYHO4IL{5f0i!oos#oW3^AHCf^OMF5yVqgwqj9f(vbMGu>h2XO^L z45l(r7A+=`wM5=2NGoa$RRd3bAAe>Rq0eZimZCTHlVQlus(O-ebIslY^Bn3T^6CEC ziWN;!LnUt3K{#|#vV_9!D+#=?FqTUqRX?SGeqoo!s*Q_$A&WGmxEXHOMaa%?_eEoM zIW|J+>D{?=XJ$=vx2b8isTZOk00L%~^At&VQkexsBM7VLc(G=&I~wD@jAoit$9!h# z(K1(e8TU|(z_Zm;l`Ga<0*bRRE@NOV;b1T$>U}Bm0p5u^jf~c$rZthL6sUbJ52)Z~ps|)JX zS6j`jE~qINPw=<-Nba4`W2Zv*y&>yjaY>@~B)>$bnid;xvbeYc*OZl?)ZVly51(uw z?riUDip}2J-rIb!y}P~p%v9n6V)=UGD+>PcdPQX*Dgsihp)TtsFsD3&UKu6c%0);w zP87W9?OlL)vCa>M#Hps&DroJd}esmOa_!OrEt582sKE6feVF+gL%L8FsfE6dYP+@`o`BMomIMyG1uTHEtws_w ztZh%N*eU0T!COFv5>6TZO^}P1c_ArR(Oh9k1k6;d!!1NCEGkJ0Ct#MiS^IfrB5}Qb z-$Q*FLa%d_nI{sc5O3U*^P@1J6R1%2>~l?sNZ>-O|MTgK?akdo+0lE4PxtmWcU~TD zZtrfrbjn3H036iAOK^Yc4p9P)cyfsH+MYV|aXp*BbC~0`-MSKqG0pAeGt|NWz;D2D zb|fpy-6ZiwU^S2)cFJ6HLvjHDe(=$YcqM^UaLO7J`bl#L9!7k*pCh&+Z>!qDb9K1C zzI(8@zQ4Kq^q&`mk%9H8$I5@MN_Zyl&W0>?mlj+>0g{LdaitF(UhQuGii+*o4*#jv z^-odZe`u1tqV4{#*#9Pud}i~%E#xh3>wo{y#$I#qto`qP+5Y2hw*0$+{qOz}``=wS zK)|#<#;(?e$_6gN9-T)0b6mmt#6$36q1kD|*uWu(dBKRkT>?L5k>!ZdIqIE}D~yvj z(VL)vR2mM_Qz-A>CmE1-r=jp7z1C&#o8<=5mf~I)=I^3hu$Ld(R$(}fD`|bD*e2;E zb`3UJ?yAvJr8n6uWK)oW+C*TNPJ#T>yLmHcpvYC=5(!QRkfNX~^c53~{V}hx85*{6 zzf3P7yMV`w*^1r1b_(ICtLKm|Uo}a(jU;MrGdlP&7F^w0#HA*C({I{ADBkVVI=D$_ z=?|h$!6E}?ND~}xM#;|gCdkU32#0~U+#tqb0ZsB8h?Y}=kR$Wp+8P$!RpxA%2cugA z&qaf2q=?OD48n7dqa5pYpaKLx?*sftT$LiXb#ZW@LE?nr3n2Xf=M{Mz8(8BI95@09 zfh;`?g6&J7O%ai|BXba29f5;&np?CYX9mFswQS9hPh3o}6~@Ss>ty$u`c(gnLKv;( znvZzDzS)LiXo+I5Qq9YAN8Vr<7Lg_|nNlk}V&o^Midspw3s7o}dX*YMR0w&2FR}o| zyh%eK!!1sEJ`}#c8dB8;Qz<0W8?fBKW)xEpKtwzG67uMXCk+v@(USDB)6N@{>ncSE zuvEDE8IZq4$09F_p9a#j0)?iC7{PllmW14`M{U}Za=X#4G+SB|=N1bZ%2=n2N^rSU zEFl%r=1Cah9vcMa+UHZ|9wrZ3P(Kd)BE@o$lJ^tu_14be(W@6P5q_3c{+Sh0Wm9RN zat^r`L5t=aEF8FL9+u>0E*_JKW=AuI$i~WO1Ra3K23#8?avi-pESc$V45*o1qbtiNi}0 zP5qMfJK}*t(TELp7TEAd9rRS}##gEM^?T4o6hLl$Me7+s^n$K23JTrzTx8-D41FaF zgxg(8&GF&G^9uC%62G^rv!*Kq+|^?Mq)+n)JrfwSjYCF8)EHDsY>Q zKwkk^(K?I@d0KN=-AcG3Yji*@T=9KzRHc6Tb$8eubq2@(c(r02P-d(OeZgQ^Z}A9L0C7UjO~j35YTDeTF70uE-F46^jqq^cU5%2X2_O~Y3RD~o zk%TeI-ZomSEzWdi=#1Xkty{h*AsKZxJMyBi55tULyjxl1aXi6)Qg>gdF2TCwV#^|DEv5!vxN^+W-eT4 zWbV^lFcu`gM%zDI0mM%FPSavW`!h#$?SGU%T0#8EMoK$TKZb&|DcY&gj%AuMVipAy zww5y6!OK!70nRq$Z=shYODuU< zE=BwXn?Zt8fG&b3m*V4#)vR3dn$jKkR=k-kIAr2LM2XpY!j+PI_Y|2_*+I-G*c3)*#IZ?*GWO9t{K@+zM-hphM6=P{h1zB$+wszFhceAaZ6?}or+awrK)|n0+VA!sZ6XCwp51mP5 zSeC$&Z2Up(Gjz{&ArIac3bINngvcA=ri&j@`6J|vP%n>e!BLi5NDsr(_ zZ7Ri=8-r{^b9M+kBs<%Ta^qsBrqsi8bv6YIPS2<>p86Zj-lF ztPt6$ES)*6kT6RbSxJ(^AWam}J9Z1WvDySsJy1J9pZ>uk1fDghXD`*uU9FO7b*MP) zS+<-3*)K*h0&}P|{%&ZYqR)uItc{4pDV zLDio-ilLnrNAu(ZYgj}fm;gPQs9;Xw-mUgz%2!CDL@MR5B8}u-2eap literal 0 HcmV?d00001 diff --git a/patches/smtp-tls.patch b/patches/smtp-tls.patch new file mode 100644 index 0000000..8315040 --- /dev/null +++ b/patches/smtp-tls.patch @@ -0,0 +1,1676 @@ +Frederik Vermeulen 20200107 +http://inoa.net/qmail-tls/ + +This patch implements RFC 3207 in qmail. +This means you can get SSL or TLS encrypted and +authenticated SMTP between the MTAs and from MUA to MTA. +The code is considered experimental (but has worked for +many since its first release on 1999-03-21). + +Usage: - install OpenSSL-1.1.0 http://www.openssl.org/ or later + (any version since 0.9.8 is presumed to work) + - apply patch to netqmail-1.06 http://www.usenix.org.uk/mirrors/qmail/netqmail + The patches to qmail-remote.c and qmail-smtpd.c can be applied + separately. + - provide a server certificate in /var/qmail/control/servercert.pem. + "make cert" makes a self-signed certificate. + "make cert-req" makes a certificate request. + Note: you can add the CA certificate and intermediate + certs to the end of servercert.pem. + - replace qmail-smtpd and/or qmail-remote binary + - verify operation (header information should show + something like + "Received [..] with (DHE-RSA-AES256-SHA encrypted) SMTP;") + +Optional: - when DEBUG is defined, some extra TLS info will be logged + - qmail-remote will authenticate with the certificate in + /var/qmail/control/clientcert.pem. By preference this is + the same as servercert.pem, where nsCertType should be + == server,client or be a generic certificate (no usage specified). + - when a 2048 bit RSA key is provided in /var/qmail/control/rsa2048.pem, + this key will be used instead of (slow) on-the-fly generation by + qmail-smtpd. Idem for 2048 DH param in control/dh2048.pem. + `make tmprsadh` does this. + Periodical replacement can be done by crontab: + 01 01 * * * /var/qmail/bin/update_tmprsadh > /dev/null 2>&1 + - server authentication: + qmail-remote requires authentication from servers for which + /var/qmail/control/tlshosts/host.dom.ain.pem exists. + The .pem file contains the validating CA certificates. + One of the dNSName or the CommonName attributes have to match. + WARNING: this option may cause mail to be delayed, bounced, + doublebounced, and lost. + If /var/qmail/control/tlshosts/exhaustivelist is present, + the lists of hosts in /var/qmail/control/tlshosts is + an exhaustive list of hosts TLS is tried on. + If /var/qmail/control/notlshosts/host.dom.ain is present, + no TLS is tried on this host. + - client authentication: + when relay rules would reject an incoming mail, + qmail-smtpd can allow the mail based on a presented cert. + Certs are verified against a CA list in + /var/qmail/control/clientca.pem (eg. from + http://curl.haxx.se/ca/cacert.pem) + and the cert email-address has to match a line in + /var/qmail/control/tlsclients. This email-address is logged + in the headers. CRLs can be provided through + /var/qmail/control/clientcrl.pem. + - cipher selection: + qmail-remote: + openssl cipher string (`man ciphers`) read from + /var/qmail/control/tlsclientciphers + qmail-smtpd: + openssl cipher string read from TLSCIPHERS environment variable + (can vary based on client IP address e.g.) + or if that is not available /var/qmail/control/tlsserverciphers + - smtps (deprecated SMTP over TLS via port 465): + qmail-remote: when connecting to port 465 + qmail-smtpd: when SMTPS environment variable is not empty + +Caveats: - do a `make clean` after patching + - binaries dynamically linked with current openssl versions need + recompilation when the shared openssl libs are upgraded. + - this patch could conflict with other patches (notably those + replacing \n with \r\n, which is a bad idea on encrypted links). + - needs working /dev/urandom (or EGD for openssl versions >0.9.7) + for seeding random number generator. + - packagers should make sure that installing without a valid + servercert is impossible + - when applied in combination with AUTH patch, AUTH patch + should be applied first and first part of this patch + will fail. This error can be ignored. Packagers should + cut the first 12 lines of this patch to make a happy + patch + - `make tmprsadh` is recommended (or should I say required), + otherwise DH generation can be unpredictably slow + - some need "-I/usr/kerberos/include" to be added in conf-cc + +Copyright: GPL + Links with OpenSSL + Inspiration and code from examples in SSLeay (E. Young + and T. Hudson ), + stunnel (M. Trojnara ), + Postfix/TLS (L. Jaenicke ), + modssl (R. Engelschall ), + openssl examples of E. Rescorla . + +Bug reports: mailto: + + +>----< Cut the next 12 lines if applying over AUTH server patch >---< +--- qmail-1.03/qmail-smtpd.c Mon Jun 15 03:53:16 1998 ++++ qmail-1.03-tls/qmail-smtpd.c Tue Jun 18 09:49:38 2002 +@@ -229,7 +229,8 @@ + } + void smtp_ehlo(arg) char *arg; + { +- smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); ++ smtp_greet("250-"); ++ out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); + seenmail = 0; dohelo(arg); + } + void smtp_rset() +>----< Cut previous 12 lines if applying over AUTH server patch >---< + + + +>----< The next 89 lines are the qmail-remote EHLO patch >---< +--- qmail-1.03/qmail-remote.c Mon Jun 15 03:53:16 1998 ++++ qmail-1.03-tls/qmail-remote.c Sun Nov 24 13:05:20 2002 +@@ -163,6 +163,59 @@ unsigned long smtpcode() + return code; + } + ++#ifdef EHLO ++saa ehlokw = {0}; /* list of EHLO keywords and parameters */ ++int maxehlokwlen = 0; ++ ++unsigned long ehlo() ++{ ++ stralloc *sa; ++ char *s, *e, *p; ++ unsigned long code; ++ ++ if (ehlokw.len > maxehlokwlen) maxehlokwlen = ehlokw.len; ++ ehlokw.len = 0; ++ ++# ifdef MXPS ++ if (type == 's') return 0; ++# endif ++ ++ substdio_puts(&smtpto, "EHLO "); ++ substdio_put(&smtpto, helohost.s, helohost.len); ++ substdio_puts(&smtpto, "\r\n"); ++ substdio_flush(&smtpto); ++ ++ code = smtpcode(); ++ if (code != 250) return code; ++ ++ s = smtptext.s; ++ while (*s++ != '\n') ; /* skip the first line: contains the domain */ ++ ++ e = smtptext.s + smtptext.len - 6; /* 250-?\n */ ++ while (s <= e) ++ { ++ if (!saa_readyplus(&ehlokw, 1)) temp_nomem(); ++ sa = ehlokw.sa + ehlokw.len++; ++ if (ehlokw.len > maxehlokwlen) *sa = sauninit; else sa->len = 0; ++ ++ /* smtptext is known to end in a '\n' */ ++ for (p = (s += 4); ; ++p) ++ if (*p == '\n' || *p == ' ' || *p == '\t') { ++ if (!stralloc_catb(sa, s, p - s) || !stralloc_0(sa)) temp_nomem(); ++ if (*p++ == '\n') break; ++ while (*p == ' ' || *p == '\t') ; ++ s = p; ++ } ++ s = p; ++ /* keyword should consist of alpha-num and '-' ++ * broken AUTH might use '=' instead of space */ ++ for (p = sa->s; *p; ++p) if (*p == '=') { *p = 0; break; } ++ } ++ ++ return 250; ++} ++#endif ++ + void outsmtptext() + { + int i; +@@ -224,12 +277,26 @@ void smtp() + + if (smtpcode() != 220) quit("ZConnected to "," but greeting failed"); + ++#ifdef EHLO ++ code = ehlo(); ++ ++ if (code == 250) { ++ /* add EHLO response checks here */ ++ ++ /* and if EHLO failed, use HELO */ ++ } else { ++#endif ++ + substdio_puts(&smtpto,"HELO "); + substdio_put(&smtpto,helohost.s,helohost.len); + substdio_puts(&smtpto,"\r\n"); + substdio_flush(&smtpto); + if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected"); + ++#ifdef EHLO ++ } ++#endif ++ + substdio_puts(&smtpto,"MAIL FROM:<"); + substdio_put(&smtpto,sender.s,sender.len); + substdio_puts(&smtpto,">\r\n"); +>----< Previous 89 lines are the qmail-remote EHLO patch >---< + + + +--- qmail-1.03/qmail-smtpd.c Mon Jun 15 03:53:16 1998 ++++ qmail-1.03-tls/qmail-smtpd.c Mon Jul 1 10:47:54 2002 +@@ -227,6 +227,7 @@ void smtp_helo(arg) char *arg; + smtp_greet("250 "); out("\r\n"); + seenmail = 0; dohelo(arg); + } ++/* ESMTP extensions are published here */ + void smtp_ehlo(arg) char *arg; + { + smtp_greet("250-"); +@@ -231,2 +232,5 @@ void smtp_ehlo(arg) char *arg; + { + smtp_greet("250-"); ++#ifdef TLS ++ if (!ssl) out("\r\n250-STARTTLS"); ++#endif +--- netqmail-1.06-orig/qmail-smtpd.c 2020-01-07 10:27:08.927951519 +0000 ++++ netqmail-1.06/qmail-smtpd.c 2019-05-17 12:38:07.450322978 +0000 +@@ -28,9 +28,27 @@ + unsigned int databytes = 0; + int timeout = 1200; + ++static const char *protocol = "SMTP"; ++ ++#ifdef TLS ++#include ++#include "tls.h" ++#include "ssl_timeoutio.h" ++ ++void tls_init(); ++int tls_verify(); ++void tls_nogateway(); ++int ssl_rfd = -1, ssl_wfd = -1; /* SSL_get_Xfd() are broken */ ++#endif ++ + int safewrite(fd,buf,len) int fd; char *buf; int len; + { + int r; ++#ifdef TLS ++ if (ssl && fd == ssl_wfd) ++ r = ssl_timeoutwrite(timeout, ssl_rfd, ssl_wfd, ssl, buf, len); ++ else ++#endif + r = timeoutwrite(timeout,fd,buf,len); + if (r <= 0) _exit(1); + return r; +@@ -50,7 +68,16 @@ void die_ipme() { out("421 unable to fig + void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); } + + void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); } ++#ifndef TLS + void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } ++#else ++void err_nogateway() ++{ ++ out("553 sorry, that domain isn't in my list of allowed rcpthosts"); ++ tls_nogateway(); ++ out(" (#5.7.1)\r\n"); ++} ++#endif + void err_unimpl(arg) char *arg; { out("502 unimplemented (#5.5.1)\r\n"); } + void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); } + void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); } +@@ -131,6 +158,11 @@ void setup() + if (!remotehost) remotehost = "unknown"; + remoteinfo = env_get("TCPREMOTEINFO"); + relayclient = env_get("RELAYCLIENT"); ++ ++#ifdef TLS ++ if (env_get("SMTPS")) { smtps = 1; tls_init(); } ++ else ++#endif + dohelo(remotehost); + } + +@@ -213,6 +245,9 @@ int addrallowed() + int r; + r = rcpthosts(addr.s,str_len(addr.s)); + if (r == -1) die_control(); ++#ifdef TLS ++ if (r == 0) if (tls_verify()) r = -2; ++#endif + return r; + } + +@@ -230,9 +265,13 @@ void smtp_helo(arg) char *arg; + /* ESMTP extensions are published here */ + void smtp_ehlo(arg) char *arg; + { ++#ifdef TLS ++ struct stat st; ++#endif + smtp_greet("250-"); + #ifdef TLS +- if (!ssl) out("\r\n250-STARTTLS"); ++ if (!ssl && (stat("control/servercert.pem",&st) == 0)) ++ out("\r\n250-STARTTLS"); + #endif + out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); + seenmail = 0; dohelo(arg); +@@ -274,6 +313,11 @@ int saferead(fd,buf,len) int fd; char *b + { + int r; + flush(); ++#ifdef TLS ++ if (ssl && fd == ssl_rfd) ++ r = ssl_timeoutread(timeout, ssl_rfd, ssl_wfd, ssl, buf, len); ++ else ++#endif + r = timeoutread(timeout,fd,buf,len); + if (r == -1) if (errno == error_timeout) die_alarm(); + if (r <= 0) die_read(); +@@ -282,6 +326,9 @@ int saferead(fd,buf,len) int fd; char *b + + char ssinbuf[1024]; + substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf); ++#ifdef TLS ++void flush_io() { ssin.p = 0; flush(); } ++#endif + + struct qmail qqt; + unsigned int bytestooverflow = 0; +@@ -383,7 +430,7 @@ void smtp_data(arg) char *arg; { + qp = qmail_qp(&qqt); + out("354 go ahead\r\n"); + +- received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); ++ received(&qqt,protocol,local,remoteip,remotehost,remoteinfo,fakehelo); + blast(&hops); + hops = (hops >= MAXHOPS); + if (hops) qmail_fail(&qqt); +@@ -399,6 +446,271 @@ void smtp_data(arg) char *arg; { + out("\r\n"); + } + ++#ifdef TLS ++stralloc proto = {0}; ++int ssl_verified = 0; ++const char *ssl_verify_err = 0; ++ ++void smtp_tls(char *arg) ++{ ++ if (ssl) err_unimpl(); ++ else if (*arg) out("501 Syntax error (no parameters allowed) (#5.5.4)\r\n"); ++ else tls_init(); ++} ++ ++RSA *tmp_rsa_cb(SSL *ssl, int export, int keylen) ++{ ++ RSA *rsa; ++ ++ if (!export) keylen = 2048; ++ if (keylen == 2048) { ++ FILE *in = fopen("control/rsa2048.pem", "r"); ++ if (in) { ++ rsa = PEM_read_RSAPrivateKey(in, NULL, NULL, NULL); ++ fclose(in); ++ if (rsa) return rsa; ++ } ++ } ++#if OPENSSL_VERSION_NUMBER >= 0x10100000L ++ BIGNUM *e; /*exponent */ ++ e = BN_new(); ++ BN_set_word(e, RSA_F4); ++ if (RSA_generate_key_ex(rsa, keylen, e, NULL) == 1) ++ return rsa; ++ return NULL; ++#else ++ return RSA_generate_key(keylen, RSA_F4, NULL, NULL); ++#endif ++} ++ ++DH *tmp_dh_cb(SSL *ssl, int export, int keylen) ++{ ++ DH *dh; ++ ++ if (!export) keylen = 2048; ++ if (keylen == 2048) { ++ FILE *in = fopen("control/dh2048.pem", "r"); ++ if (in) { ++ dh = PEM_read_DHparams(in, NULL, NULL, NULL); ++ fclose(in); ++ if (dh) return dh; ++ } ++ } ++#if OPENSSL_VERSION_NUMBER >= 0x10100000L ++ if((dh = DH_new()) && (DH_generate_parameters_ex(dh, keylen, DH_GENERATOR_2, NULL) == 1)) ++ return dh; ++ return NULL; ++#else ++ return DH_generate_parameters(keylen, DH_GENERATOR_2, NULL, NULL); ++#endif ++} ++ ++/* don't want to fail handshake if cert isn't verifiable */ ++int verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) { return 1; } ++ ++void tls_nogateway() ++{ ++ /* there may be cases when relayclient is set */ ++ if (!ssl || relayclient) return; ++ out("; no valid cert for gatewaying"); ++ if (ssl_verify_err) { out(": "); out(ssl_verify_err); } ++} ++void tls_out(const char *s1, const char *s2) ++{ ++ out("454 TLS "); out(s1); ++ if (s2) { out(": "); out(s2); } ++ out(" (#4.3.0)\r\n"); flush(); ++} ++void tls_err(const char *s) { tls_out(s, ssl_error()); if (smtps) die_read(); } ++ ++# define CLIENTCA "control/clientca.pem" ++# define CLIENTCRL "control/clientcrl.pem" ++# define SERVERCERT "control/servercert.pem" ++ ++int tls_verify() ++{ ++ stralloc clients = {0}; ++ struct constmap mapclients; ++ ++ if (!ssl || relayclient || ssl_verified) return 0; ++ ssl_verified = 1; /* don't do this twice */ ++ ++ /* request client cert to see if it can be verified by one of our CAs ++ * and the associated email address matches an entry in tlsclients */ ++ switch (control_readfile(&clients, "control/tlsclients", 0)) ++ { ++ case 1: ++ if (constmap_init(&mapclients, clients.s, clients.len, 0)) { ++ /* if CLIENTCA contains all the standard root certificates, a ++ * 0.9.6b client might fail with SSL_R_EXCESSIVE_MESSAGE_SIZE; ++ * it is probably due to 0.9.6b supporting only 8k key exchange ++ * data while the 0.9.6c release increases that limit to 100k */ ++ STACK_OF(X509_NAME) *sk = SSL_load_client_CA_file(CLIENTCA); ++ if (sk) { ++ SSL_set_client_CA_list(ssl, sk); ++ SSL_set_verify(ssl, SSL_VERIFY_PEER, verify_cb); ++ break; ++ } ++ constmap_free(&mapclients); ++ } ++ case 0: alloc_free(clients.s); return 0; ++ case -1: die_control(); ++ } ++ ++ if (ssl_timeoutrehandshake(timeout, ssl_rfd, ssl_wfd, ssl) <= 0) { ++ const char *err = ssl_error_str(); ++ tls_out("rehandshake failed", err); die_read(); ++ } ++ ++ do { /* one iteration */ ++ X509 *peercert; ++ X509_NAME *subj; ++ stralloc email = {0}; ++ ++ int n = SSL_get_verify_result(ssl); ++ if (n != X509_V_OK) ++ { ssl_verify_err = X509_verify_cert_error_string(n); break; } ++ peercert = SSL_get_peer_certificate(ssl); ++ if (!peercert) break; ++ ++ subj = X509_get_subject_name(peercert); ++ n = X509_NAME_get_index_by_NID(subj, NID_pkcs9_emailAddress, -1); ++ if (n >= 0) { ++ const ASN1_STRING *s = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subj, n)); ++ if (s) { email.len = s->length; email.s = s->data; } ++ } ++ ++ if (email.len <= 0) ++ ssl_verify_err = "contains no email address"; ++ else if (!constmap(&mapclients, email.s, email.len)) ++ ssl_verify_err = "email address not in my list of tlsclients"; ++ else { ++ /* add the cert email to the proto if it helped allow relaying */ ++ --proto.len; ++ if (!stralloc_cats(&proto, "\n (cert ") /* continuation line */ ++ || !stralloc_catb(&proto, email.s, email.len) ++ || !stralloc_cats(&proto, ")") ++ || !stralloc_0(&proto)) die_nomem(); ++ protocol = proto.s; ++ relayclient = ""; ++ /* also inform qmail-queue */ ++ if (!env_put("RELAYCLIENT=")) die_nomem(); ++ } ++ ++ X509_free(peercert); ++ } while (0); ++ constmap_free(&mapclients); alloc_free(clients.s); ++ ++ /* we are not going to need this anymore: free the memory */ ++ SSL_set_client_CA_list(ssl, NULL); ++ SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); ++ ++ return relayclient ? 1 : 0; ++} ++ ++void tls_init() ++{ ++ SSL *myssl; ++ SSL_CTX *ctx; ++ const char *ciphers; ++ stralloc saciphers = {0}; ++ X509_STORE *store; ++ X509_LOOKUP *lookup; ++ int session_id_context = 1; /* anything will do */ ++ ++ SSL_library_init(); ++ ++ /* a new SSL context with the bare minimum of options */ ++ ctx = SSL_CTX_new(SSLv23_server_method()); ++ if (!ctx) { tls_err("unable to initialize ctx"); return; } ++ ++ /* POODLE vulnerability */ ++ SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); ++ ++ /* renegotiation should include certificate request */ ++ SSL_CTX_set_options(ctx, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); ++ ++ /* never bother the application with retries if the transport is blocking */ ++ SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); ++ ++ /* relevant in renegotiation */ ++ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); ++ if (!SSL_CTX_set_session_id_context(ctx, (void *)&session_id_context, ++ sizeof(session_id_context))) ++ { SSL_CTX_free(ctx); tls_err("failed to set session_id_context"); return; } ++ ++ if (!SSL_CTX_use_certificate_chain_file(ctx, SERVERCERT)) ++ { SSL_CTX_free(ctx); tls_err("missing certificate"); return; } ++ SSL_CTX_load_verify_locations(ctx, CLIENTCA, NULL); ++ ++ /* crl checking */ ++ store = SSL_CTX_get_cert_store(ctx); ++ if ((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) && ++ (X509_load_crl_file(lookup, CLIENTCRL, X509_FILETYPE_PEM) == 1)) ++ X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | ++ X509_V_FLAG_CRL_CHECK_ALL); ++ ++#if OPENSSL_VERSION_NUMBER >= 0x10100000L ++ /* support ECDH */ ++ SSL_CTX_set_ecdh_auto(ctx,1); ++#endif ++ ++ SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); ++ ++ /* a new SSL object, with the rest added to it directly to avoid copying */ ++ myssl = SSL_new(ctx); ++ SSL_CTX_free(ctx); ++ if (!myssl) { tls_err("unable to initialize ssl"); return; } ++ ++ /* this will also check whether public and private keys match */ ++ if (!SSL_use_RSAPrivateKey_file(myssl, SERVERCERT, SSL_FILETYPE_PEM)) ++ { SSL_free(myssl); tls_err("no valid RSA private key"); return; } ++ ++ ciphers = env_get("TLSCIPHERS"); ++ if (!ciphers) { ++ if (control_readfile(&saciphers, "control/tlsserverciphers", 0) == -1) ++ { SSL_free(myssl); die_control(); } ++ if (saciphers.len) { /* convert all '\0's except the last one to ':' */ ++ int i; ++ for (i = 0; i < saciphers.len - 1; ++i) ++ if (!saciphers.s[i]) saciphers.s[i] = ':'; ++ ciphers = saciphers.s; ++ } ++ } ++ if (!ciphers || !*ciphers) ciphers = "DEFAULT"; ++ SSL_set_cipher_list(myssl, ciphers); ++ alloc_free(saciphers.s); ++ ++ SSL_set_tmp_rsa_callback(myssl, tmp_rsa_cb); ++ SSL_set_tmp_dh_callback(myssl, tmp_dh_cb); ++ SSL_set_rfd(myssl, ssl_rfd = substdio_fileno(&ssin)); ++ SSL_set_wfd(myssl, ssl_wfd = substdio_fileno(&ssout)); ++ ++ if (!smtps) { out("220 ready for tls\r\n"); flush(); } ++ ++ if (ssl_timeoutaccept(timeout, ssl_rfd, ssl_wfd, myssl) <= 0) { ++ /* neither cleartext nor any other response here is part of a standard */ ++ const char *err = ssl_error_str(); ++ tls_out("connection failed", err); ssl_free(myssl); die_read(); ++ } ++ ssl = myssl; ++ ++ /* populate the protocol string, used in Received */ ++ if (!stralloc_copys(&proto, "ESMTPS (") ++ || !stralloc_cats(&proto, SSL_get_cipher(ssl)) ++ || !stralloc_cats(&proto, " encrypted)")) die_nomem(); ++ if (!stralloc_0(&proto)) die_nomem(); ++ protocol = proto.s; ++ ++ /* have to discard the pre-STARTTLS HELO/EHLO argument, if any */ ++ dohelo(remotehost); ++} ++ ++# undef SERVERCERT ++# undef CLIENTCA ++ ++#endif ++ + struct commands smtpcommands[] = { + { "rcpt", smtp_rcpt, 0 } + , { "mail", smtp_mail, 0 } +@@ -408,6 +720,9 @@ struct commands smtpcommands[] = { + , { "ehlo", smtp_ehlo, flush } + , { "rset", smtp_rset, 0 } + , { "help", smtp_help, flush } ++#ifdef TLS ++, { "starttls", smtp_tls, flush_io } ++#endif + , { "noop", err_noop, flush } + , { "vrfy", err_vrfy, flush } + , { 0, err_unimpl, flush } +--- netqmail-1.06-orig/qmail-remote.c 2020-01-07 10:27:08.927951519 +0000 ++++ netqmail-1.06/qmail-remote.c 2019-11-14 09:25:07.561610517 +0000 +@@ -48,6 +48,17 @@ saa reciplist = {0}; + + struct ip_address partner; + ++#ifdef TLS ++# include ++# include "tls.h" ++# include "ssl_timeoutio.h" ++# include ++# define EHLO 1 ++ ++int tls_init(); ++const char *ssl_err_str = 0; ++#endif ++ + void out(s) char *s; { if (substdio_puts(subfdoutsmall,s) == -1) _exit(0); } + void zero() { if (substdio_put(subfdoutsmall,"\0",1) == -1) _exit(0); } + void zerodie() { zero(); substdio_flush(subfdoutsmall); _exit(0); } +@@ -99,6 +110,9 @@ void dropped() { + outhost(); + out(" but connection died. "); + if (flagcritical) out("Possible duplicate! "); ++#ifdef TLS ++ if (ssl_err_str) { out((char *)ssl_err_str); out(" "); } ++#endif + out("(#4.4.2)\n"); + zerodie(); + } +@@ -110,6 +124,12 @@ int timeout = 1200; + int saferead(fd,buf,len) int fd; char *buf; int len; + { + int r; ++#ifdef TLS ++ if (ssl) { ++ r = ssl_timeoutread(timeout, smtpfd, smtpfd, ssl, buf, len); ++ if (r < 0) ssl_err_str = ssl_error_str(); ++ } else ++#endif + r = timeoutread(timeout,smtpfd,buf,len); + if (r <= 0) dropped(); + return r; +@@ -117,6 +137,12 @@ int saferead(fd,buf,len) int fd; char *b + int safewrite(fd,buf,len) int fd; char *buf; int len; + { + int r; ++#ifdef TLS ++ if (ssl) { ++ r = ssl_timeoutwrite(timeout, smtpfd, smtpfd, ssl, buf, len); ++ if (r < 0) ssl_err_str = ssl_error_str(); ++ } else ++#endif + r = timeoutwrite(timeout,smtpfd,buf,len); + if (r <= 0) dropped(); + return r; +@@ -194,19 +220,25 @@ unsigned long ehlo() + e = smtptext.s + smtptext.len - 6; /* 250-?\n */ + while (s <= e) + { ++ int wasspace = 0; ++ + if (!saa_readyplus(&ehlokw, 1)) temp_nomem(); + sa = ehlokw.sa + ehlokw.len++; + if (ehlokw.len > maxehlokwlen) *sa = sauninit; else sa->len = 0; + +- /* smtptext is known to end in a '\n' */ +- for (p = (s += 4); ; ++p) +- if (*p == '\n' || *p == ' ' || *p == '\t') { +- if (!stralloc_catb(sa, s, p - s) || !stralloc_0(sa)) temp_nomem(); +- if (*p++ == '\n') break; +- while (*p == ' ' || *p == '\t') ; +- s = p; +- } +- s = p; ++ /* smtptext is known to end in a '\n' */ ++ for (p = (s += 4); ; ++p) ++ if (*p == '\n' || *p == ' ' || *p == '\t') { ++ if (!wasspace) ++ if (!stralloc_catb(sa, s, p - s) || !stralloc_0(sa)) temp_nomem(); ++ if (*p == '\n') break; ++ wasspace = 1; ++ } else if (wasspace == 1) { ++ wasspace = 0; ++ s = p; ++ } ++ s = ++p; ++ + /* keyword should consist of alpha-num and '-' + * broken AUTH might use '=' instead of space */ + for (p = sa->s; *p; ++p) if (*p == '=') { *p = 0; break; } +@@ -232,6 +264,17 @@ void quit(prepend,append) + char *prepend; + char *append; + { ++#ifdef TLS ++ /* shouldn't talk to the client unless in an appropriate state */ ++#if OPENSSL_VERSION_NUMBER >= 0x10100000L ++ OSSL_HANDSHAKE_STATE state = ssl ? SSL_get_state(ssl) : TLS_ST_BEFORE; ++ if (state & TLS_ST_OK || (!smtps && state & TLS_ST_BEFORE)) ++ ++#else ++ int state = ssl ? ssl->state : SSL_ST_BEFORE; ++ if (state & SSL_ST_OK || (!smtps && state & SSL_ST_BEFORE)) ++#endif ++#endif + substdio_putsflush(&smtpto,"QUIT\r\n"); + /* waiting for remote side is just too ridiculous */ + out(prepend); +@@ -239,6 +282,30 @@ char *append; + out(append); + out(".\n"); + outsmtptext(); ++ ++#if defined(TLS) && defined(DEBUG) ++ if (ssl) { ++ X509 *peercert; ++ ++ out("STARTTLS proto="); out(SSL_get_version(ssl)); ++ out("; cipher="); out(SSL_get_cipher(ssl)); ++ ++ /* we want certificate details */ ++ if (peercert = SSL_get_peer_certificate(ssl)) { ++ char *str; ++ ++ str = X509_NAME_oneline(X509_get_subject_name(peercert), NULL, 0); ++ out("; subject="); out(str); OPENSSL_free(str); ++ ++ str = X509_NAME_oneline(X509_get_issuer_name(peercert), NULL, 0); ++ out("; issuer="); out(str); OPENSSL_free(str); ++ ++ X509_free(peercert); ++ } ++ out(";\n"); ++ } ++#endif ++ + zerodie(); + } + +@@ -267,6 +334,210 @@ void blast() + substdio_flush(&smtpto); + } + ++#ifdef TLS ++char *partner_fqdn = 0; ++ ++# define TLS_QUIT quit(ssl ? "; connected to " : "; connecting to ", "") ++void tls_quit(const char *s1, const char *s2) ++{ ++ out((char *)s1); if (s2) { out(": "); out((char *)s2); } TLS_QUIT; ++} ++# define tls_quit_error(s) tls_quit(s, ssl_error()) ++ ++int match_partner(const char *s, int len) ++{ ++ if (!case_diffb(partner_fqdn, len, s) && !partner_fqdn[len]) return 1; ++ /* we also match if the name is *.domainname */ ++ if (*s == '*') { ++ const char *domain = partner_fqdn + str_chr(partner_fqdn, '.'); ++ if (!case_diffb(domain, --len, ++s) && !domain[len]) return 1; ++ } ++ return 0; ++} ++ ++/* don't want to fail handshake if certificate can't be verified */ ++int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; } ++ ++int tls_init() ++{ ++ int i; ++ SSL *myssl; ++ SSL_CTX *ctx; ++ stralloc saciphers = {0}; ++ const char *ciphers, *servercert = 0; ++ ++ if (partner_fqdn) { ++ struct stat st; ++ stralloc tmp = {0}; ++ if (!stralloc_copys(&tmp, "control/tlshosts/") ++ || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn)) ++ || !stralloc_catb(&tmp, ".pem", 5)) temp_nomem(); ++ if (stat(tmp.s, &st) == 0) ++ servercert = tmp.s; ++ else { ++ if (!stralloc_copys(&tmp, "control/notlshosts/") ++ || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn)+1)) ++ temp_nomem(); ++ if ((stat("control/tlshosts/exhaustivelist", &st) == 0) || ++ (stat(tmp.s, &st) == 0)) { ++ alloc_free(tmp.s); ++ return 0; ++ } ++ alloc_free(tmp.s); ++ } ++ } ++ ++ if (!smtps) { ++ stralloc *sa = ehlokw.sa; ++ unsigned int len = ehlokw.len; ++ /* look for STARTTLS among EHLO keywords */ ++ for ( ; len && case_diffs(sa->s, "STARTTLS"); ++sa, --len) ; ++ if (!len) { ++ if (!servercert) return 0; ++ out("ZNo TLS achieved while "); out((char *)servercert); ++ out(" exists"); smtptext.len = 0; TLS_QUIT; ++ } ++ } ++ ++ SSL_library_init(); ++ ctx = SSL_CTX_new(SSLv23_client_method()); ++ if (!ctx) { ++ if (!smtps && !servercert) return 0; ++ smtptext.len = 0; ++ tls_quit_error("ZTLS error initializing ctx"); ++ } ++ ++ /* POODLE vulnerability */ ++ SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); ++ ++ if (servercert) { ++ if (!SSL_CTX_load_verify_locations(ctx, servercert, NULL)) { ++ SSL_CTX_free(ctx); ++ smtptext.len = 0; ++ out("ZTLS unable to load "); tls_quit_error(servercert); ++ } ++ /* set the callback here; SSL_set_verify didn't work before 0.9.6c */ ++ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb); ++ } ++ ++ /* let the other side complain if it needs a cert and we don't have one */ ++# define CLIENTCERT "control/clientcert.pem" ++ if (SSL_CTX_use_certificate_chain_file(ctx, CLIENTCERT)) ++ SSL_CTX_use_RSAPrivateKey_file(ctx, CLIENTCERT, SSL_FILETYPE_PEM); ++# undef CLIENTCERT ++ ++#if OPENSSL_VERSION_NUMBER >= 0x10101000L ++ SSL_CTX_set_post_handshake_auth(ctx, 1); ++#endif ++ ++ myssl = SSL_new(ctx); ++ SSL_CTX_free(ctx); ++ if (!myssl) { ++ if (!smtps && !servercert) return 0; ++ smtptext.len = 0; ++ tls_quit_error("ZTLS error initializing ssl"); ++ } ++ ++ if (!smtps) substdio_putsflush(&smtpto, "STARTTLS\r\n"); ++ ++ /* while the server is preparing a response, do something else */ ++ if (control_readfile(&saciphers, "control/tlsclientciphers", 0) == -1) ++ { SSL_free(myssl); temp_control(); } ++ if (saciphers.len) { ++ for (i = 0; i < saciphers.len - 1; ++i) ++ if (!saciphers.s[i]) saciphers.s[i] = ':'; ++ ciphers = saciphers.s; ++ } ++ else ciphers = "DEFAULT"; ++ SSL_set_cipher_list(myssl, ciphers); ++ alloc_free(saciphers.s); ++ ++ SSL_set_fd(myssl, smtpfd); ++ ++ /* read the response to STARTTLS */ ++ if (!smtps) { ++ if (smtpcode() != 220) { ++ SSL_free(myssl); ++ if (!servercert) return 0; ++ out("ZSTARTTLS rejected while "); ++ out((char *)servercert); out(" exists"); TLS_QUIT; ++ } ++ smtptext.len = 0; ++ } ++ ++ ssl = myssl; ++ if (ssl_timeoutconn(timeout, smtpfd, smtpfd, ssl) <= 0) ++ tls_quit("ZTLS connect failed", ssl_error_str()); ++ ++ if (servercert) { ++ X509 *peercert; ++ STACK_OF(GENERAL_NAME) *gens; ++ int found_gen_dns = 0; ++ int matched_gen_dns = 0; ++ ++ int r = SSL_get_verify_result(ssl); ++ if (r != X509_V_OK) { ++ out("ZTLS unable to verify server with "); ++ tls_quit(servercert, X509_verify_cert_error_string(r)); ++ } ++ alloc_free(servercert); ++ ++ peercert = SSL_get_peer_certificate(ssl); ++ if (!peercert) { ++ out("ZTLS unable to verify server "); ++ tls_quit(partner_fqdn, "no certificate provided"); ++ } ++ ++ /* RFC 2595 section 2.4: find a matching name ++ * first find a match among alternative names */ ++ gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0); ++ if (gens) { ++ for (i = 0, r = sk_GENERAL_NAME_num(gens); i < r; ++i) ++ { ++ const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i); ++ if (gn->type == GEN_DNS){ ++ found_gen_dns = 1; ++ if (match_partner(gn->d.ia5->data, gn->d.ia5->length)){ ++ matched_gen_dns = 1; ++ break; ++ } ++ } ++ } ++ sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); ++ } ++ ++ /* no SubjectAltName of type DNS found, look up commonName */ ++ if (!found_gen_dns) { ++ stralloc peer = {0}; ++ X509_NAME *subj = X509_get_subject_name(peercert); ++ i = X509_NAME_get_index_by_NID(subj, NID_commonName, -1); ++ if (i >= 0) { ++ const ASN1_STRING *s = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subj, i)); ++ if (s) { peer.len = s->length; peer.s = s->data; } ++ } ++ if (peer.len <= 0) { ++ out("ZTLS unable to verify server "); ++ tls_quit(partner_fqdn, "certificate contains no valid commonName"); ++ } ++ if (!match_partner(peer.s, peer.len)) { ++ out("ZTLS unable to verify server "); out(partner_fqdn); ++ out(": received certificate for "); outsafe(&peer); TLS_QUIT; ++ } ++ } else if (!matched_gen_dns) { ++ out("ZTLS unable to verify server "); ++ tls_quit(partner_fqdn, "certificate contains no matching dNSNnames"); ++ } ++ ++ X509_free(peercert); ++ } ++ ++ if (smtps) if (smtpcode() != 220) ++ quit("ZTLS Connected to "," but greeting failed"); ++ ++ return 1; ++} ++#endif ++ + stralloc recip = {0}; + + void smtp() +@@ -274,12 +545,37 @@ void smtp() + unsigned long code; + int flagbother; + int i; ++ ++#ifndef PORT_SMTP ++ /* the qmtpc patch uses smtp_port and undefines PORT_SMTP */ ++# define port smtp_port ++#endif ++ ++#ifdef TLS ++# ifdef MXPS ++ if (type == 'S') smtps = 1; ++ else if (type != 's') ++# endif ++ if (port == 465) smtps = 1; ++ if (!smtps) ++#endif + + if (smtpcode() != 220) quit("ZConnected to "," but greeting failed"); + + #ifdef EHLO ++# ifdef TLS ++ if (!smtps) ++# endif + code = ehlo(); + ++# ifdef TLS ++ if (tls_init()) ++ /* RFC2487 says we should issue EHLO (even if we might not need ++ * extensions); at the same time, it does not prohibit a server ++ * to reject the EHLO and make us fallback to HELO */ ++ code = ehlo(); ++# endif ++ + if (code == 250) { + /* add EHLO response checks here */ + +@@ -484,6 +780,9 @@ char **argv; + if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) { + tcpto_err(&ip.ix[i].ip,0); + partner = ip.ix[i].ip; ++#ifdef TLS ++ partner_fqdn = ip.ix[i].fqdn; ++#endif + smtp(); /* does not return */ + } + tcpto_err(&ip.ix[i].ip,errno == error_timeout); +--- netqmail-1.06-orig/qmail-smtpd.8 1998-06-15 10:53:16.000000000 +0000 ++++ netqmail-1.06/qmail-smtpd.8 2016-09-18 09:51:27.707704853 +0000 +@@ -14,6 +14,15 @@ must be supplied several environment var + see + .BR tcp-environ(5) . + ++If the environment variable ++.B SMTPS ++is non-empty, ++.B qmail-smtpd ++starts a TLS session (to support the deprecated SMTPS protocol, ++normally on port 465). Otherwise, ++.B qmail-smtpd ++offers the STARTTLS extension to ESMTP. ++ + .B qmail-smtpd + is responsible for counting hops. + It rejects any message with 100 or more +@@ -49,6 +58,19 @@ may be of the form + .BR @\fIhost , + meaning every address at + .IR host . ++ ++.TP 5 ++.I clientca.pem ++A list of Certifying Authority (CA) certificates that are used to verify ++the client-presented certificates during a TLS-encrypted session. ++ ++.TP 5 ++.I clientcrl.pem ++A list of Certificate Revocation Lists (CRLs). If present it ++should contain the CRLs of the CAs in ++.I clientca.pem ++and client certs will be checked for revocation. ++ + .TP 5 + .I databytes + Maximum number of bytes allowed in a message, +@@ -76,6 +98,14 @@ If the environment variable + .B DATABYTES + is set, it overrides + .IR databytes . ++ ++.TP 5 ++.I dh2048.pem ++If these 2048 bit DH parameters are provided, ++.B qmail-smtpd ++will use them for TLS sessions instead of generating one on-the-fly ++(which is very timeconsuming). ++ + .TP 5 + .I localiphost + Replacement host name for local IP addresses. +@@ -151,6 +181,19 @@ may include wildcards: + + Envelope recipient addresses without @ signs are + always allowed through. ++ ++.TP 5 ++.I rsa2048.pem ++If this 2048 bit RSA key is provided, ++.B qmail-smtpd ++will use it for TLS sessions instead of generating one on-the-fly. ++ ++.TP 5 ++.I servercert.pem ++SSL certificate to be presented to clients in TLS-encrypted sessions. ++Should contain both the certificate and the private key. Certifying Authority ++(CA) and intermediate certificates can be added at the end of the file. ++ + .TP 5 + .I smtpgreeting + SMTP greeting message. +@@ -169,6 +212,24 @@ Number of seconds + .B qmail-smtpd + will wait for each new buffer of data from the remote SMTP client. + Default: 1200. ++ ++.TP 5 ++.I tlsclients ++A list of email addresses. When relay rules would reject an incoming message, ++.B qmail-smtpd ++can allow it if the client presents a certificate that can be verified against ++the CA list in ++.I clientca.pem ++and the certificate email address is in ++.IR tlsclients . ++ ++.TP 5 ++.I tlsserverciphers ++A set of OpenSSL cipher strings. Multiple ciphers contained in a ++string should be separated by a colon. If the environment variable ++.B TLSCIPHERS ++is set to such a string, it takes precedence. ++ + .SH "SEE ALSO" + tcp-env(1), + tcp-environ(5), +--- netqmail-1.06-orig/qmail-remote.8 1998-06-15 10:53:16.000000000 +0000 ++++ netqmail-1.06/qmail-remote.8 2015-12-01 15:54:59.029940779 +0000 +@@ -114,6 +114,10 @@ arguments. + always exits zero. + .SH "CONTROL FILES" + .TP 5 ++.I clientcert.pem ++SSL certificate that is used to authenticate with the remote server ++during a TLS session. ++.TP 5 + .I helohost + Current host name, + for use solely in saying hello to the remote SMTP server. +@@ -123,6 +127,16 @@ if that is supplied; + otherwise + .B qmail-remote + refuses to run. ++ ++.TP 5 ++.I notlshosts/ ++.B qmail-remote ++will not try TLS on servers for which this file exists ++.RB ( ++is the fully-qualified domain name of the server). ++.IR (tlshosts/.pem ++takes precedence over this file however). ++ + .TP 5 + .I smtproutes + Artificial SMTP routes. +@@ -156,6 +170,8 @@ may be empty; + this tells + .B qmail-remote + to look up MX records as usual. ++.I port ++value of 465 (deprecated smtps port) causes TLS session to be started. + .I smtproutes + may include wildcards: + +@@ -195,6 +211,33 @@ Number of seconds + .B qmail-remote + will wait for each response from the remote SMTP server. + Default: 1200. ++ ++.TP 5 ++.I tlsclientciphers ++A set of OpenSSL client cipher strings. Multiple ciphers ++contained in a string should be separated by a colon. ++ ++.TP 5 ++.I tlshosts/.pem ++.B qmail-remote ++requires TLS authentication from servers for which this file exists ++.RB ( ++is the fully-qualified domain name of the server). One of the ++.I dNSName ++or the ++.I CommonName ++attributes have to match. The file contains the trusted CA certificates. ++ ++.B WARNING: ++this option may cause mail to be delayed, bounced, doublebounced, or lost. ++ ++.TP 5 ++.I tlshosts/exhaustivelist ++if this file exists ++no TLS will be tried on hosts other than those for which a file ++.B tlshosts/.pem ++exists. ++ + .SH "SEE ALSO" + addresses(5), + envelopes(5), +--- netqmail-1.06-orig/qmail-control.9 1998-06-15 10:53:16.000000000 +0000 ++++ netqmail-1.06/qmail-control.9 2015-12-08 00:33:06.248714330 +0000 +@@ -43,11 +43,14 @@ control default used by + .I badmailfrom \fR(none) \fRqmail-smtpd + .I bouncefrom \fRMAILER-DAEMON \fRqmail-send + .I bouncehost \fIme \fRqmail-send ++.I clientca.pem \fR(none) \fRqmail-smtpd ++.I clientcert.pem \fR(none) \fRqmail-remote + .I concurrencylocal \fR10 \fRqmail-send + .I concurrencyremote \fR20 \fRqmail-send + .I defaultdomain \fIme \fRqmail-inject + .I defaulthost \fIme \fRqmail-inject + .I databytes \fR0 \fRqmail-smtpd ++.I dh2048.pem \fR(none) \fRqmail-smtpd + .I doublebouncehost \fIme \fRqmail-send + .I doublebounceto \fRpostmaster \fRqmail-send + .I envnoathost \fIme \fRqmail-send +@@ -61,11 +64,17 @@ control default used by + .I qmqpservers \fR(none) \fRqmail-qmqpc + .I queuelifetime \fR604800 \fRqmail-send + .I rcpthosts \fR(none) \fRqmail-smtpd ++.I rsa2048.pem \fR(none) \fRqmail-smtpd ++.I servercert.pem \fR(none) \fRqmail-smtpd + .I smtpgreeting \fIme \fRqmail-smtpd + .I smtproutes \fR(none) \fRqmail-remote + .I timeoutconnect \fR60 \fRqmail-remote + .I timeoutremote \fR1200 \fRqmail-remote + .I timeoutsmtpd \fR1200 \fRqmail-smtpd ++.I tlsclients \fR(none) \fRqmail-smtpd ++.I tlsclientciphers \fR(none) \fRqmail-remote ++.I tlshosts/FQDN.pem \fR(none) \fRqmail-remote ++.I tlsserverciphers \fR(none) \fRqmail-smtpd + .I virtualdomains \fR(none) \fRqmail-send + .fi + .RE +--- netqmail-1.06-orig/dns.c 2007-11-30 20:22:54.000000000 +0000 ++++ netqmail-1.06/dns.c 2019-04-08 15:22:04.390598941 +0000 +@@ -267,12 +267,14 @@ stralloc *sa; + int pref; + { + int r; +- struct ip_mx ix; ++ struct ip_mx ix = {0}; + + if (!stralloc_copy(&glue,sa)) return DNS_MEM; + if (!stralloc_0(&glue)) return DNS_MEM; + if (glue.s[0]) { ++#ifndef IX_FQDN + ix.pref = 0; ++#endif + if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) + { + if (!ipalloc_append(ia,&ix)) return DNS_MEM; +@@ -291,9 +293,16 @@ int pref; + ix.ip = ip; + ix.pref = pref; + if (r == DNS_SOFT) return DNS_SOFT; +- if (r == 1) ++ if (r == 1) { ++#ifdef IX_FQDN ++ ix.fqdn = glue.s; ++#endif + if (!ipalloc_append(ia,&ix)) return DNS_MEM; + } ++ } ++#ifdef IX_FQDN ++ glue.s = 0; ++#endif + return 0; + } + +@@ -313,7 +322,7 @@ unsigned long random; + { + int r; + struct mx { stralloc sa; unsigned short p; } *mx; +- struct ip_mx ix; ++ struct ip_mx ix = {0}; + int nummx; + int i; + int j; +@@ -325,7 +334,9 @@ unsigned long random; + if (!stralloc_copy(&glue,sa)) return DNS_MEM; + if (!stralloc_0(&glue)) return DNS_MEM; + if (glue.s[0]) { ++#ifndef IX_FQDN + ix.pref = 0; ++#endif + if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) + { + if (!ipalloc_append(ia,&ix)) return DNS_MEM; +--- netqmail-1.06-orig/hier.c 1998-06-15 10:53:16.000000000 +0000 ++++ netqmail-1.06/hier.c 2015-12-01 15:54:59.033940812 +0000 +@@ -143,6 +143,9 @@ void hier() + c(auto_qmail,"bin","qail",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","elq",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","pinq",auto_uido,auto_gidq,0755); ++#ifdef TLS ++ c(auto_qmail,"bin","update_tmprsadh",auto_uido,auto_gidq,0755); ++#endif + + c(auto_qmail,"man/man5","addresses.5",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat5","addresses.0",auto_uido,auto_gidq,0644); +--- netqmail-1.06-orig/ipalloc.h 1998-06-15 10:53:16.000000000 +0000 ++++ netqmail-1.06/ipalloc.h 2015-12-01 15:54:59.033940812 +0000 +@@ -3,7 +3,15 @@ + + #include "ip.h" + ++#ifdef TLS ++# define IX_FQDN 1 ++#endif ++ ++#ifdef IX_FQDN ++struct ip_mx { struct ip_address ip; int pref; char *fqdn; } ; ++#else + struct ip_mx { struct ip_address ip; int pref; } ; ++#endif + + #include "gen_alloc.h" + +--- netqmail-1.06-orig/tls.c 2020-01-07 10:27:08.931951527 +0000 ++++ netqmail-1.06/tls.c 2019-04-08 15:17:31.924930725 +0000 +@@ -0,0 +1,27 @@ ++#ifdef TLS ++#include "exit.h" ++#include "error.h" ++#include ++#include ++ ++int smtps = 0; ++SSL *ssl = NULL; ++ ++void ssl_free(SSL *myssl) { SSL_shutdown(myssl); SSL_free(myssl); } ++void ssl_exit(int status) { if (ssl) ssl_free(ssl); _exit(status); } ++ ++const char *ssl_error() ++{ ++ int r = ERR_get_error(); ++ if (!r) return NULL; ++ SSL_load_error_strings(); ++ return ERR_error_string(r, NULL); ++} ++const char *ssl_error_str() ++{ ++ const char *err = ssl_error(); ++ if (err) return err; ++ if (!errno) return 0; ++ return (errno == error_timeout) ? "timed out" : error_str(errno); ++} ++#endif +--- netqmail-1.06-orig/tls.h 2020-01-07 10:27:08.931951527 +0000 ++++ netqmail-1.06/tls.h 2015-12-01 15:54:59.033940812 +0000 +@@ -0,0 +1,16 @@ ++#ifndef TLS_H ++#define TLS_H ++ ++#include ++ ++extern int smtps; ++extern SSL *ssl; ++ ++void ssl_free(SSL *myssl); ++void ssl_exit(int status); ++# define _exit ssl_exit ++ ++const char *ssl_error(); ++const char *ssl_error_str(); ++ ++#endif +--- netqmail-1.06-orig/ssl_timeoutio.c 2020-01-07 10:27:08.931951527 +0000 ++++ netqmail-1.06/ssl_timeoutio.c 2020-01-07 10:04:16.529224478 +0000 +@@ -0,0 +1,126 @@ ++#ifdef TLS ++#include "select.h" ++#include "error.h" ++#include "ndelay.h" ++#include "now.h" ++#include "ssl_timeoutio.h" ++ ++int ssl_timeoutio(int (*fun)(), ++ int t, int rfd, int wfd, SSL *ssl, char *buf, int len) ++{ ++ int n; ++ const datetime_sec end = (datetime_sec)t + now(); ++ ++ do { ++ fd_set fds; ++ struct timeval tv; ++ ++ const int r = buf ? fun(ssl, buf, len) : fun(ssl); ++ if (r > 0) return r; ++ ++ t = end - now(); ++ if (t < 0) break; ++ tv.tv_sec = (time_t)t; tv.tv_usec = 0; ++ ++ FD_ZERO(&fds); ++ switch (SSL_get_error(ssl, r)) ++ { ++ default: return r; /* some other error */ ++ case SSL_ERROR_WANT_READ: ++ FD_SET(rfd, &fds); n = select(rfd + 1, &fds, NULL, NULL, &tv); ++ break; ++ case SSL_ERROR_WANT_WRITE: ++ FD_SET(wfd, &fds); n = select(wfd + 1, NULL, &fds, NULL, &tv); ++ break; ++ } ++ ++ /* n is the number of descriptors that changed status */ ++ } while (n > 0); ++ ++ if (n != -1) errno = error_timeout; ++ return -1; ++} ++ ++int ssl_timeoutaccept(int t, int rfd, int wfd, SSL *ssl) ++{ ++ int r; ++ ++ /* if connection is established, keep NDELAY */ ++ if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1; ++ r = ssl_timeoutio(SSL_accept, t, rfd, wfd, ssl, NULL, 0); ++ ++ if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); } ++ else SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); ++ ++ return r; ++} ++ ++int ssl_timeoutconn(int t, int rfd, int wfd, SSL *ssl) ++{ ++ int r; ++ ++ /* if connection is established, keep NDELAY */ ++ if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1; ++ r = ssl_timeoutio(SSL_connect, t, rfd, wfd, ssl, NULL, 0); ++ ++ if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); } ++ else SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); ++ ++ return r; ++} ++ ++int ssl_timeoutrehandshake(int t, int rfd, int wfd, SSL *ssl) ++{ ++ int r=0; ++ ++#if OPENSSL_VERSION_NUMBER >= 0x10101000L ++ if (SSL_version(ssl) >= TLS1_3_VERSION){ ++ if(SSL_verify_client_post_handshake(ssl) != 1) ++ return -EPROTO; ++ } else ++#endif ++ { ++ r = SSL_renegotiate(ssl); ++ if (r<=0) return r; ++ } ++ ++#if OPENSSL_VERSION_NUMBER >= 0x10100000L ++ char buf[1]; /* dummy read buffer */ ++ struct timeval tv; ++ fd_set fds; ++ r = ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0); ++ if (r <=0) return r; ++#if OPENSSL_VERSION_NUMBER >= 0x10101000L ++ if (SSL_version(ssl) >= TLS1_3_VERSION) return r; ++#endif ++ ++ tv.tv_sec = (time_t)t; tv.tv_usec = 0; ++ FD_ZERO(&fds); FD_SET(rfd, &fds); ++ if ((r = select(rfd + 1, &fds, NULL, NULL, &tv)>0) && FD_ISSET(rfd, &fds)){ ++ r = SSL_read(ssl, buf, 1); ++ if (SSL_get_error(ssl, r) == SSL_ERROR_WANT_READ) r = 1; /*ignore */ ++ } ++ if (r <=0) return r; ++#else ++ r = ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0); ++ if (r <= 0 || ssl->type == SSL_ST_CONNECT) return r; ++ ++ /* this is for the server only */ ++ ssl->state = SSL_ST_ACCEPT; ++#endif ++ return ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0); ++} ++ ++int ssl_timeoutread(int t, int rfd, int wfd, SSL *ssl, char *buf, int len) ++{ ++ if (!buf) return 0; ++ if (SSL_pending(ssl)) return SSL_read(ssl, buf, len); ++ return ssl_timeoutio(SSL_read, t, rfd, wfd, ssl, buf, len); ++} ++ ++int ssl_timeoutwrite(int t, int rfd, int wfd, SSL *ssl, char *buf, int len) ++{ ++ if (!buf) return 0; ++ return ssl_timeoutio(SSL_write, t, rfd, wfd, ssl, buf, len); ++} ++#endif +--- netqmail-1.06-orig/ssl_timeoutio.h 2020-01-07 10:27:08.931951527 +0000 ++++ netqmail-1.06/ssl_timeoutio.h 2019-03-22 21:11:16.610440636 +0000 +@@ -0,0 +1,21 @@ ++#ifndef SSL_TIMEOUTIO_H ++#define SSL_TIMEOUTIO_H ++ ++#include ++ ++/* the version is like this: 0xMNNFFPPS: major minor fix patch status */ ++#if OPENSSL_VERSION_NUMBER < 0x00908000L ++# error "Need OpenSSL version at least 0.9.8" ++#endif ++ ++int ssl_timeoutconn(int t, int rfd, int wfd, SSL *ssl); ++int ssl_timeoutaccept(int t, int rfd, int wfd, SSL *ssl); ++int ssl_timeoutrehandshake(int t, int rfd, int wfd, SSL *ssl); ++ ++int ssl_timeoutread(int t, int rfd, int wfd, SSL *ssl, char *buf, int len); ++int ssl_timeoutwrite(int t, int rfd, int wfd, SSL *ssl, char *buf, int len); ++ ++int ssl_timeoutio( ++ int (*fun)(), int t, int rfd, int wfd, SSL *ssl, char *buf, int len); ++ ++#endif +--- netqmail-1.06-orig/TARGETS 1998-06-15 10:53:16.000000000 +0000 ++++ netqmail-1.06/TARGETS 2015-12-01 15:54:59.033940812 +0000 +@@ -168,6 +168,8 @@ control.o + constmap.o + timeoutread.o + timeoutwrite.o ++tls.o ++ssl_timeoutio.o + timeoutconn.o + tcpto.o + dns.o +@@ -320,6 +322,7 @@ binm2 + binm2+df + binm3 + binm3+df ++Makefile-cert + it + qmail-local.0 + qmail-lspawn.0 +@@ -385,3 +388,4 @@ forgeries.0 + man + setup + check ++update_tmprsadh +--- netqmail-1.06-orig/Makefile-cert.mk 2020-01-07 10:27:08.931951527 +0000 ++++ netqmail-1.06/Makefile-cert.mk 2015-12-01 15:54:59.033940812 +0000 +@@ -0,0 +1,21 @@ ++cert-req: req.pem ++cert cert-req: QMAIL/control/clientcert.pem ++ @: ++ ++QMAIL/control/clientcert.pem: QMAIL/control/servercert.pem ++ ln -s $< $@ ++ ++QMAIL/control/servercert.pem: ++ PATH=$$PATH:/usr/local/ssl/bin \ ++ openssl req -new -x509 -nodes -days 366 -out $@ -keyout $@ ++ chmod 640 $@ ++ chown `head -2 conf-users | tail -1`:`head -1 conf-groups` $@ ++ ++req.pem: ++ PATH=$$PATH:/usr/local/ssl/bin openssl req \ ++ -new -nodes -out $@ -keyout QMAIL/control/servercert.pem ++ chmod 640 QMAIL/control/servercert.pem ++ chown `head -2 conf-users | tail -1`:`head -1 conf-groups` QMAIL/control/servercert.pem ++ @echo ++ @echo "Send req.pem to your CA to obtain signed_req.pem, and do:" ++ @echo "cat signed_req.pem >> QMAIL/control/servercert.pem" +--- netqmail-1.06-orig/conf-cc 1998-06-15 10:53:16.000000000 +0000 ++++ netqmail-1.06/conf-cc 2020-01-07 10:15:59.770628786 +0000 +@@ -1,3 +1,3 @@ +-cc -O2 ++cc -O2 -DTLS=20200107 -I/usr/local/ssl/include + + This will be used to compile .c files. +--- netqmail-1.06-orig/Makefile 2007-11-30 20:22:54.000000000 +0000 ++++ netqmail-1.06/Makefile 2015-12-01 15:54:59.033940812 +0000 +@@ -808,7 +808,7 @@ dnsptr dnsip dnsmxip dnsfq hostname ipme + forward preline condredirect bouncesaying except maildirmake \ + maildir2mbox maildirwatch qail elq pinq idedit install-big install \ + instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \ +-binm3 binm3+df ++binm3 binm3+df update_tmprsadh + + load: \ + make-load warn-auto.sh systype +@@ -1444,6 +1444,7 @@ ndelay.a case.a sig.a open.a lock.a seek + substdio.a error.a str.a fs.a auto_qmail.o dns.lib socket.lib + ./load qmail-remote control.o constmap.o timeoutread.o \ + timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \ ++ tls.o ssl_timeoutio.o -L/usr/local/ssl/lib -lssl -lcrypto \ + ipalloc.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 \ + str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib` +@@ -1539,6 +1540,7 @@ open.a sig.a case.a env.a stralloc.a all + fs.a auto_qmail.o socket.lib + ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ + timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ ++ tls.o ssl_timeoutio.o ndelay.a -L/usr/local/ssl/lib -lssl -lcrypto \ + received.o date822fmt.o now.o qmail.o cdb.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 `cat \ +@@ -1827,7 +1829,8 @@ date822fmt.h date822fmt.c dns.h dns.c tr + ipalloc.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 \ + 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 \ ++update_tmprsadh + shar -m `cat FILES` > shar + chmod 400 shar + +@@ -2108,6 +2111,19 @@ timeoutwrite.o: \ + compile timeoutwrite.c timeoutwrite.h select.h error.h readwrite.h + ./compile timeoutwrite.c + ++qmail-smtpd: tls.o ssl_timeoutio.o ndelay.a ++qmail-remote: tls.o ssl_timeoutio.o ++qmail-smtpd.o: tls.h ssl_timeoutio.h ++qmail-remote.o: tls.h ssl_timeoutio.h ++ ++tls.o: \ ++compile tls.c exit.h error.h ++ ./compile tls.c ++ ++ssl_timeoutio.o: \ ++compile ssl_timeoutio.c ssl_timeoutio.h select.h error.h ndelay.h ++ ./compile ssl_timeoutio.c ++ + token822.o: \ + compile token822.c stralloc.h gen_alloc.h alloc.h str.h token822.h \ + gen_alloc.h gen_allocdefs.h +@@ -2139,3 +2155,26 @@ compile wait_nohang.c haswaitp.h + wait_pid.o: \ + compile wait_pid.c error.h haswaitp.h + ./compile wait_pid.c ++ ++cert cert-req: \ ++Makefile-cert ++ @$(MAKE) -sf $< $@ ++ ++Makefile-cert: \ ++conf-qmail conf-users conf-groups Makefile-cert.mk ++ @cat Makefile-cert.mk \ ++ | sed s}QMAIL}"`head -1 conf-qmail`"}g \ ++ > $@ ++ ++update_tmprsadh: \ ++conf-qmail conf-users conf-groups update_tmprsadh.sh ++ @cat update_tmprsadh.sh\ ++ | sed s}UGQMAILD}"`head -2 conf-users|tail -1`:`head -1 conf-groups`"}g \ ++ | sed s}QMAIL}"`head -1 conf-qmail`"}g \ ++ > $@ ++ chmod 755 update_tmprsadh ++ ++tmprsadh: \ ++update_tmprsadh ++ echo "Creating new temporary RSA and DH parameters" ++ ./update_tmprsadh +--- netqmail-1.06-orig/update_tmprsadh.sh 2020-01-07 10:27:08.931951527 +0000 ++++ netqmail-1.06/update_tmprsadh.sh 2015-12-08 00:32:33.936474103 +0000 +@@ -0,0 +1,19 @@ ++#!/bin/sh ++ ++# Update temporary RSA and DH keys ++# Frederik Vermeulen 2004-05-31 GPL ++ ++umask 0077 || exit 0 ++ ++export PATH="$PATH:/usr/local/bin/ssl:/usr/sbin" ++ ++openssl genrsa -out QMAIL/control/rsa2048.new 2048 && ++chmod 600 QMAIL/control/rsa2048.new && ++chown UGQMAILD QMAIL/control/rsa2048.new && ++mv -f QMAIL/control/rsa2048.new QMAIL/control/rsa2048.pem ++echo ++ ++openssl dhparam -2 -out QMAIL/control/dh2048.new 2048 && ++chmod 600 QMAIL/control/dh2048.new && ++chown UGQMAILD QMAIL/control/dh2048.new && ++mv -f QMAIL/control/dh2048.new QMAIL/control/dh2048.pem diff --git a/patches/smtpd-logging.patch b/patches/smtpd-logging.patch new file mode 100644 index 0000000..537ed9b --- /dev/null +++ b/patches/smtpd-logging.patch @@ -0,0 +1,421 @@ +This patch written by Andrew Richards 2010. For more details please +see http://free.acrconsulting.co.uk/ +diff -Nru netqmail-1.06/errbits.c netqmail-1.06_logmsg13/errbits.c +--- netqmail-1.06/errbits.c 1970-01-01 01:00:00.000000000 +0100 ++++ netqmail-1.06_logmsg13/errbits.c 2010-02-10 17:35:19.000000000 +0000 +@@ -0,0 +1,62 @@ ++#include "stralloc.h" ++#include "readwrite.h" ++#include "errbits.h" ++#include "substdio.h" ++#include "fmt.h" ++#include "exit.h" ++ ++char sserrbuf[512]; ++static substdio sserr = SUBSTDIO_FDBUF(write,2,sserrbuf,sizeof sserrbuf); ++static stralloc foo = {0}; ++ ++static char pid_str[FMT_ULONG]="?PID?"; ++ ++void esetfd(fd) int fd; { sserr.fd=fd; } ++ ++void eout(s1) char *s1; {substdio_puts(&sserr,s1);} ++void eout2(s1,s2) char *s1,*s2; {substdio_puts(&sserr,s1);substdio_puts(&sserr,s2);} ++void eout3(s1,s2,s3) char *s1,*s2,*s3; {substdio_puts(&sserr,s1);substdio_puts(&sserr,s2);substdio_puts(&sserr,s3);} ++ ++void epid() ++{ ++ if (*pid_str == '?') /* not yet set from getpid() */ ++ pid_str[fmt_ulong(pid_str,getpid())] = 0; ++ eout(pid_str); ++} ++void eflush() { substdio_flush(&sserr); } ++ ++/* The functions below here come from qsutil.c with minor changes */ ++void eoutsa(sa) stralloc *sa; { substdio_putflush(&sserr,sa->s,sa->len); } ++ ++static void nomem() { substdio_putsflush(&sserr,"Out Of Memory: quitting.\n"); _exit(1); } ++ ++static int issafe(ch) char ch; ++{ /* Differs from qsutil.c version: space and % permitted */ ++ if (ch == ':') return 0; /* Replace since used as delimiter in logs */ ++ if (ch == '<') return 0; /* Replace since used around addresses in logs */ ++ if (ch == '>') return 0; /* Replace since used around addresses in logs */ ++ if (ch < 32) return 0; /* Note that space (32) is permitted */ ++ if (ch > 126) return 0; ++ return 1; ++} ++ ++void eoutclean(s) char *s; ++{ ++ int i; ++ while (!stralloc_copys(&foo,s)) nomem(); ++ for (i = 0;i < foo.len;++i) ++ if (foo.s[i] == '\n') ++ foo.s[i] = '/'; ++ else ++ if (!issafe(foo.s[i])) ++ foo.s[i] = '_'; ++ eoutsa(&foo); ++} ++ ++static char ulongstr[FMT_ULONG]; ++void eoutulong(u) unsigned long u; ++{ ++ ulongstr[fmt_ulong(ulongstr,u)] = 0; ++ eout(ulongstr); ++} ++ +diff -Nru netqmail-1.06/errbits.h netqmail-1.06_logmsg13/errbits.h +--- netqmail-1.06/errbits.h 1970-01-01 01:00:00.000000000 +0100 ++++ netqmail-1.06_logmsg13/errbits.h 2010-02-10 18:20:14.000000000 +0000 +@@ -0,0 +1,15 @@ ++extern void esetfd(); /* functions in this module default to FD 2 (stderr) for output, change with esetfd */ ++extern void eout(); ++extern void eout2(); ++extern void eout3(); ++#define eout4(s1,s2,s3,s4) { eout3(s1,s2,s3); eout(s4); } ++#define eout5(s1,s2,s3,s4,s5) { eout3(s1,s2,s3); eout2(s4,s5); } ++#define eout6(s1,s2,s3,s4,s5,s6) { eout3(s1,s2,s3); eout3(s4,s5,s6); } ++#define eout7(s1,s2,s3,s4,s5,s6,s7) { eout3(s1,s2,s3); eout4(s4,s5,s6,s7); } ++#define eout8(s1,s2,s3,s4,s5,s6,s7,s8) { eout3(s1,s2,s3); eout5(s4,s5,s6,s7,s8); } ++#define eout9(s1,s2,s3,s4,s5,s6,s7,s8,s9) { eout3(s1,s2,s3); eout6(s4,s5,s6,s7,s8,s9); } ++extern void eoutsa(); ++extern void epid(); ++extern void eflush(); ++extern void eoutclean(); ++extern void eoutulong(); +diff -Nru netqmail-1.06/Makefile netqmail-1.06_logmsg13/Makefile +--- netqmail-1.06/Makefile 2007-11-30 20:22:54.000000000 +0000 ++++ netqmail-1.06_logmsg13/Makefile 2009-08-31 22:30:46.000000000 +0100 +@@ -508,6 +508,10 @@ + compile envread.c env.h str.h + ./compile envread.c + ++errbits.o: \ ++compile errbits.c errbits.h stralloc.h gen_alloc.h fmt.h exit.h ++ ./compile errbits.c ++ + error.a: \ + makelib error.o error_str.o error_temp.o + ./makelib error.a error.o error_str.o error_temp.o +@@ -1535,12 +1539,14 @@ + load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \ + timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ + date822fmt.o now.o qmail.o cdb.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 \ ++open.a sig.a case.a env.a stralloc.a errbits.o \ ++alloc.a substdio.a error.a str.a \ + fs.a auto_qmail.o socket.lib + ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ + timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ + received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ + datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ ++ errbits.o \ + alloc.a substdio.a error.a str.a fs.a auto_qmail.o `cat \ + socket.lib` + +diff -Nru netqmail-1.06/qmail-smtpd.c netqmail-1.06_logmsg13/qmail-smtpd.c +--- netqmail-1.06/qmail-smtpd.c 2007-11-30 20:22:54.000000000 +0000 ++++ netqmail-1.06_logmsg13/qmail-smtpd.c 2010-02-15 22:27:09.000000000 +0000 +@@ -23,16 +23,35 @@ + #include "timeoutread.h" + #include "timeoutwrite.h" + #include "commands.h" ++#include "errbits.h" + ++#define enew() { eout("qmail-smtpd: pid "); epid(); eout3(" from ",remoteip,": "); } ++/* Or if you prefer shorter log messages (deduce IP from tcpserver PID entry), */ ++/* { eout("qmail-smtpd: pid "); epid(); eout(": "); } */ + #define MAXHOPS 100 + unsigned int databytes = 0; + int timeout = 1200; + ++char *remoteip="(not yet set)"; ++char *remotehost; ++char *remoteinfo; ++char *local; ++char *relayclient; ++ ++stralloc mailfrom = {0}; ++stralloc rcptto = {0}; ++int rcptcount; ++stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */ ++ + int safewrite(fd,buf,len) int fd; char *buf; int len; + { + int r; + r = timeoutwrite(timeout,fd,buf,len); +- if (r <= 0) _exit(1); ++ if (r <= 0) ++ { ++ enew(); eout("Write error (disconnect?): quitting\n"); eflush(); ++ _exit(1); ++ } + return r; + } + +@@ -42,22 +61,91 @@ + void flush() { substdio_flush(&ssout); } + void out(s) char *s; { substdio_puts(&ssout,s); } + +-void die_read() { _exit(1); } +-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_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 straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); } +- +-void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); } +-void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } +-void err_unimpl(arg) char *arg; { out("502 unimplemented (#5.5.1)\r\n"); } +-void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); } +-void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); } +-void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); } +-void err_noop(arg) char *arg; { out("250 ok\r\n"); } +-void err_vrfy(arg) char *arg; { out("252 send some mail, i'll try my best\r\n"); } +-void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); } ++void die_read() ++{ ++ enew(); eout("Read error (disconnect?): quitting\n"); eflush(); _exit(1); ++} ++void die_alarm() ++{ ++ enew(); eout("Connection timed out: quitting\n"); eflush(); ++ out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); ++} ++void die_nomem() ++{ ++ enew(); eout("Out of memory: quitting\n"); eflush(); ++ out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); ++} ++void die_control() ++{ ++ enew(); eout("Unable to read controls: quitting\n"); eflush(); ++ out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); ++} ++void die_ipme() ++{ ++ enew(); eout("Unable to figure out my IP addresses: quitting\n"); eflush(); ++ out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); ++} ++void straynewline() ++{ ++ enew(); eout("Stray newline: quitting\n"); eflush(); ++ out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); ++} ++ ++void err_bmf() ++{ ++ enew(); eout("Sender address in badmailfrom\n"); eflush(); ++ out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); ++} ++void err_nogateway() ++{ ++ enew(); eout("Recipient domain not in rcpthosts <"); eoutclean(addr.s); eout(">\n"); eflush(); ++ out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); ++} ++void err_unimpl(arg) char *arg; ++{ ++ enew(); eout("Unimplemented command\n"); eflush(); ++ out("502 unimplemented (#5.5.1)\r\n"); ++} ++void err_syntax(cmd) char *cmd; ++{ ++ enew(); eout2(cmd," with too long address ("); eoutulong((unsigned long)addr.len); eout(" bytes) given\n"); eflush(); ++ out("555 syntax error (#5.5.4)\r\n"); ++} ++void err_wantmail() ++{ ++ enew(); eout("Attempted RCPT or DATA before MAIL\n"); eflush(); ++ out("503 MAIL first (#5.5.1)\r\n"); ++} ++void err_wantrcpt() ++{ ++ enew(); eout("Attempted DATA before RCPT\n"); eflush(); ++ out("503 RCPT first (#5.5.1)\r\n"); ++} ++void err_noop(arg) char *arg; ++{ ++ enew(); eout("NOOP\n"); eflush(); ++ out("250 ok\r\n"); ++} ++void err_vrfy(arg) char *arg; ++{ ++ enew(); eout("VRFY requested\n"); eflush(); ++ out("252 send some mail, i'll try my best\r\n"); ++} ++void err_qqt() ++{ ++ enew(); eout("qqt failure\n"); eflush(); ++ out("451 qqt failure (#4.3.0)\r\n"); ++} ++void err_hops() ++{ ++ enew(); eout("Exceeded hop count\n"); eflush(); ++ out("554 too many hops, this message is looping (#5.4.6)\r\n"); ++} ++void err_databytes() ++{ ++ enew(); eout("Exceeded DATABYTES limit\n"); eflush(); ++ out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); ++} + + + stralloc greeting = {0}; +@@ -73,15 +161,10 @@ + } + void smtp_quit(arg) char *arg; + { ++ enew(); eout("Remote end QUIT: quitting\n"); eflush(); + smtp_greet("221 "); out("\r\n"); flush(); _exit(0); + } + +-char *remoteip; +-char *remotehost; +-char *remoteinfo; +-char *local; +-char *relayclient; +- + stralloc helohost = {0}; + char *fakehelo; /* pointer into helohost, or 0 */ + +@@ -132,11 +215,10 @@ + remoteinfo = env_get("TCPREMOTEINFO"); + relayclient = env_get("RELAYCLIENT"); + dohelo(remotehost); ++ enew(); eout("New session\n"); eflush(); + } + + +-stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */ +- + int addrparse(arg) + char *arg; + { +@@ -219,37 +301,40 @@ + + int seenmail = 0; + int flagbarf; /* defined if seenmail */ +-stralloc mailfrom = {0}; +-stralloc rcptto = {0}; + + void smtp_helo(arg) char *arg; + { ++ enew(); eout("Received HELO "); eoutclean(arg); eout("\n"); eflush(); + smtp_greet("250 "); out("\r\n"); + seenmail = 0; dohelo(arg); + } + void smtp_ehlo(arg) char *arg; + { ++ enew(); eout("Received EHLO "); eoutclean(arg); eout("\n"); eflush(); + smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); + seenmail = 0; dohelo(arg); + } + void smtp_rset(arg) char *arg; + { + seenmail = 0; ++ enew(); eout("Session RSET\n"); eflush(); + out("250 flushed\r\n"); + } + void smtp_mail(arg) char *arg; + { +- if (!addrparse(arg)) { err_syntax(); return; } ++ if (!addrparse(arg)) { err_syntax("MAIL"); return; } + flagbarf = bmfcheck(); + seenmail = 1; + if (!stralloc_copys(&rcptto,"")) die_nomem(); + if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); + if (!stralloc_0(&mailfrom)) die_nomem(); ++ rcptcount = 0; ++ enew(); eout("Sender <"); eoutclean(mailfrom.s); eout(">\n"); eflush(); + out("250 ok\r\n"); + } + void smtp_rcpt(arg) char *arg; { + if (!seenmail) { err_wantmail(); return; } +- if (!addrparse(arg)) { err_syntax(); return; } ++ if (!addrparse(arg)) { err_syntax("RCPT"); return; } + if (flagbarf) { err_bmf(); return; } + if (relayclient) { + --addr.len; +@@ -261,6 +346,8 @@ + if (!stralloc_cats(&rcptto,"T")) die_nomem(); + if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); ++ ++rcptcount; ++ enew(); eout("Recipient <"); eoutclean(addr.s); eout(">\n"); eflush(); + out("250 ok\r\n"); + } + +@@ -280,6 +367,7 @@ + + struct qmail qqt; + unsigned int bytestooverflow = 0; ++unsigned int messagebytes = 0; + + void put(ch) + char *ch; +@@ -287,6 +375,7 @@ + if (bytestooverflow) + if (!--bytestooverflow) + qmail_fail(&qqt); ++ messagebytes++; + qmail_put(&qqt,ch,1); + } + +@@ -363,6 +452,10 @@ + accept_buf[fmt_ulong(accept_buf,qp)] = 0; + out(accept_buf); + out("\r\n"); ++ enew(); eout3("Message accepted, qp ",accept_buf," ("); ++ eoutulong((unsigned long)rcptcount); eout(" recipients, "); ++ eoutulong((unsigned long)messagebytes); eout(" bytes)\n"); ++ eflush(); + } + + void smtp_data(arg) char *arg; { +@@ -374,6 +467,7 @@ + if (!rcptto.len) { err_wantrcpt(); return; } + seenmail = 0; + if (databytes) bytestooverflow = databytes + 1; ++ messagebytes = 0; + if (qmail_open(&qqt) == -1) { err_qqt(); return; } + qp = qmail_qp(&qqt); + out("354 go ahead\r\n"); +@@ -387,11 +481,15 @@ + + qqx = qmail_close(&qqt); + if (!*qqx) { acceptmessage(qp); return; } +- if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; } +- if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; } ++ if (hops) { err_hops(); return; } ++ if (databytes) if (!bytestooverflow) { err_databytes(); return; } + if (*qqx == 'D') out("554 "); else out("451 "); + out(qqx + 1); + out("\r\n"); ++ enew(); eout("Message rejected ("); ++ if (*qqx == 'D') eout("554 "); else eout("451 "); ++ eoutclean(qqx + 1); eout(")\n"); ++ eflush(); + } + + struct commands smtpcommands[] = { +@@ -411,6 +509,7 @@ + void main() + { + sig_pipeignore(); ++ /* esetfd(2); Errors default to FD2 (stderr), change here if needed */ + if (chdir(auto_qmail) == -1) die_control(); + setup(); + if (ipme_init() != 1) die_ipme(); +diff -Nru netqmail-1.06/TARGETS netqmail-1.06_logmsg13/TARGETS +--- netqmail-1.06/TARGETS 1998-06-15 11:53:16.000000000 +0100 ++++ netqmail-1.06_logmsg13/TARGETS 2009-08-31 22:30:46.000000000 +0100 +@@ -96,6 +96,7 @@ + error_str.o + error_temp.o + error.a ++errbits.o + str_len.o + str_diff.o + str_diffn.o diff --git a/patches/smtpd-spf.patch b/patches/smtpd-spf.patch new file mode 100644 index 0000000..8c824a8 --- /dev/null +++ b/patches/smtpd-spf.patch @@ -0,0 +1,2173 @@ +diff -Nur qmail-1.03.orig/byte_cspn.c qmail-1.03/byte_cspn.c +--- qmail-1.03.orig/byte_cspn.c 1970-01-01 01:00:00.000000000 +0100 ++++ qmail-1.03/byte_cspn.c 2004-01-29 13:48:03.000000000 +0100 +@@ -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; ++} +diff -Nur qmail-1.03.orig/byte.h qmail-1.03/byte.h +--- qmail-1.03.orig/byte.h 1998-06-15 12:53:16.000000000 +0200 ++++ qmail-1.03/byte.h 2004-01-29 13:46:57.000000000 +0100 +@@ -3,6 +3,8 @@ + + extern unsigned int byte_chr(); + extern unsigned int byte_rchr(); ++extern unsigned int byte_cspn(); ++extern unsigned int byte_rcspn(); + extern void byte_copy(); + extern void byte_copyr(); + extern int byte_diff(); +diff -Nur qmail-1.03.orig/byte_rcspn.c qmail-1.03/byte_rcspn.c +--- qmail-1.03.orig/byte_rcspn.c 1970-01-01 01:00:00.000000000 +0100 ++++ qmail-1.03/byte_rcspn.c 2004-01-29 13:47:52.000000000 +0100 +@@ -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; ++} ++ +diff -Nur qmail-1.03.orig/dns.c qmail-1.03/dns.c +--- qmail-1.03.orig/dns.c 2004-07-12 14:46:11.000000000 +0200 ++++ qmail-1.03/dns.c 2004-10-26 20:58:14.904288016 +0200 +@@ -11,6 +11,7 @@ + extern int h_errno; + #include "ip.h" + #include "ipalloc.h" ++#include "strsalloc.h" + #include "fmt.h" + #include "alloc.h" + #include "str.h" +@@ -29,6 +30,7 @@ + static int numanswers; + static char name[MAXDNAME]; + static struct ip_address ip; ++static stralloc txt = {0}; + unsigned short pref; + + static stralloc glue = {0}; +@@ -179,6 +181,49 @@ + 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) + int flagsearch; + { +@@ -237,15 +282,18 @@ + return len; + } + +-int dns_ptr(sa,ip) +-stralloc *sa; ++static int dns_ptrplus(ssa,ip) ++strsalloc *ssa; + struct ip_address *ip; + { ++ stralloc sa = {0}; + int r; + +- if (!stralloc_ready(sa,iaafmt((char *) 0,ip))) return DNS_MEM; +- sa->len = iaafmt(sa->s,ip); +- switch(resolve(sa,T_PTR)) ++ if (!stralloc_ready(&sa,iaafmt((char *) 0,ip))) return DNS_MEM; ++ sa.len = iaafmt(sa.s,ip); ++ r = resolve(&sa,T_PTR); ++ alloc_free(sa.s); ++ switch(r) + { + case DNS_MEM: return DNS_MEM; + case DNS_SOFT: return DNS_SOFT; +@@ -256,13 +304,35 @@ + if (r == DNS_SOFT) return DNS_SOFT; + if (r == 1) + { +- if (!stralloc_copys(sa,name)) return DNS_MEM; +- return 0; ++ stralloc sa2 = {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; + } + ++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) + ipalloc *ia; + stralloc *sa; +@@ -398,3 +468,49 @@ + alloc_free(mx); + 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; ++} +diff -Nur qmail-1.03.orig/dnsfq.c qmail-1.03/dnsfq.c +--- qmail-1.03.orig/dnsfq.c 1998-06-15 12:53:16.000000000 +0200 ++++ qmail-1.03/dnsfq.c 2004-01-29 02:36:34.000000000 +0100 +@@ -5,15 +5,19 @@ + #include "dnsdoe.h" + #include "ip.h" + #include "ipalloc.h" ++#include "strsalloc.h" + #include "exit.h" + + stralloc sa = {0}; ++strsalloc ssa = {0}; + ipalloc ia = {0}; + + void main(argc,argv) + int argc; + char **argv; + { ++ int j; ++ + if (!argv[1]) _exit(100); + + if (!stralloc_copys(&sa,argv[1])) +@@ -25,8 +29,11 @@ + { + substdio_putsflush(subfderr,"no IP addresses\n"); _exit(100); + } +- dnsdoe(dns_ptr(&sa,&ia.ix[0].ip)); +- substdio_putflush(subfdout,sa.s,sa.len); +- substdio_putsflush(subfdout,"\n"); ++ dnsdoe(dns_ptr(&ssa,&ia.ix[0].ip)); ++ for(j = 0;j < ssa.len;++j) ++ { ++ substdio_putflush(subfdout,ssa.sa[j].s,ssa.sa[j].len); ++ substdio_putsflush(subfdout,"\n"); ++ } + _exit(0); + } +diff -Nur qmail-1.03.orig/dns.h qmail-1.03/dns.h +--- qmail-1.03.orig/dns.h 1998-06-15 12:53:16.000000000 +0200 ++++ qmail-1.03/dns.h 2004-01-28 17:24:01.000000000 +0100 +@@ -10,5 +10,6 @@ + int dns_mxip(); + int dns_ip(); + int dns_ptr(); ++int dns_txt(); + + #endif +diff -Nur qmail-1.03.orig/dnsptr.c qmail-1.03/dnsptr.c +--- qmail-1.03.orig/dnsptr.c 1998-06-15 12:53:16.000000000 +0200 ++++ qmail-1.03/dnsptr.c 2004-01-29 02:39:09.000000000 +0100 +@@ -6,22 +6,28 @@ + #include "dns.h" + #include "dnsdoe.h" + #include "ip.h" ++#include "strsalloc.h" + #include "exit.h" + +-stralloc sa = {0}; ++strsalloc ssa = {0}; + struct ip_address ip; + + void main(argc,argv) + int argc; + char **argv; + { ++ int j; ++ + if (!argv[1]) _exit(100); + + ip_scan(argv[1],&ip); + + dns_init(0); +- dnsdoe(dns_ptr(&sa,&ip)); +- substdio_putflush(subfdout,sa.s,sa.len); +- substdio_putsflush(subfdout,"\n"); ++ dnsdoe(dns_ptr(&ssa,&ip)); ++ for(j = 0;j < ssa.len;++j) ++ { ++ substdio_putflush(subfdout,ssa.sa[j].s,ssa.sa[j].len); ++ substdio_putsflush(subfdout,"\n"); ++ } + _exit(0); + } +diff -Nur qmail-1.03.orig/dnstxt.c qmail-1.03/dnstxt.c +--- qmail-1.03.orig/dnstxt.c 1970-01-01 01:00:00.000000000 +0100 ++++ qmail-1.03/dnstxt.c 2004-01-29 02:25:23.000000000 +0100 +@@ -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); ++} +diff -Nur qmail-1.03.orig/FILES qmail-1.03/FILES +--- qmail-1.03.orig/FILES 1998-06-15 12:53:16.000000000 +0200 ++++ qmail-1.03/FILES 2004-02-02 15:12:35.000000000 +0100 +@@ -135,6 +135,8 @@ + dnsip.c + dnsmxip.c + dnsptr.c ++dnstxt.c ++spfquery.c + hostname.c + ipmeprint.c + tcp-env.c +@@ -335,13 +337,16 @@ + byte.h + byte_chr.c + byte_copy.c ++byte_cspn.c + byte_cr.c + byte_diff.c + byte_rchr.c ++byte_rcspn.c + byte_zero.c + str.h + str_chr.c + str_cpy.c ++str_cpyb.c + str_diff.c + str_diffn.c + str_len.c +@@ -401,6 +406,8 @@ + date822fmt.c + dns.h + dns.c ++spf.h ++spf.c + trylsock.c + tryrsolv.c + ip.h +diff -Nur qmail-1.03.orig/Makefile qmail-1.03/Makefile +--- qmail-1.03.orig/Makefile 2004-01-29 03:48:26.000000000 +0100 ++++ qmail-1.03/Makefile 2004-02-10 13:00:35.000000000 +0100 +@@ -203,6 +203,10 @@ + compile byte_cr.c byte.h + ./compile byte_cr.c + ++byte_cspn.o: \ ++compile byte_cspn.c byte.h ++ ./compile byte_cspn.c ++ + byte_diff.o: \ + compile byte_diff.c byte.h + ./compile byte_diff.c +@@ -211,6 +215,10 @@ + compile byte_rchr.c byte.h + ./compile byte_rchr.c + ++byte_rcspn.o: \ ++compile byte_rcspn.c byte.h ++ ./compile byte_rcspn.c ++ + byte_zero.o: \ + compile byte_zero.c byte.h + ./compile byte_zero.c +@@ -393,84 +401,96 @@ + rm -f trydrent.o + + dns.lib: \ +-tryrsolv.c compile load socket.lib dns.o ipalloc.o ip.o stralloc.a \ +-alloc.a error.a fs.a str.a ++tryrsolv.c compile load socket.lib dns.o ipalloc.o strsalloc.o ip.o \ ++stralloc.a alloc.a error.a fs.a str.a + ( ( ./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 \ + && echo -lresolv || exit 0 ) > dns.lib + rm -f tryrsolv.o tryrsolv + + dns.o: \ +-compile dns.c ip.h ipalloc.h ip.h gen_alloc.h fmt.h alloc.h str.h \ +-stralloc.h gen_alloc.h dns.h case.h ++compile dns.c ip.h ipalloc.h strsalloc.h gen_alloc.h fmt.h alloc.h \ ++str.h stralloc.h dns.h case.h + ./compile dns.c + + 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 +- ./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 \ + socket.lib` + + 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 + ./compile dnscname.c + + 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 + + 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 +- ./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 \ + socket.lib` + + dnsfq.o: \ +-compile dnsfq.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \ +-dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h exit.h ++compile dnsfq.c substdio.h subfd.h stralloc.h gen_alloc.h \ ++dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h exit.h + ./compile dnsfq.c + + 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 +- ./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 \ + socket.lib` + + dnsip.o: \ +-compile dnsip.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \ +-dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h exit.h ++compile dnsip.c substdio.h subfd.h stralloc.h gen_alloc.h \ ++dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h exit.h + ./compile dnsip.c + + dnsmxip: \ +-load dnsmxip.o dns.o dnsdoe.o ip.o ipalloc.o now.o stralloc.a 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.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o now.o stralloc.a \ ++alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib ++ ./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 \ + dns.lib` `cat socket.lib` + + dnsmxip.o: \ +-compile dnsmxip.c substdio.h subfd.h substdio.h stralloc.h \ +-gen_alloc.h fmt.h dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h \ ++compile dnsmxip.c substdio.h subfd.h stralloc.h \ ++gen_alloc.h fmt.h dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h \ + now.h datetime.h exit.h + ./compile dnsmxip.c + + 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 +- ./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 \ + socket.lib` + + 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 + ./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.5 + nroff -man dot-qmail.5 > dot-qmail.0 +@@ -777,24 +797,24 @@ + ./compile ip.c + + 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 + ./compile ipalloc.c + + ipme.o: \ +-compile ipme.c hassalen.h byte.h ip.h ipalloc.h ip.h gen_alloc.h \ +-stralloc.h gen_alloc.h ipme.h ip.h ipalloc.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 strsalloc.h + ./compile ipme.c + + ipmeprint: \ +-load ipmeprint.o ipme.o ip.o ipalloc.o stralloc.a alloc.a substdio.a \ +-error.a str.a fs.a socket.lib +- ./load ipmeprint ipme.o ip.o ipalloc.o stralloc.a alloc.a \ +- substdio.a error.a str.a fs.a `cat socket.lib` ++load ipmeprint.o ipme.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \ ++substdio.a error.a str.a fs.a socket.lib ++ ./load ipmeprint ipme.o ip.o ipalloc.o strsalloc.o stralloc.a \ ++ alloc.a substdio.a error.a str.a fs.a `cat socket.lib` + + ipmeprint.o: \ + 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 + + it: \ +@@ -804,11 +824,11 @@ + qmail-pw2u qmail-qread qmail-qstat qmail-tcpto qmail-tcpok \ + qmail-pop3d qmail-popup qmail-qmqpc qmail-qmqpd qmail-qmtpd \ + 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 qbiff \ + forward preline condredirect bouncesaying except maildirmake \ + maildir2mbox maildirwatch qail elq pinq idedit install-big install \ + instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \ +-binm3 binm3+df ++binm3 binm3+df spfquery + + load: \ + make-load warn-auto.sh systype +@@ -1439,12 +1459,12 @@ + + qmail-remote: \ + 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 \ + substdio.a error.a str.a fs.a auto_qmail.o dns.lib socket.lib + ./load qmail-remote control.o constmap.o timeoutread.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 \ + str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib` + +@@ -1455,7 +1475,7 @@ + qmail-remote.o: \ + 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 \ +-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 \ + tcpto.h readwrite.h timeoutconn.h timeoutread.h timeoutwrite.h + ./compile qmail-remote.c +@@ -1528,21 +1548,21 @@ + 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 \ + 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 + + qmail-smtpd: \ + load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \ +-timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ +-date822fmt.o now.o qmail.o cdb.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 socket.lib ++timeoutwrite.o ip.o ipme.o ipalloc.o strsalloc.o control.o constmap.o \ ++received.o date822fmt.o now.o qmail.o spf.o dns.o cdb.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 socket.lib dns.lib + ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ +- timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ +- received.o date822fmt.o now.o qmail.o cdb.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 `cat \ +- socket.lib` ++ timeoutwrite.o ip.o ipme.o ipalloc.o strsalloc.o control.o \ ++ constmap.o received.o date822fmt.o now.o qmail.o spf.o dns.o cdb.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 fs.a auto_qmail.o \ ++ str.a `cat socket.lib` `cat dns.lib` + + qmail-smtpd.0: \ + qmail-smtpd.8 +@@ -1551,9 +1571,9 @@ + qmail-smtpd.o: \ + 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 \ +-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 \ +-exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h ++exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h spf.h + ./compile qmail-smtpd.c + + qmail-start: \ +@@ -1779,7 +1799,7 @@ + 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-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 \ + except.c bouncesaying.c condredirect.c maildirmake.c maildir2mbox.c \ + maildirwatch.c splogger.c qail.sh elq.sh pinq.sh qmail-upq.sh \ +@@ -1813,8 +1833,9 @@ + 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 \ + 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 \ +-str_chr.c str_cpy.c str_diff.c str_diffn.c str_len.c str_rchr.c \ ++byte_copy.c byte_cr.c byte_cspn.c byte_diff.c byte_rchr.c byte_rcspn.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 \ + 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 \ +@@ -1824,7 +1845,7 @@ + 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 \ + 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 \ + 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 +@@ -1897,6 +1918,23 @@ + ./chkspawn + ./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: \ + 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 \ +@@ -1912,12 +1950,12 @@ + ./compile splogger.c + + str.a: \ +-makelib str_len.o str_diff.o str_diffn.o str_cpy.o str_chr.o \ +-str_rchr.o str_start.o byte_chr.o byte_rchr.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 \ +- str_chr.o str_rchr.o str_start.o byte_chr.o byte_rchr.o \ +- byte_diff.o byte_copy.o byte_cr.o byte_zero.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_cspn.o byte_rcspn.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 str_cpyb.o \ ++ str_chr.o str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_cspn.o \ ++ byte_rcspn.o byte_diff.o byte_copy.o byte_cr.o byte_zero.o + + str_chr.o: \ + compile str_chr.c str.h +@@ -1927,6 +1965,10 @@ + compile str_cpy.c str.h + ./compile str_cpy.c + ++str_cpyb.o: \ ++compile str_cpyb.c str.h ++ ./compile str_cpyb.c ++ + str_diff.o: \ + compile str_diff.c str.h + ./compile str_diff.c +@@ -2006,6 +2048,11 @@ + compile strerr_sys.c error.h strerr.h + ./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: \ + compile subfderr.c readwrite.h substdio.h subfd.h substdio.h + ./compile subfderr.c +@@ -2066,11 +2113,11 @@ + + tcp-env: \ + 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 \ +-stralloc.a alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib ++timeoutconn.o ip.o ipalloc.o strsalloc.o case.a ndelay.a sig.a env.a \ ++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 \ +- timeoutwrite.o timeoutconn.o ip.o ipalloc.o case.a ndelay.a \ +- sig.a env.a getopt.a stralloc.a alloc.a substdio.a error.a \ ++ timeoutwrite.o timeoutconn.o ip.o ipalloc.o strsalloc.o case.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` + + tcp-env.0: \ +diff -Nur qmail-1.03.orig/qmail-control.9 qmail-1.03/qmail-control.9 +--- qmail-1.03.orig/qmail-control.9 1998-06-15 12:53:16.000000000 +0200 ++++ qmail-1.03/qmail-control.9 2004-02-02 14:58:56.000000000 +0100 +@@ -63,6 +63,10 @@ + .I rcpthosts \fR(none) \fRqmail-smtpd + .I smtpgreeting \fIme \fRqmail-smtpd + .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 timeoutremote \fR1200 \fRqmail-remote + .I timeoutsmtpd \fR1200 \fRqmail-smtpd +diff -Nur qmail-1.03.orig/qmail-showctl.c qmail-1.03/qmail-showctl.c +--- qmail-1.03.orig/qmail-showctl.c 1998-06-15 12:53:16.000000000 +0200 ++++ qmail-1.03/qmail-showctl.c 2004-03-12 17:30:02.000000000 +0100 +@@ -15,6 +15,7 @@ + #include "auto_patrn.h" + #include "auto_spawn.h" + #include "auto_split.h" ++#include "spf.h" + + stralloc me = {0}; + int meok; +@@ -257,6 +258,10 @@ + + do_str("smtpgreeting",1,"smtpgreeting","SMTP greeting: 220 "); + 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("timeoutremote","1200","SMTP client data timeout is "," seconds"); + do_int("timeoutsmtpd","1200","SMTP server data timeout is "," seconds"); +@@ -292,6 +297,10 @@ + if (str_equal(d->d_name,"rcpthosts")) continue; + if (str_equal(d->d_name,"smtpgreeting")) 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,"timeoutremote")) continue; + if (str_equal(d->d_name,"timeoutsmtpd")) continue; +diff -Nur qmail-1.03.orig/qmail-smtpd.8 qmail-1.03/qmail-smtpd.8 +--- qmail-1.03.orig/qmail-smtpd.8 1998-06-15 12:53:16.000000000 +0200 ++++ qmail-1.03/qmail-smtpd.8 2004-02-05 20:04:25.000000000 +0100 +@@ -169,6 +169,41 @@ + .B qmail-smtpd + will wait for each new buffer of data from the remote SMTP client. + Default: 1200. ++.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" + tcp-env(1), + tcp-environ(5), +diff -Nur qmail-1.03.orig/qmail-smtpd.c qmail-1.03/qmail-smtpd.c +--- qmail-1.03.orig/qmail-smtpd.c 1998-06-15 12:53:16.000000000 +0200 ++++ qmail-1.03/qmail-smtpd.c 2004-10-26 20:59:47.563201720 +0200 +@@ -23,10 +23,12 @@ + #include "timeoutread.h" + #include "timeoutwrite.h" + #include "commands.h" ++#include "spf.h" + + #define MAXHOPS 100 + unsigned int databytes = 0; + int timeout = 1200; ++unsigned int spfbehavior = 0; + + int safewrite(fd,buf,len) int fd; char *buf; int len; + { +@@ -61,6 +63,9 @@ + + + stralloc greeting = {0}; ++stralloc spflocal = {0}; ++stralloc spfguess = {0}; ++stralloc spfexp = {0}; + + void smtp_greet(code) char *code; + { +@@ -122,6 +127,19 @@ + if (x) { scan_ulong(x,&u); databytes = u; } + if (!(databytes + 1)) --databytes; + ++ 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"); + if (!remoteip) remoteip = "unknown"; + local = env_get("TCPLOCALHOST"); +@@ -219,6 +237,8 @@ + + int seenmail = 0; + int flagbarf; /* defined if seenmail */ ++int flagbarfspf; ++stralloc spfbarfmsg = {0}; + stralloc mailfrom = {0}; + stralloc rcptto = {0}; + +@@ -237,20 +257,79 @@ + seenmail = 0; + out("250 flushed\r\n"); + } ++ + void smtp_mail(arg) char *arg; + { ++ int r; ++ + if (!addrparse(arg)) { err_syntax(); return; } + 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; + if (!stralloc_copys(&rcptto,"")) die_nomem(); + if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); + if (!stralloc_0(&mailfrom)) die_nomem(); + 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; { + if (!seenmail) { err_wantmail(); return; } + if (!addrparse(arg)) { err_syntax(); return; } + if (flagbarf) { err_bmf(); return; } ++ if (flagbarfspf) { err_spf(); return; } + if (relayclient) { + --addr.len; + if (!stralloc_cats(&addr,relayclient)) die_nomem(); +@@ -351,6 +430,25 @@ + } + } + ++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]; + void acceptmessage(qp) unsigned long qp; + { +@@ -379,6 +477,7 @@ + out("354 go ahead\r\n"); + + received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); ++ spfreceived(); + blast(&hops); + hops = (hops >= MAXHOPS); + if (hops) qmail_fail(&qqt); +diff -Nur qmail-1.03.orig/spf.c qmail-1.03/spf.c +--- qmail-1.03.orig/spf.c 1970-01-01 01:00:00.000000000 +0100 ++++ qmail-1.03/spf.c 2004-10-26 20:59:53.225340944 +0200 +@@ -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; ++} +diff -Nur qmail-1.03.orig/spf.h qmail-1.03/spf.h +--- qmail-1.03.orig/spf.h 1970-01-01 01:00:00.000000000 +0100 ++++ qmail-1.03/spf.h 2004-02-02 15:14:59.000000000 +0100 +@@ -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 +diff -Nur qmail-1.03.orig/spfquery.c qmail-1.03/spfquery.c +--- qmail-1.03.orig/spfquery.c 1970-01-01 01:00:00.000000000 +0100 ++++ qmail-1.03/spfquery.c 2004-10-26 20:59:58.471543400 +0200 +@@ -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 [] []\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); ++} +diff -Nur qmail-1.03.orig/str_cpyb.c qmail-1.03/str_cpyb.c +--- qmail-1.03.orig/str_cpyb.c 1970-01-01 01:00:00.000000000 +0100 ++++ qmail-1.03/str_cpyb.c 2004-01-28 17:24:01.000000000 +0100 +@@ -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; ++} +diff -Nur qmail-1.03.orig/str.h qmail-1.03/str.h +--- qmail-1.03.orig/str.h 1998-06-15 12:53:16.000000000 +0200 ++++ qmail-1.03/str.h 2004-01-28 22:41:57.000000000 +0100 +@@ -2,6 +2,7 @@ + #define STR_H + + extern unsigned int str_copy(); ++extern unsigned int str_copyb(); + extern int str_diff(); + extern int str_diffn(); + extern unsigned int str_len(); +diff -Nur qmail-1.03.orig/strsalloc.c qmail-1.03/strsalloc.c +--- qmail-1.03.orig/strsalloc.c 1970-01-01 01:00:00.000000000 +0100 ++++ qmail-1.03/strsalloc.c 2004-01-29 02:24:19.000000000 +0100 +@@ -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) +diff -Nur qmail-1.03.orig/strsalloc.h qmail-1.03/strsalloc.h +--- qmail-1.03.orig/strsalloc.h 1970-01-01 01:00:00.000000000 +0100 ++++ qmail-1.03/strsalloc.h 2004-01-29 02:23:54.000000000 +0100 +@@ -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 +diff -Nur qmail-1.03.orig/TARGETS qmail-1.03/TARGETS +--- qmail-1.03.orig/TARGETS 1998-06-15 12:53:16.000000000 +0200 ++++ qmail-1.03/TARGETS 2004-01-29 19:28:12.000000000 +0100 +@@ -100,11 +100,14 @@ + 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_cspn.o ++byte_rcspn.o + byte_diff.o + byte_copy.o + byte_cr.o +@@ -171,8 +174,10 @@ + timeoutconn.o + tcpto.o + dns.o ++spf.o + ip.o + ipalloc.o ++strsalloc.o + hassalen.h + ipme.o + ndelay.o +@@ -212,6 +217,9 @@ + headerbody.o + hfield.o + token822.o ++spf.o ++spfquery.o ++spfquery + qmail-inject + predate.o + predate +@@ -270,6 +278,8 @@ + dnsip + dnsmxip.o + dnsmxip ++dnstxt.o ++dnstxt + dnsfq.o + dnsfq + hostname.o +diff -Nur qmail-1.03.orig/tcp-env.c qmail-1.03/tcp-env.c +--- qmail-1.03.orig/tcp-env.c 1998-06-15 12:53:16.000000000 +0200 ++++ qmail-1.03/tcp-env.c 2004-01-29 02:44:17.000000000 +0100 +@@ -10,6 +10,7 @@ + #include "scan.h" + #include "subgetopt.h" + #include "ip.h" ++#include "strsalloc.h" + #include "dns.h" + #include "byte.h" + #include "remoteinfo.h" +@@ -34,6 +35,7 @@ + int argc; + char *argv[]; + { ++ strsalloc ssa = {0}; + int dummy; + char *proto; + int opt; +@@ -74,12 +76,13 @@ + temp[ip_fmt(temp,&iplocal)] = 0; + if (!env_put2("TCPLOCALIP",temp)) die(); + +- switch(dns_ptr(&localname,&iplocal)) ++ switch(dns_ptr(&ssa,&iplocal)) + { + case DNS_MEM: die(); + case DNS_SOFT: + if (!stralloc_copys(&localname,"softdnserror")) die(); + case 0: ++ if (!stralloc_copy(&localname,&ssa.sa[0])) die(); + if (!stralloc_0(&localname)) die(); + case_lowers(localname.s); + if (!env_put2("TCPLOCALHOST",localname.s)) die(); +@@ -99,12 +102,13 @@ + temp[ip_fmt(temp,&ipremote)] = 0; + if (!env_put2("TCPREMOTEIP",temp)) die(); + +- switch(dns_ptr(&remotename,&ipremote)) ++ switch(dns_ptr(&ssa,&ipremote)) + { + case DNS_MEM: die(); + case DNS_SOFT: + if (!stralloc_copys(&remotename,"softdnserror")) die(); + case 0: ++ if (!stralloc_copy(&remotename,&ssa.sa[0])) die(); + if (!stralloc_0(&remotename)) die(); + case_lowers(remotename.s); + if (!env_put2("TCPREMOTEHOST",remotename.s)) die(); diff --git a/patches/spp-smtpauth-eh.patch b/patches/spp-smtpauth-eh.patch new file mode 100644 index 0000000..29092f6 --- /dev/null +++ b/patches/spp-smtpauth-eh.patch @@ -0,0 +1,431 @@ +--- Makefile 2004-08-02 14:40:20.000000000 +0200 ++++ Makefile 2005-01-19 12:59:54.000000000 +0100 +@@ -1535,18 +1535,23 @@ + auto_split.h + ./compile qmail-showctl.c + ++qmail-spp.o: \ ++compile qmail-spp.c readwrite.h stralloc.h substdio.h control.h str.h \ ++byte.h env.h exit.h wait.h fork.h fd.h fmt.h getln.h ++ ./compile qmail-spp.c ++ + qmail-smtpd: \ + load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \ + timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ + date822fmt.o now.o qmail.o cdb.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 socket.lib ++fs.a auto_qmail.o base64.o qmail-spp.o socket.lib + ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ + timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ + received.o date822fmt.o now.o qmail.o cdb.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 \ +- socket.lib` ++ datetime.a getln.a open.a sig.a case.a qmail-spp.o env.a stralloc.a \ ++ alloc.a substdio.a error.a str.a fs.a auto_qmail.o base64.o \ ++ `cat socket.lib` + + qmail-smtpd.0: \ + qmail-smtpd.8 +@@ -1555,7 +1560,7 @@ + qmail-smtpd.o: \ + 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 \ +-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 ip.h gen_alloc.h ip.h qmail.h qmail-spp.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 + ./compile qmail-smtpd.c +--- qmail-smtpd.c 2004-08-02 14:40:20.000000000 +0200 ++++ qmail-smtpd.c 2006-04-01 22:21:52.000000000 +0200 +@@ -24,6 +24,9 @@ + #include "timeoutwrite.h" + #include "commands.h" + #include "wait.h" ++#include "qmail-spp.h" ++ ++int spp_val; + + #define CRAM_MD5 + #define AUTHSLEEP 5 +@@ -125,6 +128,7 @@ + if (timeout <= 0) timeout = 1; + + if (rcpthosts_init() == -1) die_control(); ++ if (spp_init() == -1) die_control(); + + bmfok = control_readfile(&bmf,"control/badmailfrom",0); + if (bmfok == -1) die_control(); +@@ -233,16 +237,19 @@ + + int seenmail = 0; + int flagbarf; /* defined if seenmail */ ++int allowed; + stralloc mailfrom = {0}; + stralloc rcptto = {0}; + + void smtp_helo(arg) char *arg; + { ++ if(!spp_helo(arg)) return; + smtp_greet("250 "); out("\r\n"); + seenmail = 0; dohelo(arg); + } + void smtp_ehlo(arg) char *arg; + { ++ if(!spp_helo(arg)) return; + smtp_greet("250-"); + out("\r\n250-PIPELINING\r\n250-8BITMIME\r\n"); + #ifdef CRAM_MD5 +@@ -254,12 +261,15 @@ + } + void smtp_rset() + { ++ spp_rset(); + seenmail = 0; + out("250 flushed\r\n"); + } + void smtp_mail(arg) char *arg; + { + if (!addrparse(arg)) { err_syntax(); return; } ++ if (!(spp_val = spp_mail())) return; ++ if (spp_val == 1) + flagbarf = bmfcheck(); + seenmail = 1; + if (!stralloc_copys(&rcptto,"")) die_nomem(); +@@ -271,13 +281,18 @@ + if (!seenmail) { err_wantmail(); return; } + if (!addrparse(arg)) { err_syntax(); return; } + if (flagbarf) { err_bmf(); return; } ++ if (!relayclient) allowed = addrallowed(); ++ else allowed = 1; ++ if (!(spp_val = spp_rcpt(allowed))) return; + if (relayclient) { + --addr.len; + if (!stralloc_cats(&addr,relayclient)) die_nomem(); + if (!stralloc_0(&addr)) die_nomem(); + } +- else +- if (!addrallowed()) { err_nogateway(); return; } ++ else if (spp_val == 1) { ++ if (!allowed) { err_nogateway(); return; } ++ } ++ spp_rcpt_accepted(); + if (!stralloc_cats(&rcptto,"T")) die_nomem(); + if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); +@@ -392,6 +407,7 @@ + + if (!seenmail) { err_wantmail(); return; } + if (!rcptto.len) { err_wantrcpt(); return; } ++ if (!spp_data()) return; + seenmail = 0; + if (databytes) bytestooverflow = databytes + 1; + if (qmail_open(&qqt) == -1) { err_qqt(); return; } +@@ -399,6 +415,8 @@ + out("354 go ahead\r\n"); + + received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); ++ qmail_put(&qqt,sppheaders.s,sppheaders.len); /* set in qmail-spp.c */ ++ spp_rset(); + blast(&hops); + hops = (hops >= MAXHOPS); + if (hops) qmail_fail(&qqt); +@@ -624,6 +642,7 @@ + + switch (authcmds[i].fun(arg)) { + case 0: ++ if (!spp_auth(authcmds[i].text, user.s)) return; + flagauth = 1; + relayclient = ""; + remoteinfo = user.s; +@@ -663,8 +682,10 @@ + if (chdir(auto_qmail) == -1) die_control(); + setup(); + if (ipme_init() != 1) die_ipme(); ++ if (spp_connect()) { + smtp_greet("220 "); + out(" ESMTP\r\n"); ++ } + if (commands(&ssin,&smtpcommands) == 0) die_read(); + die_nomem(); + } +--- qmail-spp.c 1970-01-01 01:00:00.000000000 +0100 ++++ qmail-spp.c 2006-04-01 22:47:53.000000000 +0200 +@@ -0,0 +1,259 @@ ++/* ++ * Copyright (C) 2004-2005 Pawel Foremski ++ * ++ * 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. ++ * ++ *** Note ++ * ++ * This is the core of qmail-spp patch for qmail ++ * ++ * Why I made it a separate file? Because I wanted qmail-spp to apply more ++ * cleanly on heavily patched qmail sources and to make it bit simpler to ++ * maintain, so don't treat it as a library. ++ * ++ * "..." comments marks places where code for other SMTP commands should be ++ * added, if needed. ++ * ++ */ ++ ++#include "readwrite.h" ++#include "stralloc.h" ++#include "substdio.h" ++#include "control.h" ++#include "str.h" ++#include "byte.h" ++#include "env.h" ++#include "exit.h" ++#include "wait.h" ++#include "fork.h" ++#include "fd.h" ++#include "fmt.h" ++#include "getln.h" ++ ++/* stuff needed from qmail-smtpd */ ++extern void flush(); ++extern void out(); ++extern void die_nomem(); ++extern stralloc addr; ++/* *** */ ++ ++stralloc sppheaders = {0}; ++static int spprun = 0; ++static int sppfok = 0; ++static int sppret; ++static stralloc sppf = {0}; ++static stralloc plugins_dummy = {0}, plugins_connect = {0}, plugins_helo = {0}, plugins_mail = {0}, ++ plugins_rcpt = {0}, plugins_data = {0}, plugins_auth = {0}; /* ... */ ++static stralloc error_mail = {0}, error_rcpt = {0}, error_data = {0}; /* ... */ ++static stralloc sppmsg = {0}; ++static char rcptcountstr[FMT_ULONG]; ++static unsigned long rcptcount; ++static unsigned long rcptcountall; ++static substdio ssdown; ++static char downbuf[128]; ++ ++static void err_spp(s1, s2) char *s1, *s2; { out("451 qmail-spp failure: "); out(s1); out(": "); out(s2); out(" (#4.3.0)\r\n"); } ++ ++int spp_init() ++{ ++ int i, len = 0; ++ stralloc *plugins_to; ++ char *x, *conffile = "control/smtpplugins"; ++ ++ if (!env_get("NOSPP")) { ++ spprun = 1; ++ plugins_to = &plugins_dummy; ++ x = env_get("SPPCONFFILE"); ++ if (x && *x) conffile = x; ++ sppfok = control_readfile(&sppf, conffile, 0); ++ if (sppfok != 1) return -1; ++ for (i = 0; i < sppf.len; i += len) { ++ len = str_len(sppf.s + i) + 1; ++ if (sppf.s[i] == '[') ++ switch (sppf.s[i + 1]) { ++ case 'c': plugins_to = &plugins_connect; break; ++ case 'h': plugins_to = &plugins_helo; break; ++ case 'm': plugins_to = &plugins_mail; break; ++ case 'r': plugins_to = &plugins_rcpt; break; ++ case 'd': plugins_to = &plugins_data; break; ++ case 'a': plugins_to = &plugins_auth; break; ++ /* ... */ ++ default: plugins_to = &plugins_dummy; ++ } ++ else ++ if (!stralloc_catb(plugins_to, sppf.s + i, len)) die_nomem(); ++ } ++ } ++ ++ return 0; ++} ++ ++void sppout() { if (sppmsg.len) out(sppmsg.s); out("\r\n"); } ++ ++int spp(plugins, addrenv) stralloc *plugins; char *addrenv; ++{ ++ static int pipes[2]; ++ static int i, pid, wstat, match, last; ++ static stralloc data = {0}; ++ static char *(args[4]); ++ static stralloc *errors_to; ++ ++ if (!spprun) return 1; ++ if (addrenv) if (!env_put2(addrenv, addr.s)) die_nomem(); ++ last = 0; ++ ++ for (i = 0; i < plugins->len; i += str_len(plugins->s + i) + 1) { ++ if (plugins->s[i] == ':') ++ { args[0] = "/bin/sh"; args[1] = "-c"; args[2] = plugins->s + i + 1; args[3] = 0; } ++ else ++ { args[0] = plugins->s + i; args[1] = 0; } ++ ++ if (pipe(pipes) == -1) ++ { err_spp(plugins->s + i, "can't pipe()"); return 0; } ++ ++ switch (pid = vfork()) { ++ case -1: ++ err_spp(plugins->s + i, "vfork() failed"); ++ return 0; ++ case 0: ++ close(0); close(pipes[0]); fd_move(1, pipes[1]); ++ execv(*args, args); ++ _exit(120); ++ } ++ ++ close(pipes[1]); ++ substdio_fdbuf(&ssdown, read, pipes[0], downbuf, sizeof(downbuf)); ++ do { ++ if (getln(&ssdown, &data, &match, '\n') == -1) die_nomem(); ++ if (data.len > 1) { ++ data.s[data.len - 1] = 0; ++ switch (data.s[0]) { ++ case 'H': ++ if (!stralloc_catb(&sppheaders, data.s + 1, data.len - 2)) die_nomem(); ++ if (!stralloc_append(&sppheaders, "\n")) die_nomem(); ++ break; ++ case 'C': ++ if (addrenv) { ++ if (!stralloc_copyb(&addr, data.s + 1, data.len - 1)) die_nomem(); ++ if (!env_put2(addrenv, addr.s)) die_nomem(); ++ } ++ break; ++ case 'S': if (!env_put(data.s + 1)) die_nomem(); break; ++ case 'U': if (!env_unset(data.s + 1)) die_nomem(); break; ++ case 'A': spprun = 0; ++ case 'O': ++ case 'N': ++ case 'D': last = 1; match = 0; break; ++ case 'E': ++ case 'R': last = 1; match = 0; ++ case 'P': out(data.s + 1); out("\r\n"); break; ++ case 'L': ++ switch (data.s[1]) { ++ case 'M': errors_to = &error_mail; break; ++ case 'R': errors_to = &error_rcpt; break; ++ case 'D': errors_to = &error_data; break; ++ /* ... */ ++ default: errors_to = 0; ++ } ++ if (errors_to) { ++ if (!stralloc_catb(errors_to, data.s + 2, data.len - 3)) die_nomem(); ++ if (!stralloc_catb(errors_to, "\r\n", 2)) die_nomem(); ++ } ++ break; ++ } ++ } ++ } while (match); ++ ++ close(pipes[0]); ++ if (wait_pid(&wstat,pid) == -1) { err_spp(plugins->s + i, "wait_pid() failed"); return 0; } ++ if (wait_crashed(wstat)) { err_spp(plugins->s + i, "child crashed"); return 0; } ++ if (wait_exitcode(wstat) == 120) { err_spp(plugins->s + i, "can't execute"); return 0; } ++ ++ if (last) ++ switch (*data.s) { ++ case 'E': return 0; ++ case 'A': ++ case 'N': return 1; ++ case 'O': return 2; ++ case 'R': ++ case 'D': flush(); _exit(0); ++ } ++ } ++ ++ return 1; ++} ++ ++int spp_errors(errors) stralloc *errors; ++{ ++ if (!errors->len) return 1; ++ if (!stralloc_0(errors)) die_nomem(); ++ out(errors->s); ++ return 0; ++} ++ ++int spp_connect() { return spp(&plugins_connect, 0); } ++ ++int spp_helo(arg) char *arg; ++{ ++ if (!env_put2("SMTPHELOHOST", arg)) die_nomem(); ++ return spp(&plugins_helo, 0); ++} ++ ++void spp_rset() ++{ ++ if (!stralloc_copys(&sppheaders, "")) die_nomem(); ++ if (!stralloc_copys(&error_mail, "")) die_nomem(); ++ if (!stralloc_copys(&error_rcpt, "")) die_nomem(); ++ if (!stralloc_copys(&error_data, "")) die_nomem(); ++ /* ... */ ++ rcptcount = rcptcountall = 0; ++} ++ ++int spp_mail() ++{ ++ if (!spp_errors(&error_mail)) return 0; ++ rcptcount = rcptcountall = 0; ++ return spp(&plugins_mail, "SMTPMAILFROM"); ++} ++ ++int spp_rcpt(allowed) int allowed; ++{ ++ if (!spp_errors(&error_rcpt)) return 0; ++ rcptcountstr[fmt_ulong(rcptcountstr, rcptcount)] = 0; ++ if (!env_put2("SMTPRCPTCOUNT", rcptcountstr)) die_nomem(); ++ rcptcountstr[fmt_ulong(rcptcountstr, ++rcptcountall)] = 0; ++ if (!env_put2("SMTPRCPTCOUNTALL", rcptcountstr)) die_nomem(); ++ if (!env_put2("SMTPRCPTHOSTSOK", allowed ? "1" : "0")) die_nomem(); ++ sppret = spp(&plugins_rcpt, "SMTPRCPTTO"); ++ return sppret; ++} ++ ++void spp_rcpt_accepted() { rcptcount++; } ++ ++int spp_data() ++{ ++ if (!spp_errors(&error_data)) return 0; ++ return spp(&plugins_data, 0); ++} ++ ++int spp_auth(method, user) char *method, *user; ++{ ++ if (!env_put2("SMTPAUTHMETHOD", method)) die_nomem(); ++ if (!env_put2("SMTPAUTHUSER", user)) die_nomem(); ++ return spp(&plugins_auth, 0); ++} ++ ++/* ... */ +--- qmail-spp.h 1970-01-01 01:00:00.000000000 +0100 ++++ qmail-spp.h 2004-11-26 15:49:29.000000000 +0100 +@@ -0,0 +1,14 @@ ++#ifndef QMAIL_SPP_H ++#define QMAIL_SPP_H ++ ++extern stralloc sppheaders; ++extern int spp_init(); ++extern int spp_connect(); ++extern int spp_helo(); ++extern void spp_rset(); ++extern int spp_mail(); ++extern int spp_rcpt(); ++extern int spp_data(); ++extern int spp_auth(); ++ ++#endif diff --git a/patches/spp-smtpauth-tls-20060105.patch b/patches/spp-smtpauth-tls-20060105.patch new file mode 100644 index 0000000..cc9e643 --- /dev/null +++ b/patches/spp-smtpauth-tls-20060105.patch @@ -0,0 +1,441 @@ +diff -ruN netqmail-1.05-tls-new/Makefile netqmail-1.05/Makefile +--- Makefile 2006-04-02 17:02:34.000000000 -0400 ++++ Makefile 2006-04-02 17:09:20.000000000 -0400 +@@ -1536,19 +1536,24 @@ + auto_split.h + ./compile qmail-showctl.c + ++qmail-spp.o: \ ++compile qmail-spp.c readwrite.h stralloc.h substdio.h control.h str.h \ ++byte.h env.h exit.h wait.h fork.h fd.h fmt.h getln.h ++ ./compile qmail-spp.c ++ + qmail-smtpd: \ + load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \ + timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ + date822fmt.o now.o qmail.o cdb.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 socket.lib ++fs.a auto_qmail.o base64.o qmail-spp.o socket.lib + ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ + timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ + tls.o ssl_timeoutio.o ndelay.a -L/usr/local/ssl/lib -lssl -lcrypto \ + received.o date822fmt.o now.o qmail.o cdb.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 \ +- socket.lib` ++ datetime.a getln.a open.a sig.a case.a qmail-spp.o env.a stralloc.a \ ++ alloc.a substdio.a error.a str.a fs.a auto_qmail.o base64.o ++ `cat socket.lib` + + qmail-smtpd.0: \ + qmail-smtpd.8 +@@ -1557,7 +1562,7 @@ + qmail-smtpd.o: \ + 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 \ +-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 ip.h gen_alloc.h ip.h qmail.h qmail-spp.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 + ./compile qmail-smtpd.c +diff -ruN netqmail-1.05-tls-new/qmail-smtpd.c netqmail-1.05/qmail-smtpd.c +--- qmail-smtpd.c 2006-04-02 17:02:34.000000000 -0400 ++++ qmail-smtpd.c 2006-04-02 17:19:33.000000000 -0400 +@@ -24,6 +24,9 @@ + #include "timeoutwrite.h" + #include "commands.h" + #include "wait.h" ++#include "qmail-spp.h" ++ ++int spp_val; + + #define CRAM_MD5 + #define AUTHSLEEP 5 +@@ -151,6 +154,7 @@ + if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control(); + if (timeout <= 0) timeout = 1; + if (rcpthosts_init() == -1) die_control(); ++ if (spp_init() == -1) die_control(); + + bmfok = control_readfile(&bmf,"control/badmailfrom",0); + if (bmfok == -1) die_control(); +@@ -268,6 +272,7 @@ + + int seenmail = 0; + int flagbarf; /* defined if seenmail */ ++int allowed; + int flagsize; + stralloc mailfrom = {0}; + stralloc rcptto = {0}; +@@ -335,6 +340,7 @@ + + void smtp_helo(arg) char *arg; + { ++ if(!spp_helo(arg)) return; + smtp_greet("250 "); out("\r\n"); + seenmail = 0; dohelo(arg); + } +@@ -345,6 +351,7 @@ + struct stat st; + #endif + char size[FMT_ULONG]; ++ if(!spp_helo(arg)) return; + smtp_greet("250-"); + #ifdef TLS + if (!ssl && (stat("control/servercert.pem",&st) == 0)) +@@ -362,6 +369,7 @@ + } + void smtp_rset(arg) char *arg; + { ++ spp_rset(); + seenmail = 0; + out("250 flushed\r\n"); + } +@@ -371,6 +379,8 @@ + flagsize = 0; + mailfrom_parms(arg); + if (flagsize) { err_size(); return; } ++ if (!(spp_val = spp_mail())) return; ++ if (spp_val == 1) + flagbarf = bmfcheck(); + seenmail = 1; + if (!stralloc_copys(&rcptto,"")) die_nomem(); +@@ -382,13 +392,18 @@ + if (!seenmail) { err_wantmail(); return; } + if (!addrparse(arg)) { err_syntax(); return; } + if (flagbarf) { err_bmf(); return; } ++ if (!relayclient) allowed = addrallowed(); ++ else allowed = 1; ++ if (!(spp_val = spp_rcpt(allowed))) return; + if (relayclient) { + --addr.len; + if (!stralloc_cats(&addr,relayclient)) die_nomem(); + if (!stralloc_0(&addr)) die_nomem(); + } +- else +- if (!addrallowed()) { err_nogateway(); return; } ++ else if (spp_val == 1) { ++ if (!allowed) { err_nogateway(); return; } ++ } ++ spp_rcpt_accepted(); + if (!stralloc_cats(&rcptto,"T")) die_nomem(); + if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); +@@ -508,6 +523,7 @@ + + if (!seenmail) { err_wantmail(); return; } + if (!rcptto.len) { err_wantrcpt(); return; } ++ if (!spp_data()) return; + seenmail = 0; + if (databytes) bytestooverflow = databytes + 1; + if (qmail_open(&qqt) == -1) { err_qqt(); return; } +@@ -515,6 +531,8 @@ + out("354 go ahead\r\n"); + + received(&qqt,protocol,local,remoteip,remotehost,remoteinfo,fakehelo); ++ qmail_put(&qqt,sppheaders.s,sppheaders.len); /* set in qmail-spp.c */ ++ spp_rset(); + blast(&hops); + hops = (hops >= MAXHOPS); + if (hops) qmail_fail(&qqt); +@@ -738,6 +756,7 @@ + + switch (authcmds[i].fun(arg)) { + case 0: ++ if (!spp_auth(authcmds[i].text, user.s)) return; + flagauth = 1; + protocol = "ESMTPA"; + relayclient = ""; +@@ -1016,8 +1035,10 @@ + if (chdir(auto_qmail) == -1) die_control(); + setup(); + if (ipme_init() != 1) die_ipme(); ++ if (spp_connect()) { + smtp_greet("220 "); + out(" ESMTP\r\n"); ++ } + if (commands(&ssin,&smtpcommands) == 0) die_read(); + die_nomem(); + } +diff -ruN netqmail-1.05-tls-new/qmail-spp.c netqmail-1.05/qmail-spp.c +--- qmail-spp.c 1969-12-31 19:00:00.000000000 -0500 ++++ qmail-spp.c 2006-04-02 17:22:52.000000000 -0400 +@@ -0,0 +1,259 @@ ++/* ++ * Copyright (C) 2004-2005 Pawel Foremski ++ * ++ * 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. ++ * ++ *** Note ++ * ++ * This is the core of qmail-spp patch for qmail ++ * ++ * Why I made it a separate file? Because I wanted qmail-spp to apply more ++ * cleanly on heavily patched qmail sources and to make it bit simpler to ++ * maintain, so don't treat it as a library. ++ * ++ * "..." comments marks places where code for other SMTP commands should be ++ * added, if needed. ++ * ++ */ ++ ++#include "readwrite.h" ++#include "stralloc.h" ++#include "substdio.h" ++#include "control.h" ++#include "str.h" ++#include "byte.h" ++#include "env.h" ++#include "exit.h" ++#include "wait.h" ++#include "fork.h" ++#include "fd.h" ++#include "fmt.h" ++#include "getln.h" ++ ++/* stuff needed from qmail-smtpd */ ++extern void flush(); ++extern void out(); ++extern void die_nomem(); ++extern stralloc addr; ++/* *** */ ++ ++stralloc sppheaders = {0}; ++static int spprun = 0; ++static int sppfok = 0; ++static int sppret; ++static stralloc sppf = {0}; ++static stralloc plugins_dummy = {0}, plugins_connect = {0}, plugins_helo = {0}, plugins_mail = {0}, ++ plugins_rcpt = {0}, plugins_data = {0}, plugins_auth = {0}; /* ... */ ++static stralloc error_mail = {0}, error_rcpt = {0}, error_data = {0}; /* ... */ ++static stralloc sppmsg = {0}; ++static char rcptcountstr[FMT_ULONG]; ++static unsigned long rcptcount; ++static unsigned long rcptcountall; ++static substdio ssdown; ++static char downbuf[128]; ++ ++static void err_spp(s1, s2) char *s1, *s2; { out("451 qmail-spp failure: "); out(s1); out(": "); out(s2); out(" (#4.3.0)\r\n"); } ++ ++int spp_init() ++{ ++ int i, len = 0; ++ stralloc *plugins_to; ++ char *x, *conffile = "control/smtpplugins"; ++ ++ if (!env_get("NOSPP")) { ++ spprun = 1; ++ plugins_to = &plugins_dummy; ++ x = env_get("SPPCONFFILE"); ++ if (x && *x) conffile = x; ++ sppfok = control_readfile(&sppf, conffile, 0); ++ if (sppfok != 1) return -1; ++ for (i = 0; i < sppf.len; i += len) { ++ len = str_len(sppf.s + i) + 1; ++ if (sppf.s[i] == '[') ++ switch (sppf.s[i + 1]) { ++ case 'c': plugins_to = &plugins_connect; break; ++ case 'h': plugins_to = &plugins_helo; break; ++ case 'm': plugins_to = &plugins_mail; break; ++ case 'r': plugins_to = &plugins_rcpt; break; ++ case 'd': plugins_to = &plugins_data; break; ++ case 'a': plugins_to = &plugins_auth; break; ++ /* ... */ ++ default: plugins_to = &plugins_dummy; ++ } ++ else ++ if (!stralloc_catb(plugins_to, sppf.s + i, len)) die_nomem(); ++ } ++ } ++ ++ return 0; ++} ++ ++void sppout() { if (sppmsg.len) out(sppmsg.s); out("\r\n"); } ++ ++int spp(plugins, addrenv) stralloc *plugins; char *addrenv; ++{ ++ static int pipes[2]; ++ static int i, pid, wstat, match, last; ++ static stralloc data = {0}; ++ static char *(args[4]); ++ static stralloc *errors_to; ++ ++ if (!spprun) return 1; ++ if (addrenv) if (!env_put2(addrenv, addr.s)) die_nomem(); ++ last = 0; ++ ++ for (i = 0; i < plugins->len; i += str_len(plugins->s + i) + 1) { ++ if (plugins->s[i] == ':') ++ { args[0] = "/bin/sh"; args[1] = "-c"; args[2] = plugins->s + i + 1; args[3] = 0; } ++ else ++ { args[0] = plugins->s + i; args[1] = 0; } ++ ++ if (pipe(pipes) == -1) ++ { err_spp(plugins->s + i, "can't pipe()"); return 0; } ++ ++ switch (pid = vfork()) { ++ case -1: ++ err_spp(plugins->s + i, "vfork() failed"); ++ return 0; ++ case 0: ++ close(0); close(pipes[0]); fd_move(1, pipes[1]); ++ execv(*args, args); ++ _exit(120); ++ } ++ ++ close(pipes[1]); ++ substdio_fdbuf(&ssdown, read, pipes[0], downbuf, sizeof(downbuf)); ++ do { ++ if (getln(&ssdown, &data, &match, '\n') == -1) die_nomem(); ++ if (data.len > 1) { ++ data.s[data.len - 1] = 0; ++ switch (data.s[0]) { ++ case 'H': ++ if (!stralloc_catb(&sppheaders, data.s + 1, data.len - 2)) die_nomem(); ++ if (!stralloc_append(&sppheaders, "\n")) die_nomem(); ++ break; ++ case 'C': ++ if (addrenv) { ++ if (!stralloc_copyb(&addr, data.s + 1, data.len - 1)) die_nomem(); ++ if (!env_put2(addrenv, addr.s)) die_nomem(); ++ } ++ break; ++ case 'S': if (!env_put(data.s + 1)) die_nomem(); break; ++ case 'U': if (!env_unset(data.s + 1)) die_nomem(); break; ++ case 'A': spprun = 0; ++ case 'O': ++ case 'N': ++ case 'D': last = 1; match = 0; break; ++ case 'E': ++ case 'R': last = 1; match = 0; ++ case 'P': out(data.s + 1); out("\r\n"); break; ++ case 'L': ++ switch (data.s[1]) { ++ case 'M': errors_to = &error_mail; break; ++ case 'R': errors_to = &error_rcpt; break; ++ case 'D': errors_to = &error_data; break; ++ /* ... */ ++ default: errors_to = 0; ++ } ++ if (errors_to) { ++ if (!stralloc_catb(errors_to, data.s + 2, data.len - 3)) die_nomem(); ++ if (!stralloc_catb(errors_to, "\r\n", 2)) die_nomem(); ++ } ++ break; ++ } ++ } ++ } while (match); ++ ++ close(pipes[0]); ++ if (wait_pid(&wstat,pid) == -1) { err_spp(plugins->s + i, "wait_pid() failed"); return 0; } ++ if (wait_crashed(wstat)) { err_spp(plugins->s + i, "child crashed"); return 0; } ++ if (wait_exitcode(wstat) == 120) { err_spp(plugins->s + i, "can't execute"); return 0; } ++ ++ if (last) ++ switch (*data.s) { ++ case 'E': return 0; ++ case 'A': ++ case 'N': return 1; ++ case 'O': return 2; ++ case 'R': ++ case 'D': flush(); _exit(0); ++ } ++ } ++ ++ return 1; ++} ++ ++int spp_errors(errors) stralloc *errors; ++{ ++ if (!errors->len) return 1; ++ if (!stralloc_0(errors)) die_nomem(); ++ out(errors->s); ++ return 0; ++} ++ ++int spp_connect() { return spp(&plugins_connect, 0); } ++ ++int spp_helo(arg) char *arg; ++{ ++ if (!env_put2("SMTPHELOHOST", arg)) die_nomem(); ++ return spp(&plugins_helo, 0); ++} ++ ++void spp_rset() ++{ ++ if (!stralloc_copys(&sppheaders, "")) die_nomem(); ++ if (!stralloc_copys(&error_mail, "")) die_nomem(); ++ if (!stralloc_copys(&error_rcpt, "")) die_nomem(); ++ if (!stralloc_copys(&error_data, "")) die_nomem(); ++ /* ... */ ++ rcptcount = rcptcountall = 0; ++} ++ ++int spp_mail() ++{ ++ if (!spp_errors(&error_mail)) return 0; ++ rcptcount = rcptcountall = 0; ++ return spp(&plugins_mail, "SMTPMAILFROM"); ++} ++ ++int spp_rcpt(allowed) int allowed; ++{ ++ if (!spp_errors(&error_rcpt)) return 0; ++ rcptcountstr[fmt_ulong(rcptcountstr, rcptcount)] = 0; ++ if (!env_put2("SMTPRCPTCOUNT", rcptcountstr)) die_nomem(); ++ rcptcountstr[fmt_ulong(rcptcountstr, ++rcptcountall)] = 0; ++ if (!env_put2("SMTPRCPTCOUNTALL", rcptcountstr)) die_nomem(); ++ if (!env_put2("SMTPRCPTHOSTSOK", allowed ? "1" : "0")) die_nomem(); ++ sppret = spp(&plugins_rcpt, "SMTPRCPTTO"); ++ return sppret; ++} ++ ++void spp_rcpt_accepted() { rcptcount++; } ++ ++int spp_data() ++{ ++ if (!spp_errors(&error_data)) return 0; ++ return spp(&plugins_data, 0); ++} ++ ++int spp_auth(method, user) char *method, *user; ++{ ++ if (!env_put2("SMTPAUTHMETHOD", method)) die_nomem(); ++ if (!env_put2("SMTPAUTHUSER", user)) die_nomem(); ++ return spp(&plugins_auth, 0); ++} ++ ++/* ... */ +diff -ruN netqmail-1.05-tls-new/qmail-spp.h netqmail-1.05/qmail-spp.h +--- qmail-spp.h 1969-12-31 19:00:00.000000000 -0500 ++++ qmail-spp.h 2006-04-02 17:23:10.000000000 -0400 +@@ -0,0 +1,14 @@ ++#ifndef QMAIL_SPP_H ++#define QMAIL_SPP_H ++ ++extern stralloc sppheaders; ++extern int spp_init(); ++extern int spp_connect(); ++extern int spp_helo(); ++extern void spp_rset(); ++extern int spp_mail(); ++extern int spp_rcpt(); ++extern int spp_data(); ++extern int spp_auth(); ++ ++#endif diff --git a/patches/spp-smtpauth.patch b/patches/spp-smtpauth.patch new file mode 100644 index 0000000..cd71430 --- /dev/null +++ b/patches/spp-smtpauth.patch @@ -0,0 +1,431 @@ +--- Makefile 2004-01-02 13:05:51.000000000 +0100 ++++ Makefile 2005-01-16 19:15:29.000000000 +0100 +@@ -1535,18 +1535,23 @@ + auto_split.h + ./compile qmail-showctl.c + ++qmail-spp.o: \ ++compile qmail-spp.c readwrite.h stralloc.h substdio.h control.h str.h \ ++byte.h env.h exit.h wait.h fork.h fd.h fmt.h getln.h ++ ./compile qmail-spp.c ++ + qmail-smtpd: \ + load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \ + timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ + date822fmt.o now.o qmail.o cdb.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 socket.lib ++fs.a auto_qmail.o base64.o qmail-spp.o socket.lib + ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ + timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ + received.o date822fmt.o now.o qmail.o cdb.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 \ +- socket.lib` ++ datetime.a getln.a open.a sig.a case.a qmail-spp.o env.a stralloc.a \ ++ alloc.a substdio.a error.a str.a fs.a auto_qmail.o base64.o \ ++ `cat socket.lib` + + qmail-smtpd.0: \ + qmail-smtpd.8 +@@ -1555,7 +1560,7 @@ + qmail-smtpd.o: \ + 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 \ +-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 ip.h gen_alloc.h ip.h qmail.h qmail-spp.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 wait.h \ + fd.h base64.h +--- qmail-smtpd.c 2004-01-02 13:05:51.000000000 +0100 ++++ qmail-smtpd.c 2006-04-01 22:20:06.000000000 +0200 +@@ -25,6 +25,9 @@ + #include "commands.h" + #include "wait.h" + #include "fd.h" ++#include "qmail-spp.h" ++ ++int spp_val; + + #define AUTHCRAM + #define MAXHOPS 100 +@@ -123,6 +126,7 @@ + if (timeout <= 0) timeout = 1; + + if (rcpthosts_init() == -1) die_control(); ++ if (spp_init() == -1) die_control(); + + bmfok = control_readfile(&bmf,"control/badmailfrom",0); + if (bmfok == -1) die_control(); +@@ -231,16 +235,19 @@ + + int seenmail = 0; + int flagbarf; /* defined if seenmail */ ++int allowed; + stralloc mailfrom = {0}; + stralloc rcptto = {0}; + + void smtp_helo(arg) char *arg; + { ++ if(!spp_helo(arg)) return; + smtp_greet("250 "); out("\r\n"); + seenmail = 0; dohelo(arg); + } + void smtp_ehlo(arg) char *arg; + { ++ if(!spp_helo(arg)) return; + smtp_greet("250-"); + #ifdef AUTHCRAM + out("\r\n250-AUTH LOGIN CRAM-MD5 PLAIN"); +@@ -254,12 +261,15 @@ + } + void smtp_rset() + { ++ spp_rset(); + seenmail = 0; + out("250 flushed\r\n"); + } + void smtp_mail(arg) char *arg; + { + if (!addrparse(arg)) { err_syntax(); return; } ++ if (!(spp_val = spp_mail())) return; ++ if (spp_val == 1) + flagbarf = bmfcheck(); + seenmail = 1; + if (!stralloc_copys(&rcptto,"")) die_nomem(); +@@ -271,13 +281,18 @@ + if (!seenmail) { err_wantmail(); return; } + if (!addrparse(arg)) { err_syntax(); return; } + if (flagbarf) { err_bmf(); return; } ++ if (!relayclient) allowed = addrallowed(); ++ else allowed = 1; ++ if (!(spp_val = spp_rcpt(allowed))) return; + if (relayclient) { + --addr.len; + if (!stralloc_cats(&addr,relayclient)) die_nomem(); + if (!stralloc_0(&addr)) die_nomem(); + } +- else +- if (!addrallowed()) { err_nogateway(); return; } ++ else if (spp_val == 1) { ++ if (!allowed) { err_nogateway(); return; } ++ } ++ spp_rcpt_accepted(); + if (!stralloc_cats(&rcptto,"T")) die_nomem(); + if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); +@@ -392,6 +407,7 @@ + + if (!seenmail) { err_wantmail(); return; } + if (!rcptto.len) { err_wantrcpt(); return; } ++ if (!spp_data()) return; + seenmail = 0; + if (databytes) bytestooverflow = databytes + 1; + if (qmail_open(&qqt) == -1) { err_qqt(); return; } +@@ -399,6 +415,8 @@ + out("354 go ahead\r\n"); + + received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); ++ qmail_put(&qqt,sppheaders.s,sppheaders.len); /* set in qmail-spp.c */ ++ spp_rset(); + blast(&hops); + hops = (hops >= MAXHOPS); + if (hops) qmail_fail(&qqt); +@@ -617,6 +635,7 @@ + + switch (authcmds[i].fun(arg)) { + case 0: ++ if (!spp_auth(authcmds[i].text, user.s)) return; + authd = 1; + relayclient = ""; + remoteinfo = user.s; +@@ -655,8 +674,10 @@ + if (chdir(auto_qmail) == -1) die_control(); + setup(); + if (ipme_init() != 1) die_ipme(); ++ if (spp_connect()) { + smtp_greet("220 "); + out(" ESMTP\r\n"); ++ } + if (commands(&ssin,&smtpcommands) == 0) die_read(); + die_nomem(); + } +--- qmail-spp.c 1970-01-01 01:00:00.000000000 +0100 ++++ qmail-spp.c 2006-04-01 22:47:15.000000000 +0200 +@@ -0,0 +1,259 @@ ++/* ++ * Copyright (C) 2004-2005 Pawel Foremski ++ * ++ * 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. ++ * ++ *** Note ++ * ++ * This is the core of qmail-spp patch for qmail ++ * ++ * Why I made it a separate file? Because I wanted qmail-spp to apply more ++ * cleanly on heavily patched qmail sources and to make it bit simpler to ++ * maintain, so don't treat it as a library. ++ * ++ * "..." comments marks places where code for other SMTP commands should be ++ * added, if needed. ++ * ++ */ ++ ++#include "readwrite.h" ++#include "stralloc.h" ++#include "substdio.h" ++#include "control.h" ++#include "str.h" ++#include "byte.h" ++#include "env.h" ++#include "exit.h" ++#include "wait.h" ++#include "fork.h" ++#include "fd.h" ++#include "fmt.h" ++#include "getln.h" ++ ++/* stuff needed from qmail-smtpd */ ++extern void flush(); ++extern void out(); ++extern void die_nomem(); ++extern stralloc addr; ++/* *** */ ++ ++stralloc sppheaders = {0}; ++static int spprun = 0; ++static int sppfok = 0; ++static int sppret; ++static stralloc sppf = {0}; ++static stralloc plugins_dummy = {0}, plugins_connect = {0}, plugins_helo = {0}, plugins_mail = {0}, ++ plugins_rcpt = {0}, plugins_data = {0}, plugins_auth = {0}; /* ... */ ++static stralloc error_mail = {0}, error_rcpt = {0}, error_data = {0}; /* ... */ ++static stralloc sppmsg = {0}; ++static char rcptcountstr[FMT_ULONG]; ++static unsigned long rcptcount; ++static unsigned long rcptcountall; ++static substdio ssdown; ++static char downbuf[128]; ++ ++static void err_spp(s1, s2) char *s1, *s2; { out("451 qmail-spp failure: "); out(s1); out(": "); out(s2); out(" (#4.3.0)\r\n"); } ++ ++int spp_init() ++{ ++ int i, len = 0; ++ stralloc *plugins_to; ++ char *x, *conffile = "control/smtpplugins"; ++ ++ if (!env_get("NOSPP")) { ++ spprun = 1; ++ plugins_to = &plugins_dummy; ++ x = env_get("SPPCONFFILE"); ++ if (x && *x) conffile = x; ++ sppfok = control_readfile(&sppf, conffile, 0); ++ if (sppfok != 1) return -1; ++ for (i = 0; i < sppf.len; i += len) { ++ len = str_len(sppf.s + i) + 1; ++ if (sppf.s[i] == '[') ++ switch (sppf.s[i + 1]) { ++ case 'c': plugins_to = &plugins_connect; break; ++ case 'h': plugins_to = &plugins_helo; break; ++ case 'm': plugins_to = &plugins_mail; break; ++ case 'r': plugins_to = &plugins_rcpt; break; ++ case 'd': plugins_to = &plugins_data; break; ++ case 'a': plugins_to = &plugins_auth; break; ++ /* ... */ ++ default: plugins_to = &plugins_dummy; ++ } ++ else ++ if (!stralloc_catb(plugins_to, sppf.s + i, len)) die_nomem(); ++ } ++ } ++ ++ return 0; ++} ++ ++void sppout() { if (sppmsg.len) out(sppmsg.s); out("\r\n"); } ++ ++int spp(plugins, addrenv) stralloc *plugins; char *addrenv; ++{ ++ static int pipes[2]; ++ static int i, pid, wstat, match, last; ++ static stralloc data = {0}; ++ static char *(args[4]); ++ static stralloc *errors_to; ++ ++ if (!spprun) return 1; ++ if (addrenv) if (!env_put2(addrenv, addr.s)) die_nomem(); ++ last = 0; ++ ++ for (i = 0; i < plugins->len; i += str_len(plugins->s + i) + 1) { ++ if (plugins->s[i] == ':') ++ { args[0] = "/bin/sh"; args[1] = "-c"; args[2] = plugins->s + i + 1; args[3] = 0; } ++ else ++ { args[0] = plugins->s + i; args[1] = 0; } ++ ++ if (pipe(pipes) == -1) ++ { err_spp(plugins->s + i, "can't pipe()"); return 0; } ++ ++ switch (pid = vfork()) { ++ case -1: ++ err_spp(plugins->s + i, "vfork() failed"); ++ return 0; ++ case 0: ++ close(0); close(pipes[0]); fd_move(1, pipes[1]); ++ execv(*args, args); ++ _exit(120); ++ } ++ ++ close(pipes[1]); ++ substdio_fdbuf(&ssdown, read, pipes[0], downbuf, sizeof(downbuf)); ++ do { ++ if (getln(&ssdown, &data, &match, '\n') == -1) die_nomem(); ++ if (data.len > 1) { ++ data.s[data.len - 1] = 0; ++ switch (data.s[0]) { ++ case 'H': ++ if (!stralloc_catb(&sppheaders, data.s + 1, data.len - 2)) die_nomem(); ++ if (!stralloc_append(&sppheaders, "\n")) die_nomem(); ++ break; ++ case 'C': ++ if (addrenv) { ++ if (!stralloc_copyb(&addr, data.s + 1, data.len - 1)) die_nomem(); ++ if (!env_put2(addrenv, addr.s)) die_nomem(); ++ } ++ break; ++ case 'S': if (!env_put(data.s + 1)) die_nomem(); break; ++ case 'U': if (!env_unset(data.s + 1)) die_nomem(); break; ++ case 'A': spprun = 0; ++ case 'O': ++ case 'N': ++ case 'D': last = 1; match = 0; break; ++ case 'E': ++ case 'R': last = 1; match = 0; ++ case 'P': out(data.s + 1); out("\r\n"); break; ++ case 'L': ++ switch (data.s[1]) { ++ case 'M': errors_to = &error_mail; break; ++ case 'R': errors_to = &error_rcpt; break; ++ case 'D': errors_to = &error_data; break; ++ /* ... */ ++ default: errors_to = 0; ++ } ++ if (errors_to) { ++ if (!stralloc_catb(errors_to, data.s + 2, data.len - 3)) die_nomem(); ++ if (!stralloc_catb(errors_to, "\r\n", 2)) die_nomem(); ++ } ++ break; ++ } ++ } ++ } while (match); ++ ++ close(pipes[0]); ++ if (wait_pid(&wstat,pid) == -1) { err_spp(plugins->s + i, "wait_pid() failed"); return 0; } ++ if (wait_crashed(wstat)) { err_spp(plugins->s + i, "child crashed"); return 0; } ++ if (wait_exitcode(wstat) == 120) { err_spp(plugins->s + i, "can't execute"); return 0; } ++ ++ if (last) ++ switch (*data.s) { ++ case 'E': return 0; ++ case 'A': ++ case 'N': return 1; ++ case 'O': return 2; ++ case 'R': ++ case 'D': flush(); _exit(0); ++ } ++ } ++ ++ return 1; ++} ++ ++int spp_errors(errors) stralloc *errors; ++{ ++ if (!errors->len) return 1; ++ if (!stralloc_0(errors)) die_nomem(); ++ out(errors->s); ++ return 0; ++} ++ ++int spp_connect() { return spp(&plugins_connect, 0); } ++ ++int spp_helo(arg) char *arg; ++{ ++ if (!env_put2("SMTPHELOHOST", arg)) die_nomem(); ++ return spp(&plugins_helo, 0); ++} ++ ++void spp_rset() ++{ ++ if (!stralloc_copys(&sppheaders, "")) die_nomem(); ++ if (!stralloc_copys(&error_mail, "")) die_nomem(); ++ if (!stralloc_copys(&error_rcpt, "")) die_nomem(); ++ if (!stralloc_copys(&error_data, "")) die_nomem(); ++ /* ... */ ++ rcptcount = rcptcountall = 0; ++} ++ ++int spp_mail() ++{ ++ if (!spp_errors(&error_mail)) return 0; ++ rcptcount = rcptcountall = 0; ++ return spp(&plugins_mail, "SMTPMAILFROM"); ++} ++ ++int spp_rcpt(allowed) int allowed; ++{ ++ if (!spp_errors(&error_rcpt)) return 0; ++ rcptcountstr[fmt_ulong(rcptcountstr, rcptcount)] = 0; ++ if (!env_put2("SMTPRCPTCOUNT", rcptcountstr)) die_nomem(); ++ rcptcountstr[fmt_ulong(rcptcountstr, ++rcptcountall)] = 0; ++ if (!env_put2("SMTPRCPTCOUNTALL", rcptcountstr)) die_nomem(); ++ if (!env_put2("SMTPRCPTHOSTSOK", allowed ? "1" : "0")) die_nomem(); ++ sppret = spp(&plugins_rcpt, "SMTPRCPTTO"); ++ return sppret; ++} ++ ++void spp_rcpt_accepted() { rcptcount++; } ++ ++int spp_data() ++{ ++ if (!spp_errors(&error_data)) return 0; ++ return spp(&plugins_data, 0); ++} ++ ++int spp_auth(method, user) char *method, *user; ++{ ++ if (!env_put2("SMTPAUTHMETHOD", method)) die_nomem(); ++ if (!env_put2("SMTPAUTHUSER", user)) die_nomem(); ++ return spp(&plugins_auth, 0); ++} ++ ++/* ... */ +--- qmail-spp.h 1970-01-01 01:00:00.000000000 +0100 ++++ qmail-spp.h 2004-11-26 15:49:29.000000000 +0100 +@@ -0,0 +1,14 @@ ++#ifndef QMAIL_SPP_H ++#define QMAIL_SPP_H ++ ++extern stralloc sppheaders; ++extern int spp_init(); ++extern int spp_connect(); ++extern int spp_helo(); ++extern void spp_rset(); ++extern int spp_mail(); ++extern int spp_rcpt(); ++extern int spp_data(); ++extern int spp_auth(); ++ ++#endif diff --git a/patches/spp.patch b/patches/spp.patch new file mode 100644 index 0000000..313d155 --- /dev/null +++ b/patches/spp.patch @@ -0,0 +1,413 @@ +--- Makefile 1998-06-15 12:53:16.000000000 +0200 ++++ Makefile 2005-01-19 12:58:24.000000000 +0100 +@@ -1531,18 +1531,23 @@ + auto_split.h + ./compile qmail-showctl.c + ++qmail-spp.o: \ ++compile qmail-spp.c readwrite.h stralloc.h substdio.h control.h str.h \ ++byte.h env.h exit.h wait.h fork.h fd.h fmt.h getln.h ++ ./compile qmail-spp.c ++ + qmail-smtpd: \ + load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \ + timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ + date822fmt.o now.o qmail.o cdb.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 socket.lib ++fs.a auto_qmail.o str.a qmail-spp.o socket.lib + ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ + timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ + received.o date822fmt.o now.o qmail.o cdb.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 `cat \ +- socket.lib` ++ datetime.a getln.a open.a sig.a case.a qmail-spp.o env.a stralloc.a \ ++ alloc.a substdio.a error.a str.a fs.a auto_qmail.o \ ++ str.a `cat socket.lib` + + qmail-smtpd.0: \ + qmail-smtpd.8 +@@ -1553,7 +1558,7 @@ + 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 \ + 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 ++exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h qmail-spp.h + ./compile qmail-smtpd.c + + qmail-start: \ +--- qmail-smtpd.c 1998-06-15 12:53:16.000000000 +0200 ++++ qmail-smtpd.c 2006-03-03 16:52:51.000000000 +0100 +@@ -23,6 +23,9 @@ + #include "timeoutread.h" + #include "timeoutwrite.h" + #include "commands.h" ++#include "qmail-spp.h" ++ ++int spp_val; + + #define MAXHOPS 100 + unsigned int databytes = 0; +@@ -111,6 +114,7 @@ + if (timeout <= 0) timeout = 1; + + if (rcpthosts_init() == -1) die_control(); ++ if (spp_init() == -1) die_control(); + + bmfok = control_readfile(&bmf,"control/badmailfrom",0); + if (bmfok == -1) die_control(); +@@ -219,27 +223,33 @@ + + int seenmail = 0; + int flagbarf; /* defined if seenmail */ ++int allowed; + stralloc mailfrom = {0}; + stralloc rcptto = {0}; + + void smtp_helo(arg) char *arg; + { ++ if(!spp_helo(arg)) return; + smtp_greet("250 "); out("\r\n"); + seenmail = 0; dohelo(arg); + } + void smtp_ehlo(arg) char *arg; + { ++ if(!spp_helo(arg)) return; + smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); + seenmail = 0; dohelo(arg); + } + void smtp_rset() + { ++ spp_rset(); + seenmail = 0; + out("250 flushed\r\n"); + } + void smtp_mail(arg) char *arg; + { + if (!addrparse(arg)) { err_syntax(); return; } ++ if (!(spp_val = spp_mail())) return; ++ if (spp_val == 1) + flagbarf = bmfcheck(); + seenmail = 1; + if (!stralloc_copys(&rcptto,"")) die_nomem(); +@@ -251,13 +261,18 @@ + if (!seenmail) { err_wantmail(); return; } + if (!addrparse(arg)) { err_syntax(); return; } + if (flagbarf) { err_bmf(); return; } ++ if (!relayclient) allowed = addrallowed(); ++ else allowed = 1; ++ if (!(spp_val = spp_rcpt(allowed))) return; + if (relayclient) { + --addr.len; + if (!stralloc_cats(&addr,relayclient)) die_nomem(); + if (!stralloc_0(&addr)) die_nomem(); + } +- else +- if (!addrallowed()) { err_nogateway(); return; } ++ else if (spp_val == 1) { ++ if (!allowed) { err_nogateway(); return; } ++ } ++ spp_rcpt_accepted(); + if (!stralloc_cats(&rcptto,"T")) die_nomem(); + if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); +@@ -372,6 +387,7 @@ + + if (!seenmail) { err_wantmail(); return; } + if (!rcptto.len) { err_wantrcpt(); return; } ++ if (!spp_data()) return; + seenmail = 0; + if (databytes) bytestooverflow = databytes + 1; + if (qmail_open(&qqt) == -1) { err_qqt(); return; } +@@ -379,6 +395,8 @@ + out("354 go ahead\r\n"); + + received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); ++ qmail_put(&qqt,sppheaders.s,sppheaders.len); /* set in qmail-spp.c */ ++ spp_rset(); + blast(&hops); + hops = (hops >= MAXHOPS); + if (hops) qmail_fail(&qqt); +@@ -414,8 +432,10 @@ + if (chdir(auto_qmail) == -1) die_control(); + setup(); + if (ipme_init() != 1) die_ipme(); ++ if (spp_connect()) { + smtp_greet("220 "); + out(" ESMTP\r\n"); ++ } + if (commands(&ssin,&smtpcommands) == 0) die_read(); + die_nomem(); + } +--- qmail-spp.c 1970-01-01 01:00:00.000000000 +0100 ++++ qmail-spp.c 2006-04-01 22:45:51.000000000 +0200 +@@ -0,0 +1,251 @@ ++/* ++ * Copyright (C) 2004-2005 Pawel Foremski ++ * ++ * 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. ++ * ++ *** Note ++ * ++ * This is the core of qmail-spp patch for qmail ++ * ++ * Why I made it a separate file? Because I wanted qmail-spp to apply more ++ * cleanly on heavily patched qmail sources and to make it bit simpler to ++ * maintain, so don't treat it as a library. ++ * ++ * "..." comments marks places where code for other SMTP commands should be ++ * added, if needed. ++ * ++ */ ++ ++#include "readwrite.h" ++#include "stralloc.h" ++#include "substdio.h" ++#include "control.h" ++#include "str.h" ++#include "byte.h" ++#include "env.h" ++#include "exit.h" ++#include "wait.h" ++#include "fork.h" ++#include "fd.h" ++#include "fmt.h" ++#include "getln.h" ++ ++/* stuff needed from qmail-smtpd */ ++extern void flush(); ++extern void out(); ++extern void die_nomem(); ++extern stralloc addr; ++/* *** */ ++ ++stralloc sppheaders = {0}; ++static int spprun = 0; ++static int sppfok = 0; ++static int sppret; ++static stralloc sppf = {0}; ++static stralloc plugins_dummy = {0}, plugins_connect = {0}, plugins_helo = {0}, plugins_mail = {0}, ++ plugins_rcpt = {0}, plugins_data = {0}; /* ... */ ++static stralloc error_mail = {0}, error_rcpt = {0}, error_data = {0}; /* ... */ ++static stralloc sppmsg = {0}; ++static char rcptcountstr[FMT_ULONG]; ++static unsigned long rcptcount; ++static unsigned long rcptcountall; ++static substdio ssdown; ++static char downbuf[128]; ++ ++static void err_spp(s1, s2) char *s1, *s2; { out("451 qmail-spp failure: "); out(s1); out(": "); out(s2); out(" (#4.3.0)\r\n"); } ++ ++int spp_init() ++{ ++ int i, len = 0; ++ stralloc *plugins_to; ++ char *x, *conffile = "control/smtpplugins"; ++ ++ if (!env_get("NOSPP")) { ++ spprun = 1; ++ plugins_to = &plugins_dummy; ++ x = env_get("SPPCONFFILE"); ++ if (x && *x) conffile = x; ++ sppfok = control_readfile(&sppf, conffile, 0); ++ if (sppfok != 1) return -1; ++ for (i = 0; i < sppf.len; i += len) { ++ len = str_len(sppf.s + i) + 1; ++ if (sppf.s[i] == '[') ++ switch (sppf.s[i + 1]) { ++ case 'c': plugins_to = &plugins_connect; break; ++ case 'h': plugins_to = &plugins_helo; break; ++ case 'm': plugins_to = &plugins_mail; break; ++ case 'r': plugins_to = &plugins_rcpt; break; ++ case 'd': plugins_to = &plugins_data; break; ++ /* ... */ ++ default: plugins_to = &plugins_dummy; ++ } ++ else ++ if (!stralloc_catb(plugins_to, sppf.s + i, len)) die_nomem(); ++ } ++ } ++ ++ return 0; ++} ++ ++void sppout() { if (sppmsg.len) out(sppmsg.s); out("\r\n"); } ++ ++int spp(plugins, addrenv) stralloc *plugins; char *addrenv; ++{ ++ static int pipes[2]; ++ static int i, pid, wstat, match, last; ++ static stralloc data = {0}; ++ static char *(args[4]); ++ static stralloc *errors_to; ++ ++ if (!spprun) return 1; ++ if (addrenv) if (!env_put2(addrenv, addr.s)) die_nomem(); ++ last = 0; ++ ++ for (i = 0; i < plugins->len; i += str_len(plugins->s + i) + 1) { ++ if (plugins->s[i] == ':') ++ { args[0] = "/bin/sh"; args[1] = "-c"; args[2] = plugins->s + i + 1; args[3] = 0; } ++ else ++ { args[0] = plugins->s + i; args[1] = 0; } ++ ++ if (pipe(pipes) == -1) ++ { err_spp(plugins->s + i, "can't pipe()"); return 0; } ++ ++ switch (pid = vfork()) { ++ case -1: ++ err_spp(plugins->s + i, "vfork() failed"); ++ return 0; ++ case 0: ++ close(0); close(pipes[0]); fd_move(1, pipes[1]); ++ execv(*args, args); ++ _exit(120); ++ } ++ ++ close(pipes[1]); ++ substdio_fdbuf(&ssdown, read, pipes[0], downbuf, sizeof(downbuf)); ++ do { ++ if (getln(&ssdown, &data, &match, '\n') == -1) die_nomem(); ++ if (data.len > 1) { ++ data.s[data.len - 1] = 0; ++ switch (data.s[0]) { ++ case 'H': ++ if (!stralloc_catb(&sppheaders, data.s + 1, data.len - 2)) die_nomem(); ++ if (!stralloc_append(&sppheaders, "\n")) die_nomem(); ++ break; ++ case 'C': ++ if (addrenv) { ++ if (!stralloc_copyb(&addr, data.s + 1, data.len - 1)) die_nomem(); ++ if (!env_put2(addrenv, addr.s)) die_nomem(); ++ } ++ break; ++ case 'S': if (!env_put(data.s + 1)) die_nomem(); break; ++ case 'U': if (!env_unset(data.s + 1)) die_nomem(); break; ++ case 'A': spprun = 0; ++ case 'O': ++ case 'N': ++ case 'D': last = 1; match = 0; break; ++ case 'E': ++ case 'R': last = 1; match = 0; ++ case 'P': out(data.s + 1); out("\r\n"); break; ++ case 'L': ++ switch (data.s[1]) { ++ case 'M': errors_to = &error_mail; break; ++ case 'R': errors_to = &error_rcpt; break; ++ case 'D': errors_to = &error_data; break; ++ /* ... */ ++ default: errors_to = 0; ++ } ++ if (errors_to) { ++ if (!stralloc_catb(errors_to, data.s + 2, data.len - 3)) die_nomem(); ++ if (!stralloc_catb(errors_to, "\r\n", 2)) die_nomem(); ++ } ++ break; ++ } ++ } ++ } while (match); ++ ++ close(pipes[0]); ++ if (wait_pid(&wstat,pid) == -1) { err_spp(plugins->s + i, "wait_pid() failed"); return 0; } ++ if (wait_crashed(wstat)) { err_spp(plugins->s + i, "child crashed"); return 0; } ++ if (wait_exitcode(wstat) == 120) { err_spp(plugins->s + i, "can't execute"); return 0; } ++ ++ if (last) ++ switch (*data.s) { ++ case 'E': return 0; ++ case 'A': ++ case 'N': return 1; ++ case 'O': return 2; ++ case 'R': ++ case 'D': flush(); _exit(0); ++ } ++ } ++ ++ return 1; ++} ++ ++int spp_errors(errors) stralloc *errors; ++{ ++ if (!errors->len) return 1; ++ if (!stralloc_0(errors)) die_nomem(); ++ out(errors->s); ++ return 0; ++} ++ ++int spp_connect() { return spp(&plugins_connect, 0); } ++ ++int spp_helo(arg) char *arg; ++{ ++ if (!env_put2("SMTPHELOHOST", arg)) die_nomem(); ++ return spp(&plugins_helo, 0); ++} ++ ++void spp_rset() ++{ ++ if (!stralloc_copys(&sppheaders, "")) die_nomem(); ++ if (!stralloc_copys(&error_mail, "")) die_nomem(); ++ if (!stralloc_copys(&error_rcpt, "")) die_nomem(); ++ if (!stralloc_copys(&error_data, "")) die_nomem(); ++ /* ... */ ++ rcptcount = rcptcountall = 0; ++} ++ ++int spp_mail() ++{ ++ if (!spp_errors(&error_mail)) return 0; ++ rcptcount = rcptcountall = 0; ++ return spp(&plugins_mail, "SMTPMAILFROM"); ++} ++ ++int spp_rcpt(allowed) int allowed; ++{ ++ if (!spp_errors(&error_rcpt)) return 0; ++ rcptcountstr[fmt_ulong(rcptcountstr, rcptcount)] = 0; ++ if (!env_put2("SMTPRCPTCOUNT", rcptcountstr)) die_nomem(); ++ rcptcountstr[fmt_ulong(rcptcountstr, ++rcptcountall)] = 0; ++ if (!env_put2("SMTPRCPTCOUNTALL", rcptcountstr)) die_nomem(); ++ if (!env_put2("SMTPRCPTHOSTSOK", allowed ? "1" : "0")) die_nomem(); ++ sppret = spp(&plugins_rcpt, "SMTPRCPTTO"); ++ return sppret; ++} ++ ++void spp_rcpt_accepted() { rcptcount++; } ++ ++int spp_data() ++{ ++ if (!spp_errors(&error_data)) return 0; ++ return spp(&plugins_data, 0); ++} ++ ++/* ... */ +--- qmail-spp.h 1970-01-01 01:00:00.000000000 +0100 ++++ qmail-spp.h 2005-01-16 18:48:55.000000000 +0100 +@@ -0,0 +1,14 @@ ++#ifndef QMAIL_SPP_H ++#define QMAIL_SPP_H ++ ++extern stralloc sppheaders; ++extern int spp_init(); ++extern int spp_connect(); ++extern int spp_helo(); ++extern void spp_rset(); ++extern int spp_mail(); ++extern int spp_rcpt(); ++extern int spp_rcpt_accepted(); ++extern int spp_data(); ++ ++#endif