This patch can be used to create qmail-dk. #fetch libdomainkeys from http://sourceforge.net/projects/domainkeys/ tar xfz libdomainkeys-0.62.tar.gz cd libdomainkeys-0.62 make tar xfz /path/to/qmail-1.03.tar.gz cd qmail-1.03 patch qmail-control.5 +qmail-dk: \ +load qmail-dk.o triggerpull.o fmtqfn.o now.o date822fmt.o \ +datetime.a seek.a ndelay.a open.a sig.a alloc.a substdio.a error.a \ +str.a fs.a auto_qmail.o auto_split.o auto_uids.o fd.a wait.a \ +../libdomainkeys.a env.a getln.a control.o stralloc.a dns.lib + ./load qmail-dk triggerpull.o fmtqfn.o now.o \ + date822fmt.o datetime.a seek.a ndelay.a open.a sig.a \ + substdio.a error.a fs.a auto_qmail.o \ + auto_split.o auto_uids.o \ + fd.a wait.a \ + ../libdomainkeys.a -lcrypto env.a control.o open.a getln.a \ + stralloc.a alloc.a scan_ulong.o str.a `cat dns.lib` + +qmail-dk.0: \ +qmail-dk.8 + nroff -man qmail-dk.8 > qmail-dk.0 + +qmail-dk.o: \ +compile qmail-dk.c readwrite.h sig.h exit.h open.h seek.h fmt.h \ +alloc.h substdio.h datetime.h now.h datetime.h triggerpull.h extra.h \ +env.h wait.h fd.h fork.h str.h \ +auto_qmail.h auto_uids.h date822fmt.h fmtqfn.h + ./compile qmail-dk.c + qmail-getpw: \ load qmail-getpw.o case.a substdio.a error.a str.a fs.a auto_break.o \ auto_usera.o diff -u orig/qmail-dk.8 ./qmail-dk.8 --- orig/qmail-dk.8 2004-08-22 21:15:13.000000000 -0400 +++ ./qmail-dk.8 2004-10-05 20:25:01.000000000 -0400 @@ -0,0 +1,115 @@ +.TH qmail-dk 8 +.SH NAME +qmail-dk \- sign/verify and queue a mail message for delivery +.SH SYNOPSIS +.B qmail-dk +.SH DESCRIPTION +.B qmail-dk +has the same interface as +.B qmail-queue +except that it inserts an appropriate DomainKeys header before it +queues the message. There are two separate ways to invoke +.BR qmail-dk . +For one way, you can patch qmail with the http://qmail.org/qmailqueue +patch and set QMAILQUEUE to point to qmail-dk in the environment when +you send or receive email. +For another way, you can rename qmail-queue to qmail-queue.orig, and set DKQUEUE=bin/qmail-queue.orig. + +.B qmail-dk +supports DomainKey signing and verification. It uses the libdomainkey +and OpenSSL libraries. To sign a message, set the +.B DKSIGN +environment variable to the pathname to the private key that will be +used to sign the message. If there is a % character in the environment +variable, it is removed and replaced by the domain name in the From: header. +If, after substituting the %, that file does not exist, the message will not be signed. +If there is no % and the file does not exist, the message will be rejected with error 32. +The selector will be taken from the +basename of the file. The private key should be created by +.BR dknewkey , +which comes with libdomainkey. + +To verify a message, set the +.B DKVERIFY +environment variable to a desired set of letters. Precisely, if you +want a libdomainkey return status to generate an error, include that +letter, where A is the first return status (DK_STAT_OK), B is the +second (DK_STAT_BADSIG), etc. The letter should be uppercase if you +want a permanent error to be returned (exit code 13), and lowercase if +you want a temporary error to be returned (exit code 82). + +For example, if you want to permanently reject messages that have a +signature that has been revoked, include the letter 'K' in the +.B DKVERIFY +environment variable. A conservative set of letters is +.BR DEGIJKfh . +Reject permanently BADSIG, NOKEY, BADKEY, SYNTAX, ARGS, REVOKED, and +INTERNAL errors, and temporarily CANTVRFY and NORESOURCE. Add in +.B B +if you want to reject messages that have a signature that doesn't +verify (presumably because the message is a forgery or has been +damaged in transit. Note that +.B qmail-dk +always inserts the +.B DomainKey-Status +header, so that messages can be +rejected at delivery time, or in the mail reader. + +Typically, you would sign messages generated on-host by setting +.B DKSIGN +in the environment before running an email program. DKSIGN will be carried +through qmail's sendmail emulation through +.B qmail-inject +to +.BR qmail-dk . +You would also set it for +.B qmail-smtpd +at the same time +.B RELAYCLIENT +is set, most often in the tcpserver cdb file. If a host is authorized +to relay, you probably want to sign messages sent by that host. +.B DKVERIFY +should be set for all other hosts. + +If neither +.B DKSIGN +nor +.B DKVERIFY +are set, then +.B DKSIGN +will be set to /etc/domainkeys/%/default. If such a private key exists, it will be used to sign the domain. + +.B qmail-dk +will ordinarily spawn qmail-queue, but if DKQUEUE is set in the environment, +the program that it points to will be executed instead. If DKQUEUE is not set, and +.B qmail-dk +has been invoked as +.B qmail-queue +then +.B qmail-queue.orig +is spawned instead. + +.SH "EXIT CODES" +.B qmail-dk +returns the same exit codes as qmail-queue with these additions: +.TP 5 +.B 32 +The private key file does not exist. +.TP 5 +.B 57 +Trouble waiting for qmail-queue to exit. +.TP 5 +.B 58 +Unable to vfork. +.TP 5 +.B 59 +Unable to create a pipe to qmail-queue. +.SH "SEE ALSO" +addresses(5), +envelopes(5), +qmail-header(5), +qmail-inject(8), +qmail-qmqpc(8), +qmail-queue(8), +qmail-send(8), +qmail-smtpd(8) diff -u orig/qmail-dk.c ./qmail-dk.c --- orig/qmail-dk.c 2004-08-22 21:15:17.000000000 -0400 +++ ./qmail-dk.c 2004-10-19 18:08:49.000000000 -0400 @@ -0,0 +1,298 @@ +#include +#include "readwrite.h" +#include "sig.h" +#include "exit.h" +#include "open.h" +#include "seek.h" +#include "fmt.h" +#include "alloc.h" +#include "str.h" +#include "stralloc.h" +#include "substdio.h" +#include "datetime.h" +#include "now.h" +#include "fork.h" +#include "wait.h" +#include "extra.h" +#include "auto_qmail.h" +#include "auto_uids.h" +#include "date822fmt.h" +#include "fmtqfn.h" +#include "env.h" +#include "control.h" +#include "../domainkeys.h" + +#define DEATH 86400 /* 24 hours; _must_ be below q-s's OSSIFIED (36 hours) */ +#define ADDR 1003 + +char inbuf[2048]; +struct substdio ssin; +char outbuf[256]; +struct substdio ssout; + +datetime_sec starttime; +struct datetime dt; +unsigned long mypid; +unsigned long uid; +char *pidfn; +int messfd; +int readfd; + +void die(e) int e; { _exit(e); } +void die_write() { die(53); } +void die_read() { die(54); } +void sigalrm() { /* thou shalt not clean up here */ die(52); } +void sigbug() { die(81); } +void maybe_die_dk(e) DK_STAT e; { + switch(e) { + case DK_STAT_BADKEY: _exit(55); + case DK_STAT_CANTVRFY: _exit(74); + case DK_STAT_NORESOURCE: _exit(51); + case DK_STAT_ARGS: _exit(12); + case DK_STAT_SYNTAX: _exit(31); + case DK_STAT_INTERNAL: _exit(81); + } +} + +unsigned int pidfmt(s,seq) +char *s; +unsigned long seq; +{ + unsigned int i; + unsigned int len; + + len = 0; + i = fmt_str(s,"/tmp/qmail-dk."); len += i; if (s) s += i; + i = fmt_ulong(s,mypid); len += i; if (s) s += i; + i = fmt_str(s,"."); len += i; if (s) s += i; + i = fmt_ulong(s,starttime); len += i; if (s) s += i; + i = fmt_str(s,"."); len += i; if (s) s += i; + i = fmt_ulong(s,seq); len += i; if (s) s += i; + ++len; if (s) *s++ = 0; + + return len; +} + +void pidopen() +{ + unsigned int len; + unsigned long seq; + + seq = 1; + len = pidfmt((char *) 0,seq); + pidfn = alloc(len); + if (!pidfn) die(51); + + for (seq = 1;seq < 10;++seq) + { + if (pidfmt((char *) 0,seq) > len) die(81); /* paranoia */ + pidfmt(pidfn,seq); + messfd = open_excl(pidfn); + if (messfd != -1) return; + } + + die(63); +} + +char tmp[FMT_ULONG]; + +char *dksign = 0; +stralloc dksignature = {0}; +stralloc dkoutput = {0}; +char *dkverify = 0; +char *dkqueue = 0; +DK_LIB *dklib; +DK *dk; +DK_STAT st; + +static void write_signature(DK *dk, char *keyfn) +{ + char advice[2048]; + char *selector; + char *from; + static stralloc keyfnfrom = {0}; + int i; + + from = dk_from(dk); + i = str_chr(keyfn, '%'); + if (keyfn[i]) { + if (!stralloc_copyb(&keyfnfrom,keyfn,i)) die(51); + if (!stralloc_cats(&keyfnfrom,from)) die(51); + if (!stralloc_cats(&keyfnfrom,keyfn + i + 1)) die(51); + } else { + if (!stralloc_copys(&keyfnfrom,keyfn)) die(51); + } + if (!stralloc_0(&keyfnfrom)) die(51); + + switch(control_readfile(&dksignature,keyfnfrom.s,0)) { + case 0: + if (keyfn[i]) return; + die(32); + case 1: break; + default: die(31); + } + for(i=0; i < dksignature.len; i++) + if (dksignature.s[i] == '\0') dksignature.s[i] = '\n'; + if (!stralloc_0(&dksignature)) die(51); + + st = dk_getsig(dk, dksignature.s, advice, sizeof(advice)); + maybe_die_dk(st); + + if (!stralloc_cats(&dkoutput, + "Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys\n" + "DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws;\n" + " s=")) die(51); + selector = keyfn; + while (*keyfn) { + if (*keyfn == '/') selector = keyfn+1; + keyfn++; + } + if (!stralloc_cats(&dkoutput,selector)) die(51); + if (!stralloc_cats(&dkoutput,"; d=")) die(51); + if (from) { + if (!stralloc_cats(&dkoutput,from)) die(51); + } else if (!stralloc_cats(&dkoutput,"unknown")) die(51); + if (!stralloc_cats(&dkoutput,";\n b=")) die(51); + if (!stralloc_cats(&dkoutput,advice)) die(51); + if (!stralloc_cats(&dkoutput," ;\n")) die(51); +} + +static char *binqqargs[2] = { "bin/qmail-queue", 0 } ; + +void main(int argc, char *argv[]) +{ + unsigned int len; + char ch; + int pim[2]; + int wstat; + unsigned long pid; + + sig_blocknone(); + umask(033); + + dksign = env_get("DKSIGN"); + dkverify = env_get("DKVERIFY"); + if (!dksign && !dkverify) + dksign = "/etc/domainkeys/%/default"; + + dkqueue = env_get("DKQUEUE"); + if (dkqueue && *dkqueue) binqqargs[0] = dkqueue; + else if (str_equal(argv[0]+str_rchr(argv[0], '/'), "/qmail-queue")) + binqqargs[0] = "bin/qmail-queue.orig"; + + if (dksign || dkverify) { + dklib = dk_init(0); + if (!dklib) die(51); + } + if (dksign) { + dk = dk_sign(dklib, &st, DK_CANON_NOFWS); + if (!dk) die(31); + } else if (dkverify) { + dk = dk_verify(dklib, &st); + if (!dk) die(31); + } + + mypid = getpid(); + uid = getuid(); + starttime = now(); + datetime_tai(&dt,starttime); + + sig_pipeignore(); + sig_miscignore(); + sig_alarmcatch(sigalrm); + sig_bugcatch(sigbug); + + alarm(DEATH); + + pidopen(); + readfd = open_read(pidfn); + if (unlink(pidfn) == -1) die(63); + + substdio_fdbuf(&ssout,write,messfd,outbuf,sizeof(outbuf)); + substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf)); + + for (;;) { + register int n; + register char *x; + int i; + + n = substdio_feed(&ssin); + if (n < 0) die_read(); + if (!n) break; + x = substdio_PEEK(&ssin); + if (dksign || dkverify) + for(i=0; i < n; i++) { + if (x[i] == '\n') st = dk_message(dk, "\r\n", 2); + else st = dk_message(dk, x+i, 1); + maybe_die_dk(st); + } + if (substdio_put(&ssout,x,n) == -1) die_write(); + substdio_SEEK(&ssin,n); + } + + if (substdio_flush(&ssout) == -1) die_write(); + + if (dksign || dkverify) { + st = dk_eom(dk, (void *)0); + maybe_die_dk(st); + if (dksign) { + write_signature(dk, dksign); + } else if (dkverify) { + char *status; + if (!stralloc_copys(&dkoutput,"DomainKey-Status: ")) die(51); + switch(st) { + case DK_STAT_OK: status = "good "; break; + case DK_STAT_BADSIG: status = "bad "; break; + case DK_STAT_NOSIG: status = "no signature"; break; + case DK_STAT_NOKEY: + case DK_STAT_CANTVRFY: status = "no key "; break; + case DK_STAT_BADKEY: status = "bad key "; break; + case DK_STAT_INTERNAL: + case DK_STAT_ARGS: + case DK_STAT_SYNTAX: status = "bad format "; break; + case DK_STAT_NORESOURCE: status = "no resources"; break; + case DK_STAT_REVOKED: status = "revoked "; break; + } + if (!stralloc_cats(&dkoutput,status)) die(51); + if (!stralloc_cats(&dkoutput,"\n")) die(51); + if (dkverify[str_chr(dkverify, 'A'+st)]) die(13); + if (dkverify[str_chr(dkverify, 'a'+st)]) die(82); + } + } + + if (pipe(pim) == -1) die(59); + + switch(pid = vfork()) { + case -1: + close(pim[0]); close(pim[1]); + die(58); + case 0: + close(pim[1]); + if (fd_move(0,pim[0]) == -1) die(120); + if (chdir(auto_qmail) == -1) die(61); + execv(*binqqargs,binqqargs); + die(120); + } + + close(pim[0]); + + substdio_fdbuf(&ssin,read,readfd,inbuf,sizeof(inbuf)); + substdio_fdbuf(&ssout,write,pim[1],outbuf,sizeof(outbuf)); + + if (substdio_bput(&ssout,dkoutput.s,dkoutput.len) == -1) die_write(); + switch(substdio_copy(&ssout,&ssin)) + { + case -2: die_read(); + case -3: die_write(); + } + + if (substdio_flush(&ssout) == -1) die_write(); + close(pim[1]); + + if (wait_pid(&wstat,pid) != pid) + die(57); + if (wait_crashed(wstat)) + die(57); + die(wait_exitcode(wstat)); + +}