=================================================================== RCS file: .cvsignore diff -N .cvsignore --- .cvsignore.orig 1 Jan 1970 00:00:00 -0000 +++ .cvsignore 27 Dec 2001 04:36:30 -0000 1.1 @@ -0,0 +1,107 @@ +*.[0-9] +auto-ccld.sh +make-load +find-systype +systype +load +make-compile +compile +fork.h +make-makelib +makelib +hassgprm.h +hassgact.h +hasflock.h +haswaitp.h +auto-str +auto_qmail.c +auto-int8 +auto_patrn.c +socket.lib +qmail-local +uint32.h +select.h +auto-int +auto_spawn.c +chkspawn +chkshsgr +hasshsgr.h +auto-uid +auto-gid +auto_uids.c +qmail-lspawn +auto_break.c +auto_usera.c +qmail-getpw +hassalen.h +dns.lib +qmail-remote +qmail-rspawn +direntry.h +auto_split.c +qmail-clean +hasmkffo.h +hasnpbg1.h +qmail-send +qmail-start +syslog.lib +splogger +qmail-queue +qmail-inject +predate +datemail +mailsubj +qmail-upq +qmail-showctl +qmail-newu +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 +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 =================================================================== RCS file: ChangeLog.arctic.org diff -N ChangeLog.arctic.org --- ChangeLog.arctic.org.orig 1 Jan 1970 00:00:00 -0000 +++ ChangeLog.arctic.org 9 Jun 2002 01:15:47 -0000 1.7 @@ -0,0 +1,43 @@ +Sat Jun 8 18:14:17 2002 dean gaudet + + * qmail-smtpd now logs when it rejects mail due to badmailfrom, + badrcptto, or mfcheck. + +Fri May 31 10:06:20 2002 dean gaudet + + * applied http://www.qmail.org/www.jedi.claranet.fr/qmail-bounce.patch + which limits the size of bounced messages + * applied http://www.qmail.org/qmail-1.03-mfcheck.3.patch which + requires a valid domain in envelope from. + +Mon Jan 28 13:01:25 2002 dean gaudet + + * update / apply my bare linefeed patch + http://arctic.org/~dean/patches/qmail-0.95-liberal-lf.patch + +Fri Dec 28 11:03:31 2001 dean gaudet + + * tagged arctic-1 to distribute patch + +Thu Dec 27 10:06:44 2001 dean gaudet + + * picked up syncdir library from , + this is necessary on linux because linux doesn't sync directory + metadata. + * tweaked make-load.sh and Makefile to use syncdir for all + executables. + +Thu Dec 27 09:35:45 2001 dean gaudet + + * qmail-1.03 was imported, source + * badrcptto patch was added, source + + * STARTTLS, SMTP AUTH, requireauth all added from patch at + + there was a small conflict with badrcptto which was easy to fix + * added (AUTH username) to Received: headers when SMTP AUTH is used + * note that there's a small difficulty with the checkpassword package + when used with qmail-smtpd (for SMTP AUTH) -- since qmail-smtpd + does NOT run as root, checkpassword won't be able to change + uid/gid ... i just commented out the uid/gid changes and recompiled, + and then named it /bin/checkpassword-smtp. =================================================================== RCS file: /home/src/cvsroot/qmail/Makefile,v retrieving revision 1.1.1.1 retrieving revision 1.5 diff -u -r1.1.1.1 -r1.5 --- Makefile.orig 27 Nov 2001 02:20:07 -0000 1.1.1.1 +++ Makefile 31 May 2002 17:07:35 -0000 1.5 @@ -810,7 +810,11 @@ instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \ binm3 binm3+df -load: \ +libsyncdir.a: \ +makelib syncdir.o + ./makelib libsyncdir.a syncdir.o + +load: libsyncdir.a \ make-load warn-auto.sh systype ( cat warn-auto.sh; ./make-load "`cat systype`" ) > load chmod 755 load @@ -1446,7 +1450,8 @@ 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 \ 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` + str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib` \ + -L/usr/local/ssl/lib -lssl -lcrypto qmail-remote.0: \ qmail-remote.8 @@ -1536,13 +1541,14 @@ 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 socket.lib dns.o 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` + socket.lib` dns.o `cat dns.lib` \ + -L/usr/local/ssl/lib -lssl -lcrypto qmail-smtpd.0: \ qmail-smtpd.8 @@ -2053,6 +2059,10 @@ compile substdo.c substdio.h str.h byte.h error.h ./compile substdo.c +syncdir.o: \ +compile syncdir.c + ./compile syncdir.c + syslog.lib: \ trysyslog.c compile load ( ( ./compile trysyslog.c && \ @@ -2139,3 +2149,22 @@ wait_pid.o: \ compile wait_pid.c error.h haswaitp.h ./compile wait_pid.c + +cert: + /usr/bin/openssl req -new -x509 -nodes \ + -out /var/qmail/control/servercert.pem -days 366 \ + -keyout /var/qmail/control/servercert.pem + chmod 640 /var/qmail/control/servercert.pem + chown qmaild.qmail /var/qmail/control/servercert.pem + ln -s /var/qmail/control/servercert.pem /var/qmail/control/clientcert.pem + +cert-req: + /usr/bin/openssl req -new -nodes \ + -out req.pem \ + -keyout /var/qmail/control/servercert.pem + chmod 640 /var/qmail/control/servercert.pem + chown qmaild.qmail /var/qmail/control/servercert.pem + ln -s /var/qmail/control/servercert.pem /var/qmail/control/clientcert.pem + @echo + @echo "Send req.pem to your CA to obtain signed_req.pem, and do:" + @echo "cat signed_req.pem >> /var/qmail/control/servercert.pem" =================================================================== RCS file: README-starttls-requireauth diff -N README-starttls-requireauth --- README-starttls-requireauth.orig 1 Jan 1970 00:00:00 -0000 +++ README-starttls-requireauth 27 Dec 2001 04:41:14 -0000 1.2 @@ -0,0 +1,1450 @@ +Wed Dec 26 20:40:00 2001 dean gaudet +- note that there's some stupid prot_gid()/prot_uid() crap + in checkpassword.c which isn't necessary for qmail-smtpd to + work with SMTP AUTH ... and since it won't be running as root + you'll need to hack checkpassword.c to disable that crap. + + +--- + + +Zach White 20010812 +http://drpepper.org/~zwhite/qmail-1.03-starttls-requireauth + +This patch combines 3 patches that seem to conflict with one another. +The first is Frederick Wermeulen's tls.patch to implement STARTTLS in +qmail-smtpd and qmail-remote. The other two are for SMTP AUTH, so you can +authenticate a user before allowing them to relay. The first is Krzysztof +Dabrowski's patch to implement SMTP AUTH, the second is dburkes@netable.com's +patch to require AUTH. + +Frederick Wermeulen released his patch under the same license as qmail, +however after consulting him the license was more restrictive than he +intended, and he has granted me permission to incorporate his patch into +this patch. + +Sources: +http://www.esat.kuleuven.ac.be/~vermeule/qmail/tls.patch STARTTLS +http://members.elysium.pl/brush/qmail-smtpd-auth/ SMTP AUTH +http://www.netable.com/~dburkes/qmail-smtpd-requireauth/ Require AUTH +http://www.elysium.pl/members/brush/cmd5checkpw/ CRAM-MD5 Checkpassword + +Usage: + +Apply this patch. Compile, install, blah, blah. Put your pem enceded server +key in /var/qmail/control/servercert.pem. Chown it to qmails, chmod it 600. +Change your qmail-smtpd invoction to call "qmail-smtpd " instead of just qmail-send. +Setup either tcp wrappers or your tcprules file to add the environment +variable REQUIREAUTH="" for IP's you wish to require authentication from. + +If you run into problems with this patch, tcpdump and strace are your friend. + +This was tested using Netscape 4.75's mail client, with the require TLS option +checked. It asked me for a password and then sent the mail. You should see +headers similer to the following if everything worked correctly: + +Received: from unknown (HELO netlsd.org) (192.168.0.13) by 192.168.0.1 with RC4-MD5 encrypted SMTP; 12 Aug 2001 23:26:59 -0000 + +The Recieved header indicates that tls was used and what cipher type. + +Questions, comments, and deathtreats can be sent to zwhite@netlsd.org. If you +are having problems getting it to work, I probably won't help you, I do +enough of that with my real job. If you've found a bug and have a patch, +I'd love to see it. If you've found a bug and don't know how to fix it, +I'll see if I can (But I'm not a programmer, and I'm not really sure this +is the best way to have combined the patches. I'm just a sysadmin who needed +this to work.) + +I've attached all the original documentation. You may find it useful. + +---- Included at the top of the starttls patchfile ---- +Frederik Vermeulen 20010627 +http://www.esat.kuleuven.ac.be/~vermeule/qmail/tls.patch + +This patch implements RFC2487 in qmail. This means you can +get SSL or TLS encrypted and authenticated SMTP between +the MTAs and between MTA and an MUA like Netscape. +The code is considered experimental (but has worked for +many since its first release on 1999-03-21). + +Usage: - install OpenSSL-0.9.6a http://www.openssl.org/ + - apply patch to qmail-1.03 http://www.qmail.org/ + Makefile and conf-cc were patched for appropriate + linking. Apart from that, 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 + somothing like + "Received [..] with DES-CBC3-SHA encrypted SMTP;") + If you don't have a server to test with, you can test + by sending mail to ping@mail.linux.student.kuleuven.ac.be, + which will bounce your mail. + +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 512 RSA key is provided in /var/qmail/control/rsa512.pem, + this key will be used instead of on-the-fly generation by + qmail-smtpd. Periodical replacement can be done by crontab: + 01 01 * * * umask 0077; /usr/local/ssl/bin/openssl genrsa \ + -out /var/qmail/control/rsa512.new 512 > /dev/null 2>&1;\ + chmod 600 /var/qmail/control/rsa512.new; chown qmaild.qmail \ + /var/qmail/control/rsa512.new; /bin/mv -f \ + /var/qmail/control/rsa512.new /var/qmail/control/rsa512.pem + - 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 + (or self-signed server certificate with openssl-0.9.5). + CommonName has to match. + WARNING: this option may cause mail to be delayed, bounced, + doublebounced, and lost. + - 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. http://www.modssl.org/ + source/cvs/exp/mod_ssl/pkg.mod_ssl/pkg.sslcfg/ca-bundle.crt) + and the cert email-address has to match a line in + /var/qmail/control/tlsclients. This email-address is logged + in the headers. + - cipher selection: + qmail-remote: + openssl cipher string read from + /var/qmail/control/tlsclientciphers + qmail-smtpd: + openssl cipher string read from TLSCIPHERS environment variable + (can be different based on client IP address e.g.) + or if that is not available /var/qmail/control/tlsserverciphers + +Caveats: - from this version on the server cert is in servercert.pem. + - 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). + - some broken servers have a problem with TLSv1 compatibility. + Uncomment the line where we set the SSL_OP_NO_TLSv1 option. + +Copyright: Same terms as qmail + Links with OpenSSL + Inspiration and code from examples in SSLeay (E. Young + and T. Hudson ), + stunnel (M. Trojnara ), + Postfix/TLS (L. Jaenicke ), + and modssl (R. Engelschall ). + Debug code, tlscipher selection, many feature suggestions, + French docs https://www.TBS-internet.com/ssl/qmail-tls.html + from Jean-Philippe Donnio + Openssl usage consulting from Bodo M"oller + Bug report from Andy Dustman + +Bug reports: mailto: + +----- README file from qmail-smtpd-auth-0.26 ----- + +This patch adds support for smtp auth authetication protocol to qmail. +It's based on Mrs.Brisby's smtp-auth patch with large enhancemets from me. + +You can always get the newest version from: +http://members.elysium.pl/brush/qmail-smtpd-auth/ + +To use all of it's fuctionality you will also have to obtain and install +my cmd5checkpw utility available at: +http://members.elysium.pl/brush/cmd5checkpw/ + +If you need more information about SMTP-AUTH itself and the client/server +support and configuration, visit: +http://members.elysium.pl/brush/smtp-auth/ + +How to install it: + +Simply patch your qmail-1.03 distribution with the included patch file and +recompile & install like usual. +Also obtain, unpack , compile and isntall cmd5checkpw utility and add sample +account to /etc/poppasswd file. + +How to use it: + +Patched qmail-smtpd should be invoked from inetd or tcpserver like this: + +smtp stream tcp nowait qmaild /var/qmail/bin/tcp-env tcp-env /var/qmail/bin/qmail-smtpd /bin/checkpassword /bin/true /bin/cmd5checkpw /bin/true + +the first parameter for qmail-smtpd is a checkpassword utility for clear +text auth types, the second is arg for checkpassword, the third is a name of +cram-md5 checkpassword utlity (my cmd5checkpw) and fourth is arg for it. + +give yout inetd a kill -HUP or restart tcpserver and here you go. + +Features: + +This patch supports the following auth methods: LOGIN, PLAIN and CRAM-MD5 + +Compatibility : + +The following MUA's are confirmed to work with this patch: + +Eudora 4.2.2 - CRAM-MD5 +The Bat 1.39 - LOGIN & CRAM-MD5 +Outlook Express 4 - LOGIN +Outlook Express 5 - LOGIN +Netscape 4.x - LOGIN +Netscape 4.0x - LOGIN +Pegassus Mail 3.1x - CRAM-MD5 + +Various compatibility issues: + +There used to be a problem with Netscape's Messenger but it's over. + +After test with Pegasus Mail 3.1 i've added the new style (rfc +recommended) greeting message. Yoo can select beetwen them by #defining or +ew#undefining USE_NEW_GREETING and USE_OLD_GREETING on the begining of +qmail-smtpd.c (around line 30). You can even enable both to maintain highest degree of +compatibility with various clients. This fix was suggested by +David Harris , the developer of Pegasus Mail. + +----- README file from qmail-smtpd-requireauth-0.30 ----- + +This patch enables you to optionally require authentication for qmail-smtpd +clients. + +How to install it: + +First, apply Krzysztof Dabrowski's qmail-smtpd-auth patch version 0.30 (see +http://members.elysium.pl/brush/qmail-smtpd-auth/). Then, simply patch the +resulting qmail-smtpd.c with the included patch file. Recompile and install +as usual. + +How to use it: + +If the REQUIREAUTH environment variable is set to anything (empty or +non-empty), the patched qmail-smtpd will require the client to authenticate +before accepting any MAIL FROM commands. + +See http://www.netable.com/~dburkes/qmail-smtpd-requireauth/index.html for a +practical usage scenario. + +----- End of Included documentation, patch below ----- + +diff -ur qmail-1.03.orig/Makefile qmail-1.03/Makefile +--- qmail-1.03.orig/Makefile Mon Jun 15 10:53:16 1998 ++++ qmail-1.03/Makefile Sun Aug 12 23:35:33 2001 +@@ -1446,7 +1446,8 @@ + 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 \ + 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` ++ str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib` \ ++ -L/usr/local/ssl/lib -lssl -lcrypto + + qmail-remote.0: \ + qmail-remote.8 +@@ -1542,7 +1543,7 @@ + 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` ++ socket.lib` -L/usr/local/ssl/lib -lssl -lcrypto + + qmail-smtpd.0: \ + qmail-smtpd.8 +@@ -2139,3 +2140,22 @@ + wait_pid.o: \ + compile wait_pid.c error.h haswaitp.h + ./compile wait_pid.c ++ ++cert: ++ /usr/local/ssl/bin/openssl req -new -x509 -nodes \ ++ -out /var/qmail/control/servercert.pem -days 366 \ ++ -keyout /var/qmail/control/servercert.pem ++ chmod 640 /var/qmail/control/servercert.pem ++ chown qmaild.qmail /var/qmail/control/servercert.pem ++ ln -s /var/qmail/control/servercert.pem /var/qmail/control/clientcert.pem ++ ++cert-req: ++ /usr/local/ssl/bin/openssl req -new -nodes \ ++ -out req.pem \ ++ -keyout /var/qmail/control/servercert.pem ++ chmod 640 /var/qmail/control/servercert.pem ++ chown qmaild.qmail /var/qmail/control/servercert.pem ++ ln -s /var/qmail/control/servercert.pem /var/qmail/control/clientcert.pem ++ @echo ++ @echo "Send req.pem to your CA to obtain signed_req.pem, and do:" ++ @echo "cat signed_req.pem >> /var/qmail/control/servercert.pem" +Only in qmail-1.03: Makefile.orig +diff -ur qmail-1.03.orig/conf-cc qmail-1.03/conf-cc +--- qmail-1.03.orig/conf-cc Mon Jun 15 10:53:16 1998 ++++ qmail-1.03/conf-cc Sun Aug 12 23:35:35 2001 +@@ -1,3 +1,3 @@ +-cc -O2 ++cc -O2 -DTLS -I/usr/local/ssl/include + + This will be used to compile .c files. +Only in qmail-1.03: conf-cc.orig +diff -ur qmail-1.03.orig/dns.c qmail-1.03/dns.c +--- qmail-1.03.orig/dns.c Mon Jun 15 10:53:16 1998 ++++ qmail-1.03/dns.c Sun Aug 12 23:35:38 2001 +@@ -270,6 +270,14 @@ + { + int r; + struct ip_mx ix; ++#ifdef TLS ++ stralloc fqdn = {0}; ++ ++ if (!stralloc_copy(&fqdn,sa)) return DNS_MEM; ++ if (!stralloc_0(&fqdn)) return DNS_MEM; ++ ix.fqdn = fqdn.s; ++ alloc_free(fqdn); ++#endif + + if (!stralloc_copy(&glue,sa)) return DNS_MEM; + if (!stralloc_0(&glue)) return DNS_MEM; +@@ -330,6 +338,9 @@ + ix.pref = 0; + if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) + { ++#ifdef TLS ++ ix.fqdn = NULL; ++#endif + if (!ipalloc_append(ia,&ix)) return DNS_MEM; + return 0; + } +Only in qmail-1.03: dns.c.orig +diff -ur qmail-1.03.orig/ipalloc.h qmail-1.03/ipalloc.h +--- qmail-1.03.orig/ipalloc.h Mon Jun 15 10:53:16 1998 ++++ qmail-1.03/ipalloc.h Sun Aug 12 23:35:38 2001 +@@ -3,7 +3,12 @@ + + #include "ip.h" + ++#ifdef TLS ++#include "stralloc.h" ++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" + +Only in qmail-1.03: ipalloc.h.orig +diff -ur qmail-1.03.orig/qmail-remote.c qmail-1.03/qmail-remote.c +--- qmail-1.03.orig/qmail-remote.c Mon Jun 15 10:53:16 1998 ++++ qmail-1.03/qmail-remote.c Sun Aug 12 23:35:34 2001 +@@ -26,8 +26,18 @@ + #include "tcpto.h" + #include "readwrite.h" + #include "timeoutconn.h" ++#ifndef TLS + #include "timeoutread.h" + #include "timeoutwrite.h" ++#endif ++ ++#ifdef TLS ++#include ++#include ++SSL *ssl = NULL; ++ ++stralloc tlsclientciphers = {0}; ++#endif + + #define HUGESMTPTEXT 5000 + +@@ -107,17 +117,94 @@ + int smtpfd; + int timeout = 1200; + ++#ifdef TLS ++int flagtimedout = 0; ++void sigalrm() ++{ ++ flagtimedout = 1; ++} ++ ++int ssl_timeoutread(timeout,fd,buf,n) int timeout; int fd; char *buf; int n; ++{ ++ int r; int saveerrno; ++ if (flagtimedout) { errno = error_timeout; return -1; } ++ alarm(timeout); ++ if (ssl) { ++ while(((r = SSL_read(ssl,buf,n)) <= 0) ++ && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_READ)); ++ if (SSL_get_error(ssl, r) != SSL_ERROR_NONE) ++ {char buf[1024]; ++ ++ out("ZTLS connection to "); outhost(); out(" died: "); ++ SSL_load_error_strings(); ++ out(ERR_error_string(ERR_get_error(), buf)); out("\n"); ++ SSL_shutdown(ssl); ++ zerodie(); ++ } ++ }else r = read(fd,buf,n); ++ saveerrno = errno; ++ alarm(0); ++ if (flagtimedout) { errno = error_timeout; return -1; } ++ errno = saveerrno; ++ return r; ++} ++ ++int ssl_timeoutwrite(timeout,fd,buf,n) int timeout; int fd; char *buf; int n; ++{ ++ int r; int saveerrno; ++ if (flagtimedout) { errno = error_timeout; return -1; } ++ alarm(timeout); ++ if (ssl) { ++ while(((r = SSL_write(ssl,buf,n)) <= 0) ++ && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_WRITE)); ++ if (SSL_get_error(ssl, r) != SSL_ERROR_NONE) ++ {char buf[1024]; ++ ++ out("ZTLS connection to "); outhost(); out(" died: "); ++ SSL_load_error_strings(); ++ out(ERR_error_string(ERR_get_error(), buf)); out("\n"); ++ SSL_shutdown(ssl); ++ zerodie(); ++ } ++ }else r = write(fd,buf,n); ++ saveerrno = errno; ++ alarm(0); ++ if (flagtimedout) { errno = error_timeout; return -1; } ++ errno = saveerrno; ++ return r; ++} ++ ++static int client_cert_cb(SSL *s,X509 **x509, EVP_PKEY **pkey) ++{ ++ out("ZTLS found no client cert in control/clientcert.pem\n"); ++ zerodie(NULL,NULL); ++} ++ ++static int verify_cb(int ok, X509_STORE_CTX * ctx) ++{ ++ return (1); ++} ++#endif ++ + int saferead(fd,buf,len) int fd; char *buf; int len; + { + int r; ++#ifdef TLS ++ r = ssl_timeoutread(timeout,smtpfd,buf,len); ++#else + r = timeoutread(timeout,smtpfd,buf,len); ++#endif + if (r <= 0) dropped(); + return r; + } + int safewrite(fd,buf,len) int fd; char *buf; int len; + { + int r; ++#ifdef TLS ++ r = ssl_timeoutwrite(timeout,smtpfd,buf,len); ++#else + r = timeoutwrite(timeout,smtpfd,buf,len); ++#endif + if (r <= 0) dropped(); + return r; + } +@@ -186,6 +273,34 @@ + out(append); + out(".\n"); + outsmtptext(); ++ ++/* TAG */ ++#if defined(TLS) && defined(DEBUG) ++#define ONELINE_NAME(X) X509_NAME_oneline(X,NULL,0) ++ ++ if(ssl){ ++ X509 *peer; ++ ++ out("STARTTLS proto="); out(SSL_get_version(ssl)); ++ out("; cipher="); out(SSL_CIPHER_get_name(SSL_get_current_cipher(ssl))); ++ ++ /* we want certificate details */ ++ peer=SSL_get_peer_certificate(ssl); ++ if (peer != NULL) { ++ char *str; ++ ++ str=ONELINE_NAME(X509_get_subject_name(peer)); ++ out("; subject="); out(str); ++ Free(str); ++ str=ONELINE_NAME(X509_get_issuer_name(peer)); ++ out("; issuer="); out(str); ++ Free(str); ++ X509_free(peer); ++ } ++ out(";\n"); ++ } ++#endif ++ + zerodie(); + } + +@@ -216,20 +331,158 @@ + + stralloc recip = {0}; + ++#ifdef TLS ++void smtp(fqdn) ++char *fqdn; ++#else + void smtp() ++#endif + { + unsigned long code; + int flagbother; + int i; +- ++#ifdef TLS ++ int needtlsauth = 0; ++ SSL_CTX *ctx; ++ int saveerrno, r; ++ ++ stralloc servercert = {0}; ++ struct stat st; ++ if(fqdn){ ++ if(!stralloc_copys(&servercert, "control/tlshosts/")) temp_nomem(); ++ if(!stralloc_catb(&servercert, fqdn, str_len(fqdn))) temp_nomem(); ++ if(!stralloc_catb(&servercert, ".pem", 4)) temp_nomem(); ++ if(!stralloc_0(&servercert)) temp_nomem(); ++ if (stat(servercert.s,&st) == 0) needtlsauth = 1; ++ } ++#endif ++ + if (smtpcode() != 220) quit("ZConnected to "," but greeting failed"); + ++#ifdef TLS ++ substdio_puts(&smtpto,"EHLO "); ++#else + substdio_puts(&smtpto,"HELO "); ++#endif + substdio_put(&smtpto,helohost.s,helohost.len); + substdio_puts(&smtpto,"\r\n"); + substdio_flush(&smtpto); ++#ifdef TLS ++ if (smtpcode() != 250){ ++ 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"); ++ } ++#else + if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected"); +- ++#endif ++ ++#ifdef TLS ++ i = 0; ++ while((i += str_chr(smtptext.s+i,'\n') + 1) && (i+12 < smtptext.len) && ++ str_diffn(smtptext.s+i+4,"STARTTLS\n",9)); ++ if (i+12 < smtptext.len) ++ { ++ substdio_puts(&smtpto,"STARTTLS\r\n"); ++ substdio_flush(&smtpto); ++ if (smtpcode() == 220) ++ { ++ SSL_library_init(); ++ if(!(ctx=SSL_CTX_new(SSLv23_client_method()))) ++ {char buf[1024]; ++ ++ out("ZTLS not available: error initializing ctx: "); ++ SSL_load_error_strings(); ++ out(ERR_error_string(ERR_get_error(), buf)); ++ out("\n"); ++ SSL_shutdown(ssl); ++ zerodie(); ++ } ++ if((stat("control/clientcert.pem", &st) == 0) && ++ ((SSL_CTX_use_RSAPrivateKey_file(ctx, "control/clientcert.pem", SSL_FILETYPE_PEM) <= 0) || ++ (SSL_CTX_use_certificate_chain_file(ctx, "control/clientcert.pem") <= 0) || ++ (SSL_CTX_check_private_key(ctx) <= 0))) ++ /* if there is a cert and it is bad, I fail ++ if there is no cert, I leave it to the other side to complain */ ++ SSL_CTX_set_client_cert_cb(ctx, client_cert_cb); ++ ++ /*SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);*/ ++ SSL_CTX_set_cipher_list(ctx,tlsclientciphers.s); ++ ++ if (needtlsauth){ ++ if (!SSL_CTX_load_verify_locations(ctx, servercert.s, NULL)) ++ {out("ZTLS unable to load "); out(servercert.s); out("\n"); ++ zerodie();} ++ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb); ++ } ++ ++ if(!(ssl=SSL_new(ctx))) ++ {char buf[1024]; ++ ++ out("ZTLS not available: error initializing ssl: "); ++ SSL_load_error_strings(); ++ out(ERR_error_string(ERR_get_error(), buf)); ++ out("\n"); ++ SSL_shutdown(ssl); ++ zerodie(); ++ } ++ SSL_set_fd(ssl,smtpfd); ++ ++ alarm(timeout); ++ r = SSL_connect(ssl); saveerrno = errno; ++ alarm(0); ++ if (flagtimedout) ++ {out("ZTLS not available: connect timed out\n"); ++ zerodie();} ++ errno = saveerrno; ++ if (r<=0) ++ {char buf[1024]; ++ ++ out("ZTLS not available: connect failed: "); ++ SSL_load_error_strings(); ++ out(ERR_error_string(ERR_get_error(), buf)); ++ out("\n"); ++ SSL_shutdown(ssl); ++ zerodie(); ++ } ++ if (needtlsauth) ++ /* should also check alternate names */ ++ {char commonName[256]; ++ ++ if ((r=SSL_get_verify_result(ssl)) != X509_V_OK) ++ {out("ZTLS unable to verify server with "); ++ out(servercert.s); out(": "); ++ out(X509_verify_cert_error_string(r)); out("\n"); ++ zerodie(); ++ } ++ X509_NAME_get_text_by_NID(X509_get_subject_name( ++ SSL_get_peer_certificate(ssl)), ++ NID_commonName, commonName, 256); ++ if (strcasecmp(fqdn,commonName)){ ++ out("ZTLS connection to "); out(fqdn); ++ out(" wanted, certificate for "); out(commonName); ++ out(" received\n"); ++ zerodie();} ++ } ++ ++ substdio_puts(&smtpto,"EHLO "); ++ substdio_put(&smtpto,helohost.s,helohost.len); ++ substdio_puts(&smtpto,"\r\n"); ++ substdio_flush(&smtpto); ++ ++ if (smtpcode() != 250) ++ { ++ quit("ZTLS connected to "," but my name was rejected"); ++ } ++ } ++ } ++ if ((!ssl) && needtlsauth) ++ {out("ZNo TLS achieved while "); out(servercert.s); out(" exists.\n"); ++ quit();} ++#endif ++ + substdio_puts(&smtpto,"MAIL FROM:<"); + substdio_put(&smtpto,sender.s,sender.len); + substdio_puts(&smtpto,">\r\n"); +@@ -324,6 +577,11 @@ + case 1: + if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break; + } ++#ifdef TLS ++ if (control_rldef(&tlsclientciphers,"control/tlsclientciphers",0,"DEFAULT") != 1) ++ temp_control(); ++ if(!stralloc_0(&tlsclientciphers)) temp_nomem(); ++#endif + } + + void main(argc,argv) +@@ -338,7 +596,10 @@ + int flagallaliases; + int flagalias; + char *relayhost; +- ++ ++#ifdef TLS ++ sig_alarmcatch(sigalrm); ++#endif + sig_pipeignore(); + if (argc < 4) perm_usage(); + if (chdir(auto_qmail) == -1) temp_chdir(); +@@ -417,7 +678,11 @@ + 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 ++ smtp(ip.ix[i].fqdn); /* does not return */ ++#else + smtp(); /* does not return */ ++#endif + } + tcpto_err(&ip.ix[i].ip,errno == error_timeout); + close(smtpfd); +Only in qmail-1.03: qmail-remote.c.orig +diff -ur qmail-1.03.orig/qmail-smtpd.c qmail-1.03/qmail-smtpd.c +--- qmail-1.03.orig/qmail-smtpd.c Mon Jun 15 10:53:16 1998 ++++ qmail-1.03/qmail-smtpd.c Sun Aug 12 23:35:34 2001 +@@ -1,4 +1,7 @@ + #include "sig.h" ++#include ++#include ++#include + #include "readwrite.h" + #include "stralloc.h" + #include "substdio.h" +@@ -16,22 +19,80 @@ + #include "scan.h" + #include "byte.h" + #include "case.h" ++#include "wait.h" + #include "env.h" + #include "now.h" + #include "exit.h" + #include "rcpthosts.h" ++#ifndef TLS + #include "timeoutread.h" + #include "timeoutwrite.h" ++#endif + #include "commands.h" ++#ifdef TLS ++#include ++SSL *ssl = NULL; ++ ++stralloc clientcert = {0}; ++stralloc tlsserverciphers = {0}; ++#endif + + #define MAXHOPS 100 ++#define USE_SMTPAUTH ++/* ++#define USE_OLD_GREETING ++#define USE_NEW_GREETING ++*/ ++ + unsigned int databytes = 0; + int timeout = 1200; + ++#ifdef TLS ++int flagtimedout = 0; ++void sigalrm() ++{ ++ flagtimedout = 1; ++} ++int ssl_timeoutread(timeout,fd,buf,n) int timeout; int fd; char *buf; int n; ++{ ++ int r; int saveerrno; ++ if (flagtimedout) { errno = error_timeout; return -1; } ++ alarm(timeout); ++ if (ssl) { ++ while(((r = SSL_read(ssl,buf,n)) <= 0) ++ && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_READ)); ++ }else r = read(fd,buf,n); ++ saveerrno = errno; ++ alarm(0); ++ if (flagtimedout) { errno = error_timeout; return -1; } ++ errno = saveerrno; ++ return r; ++} ++int ssl_timeoutwrite(timeout,fd,buf,n) int timeout; int fd; char *buf; int n; ++{ ++ int r; int saveerrno; ++ if (flagtimedout) { errno = error_timeout; return -1; } ++ alarm(timeout); ++ if (ssl) { ++ while(((r = SSL_write(ssl,buf,n)) <= 0) ++ && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_WRITE)); ++ }else r = write(fd,buf,n); ++ saveerrno = errno; ++ alarm(0); ++ if (flagtimedout) { errno = error_timeout; return -1; } ++ errno = saveerrno; ++ return r; ++} ++#endif ++ + int safewrite(fd,buf,len) int fd; char *buf; int len; + { + int r; ++#ifdef TLS ++ r = ssl_timeoutwrite(timeout,fd,buf,len); ++#else + r = timeoutwrite(timeout,fd,buf,len); ++#endif + if (r <= 0) _exit(1); + return r; + } +@@ -45,12 +106,17 @@ + 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_crash() { out("421 child crashed (#4.3.0)\r\n"); flush(); _exit(1); } ++void die_fork() { out("421 unable to start checkpassword.\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"); } ++#ifdef TLS ++void err_nogwcert() { out("553 no valid cert for gatewaying (#5.7.1)\r\n"); } ++#endif + void err_unimpl() { 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"); } +@@ -81,13 +147,16 @@ + char *remoteinfo; + char *local; + char *relayclient; ++#ifdef TLS ++char *tlsciphers; ++#endif + + stralloc helohost = {0}; + char *fakehelo; /* pointer into helohost, or 0 */ + + void dohelo(arg) char *arg; { +- if (!stralloc_copys(&helohost,arg)) die_nomem(); +- if (!stralloc_0(&helohost)) die_nomem(); ++ if (!stralloc_copys(&helohost,arg)) die_nomem(); ++ if (!stralloc_0(&helohost)) die_nomem(); + fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0; + } + +@@ -101,7 +170,10 @@ + { + char *x; + unsigned long u; +- ++#ifdef TLS ++ char *tlsciphers; ++#endif ++ + if (control_init() == -1) die_control(); + if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1) + die_control(); +@@ -116,12 +188,12 @@ + if (bmfok == -1) die_control(); + if (bmfok) + if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem(); +- ++ + if (control_readint(&databytes,"control/databytes") == -1) die_control(); + x = env_get("DATABYTES"); + if (x) { scan_ulong(x,&u); databytes = u; } + if (!(databytes + 1)) --databytes; +- ++ + remoteip = env_get("TCPREMOTEIP"); + if (!remoteip) remoteip = "unknown"; + local = env_get("TCPLOCALHOST"); +@@ -131,6 +203,17 @@ + if (!remotehost) remotehost = "unknown"; + remoteinfo = env_get("TCPREMOTEINFO"); + relayclient = env_get("RELAYCLIENT"); ++#ifdef TLS ++ if (tlsciphers = env_get("TLSCIPHERS")){ ++ if (!stralloc_copys(&tlsserverciphers,tlsciphers)) die_nomem(); ++ } ++ else { ++ if (control_rldef(&tlsserverciphers,"control/tlsserverciphers",0,"DEFAULT") != 1) ++ die_control(); ++ } ++ if (!stralloc_0(&tlsserverciphers)) die_nomem(); ++#endif ++ + dohelo(remotehost); + } + +@@ -146,7 +229,7 @@ + struct ip_address ip; + int flagesc; + int flagquoted; +- ++ + terminator = '>'; + i = str_chr(arg,'<'); + if (arg[i]) +@@ -229,7 +312,18 @@ + } + void smtp_ehlo(arg) char *arg; + { +- smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); ++ smtp_greet("250-"); ++#ifdef TLS ++# ifdef USE_SMTPAUTH ++ if (ssl) out("\r\n250-PIPELINING\r\n250-AUTH LOGIN CRAM-MD5 PLAIN\r\n250 8BITMIME\r\n"); ++ else out("\r\n250-PIPELINING\r\n250-STARTTLS\r\n250-AUTH LOGIN CRAM-MD5 PLAIN\r\n250 8BITMIME\r\n"); ++# else ++ if (ssl) out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); ++ else out("\r\n250-PIPELINING\r\n250-STARTTLS\r\n250 8BITMIME\r\n"); ++# endif /* USE_SMTP_AUTH */ ++#else ++ out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); ++#endif /* TLS */ + seenmail = 0; dohelo(arg); + } + void smtp_rset() +@@ -247,6 +341,12 @@ + if (!stralloc_0(&mailfrom)) die_nomem(); + out("250 ok\r\n"); + } ++#ifdef TLS ++static int verify_cb(int ok, X509_STORE_CTX * ctx) ++{ ++ return (1); ++} ++#endif + void smtp_rcpt(arg) char *arg; { + if (!seenmail) { err_wantmail(); return; } + if (!addrparse(arg)) { err_syntax(); return; } +@@ -257,7 +357,55 @@ + if (!stralloc_0(&addr)) die_nomem(); + } + else ++#ifndef TLS + if (!addrallowed()) { err_nogateway(); return; } ++#else ++ if (!addrallowed()) ++ { ++ if (ssl) ++ { STACK_OF(X509_NAME) *sk; ++ X509 *peercert; ++ stralloc tlsclients = {0}; ++ struct constmap maptlsclients; ++ int r; ++ ++ SSL_set_verify(ssl, ++ SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, ++ verify_cb); ++ if ((sk = SSL_load_client_CA_file("control/clientca.pem")) == NULL) ++ { err_nogateway(); return; } ++ SSL_set_client_CA_list(ssl, sk); ++ if((control_readfile(&tlsclients,"control/tlsclients",0) != 1) || ++ !constmap_init(&maptlsclients,tlsclients.s,tlsclients.len,0)) ++ { err_nogateway(); return; } ++ ++ SSL_renegotiate(ssl); ++ SSL_do_handshake(ssl); ++ ssl->state = SSL_ST_ACCEPT; ++ SSL_do_handshake(ssl); ++ if ((r = SSL_get_verify_result(ssl)) != X509_V_OK) ++ {out("553 no valid cert for gatewaying: "); ++ out(X509_verify_cert_error_string(r)); ++ out(" (#5.7.1)\r\n"); ++ return; ++ } ++ ++ if (peercert = SSL_get_peer_certificate(ssl)) ++ {char emailAddress[256]; ++ ++ X509_NAME_get_text_by_NID(X509_get_subject_name( ++ SSL_get_peer_certificate(ssl)), ++ NID_pkcs9_emailAddress, emailAddress, 256); ++ if (!stralloc_copys(&clientcert, emailAddress)) die_nomem(); ++ if (!constmap(&maptlsclients,clientcert.s,clientcert.len)) ++ { err_nogwcert(); return; } ++ relayclient = ""; ++ } ++ else { err_nogwcert(); return; } ++ } ++ else { err_nogateway(); return; } ++ } ++#endif + if (!stralloc_cats(&rcptto,"T")) die_nomem(); + if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); +@@ -269,7 +417,11 @@ + { + int r; + flush(); ++#ifdef TLS ++ r = ssl_timeoutread(timeout,fd,buf,len); ++#else + r = timeoutread(timeout,fd,buf,len); ++#endif + if (r == -1) if (errno == error_timeout) die_alarm(); + if (r <= 0) die_read(); + return r; +@@ -300,7 +452,7 @@ + int flagmaybex; /* 1 if this line might match RECEIVED, if fih */ + int flagmaybey; /* 1 if this line might match \r\n, if fih */ + int flagmaybez; /* 1 if this line might match DELIVERED, if fih */ +- ++ + state = 1; + *hops = 0; + flaginheader = 1; +@@ -369,6 +521,9 @@ + int hops; + unsigned long qp; + char *qqx; ++#ifdef TLS ++ stralloc protocolinfo = {0}; ++#endif + + if (!seenmail) { err_wantmail(); return; } + if (!rcptto.len) { err_wantrcpt(); return; } +@@ -377,8 +532,20 @@ + if (qmail_open(&qqt) == -1) { err_qqt(); return; } + qp = qmail_qp(&qqt); + out("354 go ahead\r\n"); +- ++#ifdef TLS ++ if(ssl){ ++ if (!stralloc_copys(&protocolinfo, SSL_CIPHER_get_name(SSL_get_current_cipher(ssl)))) die_nomem(); ++ if (!stralloc_catb(&protocolinfo, " encrypted SMTP", 15)) die_nomem(); ++ if (clientcert.len){ ++ if (!stralloc_catb(&protocolinfo," cert ", 6)) die_nomem(); ++ if (!stralloc_catb(&protocolinfo,clientcert.s, clientcert.len)) die_nomem(); ++ } ++ if (!stralloc_0(&protocolinfo)) die_nomem(); ++ } else if (!stralloc_copyb(&protocolinfo,"SMTP",5)) die_nomem(); ++ received(&qqt,protocolinfo.s,local,remoteip,remotehost,remoteinfo,case_diffs(remotehost,helohost.s) ? helohost.s : 0); ++#else + received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); ++#endif + blast(&hops); + hops = (hops >= MAXHOPS); + if (hops) qmail_fail(&qqt); +@@ -394,22 +561,437 @@ + out("\r\n"); + } + ++#ifdef TLS ++static RSA *tmp_rsa_cb(ssl,export,keylength) SSL *ssl; int export; int keylength; ++{ ++ RSA* rsa; ++ BIO* in; ++ ++ if (!export || keylength == 512) ++ if (in=BIO_new(BIO_s_file_internal())) ++ if (BIO_read_filename(in,"control/rsa512.pem") > 0) ++ if (rsa=PEM_read_bio_RSAPrivateKey(in,NULL,NULL,NULL)) ++ return rsa; ++ return (RSA_generate_key(export?keylength:512,RSA_F4,NULL,NULL)); ++} ++ ++void smtp_tls(arg) char *arg; ++{ ++ SSL_CTX *ctx; ++ ++ if (*arg) ++ {out("501 Syntax error (no parameters allowed) (#5.5.4)\r\n"); ++ return;} ++ ++ SSL_library_init(); ++ if(!(ctx=SSL_CTX_new(SSLv23_server_method()))) ++ {out("454 TLS not available: unable to initialize ctx (#4.3.0)\r\n"); ++ return;} ++ if(!SSL_CTX_use_RSAPrivateKey_file(ctx, "control/servercert.pem", SSL_FILETYPE_PEM)) ++ {out("454 TLS not available: missing RSA private key (#4.3.0)\r\n"); ++ return;} ++ if(!SSL_CTX_use_certificate_chain_file(ctx, "control/servercert.pem")) ++ {out("454 TLS not available: missing certificate (#4.3.0)\r\n"); ++ return;} ++ SSL_CTX_set_tmp_rsa_callback(ctx, tmp_rsa_cb); ++ SSL_CTX_set_cipher_list(ctx,tlsserverciphers.s); ++ SSL_CTX_load_verify_locations(ctx, "control/clientca.pem",NULL); ++ SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_cb); ++ ++ out("220 ready for tls\r\n"); flush(); ++ ++ if(!(ssl=SSL_new(ctx))) die_read(); ++ SSL_set_fd(ssl,0); ++ if(SSL_accept(ssl)<=0) die_read(); ++ substdio_fdbuf(&ssout,SSL_write,ssl,ssoutbuf,sizeof(ssoutbuf)); ++ ++ remotehost = env_get("TCPREMOTEHOST"); ++ if (!remotehost) remotehost = "unknown"; ++ dohelo(remotehost); ++} ++#endif ++ ++#ifdef USE_SMTPAUTH ++static unsigned char authenticated=0; ++static unsigned char *base64_alphabet = ++"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; ++static int unbase64(ch) int ch; { ++ int i; ++ if (ch == '=') return 0; ++ for (i = 0; i < 64; i++) ++ if (ch == base64_alphabet[i]) ++ return i; ++ return 0; ++} ++static int base64_dec_buffer(str,dst,len) const char *str;void *dst;int len; ++{ ++ int i, j, l; ++ unsigned char input[4], output[3], *result = (char *)dst; ++ if (str == 0) ++ return 0; ++ l = str_len(str); ++ if (dst == 0 || l > len) ++ return (l / 4) * 3; ++ memset(dst,0,len); ++ for (i=j=0; i> 4); ++ output[1] = (input[1] << 4) | (input[2] >> 2); ++ output[2] = (input[2] << 6) | (input[3]); ++ result[j] = output[0]; ++ if (str[i+1] == '=') return j+1; ++ result[j+1]=output[1]; ++ if (str[i+2] == '=') return j+2; ++ result[j+2]=output[2]; ++ j += 3; ++ } ++ return j; ++} ++static char **smtpauth_argv; ++static char *auth_argv[4]; ++static stralloc smtpauth = {0}; ++static char smtpauthlogin[65]; ++static char smtpauthpass[65]; ++static char smtpauthtimestamp[65]; ++ ++char unique[FMT_ULONG + FMT_ULONG + 3]; ++ ++void base64encode(stralloc *input, stralloc *output) ++{ ++int a=0,b=0,c=0; ++int i, j; ++int d, e, f, g; ++ ++ if (input->len == 0) return; ++ ++ for (j=i=0; i< input->len ; i += 3) ++ { ++ a=input->s[i]; ++ b= i+1 < input->len ? input->s[i+1]:0; ++ c= i+2 < input->len ? input->s[i+2]:0; ++ ++ d=base64_alphabet[ a >> 2 ]; ++ e=base64_alphabet[ ((a & 3 ) << 4) | (b >> 4)]; ++ f=base64_alphabet[ ((b & 15) << 2) | (c >> 6)]; ++ g=base64_alphabet[ c & 63 ]; ++ if (i + 1 >= input->len) f='='; ++ if (i + 2 >= input->len) g='='; ++ stralloc_append(output,&d); ++ stralloc_append(output,&e); ++ stralloc_append(output,&f); ++ stralloc_append(output,&g); ++ } ++} ++ ++static int smtpauth_getl(void) { ++ int i; ++ if (!stralloc_copys(&smtpauth, "")) return -1; ++ for (;;) { ++ if (!stralloc_readyplus(&smtpauth,1)) return -1; ++ i = substdio_get(&ssin, smtpauth.s + smtpauth.len, 1); ++ if (i != 1) return i; ++ if (smtpauth.s[smtpauth.len] == '\n') break; ++ ++smtpauth.len; ++ } ++ if (smtpauth.len > 0) if (smtpauth.s[smtpauth.len-1] == '\r') --smtpauth.len; ++ smtpauth.s[smtpauth.len] = 0; ++ return smtpauth.len; ++} ++ ++static void smtpauth_authenticate(void) ++{ ++ int st, pid, fds[2]; ++ ++ if (pipe(fds)) { ++ out("535 pipe failure\r\n"); ++ flush(); ++ _exit(0); ++ } ++ /* spawn external program ++ ++ external program should return '0' if it was successful, ++ ++ submit: /bin/checkpassword /bin/true ++ ++ */ ++ switch ((pid=fork())) { ++ case -1: die_fork(); ++ case 0: close(fds[1]); ++ fd_copy(3,fds[0]); ++ flush(); ++ execvp(auth_argv[1], auth_argv+1); ++ die_nomem(); ++ }; ++ close(fds[0]); ++ write(fds[1], smtpauthlogin, str_len(smtpauthlogin)+1); ++ write(fds[1], smtpauthpass, str_len(smtpauthpass)+1); ++ if (str_len(smtpauthtimestamp)) ++ { ++ write(fds[1], smtpauthtimestamp, str_len(smtpauthtimestamp)+1); ++ } ++ close(fds[1]); ++ wait_pid(&st, pid); ++ ++ if (wait_crashed(st)) ++ die_crash(); ++ if (wait_exitcode(st) == 0) { ++ out("235 go ahead\r\n"); ++ flush(); ++ relayclient=""; ++ authenticated=1; ++ return; ++ } ++ sleep(2); ++ out("535 auth failure\r\n"); ++ flush(); ++ return; ++} ++ ++void smtp_auth(arg) char *arg; { ++ int ret,i,toolong,start; ++ char *helper; ++ /* netscape 4.5 sends AUTH LOGIN ++ microsoft outlook express sends AUTH LOGIN ++ ++ idea is simple ++ ++ use an external program to test authority ++ if success, set 'RELAYCLIENT' ++ otherwise, let them know nicely (hangup) ++ ++ note, i really don't like djb's coding style even though i'm using it here. ++ i think using spaces for tabs is bad. ++ -mrs.brisby@nimh.org ++ */ ++ /* Here i've added support for other auth types. ++ ++ -brush@elysium.pl */ ++ if (!authenticated) ++ { ++ if ((ret=strncasecmp(arg,"login",5))==0) ++ { ++ while (arg && *arg && *arg != ' ') arg++; ++ ++ /* pass over the space */ ++ while (arg && *arg && *arg == ' ') arg++; ++ ++ if (arg && *arg) { ++ /* here's the base64 encoded login */ ++ base64_dec_buffer(arg, smtpauthlogin, sizeof(smtpauthlogin)); ++ } else { ++ out("334 VXNlcm5hbWU6\r\n"); /* b64 <- 'Username:' */ ++ flush(); ++ if (smtpauth_getl() > 0) ++ base64_dec_buffer(smtpauth.s, smtpauthlogin, sizeof(smtpauthlogin)); ++ else ++ die_read(); ++ } ++ out("334 UGFzc3dvcmQ6\r\n"); /* b64 <- 'Password:' */ ++ flush(); ++ if (smtpauth_getl() > 0) ++ base64_dec_buffer(smtpauth.s, smtpauthpass, sizeof(smtpauthpass)); ++ else ++ die_read(); ++ smtpauthtimestamp[0]=0; ++ auth_argv[1]=smtpauth_argv[1]; /* change checkpass prg */ ++ auth_argv[2]=smtpauth_argv[2]; /* change checkpass prg */ ++ auth_argv[3]=NULL; /* change checkpass prg */ ++ smtpauth_authenticate(); ++ return; ++ } ++ else if ((ret=strncasecmp(arg,"plain",5))==0) ++ { ++ static char smtpauthloginpass[200]; ++ ++ while (arg && *arg && *arg != ' ') arg++; ++ ++ /* pass over the space */ ++ while (arg && *arg && *arg == ' ') arg++; ++ ++ if (arg && *arg) ++ { ++ if(strlen(arg)*3/4 >= sizeof(smtpauthloginpass)) ++ { ++ out("535 input too long\r\n"); ++ flush(); ++ return; ++ } ++ /* here's the base64 encoded login/password */ ++ base64_dec_buffer(arg, smtpauthloginpass, sizeof(smtpauthloginpass)-1); ++ } else { ++ out("334 ok. go on.\r\n"); ++ flush(); ++ i=smtpauth_getl(); ++ if(i <= 0) ++ die_read(); ++ else if(i*3/4 >= sizeof(smtpauthloginpass)) ++ { ++ out("535 input too long\r\n"); ++ flush(); ++ return; ++ } else base64_dec_buffer(smtpauth.s, smtpauthloginpass, sizeof(smtpauthloginpass)-1); ++ } ++ smtpauthloginpass[sizeof(smtpauthloginpass)-1]=0; ++ start=strlen(smtpauthloginpass)+1; ++ if((start >= sizeof(smtpauthloginpass)) || (strlen(smtpauthloginpass+start) >= 65)) ++ { ++ out("535 malformed input\r\n"); ++ flush(); ++ return; ++ } ++ strcpy(smtpauthlogin,smtpauthloginpass+start); ++ ++ start+=strlen(smtpauthlogin)+1; ++ if((start >= sizeof(smtpauthloginpass)) || (strlen(smtpauthloginpass+start) >= 65)) ++ { ++ out("535 malformed input\r\n"); ++ flush(); ++ return; ++ } ++ strcpy(smtpauthpass,smtpauthloginpass+start); ++ ++ smtpauthtimestamp[0]=0; ++ auth_argv[1]=smtpauth_argv[1]; /* change checkpass prg */ ++ auth_argv[2]=smtpauth_argv[2]; /* change checkpass prg */ ++ auth_argv[3]=NULL; /* change checkpass prg */ ++ smtpauth_authenticate(); ++ return; ++ } ++ else if ((ret=strncasecmp(arg,"cram-md5",8))==0) ++ { ++ int r; ++ static stralloc me = {0}; ++ static stralloc greet = {0}; ++ static stralloc greetenc = {0}; ++ char *s; ++ r = control_readline(&me,"control/me"); ++ if (r != 1) ++ { ++ out("535 internal server error\r\n"); flush(); _exit(0); ++ } ++ for (r=0;r <= me.len;r++) ++ { ++ if (me.s[r]=='\n') ++ { ++ me.s[r]=0; ++ break; ++ } ++ } ++ ++ s = unique; ++ s += fmt_uint(s,getpid()); ++ *s++ = '.'; ++ s += fmt_ulong(s,(unsigned long) now()); ++ *s++ = '@'; ++ *s++ = 0; ++ if (greet.len) ++ { ++ for (i=0;i"); ++ greet.s[greet.len]=0; // obscure fix but it works ++ stralloc_readyplus(&greet,3); ++ if (greetenc.len) ++ { ++ for (i=0;i 0) ++ { ++ s=calloc((size_t) strlen(smtpauth.s),(size_t)1); ++ base64_dec_buffer(smtpauth.s, s, strlen(smtpauth.s)); ++ } ++ helper=strtok(s," "); ++ if(helper!=NULL) ++ { ++ strncpy (smtpauthlogin,helper,64); ++ } ++ else ++ { ++ out("535 malformed input\r\n"); ++ return; ++ } ++ helper=strtok(NULL," "); ++ if(helper!=NULL) ++ { ++ strncpy (smtpauthtimestamp,helper,64); ++ } ++ else ++ { ++ out("535 malformed input\r\n"); ++ return; ++ } ++ strncpy (smtpauthpass,greet.s,64); ++ auth_argv[1]=smtpauth_argv[3]; /* change checkpass prg */ ++ auth_argv[2]=smtpauth_argv[4]; /* change checkpass prg */ ++ auth_argv[3]=NULL; /* change checkpass prg */ ++ smtpauth_authenticate(); ++ return; ++ } ++ else ++ { ++ out("504 auth type not supported\r\n"); ++ flush(); ++ return; ++ } ++ } ++ else ++ { ++ out("503 you are already authenticated\r\n"); ++ flush(); ++ return; ++ } ++} ++#endif ++ + struct commands smtpcommands[] = { + { "rcpt", smtp_rcpt, 0 } + , { "mail", smtp_mail, 0 } + , { "data", smtp_data, flush } ++#ifdef USE_SMTPAUTH ++, { "auth", smtp_auth, flush } ++#endif + , { "quit", smtp_quit, flush } + , { "helo", smtp_helo, flush } + , { "ehlo", smtp_ehlo, flush } + , { "rset", smtp_rset, 0 } + , { "help", smtp_help, flush } ++#ifdef TLS ++, { "starttls", smtp_tls, flush } ++#endif + , { "noop", err_noop, flush } + , { "vrfy", err_vrfy, flush } + , { 0, err_unimpl, flush } + } ; + +-void main() ++void main(argc,argv) int argc; char **argv; + { ++#ifdef TLS ++ sig_alarmcatch(sigalrm); ++#endif ++#ifdef USE_SMTPAUTH ++ smtpauth_argv = argv; ++#endif + sig_pipeignore(); + if (chdir(auto_qmail) == -1) die_control(); + setup(); =================================================================== RCS file: /home/src/cvsroot/qmail/conf-cc,v retrieving revision 1.1.1.1 retrieving revision 1.3 diff -u -r1.1.1.1 -r1.3 --- conf-cc.orig 27 Nov 2001 02:20:07 -0000 1.1.1.1 +++ conf-cc 22 May 2002 02:37:53 -0000 1.3 @@ -1,3 +1,3 @@ -cc -O2 +cc -O2 -DTLS -I/usr/include This will be used to compile .c files. =================================================================== RCS file: /home/src/cvsroot/qmail/dns.c,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- dns.c.orig 27 Nov 2001 02:20:07 -0000 1.1.1.1 +++ dns.c 21 Dec 2001 03:29:00 -0000 1.2 @@ -270,6 +270,14 @@ { int r; struct ip_mx ix; +#ifdef TLS + stralloc fqdn = {0}; + + if (!stralloc_copy(&fqdn,sa)) return DNS_MEM; + if (!stralloc_0(&fqdn)) return DNS_MEM; + ix.fqdn = fqdn.s; + alloc_free(fqdn); +#endif if (!stralloc_copy(&glue,sa)) return DNS_MEM; if (!stralloc_0(&glue)) return DNS_MEM; @@ -330,6 +338,9 @@ ix.pref = 0; if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) { +#ifdef TLS + ix.fqdn = NULL; +#endif if (!ipalloc_append(ia,&ix)) return DNS_MEM; return 0; } =================================================================== RCS file: /home/src/cvsroot/qmail/ipalloc.h,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- ipalloc.h.orig 27 Nov 2001 02:20:07 -0000 1.1.1.1 +++ ipalloc.h 21 Dec 2001 03:29:00 -0000 1.2 @@ -3,7 +3,12 @@ #include "ip.h" +#ifdef TLS +#include "stralloc.h" +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" =================================================================== RCS file: /home/src/cvsroot/qmail/make-load.sh,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- make-load.sh.orig 27 Nov 2001 02:20:07 -0000 1.1.1.1 +++ make-load.sh 27 Dec 2001 18:08:09 -0000 1.2 @@ -1,2 +1,2 @@ echo 'main="$1"; shift' -echo exec "$LD" '-o "$main" "$main".o ${1+"$@"}' +echo exec "$LD" '-o "$main" "$main".o ${1+"$@"} -L. -lsyncdir' =================================================================== RCS file: /home/src/cvsroot/qmail/qmail-control.9,v retrieving revision 1.1.1.1 retrieving revision 1.3 diff -u -r1.1.1.1 -r1.3 --- qmail-control.9.orig 27 Nov 2001 02:20:07 -0000 1.1.1.1 +++ qmail-control.9 31 May 2002 17:40:23 -0000 1.3 @@ -43,6 +43,7 @@ .I badmailfrom \fR(none) \fRqmail-smtpd .I bouncefrom \fRMAILER-DAEMON \fRqmail-send .I bouncehost \fIme \fRqmail-send +.I bouncemaxbytes \fR50000 \fRqmail-send .I concurrencylocal \fR10 \fRqmail-send .I concurrencyremote \fR20 \fRqmail-send .I defaultdomain \fIme \fRqmail-inject @@ -55,6 +56,7 @@ .I idhost \fIme \fRqmail-inject .I localiphost \fIme \fRqmail-smtpd .I locals \fIme \fRqmail-send +.I mfcheck \fR0 \fRqmail-smtpd .I morercpthosts \fR(none) \fRqmail-smtpd .I percenthack \fR(none) \fRqmail-send .I plusdomain \fIme \fRqmail-inject =================================================================== RCS file: /home/src/cvsroot/qmail/qmail-qmqpd.c,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- qmail-qmqpd.c.orig 27 Nov 2001 02:20:07 -0000 1.1.1.1 +++ qmail-qmqpd.c 27 Dec 2001 04:55:04 -0000 1.2 @@ -78,7 +78,7 @@ if (!local) local = env_get("TCPLOCALIP"); if (!local) local = "unknown"; - received(&qq,"QMQP",local,remoteip,remotehost,remoteinfo,(char *) 0); + received(&qq,"QMQP",local,remoteip,remotehost,remoteinfo,(char *) 0, (char *) 0); } char buf[1000]; =================================================================== RCS file: /home/src/cvsroot/qmail/qmail-remote.c,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- qmail-remote.c.orig 27 Nov 2001 02:20:07 -0000 1.1.1.1 +++ qmail-remote.c 21 Dec 2001 03:29:00 -0000 1.2 @@ -26,8 +26,18 @@ #include "tcpto.h" #include "readwrite.h" #include "timeoutconn.h" +#ifndef TLS #include "timeoutread.h" #include "timeoutwrite.h" +#endif + +#ifdef TLS +#include +#include +SSL *ssl = NULL; + +stralloc tlsclientciphers = {0}; +#endif #define HUGESMTPTEXT 5000 @@ -107,17 +117,94 @@ int smtpfd; int timeout = 1200; +#ifdef TLS +int flagtimedout = 0; +void sigalrm() +{ + flagtimedout = 1; +} + +int ssl_timeoutread(timeout,fd,buf,n) int timeout; int fd; char *buf; int n; +{ + int r; int saveerrno; + if (flagtimedout) { errno = error_timeout; return -1; } + alarm(timeout); + if (ssl) { + while(((r = SSL_read(ssl,buf,n)) <= 0) + && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_READ)); + if (SSL_get_error(ssl, r) != SSL_ERROR_NONE) + {char buf[1024]; + + out("ZTLS connection to "); outhost(); out(" died: "); + SSL_load_error_strings(); + out(ERR_error_string(ERR_get_error(), buf)); out("\n"); + SSL_shutdown(ssl); + zerodie(); + } + }else r = read(fd,buf,n); + saveerrno = errno; + alarm(0); + if (flagtimedout) { errno = error_timeout; return -1; } + errno = saveerrno; + return r; +} + +int ssl_timeoutwrite(timeout,fd,buf,n) int timeout; int fd; char *buf; int n; +{ + int r; int saveerrno; + if (flagtimedout) { errno = error_timeout; return -1; } + alarm(timeout); + if (ssl) { + while(((r = SSL_write(ssl,buf,n)) <= 0) + && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_WRITE)); + if (SSL_get_error(ssl, r) != SSL_ERROR_NONE) + {char buf[1024]; + + out("ZTLS connection to "); outhost(); out(" died: "); + SSL_load_error_strings(); + out(ERR_error_string(ERR_get_error(), buf)); out("\n"); + SSL_shutdown(ssl); + zerodie(); + } + }else r = write(fd,buf,n); + saveerrno = errno; + alarm(0); + if (flagtimedout) { errno = error_timeout; return -1; } + errno = saveerrno; + return r; +} + +static int client_cert_cb(SSL *s,X509 **x509, EVP_PKEY **pkey) +{ + out("ZTLS found no client cert in control/clientcert.pem\n"); + zerodie(NULL,NULL); +} + +static int verify_cb(int ok, X509_STORE_CTX * ctx) +{ + return (1); +} +#endif + int saferead(fd,buf,len) int fd; char *buf; int len; { int r; +#ifdef TLS + r = ssl_timeoutread(timeout,smtpfd,buf,len); +#else r = timeoutread(timeout,smtpfd,buf,len); +#endif if (r <= 0) dropped(); return r; } int safewrite(fd,buf,len) int fd; char *buf; int len; { int r; +#ifdef TLS + r = ssl_timeoutwrite(timeout,smtpfd,buf,len); +#else r = timeoutwrite(timeout,smtpfd,buf,len); +#endif if (r <= 0) dropped(); return r; } @@ -186,6 +273,34 @@ out(append); out(".\n"); outsmtptext(); + +/* TAG */ +#if defined(TLS) && defined(DEBUG) +#define ONELINE_NAME(X) X509_NAME_oneline(X,NULL,0) + + if(ssl){ + X509 *peer; + + out("STARTTLS proto="); out(SSL_get_version(ssl)); + out("; cipher="); out(SSL_CIPHER_get_name(SSL_get_current_cipher(ssl))); + + /* we want certificate details */ + peer=SSL_get_peer_certificate(ssl); + if (peer != NULL) { + char *str; + + str=ONELINE_NAME(X509_get_subject_name(peer)); + out("; subject="); out(str); + Free(str); + str=ONELINE_NAME(X509_get_issuer_name(peer)); + out("; issuer="); out(str); + Free(str); + X509_free(peer); + } + out(";\n"); + } +#endif + zerodie(); } @@ -216,20 +331,158 @@ stralloc recip = {0}; +#ifdef TLS +void smtp(fqdn) +char *fqdn; +#else void smtp() +#endif { unsigned long code; int flagbother; int i; - +#ifdef TLS + int needtlsauth = 0; + SSL_CTX *ctx; + int saveerrno, r; + + stralloc servercert = {0}; + struct stat st; + if(fqdn){ + if(!stralloc_copys(&servercert, "control/tlshosts/")) temp_nomem(); + if(!stralloc_catb(&servercert, fqdn, str_len(fqdn))) temp_nomem(); + if(!stralloc_catb(&servercert, ".pem", 4)) temp_nomem(); + if(!stralloc_0(&servercert)) temp_nomem(); + if (stat(servercert.s,&st) == 0) needtlsauth = 1; + } +#endif + if (smtpcode() != 220) quit("ZConnected to "," but greeting failed"); +#ifdef TLS + substdio_puts(&smtpto,"EHLO "); +#else substdio_puts(&smtpto,"HELO "); +#endif substdio_put(&smtpto,helohost.s,helohost.len); substdio_puts(&smtpto,"\r\n"); substdio_flush(&smtpto); +#ifdef TLS + if (smtpcode() != 250){ + 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"); + } +#else if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected"); - +#endif + +#ifdef TLS + i = 0; + while((i += str_chr(smtptext.s+i,'\n') + 1) && (i+12 < smtptext.len) && + str_diffn(smtptext.s+i+4,"STARTTLS\n",9)); + if (i+12 < smtptext.len) + { + substdio_puts(&smtpto,"STARTTLS\r\n"); + substdio_flush(&smtpto); + if (smtpcode() == 220) + { + SSL_library_init(); + if(!(ctx=SSL_CTX_new(SSLv23_client_method()))) + {char buf[1024]; + + out("ZTLS not available: error initializing ctx: "); + SSL_load_error_strings(); + out(ERR_error_string(ERR_get_error(), buf)); + out("\n"); + SSL_shutdown(ssl); + zerodie(); + } + if((stat("control/clientcert.pem", &st) == 0) && + ((SSL_CTX_use_RSAPrivateKey_file(ctx, "control/clientcert.pem", SSL_FILETYPE_PEM) <= 0) || + (SSL_CTX_use_certificate_chain_file(ctx, "control/clientcert.pem") <= 0) || + (SSL_CTX_check_private_key(ctx) <= 0))) + /* if there is a cert and it is bad, I fail + if there is no cert, I leave it to the other side to complain */ + SSL_CTX_set_client_cert_cb(ctx, client_cert_cb); + + /*SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);*/ + SSL_CTX_set_cipher_list(ctx,tlsclientciphers.s); + + if (needtlsauth){ + if (!SSL_CTX_load_verify_locations(ctx, servercert.s, NULL)) + {out("ZTLS unable to load "); out(servercert.s); out("\n"); + zerodie();} + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb); + } + + if(!(ssl=SSL_new(ctx))) + {char buf[1024]; + + out("ZTLS not available: error initializing ssl: "); + SSL_load_error_strings(); + out(ERR_error_string(ERR_get_error(), buf)); + out("\n"); + SSL_shutdown(ssl); + zerodie(); + } + SSL_set_fd(ssl,smtpfd); + + alarm(timeout); + r = SSL_connect(ssl); saveerrno = errno; + alarm(0); + if (flagtimedout) + {out("ZTLS not available: connect timed out\n"); + zerodie();} + errno = saveerrno; + if (r<=0) + {char buf[1024]; + + out("ZTLS not available: connect failed: "); + SSL_load_error_strings(); + out(ERR_error_string(ERR_get_error(), buf)); + out("\n"); + SSL_shutdown(ssl); + zerodie(); + } + if (needtlsauth) + /* should also check alternate names */ + {char commonName[256]; + + if ((r=SSL_get_verify_result(ssl)) != X509_V_OK) + {out("ZTLS unable to verify server with "); + out(servercert.s); out(": "); + out(X509_verify_cert_error_string(r)); out("\n"); + zerodie(); + } + X509_NAME_get_text_by_NID(X509_get_subject_name( + SSL_get_peer_certificate(ssl)), + NID_commonName, commonName, 256); + if (strcasecmp(fqdn,commonName)){ + out("ZTLS connection to "); out(fqdn); + out(" wanted, certificate for "); out(commonName); + out(" received\n"); + zerodie();} + } + + substdio_puts(&smtpto,"EHLO "); + substdio_put(&smtpto,helohost.s,helohost.len); + substdio_puts(&smtpto,"\r\n"); + substdio_flush(&smtpto); + + if (smtpcode() != 250) + { + quit("ZTLS connected to "," but my name was rejected"); + } + } + } + if ((!ssl) && needtlsauth) + {out("ZNo TLS achieved while "); out(servercert.s); out(" exists.\n"); + quit();} +#endif + substdio_puts(&smtpto,"MAIL FROM:<"); substdio_put(&smtpto,sender.s,sender.len); substdio_puts(&smtpto,">\r\n"); @@ -324,6 +577,11 @@ case 1: if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break; } +#ifdef TLS + if (control_rldef(&tlsclientciphers,"control/tlsclientciphers",0,"DEFAULT") != 1) + temp_control(); + if(!stralloc_0(&tlsclientciphers)) temp_nomem(); +#endif } void main(argc,argv) @@ -338,7 +596,10 @@ int flagallaliases; int flagalias; char *relayhost; - + +#ifdef TLS + sig_alarmcatch(sigalrm); +#endif sig_pipeignore(); if (argc < 4) perm_usage(); if (chdir(auto_qmail) == -1) temp_chdir(); @@ -417,7 +678,11 @@ 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 + smtp(ip.ix[i].fqdn); /* does not return */ +#else smtp(); /* does not return */ +#endif } tcpto_err(&ip.ix[i].ip,errno == error_timeout); close(smtpfd); =================================================================== RCS file: /home/src/cvsroot/qmail/qmail-send.c,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- qmail-send.c.orig 27 Nov 2001 02:20:07 -0000 1.1.1.1 +++ qmail-send.c 31 May 2002 17:40:23 -0000 1.2 @@ -44,6 +44,8 @@ int lifetime = 604800; +int bouncemaxbytes = 50000; + stralloc percenthack = {0}; struct constmap mappercenthack; stralloc locals = {0}; @@ -740,9 +742,17 @@ qmail_fail(&qqt); else { + int bytestogo = bouncemaxbytes; + int bytestoget = (bytestogo < sizeof buf) ? bytestogo : sizeof buf; substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf)); - while ((r = substdio_get(&ssread,buf,sizeof(buf))) > 0) + while (bytestoget > 0 && (r = substdio_get(&ssread,buf,bytestoget)) > 0) { qmail_put(&qqt,buf,r); + bytestogo -= bytestoget; + bytestoget = (bytestogo < sizeof buf) ? bytestogo : sizeof buf; + } + if (r > 0) { + qmail_puts(&qqt,"\n\n--- End of message stripped.\n"); + } close(fd); if (r == -1) qmail_fail(&qqt); @@ -1442,6 +1452,7 @@ /* this file is too long ---------------------------------------------- MAIN */ int getcontrols() { if (control_init() == -1) return 0; + if (control_readint(&bouncemaxbytes,"control/bouncemaxbytes") == -1) return 0; if (control_readint(&lifetime,"control/queuelifetime") == -1) return 0; if (control_readint(&concurrency[0],"control/concurrencylocal") == -1) return 0; if (control_readint(&concurrency[1],"control/concurrencyremote") == -1) return 0; =================================================================== RCS file: /home/src/cvsroot/qmail/qmail-smtpd.8,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- qmail-smtpd.8.orig 27 Nov 2001 02:20:07 -0000 1.1.1.1 +++ qmail-smtpd.8 31 May 2002 17:07:36 -0000 1.2 @@ -97,6 +97,13 @@ This is done before .IR rcpthosts . .TP 5 +.I mfcheck +If set, +.B qmail-smtpd +tries to resolve the domain of the envelope from address, and +refuses mail if the domain does not resolve. It can be +handy when you want to filter out spamhosts. +.TP 5 .I morercpthosts Extra allowed RCPT domains. If =================================================================== RCS file: /home/src/cvsroot/qmail/qmail-smtpd.c,v retrieving revision 1.1.1.1 retrieving revision 1.7 diff -u -r1.1.1.1 -r1.7 --- qmail-smtpd.c.orig 27 Nov 2001 02:20:07 -0000 1.1.1.1 +++ qmail-smtpd.c 9 Jun 2002 01:15:47 -0000 1.7 @@ -1,4 +1,7 @@ #include "sig.h" +#include +#include +#include #include "readwrite.h" #include "stralloc.h" #include "substdio.h" @@ -16,22 +19,87 @@ #include "scan.h" #include "byte.h" #include "case.h" +#include "wait.h" #include "env.h" #include "now.h" #include "exit.h" #include "rcpthosts.h" +#ifndef TLS #include "timeoutread.h" #include "timeoutwrite.h" +#endif #include "commands.h" +#include "dns.h" +#ifdef TLS +#include +SSL *ssl = NULL; + +stralloc clientcert = {0}; +stralloc tlsserverciphers = {0}; +#endif #define MAXHOPS 100 +#define USE_SMTPAUTH +/* +#define USE_OLD_GREETING +#define USE_NEW_GREETING +*/ + unsigned int databytes = 0; +unsigned int mfchk = 0; int timeout = 1200; +#ifdef USE_SMTPAUTH +static unsigned char authenticated=0; +static char smtpauthlogin[65]; +#endif + +#ifdef TLS +int flagtimedout = 0; +void sigalrm() +{ + flagtimedout = 1; +} +int ssl_timeoutread(timeout,fd,buf,n) int timeout; int fd; char *buf; int n; +{ + int r; int saveerrno; + if (flagtimedout) { errno = error_timeout; return -1; } + alarm(timeout); + if (ssl) { + while(((r = SSL_read(ssl,buf,n)) <= 0) + && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_READ)); + }else r = read(fd,buf,n); + saveerrno = errno; + alarm(0); + if (flagtimedout) { errno = error_timeout; return -1; } + errno = saveerrno; + return r; +} +int ssl_timeoutwrite(timeout,fd,buf,n) int timeout; int fd; char *buf; int n; +{ + int r; int saveerrno; + if (flagtimedout) { errno = error_timeout; return -1; } + alarm(timeout); + if (ssl) { + while(((r = SSL_write(ssl,buf,n)) <= 0) + && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_WRITE)); + }else r = write(fd,buf,n); + saveerrno = errno; + alarm(0); + if (flagtimedout) { errno = error_timeout; return -1; } + errno = saveerrno; + return r; +} +#endif + int safewrite(fd,buf,len) int fd; char *buf; int len; { int r; +#ifdef TLS + r = ssl_timeoutwrite(timeout,fd,buf,len); +#else r = timeoutwrite(timeout,fd,buf,len); +#endif if (r <= 0) _exit(1); return r; } @@ -39,18 +107,40 @@ char ssoutbuf[512]; substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf); +int errwrite(fd,buf,len) int fd; char *buf; int len; +{ + int r; + r = timeoutwrite(timeout,fd,buf,len); + if (r <= 0) _exit(1); + return r; +} + +char sserrbuf[512]; +substdio sserr = SUBSTDIO_FDBUF(errwrite,2,sserrbuf,sizeof sserrbuf); + +static stralloc err; +char strnum[FMT_ULONG]; + 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_crash() { out("421 child crashed (#4.3.0)\r\n"); flush(); _exit(1); } +void die_fork() { out("421 unable to start checkpassword.\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_hmf() { out("553 sorry, your envelope sender domain must exist (#5.7.1)\r\n"); } +void err_smf() { out("451 DNS temporary failure (#4.3.0)\r\n"); } +void err_brt() { out("553 sorry, this recipient is in my badrcptto 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"); } +#ifdef TLS +void err_nogwcert() { out("553 no valid cert for gatewaying (#5.7.1)\r\n"); } +#endif void err_unimpl() { 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"); } @@ -81,13 +171,16 @@ char *remoteinfo; char *local; char *relayclient; +#ifdef TLS +char *tlsciphers; +#endif stralloc helohost = {0}; char *fakehelo; /* pointer into helohost, or 0 */ void dohelo(arg) char *arg; { - if (!stralloc_copys(&helohost,arg)) die_nomem(); - if (!stralloc_0(&helohost)) die_nomem(); + if (!stralloc_copys(&helohost,arg)) die_nomem(); + if (!stralloc_0(&helohost)) die_nomem(); fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0; } @@ -96,12 +189,18 @@ int bmfok = 0; stralloc bmf = {0}; struct constmap mapbmf; +int brtok = 0; +stralloc brt = {0}; +struct constmap mapbrt; void setup() { char *x; unsigned long u; - +#ifdef TLS + char *tlsciphers; +#endif + if (control_init() == -1) die_control(); if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1) die_control(); @@ -112,10 +211,19 @@ if (rcpthosts_init() == -1) die_control(); + if (control_readint(&mfchk,"control/mfcheck") == -1) die_control(); + x = env_get("MFCHECK"); + if (x) { scan_ulong(x,&u); mfchk = u; } + bmfok = control_readfile(&bmf,"control/badmailfrom",0); if (bmfok == -1) die_control(); if (bmfok) if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem(); + + brtok = control_readfile(&brt,"control/badrcptto",0); + if (brtok == -1) die_control(); + if (brtok) + if (!constmap_init(&mapbrt,brt.s,brt.len,0)) die_nomem(); if (control_readint(&databytes,"control/databytes") == -1) die_control(); x = env_get("DATABYTES"); @@ -131,6 +239,17 @@ if (!remotehost) remotehost = "unknown"; remoteinfo = env_get("TCPREMOTEINFO"); relayclient = env_get("RELAYCLIENT"); +#ifdef TLS + if (tlsciphers = env_get("TLSCIPHERS")){ + if (!stralloc_copys(&tlsserverciphers,tlsciphers)) die_nomem(); + } + else { + if (control_rldef(&tlsserverciphers,"control/tlsserverciphers",0,"DEFAULT") != 1) + die_control(); + } + if (!stralloc_0(&tlsserverciphers)) die_nomem(); +#endif + dohelo(remotehost); } @@ -146,7 +265,7 @@ struct ip_address ip; int flagesc; int flagquoted; - + terminator = '>'; i = str_chr(arg,'<'); if (arg[i]) @@ -201,10 +320,71 @@ { int j; if (!bmfok) return 0; - if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1; + if (constmap(&mapbmf,addr.s,addr.len - 1)) goto match; j = byte_rchr(addr.s,addr.len,'@'); if (j < addr.len) - if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1; + if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) goto match; + return 0; + +match: + if (!stralloc_copys(&err,"qmail-smtpd: pid ")) die_nomem(); + if (!stralloc_catb(&err,strnum,fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem(); + if (!stralloc_cats(&err," badmailfrom ")) die_nomem(); + if (!stralloc_catb(&err,addr.s,addr.len - 1)) die_nomem(); + if (!stralloc_cats(&err,"\n")) die_nomem(); + substdio_put(&sserr,err.s,err.len); + substdio_flush(&sserr); + return 1; +} + +int brtcheck() +{ + int j; + if (!brtok) return 0; + if (constmap(&mapbrt,addr.s,addr.len - 1)) goto match; + j = byte_rchr(addr.s,addr.len,'@'); + if (j < addr.len) + if (constmap(&mapbrt,addr.s + j,addr.len - j - 1)) goto match; + return 0; + +match: + if (!stralloc_copys(&err,"qmail-smtpd: pid ")) die_nomem(); + if (!stralloc_catb(&err,strnum,fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem(); + if (!stralloc_cats(&err," badrcptto ")) die_nomem(); + if (!stralloc_catb(&err,addr.s,addr.len - 1)) die_nomem(); + if (!stralloc_cats(&err,"\n")) die_nomem(); + substdio_put(&sserr,err.s,err.len); + substdio_flush(&sserr); + return 1; +} + +int mfcheck() +{ + stralloc sa = {0}; + ipalloc ia = {0}; + unsigned int random; + int j; + + if (!mfchk) return 0; + random = now() + (getpid() << 16); + j = byte_rchr(addr.s,addr.len,'@') + 1; + if (j < addr.len) { + stralloc_copys(&sa, addr.s + j); + dns_init(0); + j = dns_mxip(&ia,&sa,random); + if (j < 0) { + if (j != DNS_MEM) { + if (!stralloc_copys(&err,"qmail-smtpd: pid ")) die_nomem(); + if (!stralloc_catb(&err,strnum,fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem(); + if (!stralloc_cats(&err," mfcheck ")) die_nomem(); + if (!stralloc_catb(&err,addr.s,addr.len - 1)) die_nomem(); + if (!stralloc_cats(&err,(j==DNS_HARD)?" hard\n":" soft\n")) die_nomem(); + substdio_put(&sserr,err.s,err.len); + substdio_flush(&sserr); + } + return j; + } + } return 0; } @@ -229,7 +409,18 @@ } void smtp_ehlo(arg) char *arg; { - smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); + smtp_greet("250-"); +#ifdef TLS +# ifdef USE_SMTPAUTH + if (ssl) out("\r\n250-PIPELINING\r\n250-AUTH LOGIN CRAM-MD5 PLAIN\r\n250 8BITMIME\r\n"); + else out("\r\n250-PIPELINING\r\n250-STARTTLS\r\n250-AUTH LOGIN CRAM-MD5 PLAIN\r\n250 8BITMIME\r\n"); +# else + if (ssl) out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); + else out("\r\n250-PIPELINING\r\n250-STARTTLS\r\n250 8BITMIME\r\n"); +# endif /* USE_SMTP_AUTH */ +#else + out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); +#endif /* TLS */ seenmail = 0; dohelo(arg); } void smtp_rset() @@ -241,12 +432,23 @@ { if (!addrparse(arg)) { err_syntax(); return; } flagbarf = bmfcheck(); + switch(mfcheck()) { + case DNS_HARD: err_hmf(); return; + case DNS_SOFT: err_smf(); return; + case DNS_MEM: die_nomem(); + } 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"); } +#ifdef TLS +static int verify_cb(int ok, X509_STORE_CTX * ctx) +{ + return (1); +} +#endif void smtp_rcpt(arg) char *arg; { if (!seenmail) { err_wantmail(); return; } if (!addrparse(arg)) { err_syntax(); return; } @@ -257,7 +459,56 @@ if (!stralloc_0(&addr)) die_nomem(); } else +#ifndef TLS if (!addrallowed()) { err_nogateway(); return; } +#else + if (!addrallowed()) + { + if (ssl) + { STACK_OF(X509_NAME) *sk; + X509 *peercert; + stralloc tlsclients = {0}; + struct constmap maptlsclients; + int r; + + SSL_set_verify(ssl, + SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, + verify_cb); + if ((sk = SSL_load_client_CA_file("control/clientca.pem")) == NULL) + { err_nogateway(); return; } + SSL_set_client_CA_list(ssl, sk); + if((control_readfile(&tlsclients,"control/tlsclients",0) != 1) || + !constmap_init(&maptlsclients,tlsclients.s,tlsclients.len,0)) + { err_nogateway(); return; } + + SSL_renegotiate(ssl); + SSL_do_handshake(ssl); + ssl->state = SSL_ST_ACCEPT; + SSL_do_handshake(ssl); + if ((r = SSL_get_verify_result(ssl)) != X509_V_OK) + {out("553 no valid cert for gatewaying: "); + out(X509_verify_cert_error_string(r)); + out(" (#5.7.1)\r\n"); + return; + } + + if (peercert = SSL_get_peer_certificate(ssl)) + {char emailAddress[256]; + + X509_NAME_get_text_by_NID(X509_get_subject_name( + SSL_get_peer_certificate(ssl)), + NID_pkcs9_emailAddress, emailAddress, 256); + if (!stralloc_copys(&clientcert, emailAddress)) die_nomem(); + if (!constmap(&maptlsclients,clientcert.s,clientcert.len)) + { err_nogwcert(); return; } + relayclient = ""; + } + else { err_nogwcert(); return; } + } + else { err_nogateway(); return; } + } +#endif + if (!env_get("RELAYCLIENT") && brtcheck()) { err_brt(); return; } if (!stralloc_cats(&rcptto,"T")) die_nomem(); if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); if (!stralloc_0(&rcptto)) die_nomem(); @@ -269,7 +520,11 @@ { int r; flush(); +#ifdef TLS + r = ssl_timeoutread(timeout,fd,buf,len); +#else r = timeoutread(timeout,fd,buf,len); +#endif if (r == -1) if (errno == error_timeout) die_alarm(); if (r <= 0) die_read(); return r; @@ -300,7 +555,7 @@ int flagmaybex; /* 1 if this line might match RECEIVED, if fih */ int flagmaybey; /* 1 if this line might match \r\n, if fih */ int flagmaybez; /* 1 if this line might match DELIVERED, if fih */ - + state = 1; *hops = 0; flaginheader = 1; @@ -322,17 +577,16 @@ } switch(state) { case 0: - if (ch == '\n') straynewline(); + if (ch == '\n') { state = 1; break; } if (ch == '\r') { state = 4; continue; } break; case 1: /* \r\n */ - if (ch == '\n') straynewline(); if (ch == '.') { state = 2; continue; } if (ch == '\r') { state = 4; continue; } - state = 0; + if (ch != '\n') state = 0; break; case 2: /* \r\n + . */ - if (ch == '\n') straynewline(); + if (ch == '\n') return; /* this is what sendmail-8.8.4 does -djg */ if (ch == '\r') { state = 3; continue; } state = 0; break; @@ -369,6 +623,9 @@ int hops; unsigned long qp; char *qqx; +#ifdef TLS + stralloc protocolinfo = {0}; +#endif if (!seenmail) { err_wantmail(); return; } if (!rcptto.len) { err_wantrcpt(); return; } @@ -377,8 +634,21 @@ if (qmail_open(&qqt) == -1) { err_qqt(); return; } qp = qmail_qp(&qqt); out("354 go ahead\r\n"); - +#ifdef TLS + if(ssl){ + if (!stralloc_copys(&protocolinfo, SSL_CIPHER_get_name(SSL_get_current_cipher(ssl)))) die_nomem(); + if (!stralloc_catb(&protocolinfo, " encrypted SMTP", 15)) die_nomem(); + if (clientcert.len){ + if (!stralloc_catb(&protocolinfo," cert ", 6)) die_nomem(); + if (!stralloc_catb(&protocolinfo,clientcert.s, clientcert.len)) die_nomem(); + } + if (!stralloc_0(&protocolinfo)) die_nomem(); + } else if (!stralloc_copyb(&protocolinfo,"SMTP",5)) die_nomem(); + received(&qqt,protocolinfo.s,local,remoteip,remotehost,remoteinfo,case_diffs(remotehost,helohost.s) ? helohost.s : 0, + authenticated ? smtpauthlogin : 0); +#else received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); +#endif blast(&hops); hops = (hops >= MAXHOPS); if (hops) qmail_fail(&qqt); @@ -394,22 +664,435 @@ out("\r\n"); } +#ifdef TLS +static RSA *tmp_rsa_cb(ssl,export,keylength) SSL *ssl; int export; int keylength; +{ + RSA* rsa; + BIO* in; + + if (!export || keylength == 512) + if (in=BIO_new(BIO_s_file_internal())) + if (BIO_read_filename(in,"control/rsa512.pem") > 0) + if (rsa=PEM_read_bio_RSAPrivateKey(in,NULL,NULL,NULL)) + return rsa; + return (RSA_generate_key(export?keylength:512,RSA_F4,NULL,NULL)); +} + +void smtp_tls(arg) char *arg; +{ + SSL_CTX *ctx; + + if (*arg) + {out("501 Syntax error (no parameters allowed) (#5.5.4)\r\n"); + return;} + + SSL_library_init(); + if(!(ctx=SSL_CTX_new(SSLv23_server_method()))) + {out("454 TLS not available: unable to initialize ctx (#4.3.0)\r\n"); + return;} + if(!SSL_CTX_use_RSAPrivateKey_file(ctx, "control/servercert.pem", SSL_FILETYPE_PEM)) + {out("454 TLS not available: missing RSA private key (#4.3.0)\r\n"); + return;} + if(!SSL_CTX_use_certificate_chain_file(ctx, "control/servercert.pem")) + {out("454 TLS not available: missing certificate (#4.3.0)\r\n"); + return;} + SSL_CTX_set_tmp_rsa_callback(ctx, tmp_rsa_cb); + SSL_CTX_set_cipher_list(ctx,tlsserverciphers.s); + SSL_CTX_load_verify_locations(ctx, "control/clientca.pem",NULL); + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_cb); + + out("220 ready for tls\r\n"); flush(); + + if(!(ssl=SSL_new(ctx))) die_read(); + SSL_set_fd(ssl,0); + if(SSL_accept(ssl)<=0) die_read(); + substdio_fdbuf(&ssout,SSL_write,ssl,ssoutbuf,sizeof(ssoutbuf)); + + remotehost = env_get("TCPREMOTEHOST"); + if (!remotehost) remotehost = "unknown"; + dohelo(remotehost); +} +#endif + +#ifdef USE_SMTPAUTH +static unsigned char *base64_alphabet = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; +static int unbase64(ch) int ch; { + int i; + if (ch == '=') return 0; + for (i = 0; i < 64; i++) + if (ch == base64_alphabet[i]) + return i; + return 0; +} +static int base64_dec_buffer(str,dst,len) const char *str;void *dst;int len; +{ + int i, j, l; + unsigned char input[4], output[3], *result = (char *)dst; + if (str == 0) + return 0; + l = str_len(str); + if (dst == 0 || l > len) + return (l / 4) * 3; + memset(dst,0,len); + for (i=j=0; i> 4); + output[1] = (input[1] << 4) | (input[2] >> 2); + output[2] = (input[2] << 6) | (input[3]); + result[j] = output[0]; + if (str[i+1] == '=') return j+1; + result[j+1]=output[1]; + if (str[i+2] == '=') return j+2; + result[j+2]=output[2]; + j += 3; + } + return j; +} +static char **smtpauth_argv; +static char *auth_argv[4]; +static stralloc smtpauth = {0}; +static char smtpauthpass[65]; +static char smtpauthtimestamp[65]; + +char unique[FMT_ULONG + FMT_ULONG + 3]; + +void base64encode(stralloc *input, stralloc *output) +{ +int a=0,b=0,c=0; +int i, j; +int d, e, f, g; + + if (input->len == 0) return; + + for (j=i=0; i< input->len ; i += 3) + { + a=input->s[i]; + b= i+1 < input->len ? input->s[i+1]:0; + c= i+2 < input->len ? input->s[i+2]:0; + + d=base64_alphabet[ a >> 2 ]; + e=base64_alphabet[ ((a & 3 ) << 4) | (b >> 4)]; + f=base64_alphabet[ ((b & 15) << 2) | (c >> 6)]; + g=base64_alphabet[ c & 63 ]; + if (i + 1 >= input->len) f='='; + if (i + 2 >= input->len) g='='; + stralloc_append(output,&d); + stralloc_append(output,&e); + stralloc_append(output,&f); + stralloc_append(output,&g); + } +} + +static int smtpauth_getl(void) { + int i; + if (!stralloc_copys(&smtpauth, "")) return -1; + for (;;) { + if (!stralloc_readyplus(&smtpauth,1)) return -1; + i = substdio_get(&ssin, smtpauth.s + smtpauth.len, 1); + if (i != 1) return i; + if (smtpauth.s[smtpauth.len] == '\n') break; + ++smtpauth.len; + } + if (smtpauth.len > 0) if (smtpauth.s[smtpauth.len-1] == '\r') --smtpauth.len; + smtpauth.s[smtpauth.len] = 0; + return smtpauth.len; +} + +static void smtpauth_authenticate(void) +{ + int st, pid, fds[2]; + + if (pipe(fds)) { + out("535 pipe failure\r\n"); + flush(); + _exit(0); + } + /* spawn external program + + external program should return '0' if it was successful, + + submit: /bin/checkpassword /bin/true + + */ + switch ((pid=fork())) { + case -1: die_fork(); + case 0: close(fds[1]); + fd_copy(3,fds[0]); + flush(); + execvp(auth_argv[1], auth_argv+1); + die_nomem(); + }; + close(fds[0]); + write(fds[1], smtpauthlogin, str_len(smtpauthlogin)+1); + write(fds[1], smtpauthpass, str_len(smtpauthpass)+1); + if (str_len(smtpauthtimestamp)) + { + write(fds[1], smtpauthtimestamp, str_len(smtpauthtimestamp)+1); + } + close(fds[1]); + wait_pid(&st, pid); + + if (wait_crashed(st)) + die_crash(); + if (wait_exitcode(st) == 0) { + out("235 go ahead\r\n"); + flush(); + relayclient=""; + authenticated=1; + return; + } + sleep(2); + out("535 auth failure\r\n"); + flush(); + return; +} + +void smtp_auth(arg) char *arg; { + int ret,i,toolong,start; + char *helper; + /* netscape 4.5 sends AUTH LOGIN + microsoft outlook express sends AUTH LOGIN + + idea is simple + + use an external program to test authority + if success, set 'RELAYCLIENT' + otherwise, let them know nicely (hangup) + + note, i really don't like djb's coding style even though i'm using it here. + i think using spaces for tabs is bad. + -mrs.brisby@nimh.org + */ + /* Here i've added support for other auth types. + + -brush@elysium.pl */ + if (!authenticated) + { + if ((ret=strncasecmp(arg,"login",5))==0) + { + while (arg && *arg && *arg != ' ') arg++; + + /* pass over the space */ + while (arg && *arg && *arg == ' ') arg++; + + if (arg && *arg) { + /* here's the base64 encoded login */ + base64_dec_buffer(arg, smtpauthlogin, sizeof(smtpauthlogin)); + } else { + out("334 VXNlcm5hbWU6\r\n"); /* b64 <- 'Username:' */ + flush(); + if (smtpauth_getl() > 0) + base64_dec_buffer(smtpauth.s, smtpauthlogin, sizeof(smtpauthlogin)); + else + die_read(); + } + out("334 UGFzc3dvcmQ6\r\n"); /* b64 <- 'Password:' */ + flush(); + if (smtpauth_getl() > 0) + base64_dec_buffer(smtpauth.s, smtpauthpass, sizeof(smtpauthpass)); + else + die_read(); + smtpauthtimestamp[0]=0; + auth_argv[1]=smtpauth_argv[1]; /* change checkpass prg */ + auth_argv[2]=smtpauth_argv[2]; /* change checkpass prg */ + auth_argv[3]=NULL; /* change checkpass prg */ + smtpauth_authenticate(); + return; + } + else if ((ret=strncasecmp(arg,"plain",5))==0) + { + static char smtpauthloginpass[200]; + + while (arg && *arg && *arg != ' ') arg++; + + /* pass over the space */ + while (arg && *arg && *arg == ' ') arg++; + + if (arg && *arg) + { + if(strlen(arg)*3/4 >= sizeof(smtpauthloginpass)) + { + out("535 input too long\r\n"); + flush(); + return; + } + /* here's the base64 encoded login/password */ + base64_dec_buffer(arg, smtpauthloginpass, sizeof(smtpauthloginpass)-1); + } else { + out("334 ok. go on.\r\n"); + flush(); + i=smtpauth_getl(); + if(i <= 0) + die_read(); + else if(i*3/4 >= sizeof(smtpauthloginpass)) + { + out("535 input too long\r\n"); + flush(); + return; + } else base64_dec_buffer(smtpauth.s, smtpauthloginpass, sizeof(smtpauthloginpass)-1); + } + smtpauthloginpass[sizeof(smtpauthloginpass)-1]=0; + start=strlen(smtpauthloginpass)+1; + if((start >= sizeof(smtpauthloginpass)) || (strlen(smtpauthloginpass+start) >= 65)) + { + out("535 malformed input\r\n"); + flush(); + return; + } + strcpy(smtpauthlogin,smtpauthloginpass+start); + + start+=strlen(smtpauthlogin)+1; + if((start >= sizeof(smtpauthloginpass)) || (strlen(smtpauthloginpass+start) >= 65)) + { + out("535 malformed input\r\n"); + flush(); + return; + } + strcpy(smtpauthpass,smtpauthloginpass+start); + + smtpauthtimestamp[0]=0; + auth_argv[1]=smtpauth_argv[1]; /* change checkpass prg */ + auth_argv[2]=smtpauth_argv[2]; /* change checkpass prg */ + auth_argv[3]=NULL; /* change checkpass prg */ + smtpauth_authenticate(); + return; + } + else if ((ret=strncasecmp(arg,"cram-md5",8))==0) + { + int r; + static stralloc me = {0}; + static stralloc greet = {0}; + static stralloc greetenc = {0}; + char *s; + r = control_readline(&me,"control/me"); + if (r != 1) + { + out("535 internal server error\r\n"); flush(); _exit(0); + } + for (r=0;r <= me.len;r++) + { + if (me.s[r]=='\n') + { + me.s[r]=0; + break; + } + } + + s = unique; + s += fmt_uint(s,getpid()); + *s++ = '.'; + s += fmt_ulong(s,(unsigned long) now()); + *s++ = '@'; + *s++ = 0; + if (greet.len) + { + for (i=0;i"); + greet.s[greet.len]=0; // obscure fix but it works + stralloc_readyplus(&greet,3); + if (greetenc.len) + { + for (i=0;i 0) + { + s=calloc((size_t) strlen(smtpauth.s),(size_t)1); + base64_dec_buffer(smtpauth.s, s, strlen(smtpauth.s)); + } + helper=strtok(s," "); + if(helper!=NULL) + { + strncpy (smtpauthlogin,helper,64); + } + else + { + out("535 malformed input\r\n"); + return; + } + helper=strtok(NULL," "); + if(helper!=NULL) + { + strncpy (smtpauthtimestamp,helper,64); + } + else + { + out("535 malformed input\r\n"); + return; + } + strncpy (smtpauthpass,greet.s,64); + auth_argv[1]=smtpauth_argv[3]; /* change checkpass prg */ + auth_argv[2]=smtpauth_argv[4]; /* change checkpass prg */ + auth_argv[3]=NULL; /* change checkpass prg */ + smtpauth_authenticate(); + return; + } + else + { + out("504 auth type not supported\r\n"); + flush(); + return; + } + } + else + { + out("503 you are already authenticated\r\n"); + flush(); + return; + } +} +#endif + struct commands smtpcommands[] = { { "rcpt", smtp_rcpt, 0 } , { "mail", smtp_mail, 0 } , { "data", smtp_data, flush } +#ifdef USE_SMTPAUTH +, { "auth", smtp_auth, flush } +#endif , { "quit", smtp_quit, flush } , { "helo", smtp_helo, flush } , { "ehlo", smtp_ehlo, flush } , { "rset", smtp_rset, 0 } , { "help", smtp_help, flush } +#ifdef TLS +, { "starttls", smtp_tls, flush } +#endif , { "noop", err_noop, flush } , { "vrfy", err_vrfy, flush } , { 0, err_unimpl, flush } } ; -void main() +void main(argc,argv) int argc; char **argv; { +#ifdef TLS + sig_alarmcatch(sigalrm); +#endif +#ifdef USE_SMTPAUTH + smtpauth_argv = argv; +#endif sig_pipeignore(); if (chdir(auto_qmail) == -1) die_control(); setup(); =================================================================== RCS file: /home/src/cvsroot/qmail/received.c,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- received.c.orig 27 Nov 2001 02:20:07 -0000 1.1.1.1 +++ received.c 27 Dec 2001 04:55:04 -0000 1.2 @@ -37,7 +37,7 @@ /* "Received: from relay1.uu.net (HELO uunet.uu.net) (7@192.48.96.5)\n" */ /* " by silverton.berkeley.edu with SMTP; 26 Sep 1995 04:46:54 -0000\n" */ -void received(qqt,protocol,local,remoteip,remotehost,remoteinfo,helo) +void received(qqt,protocol,local,remoteip,remotehost,remoteinfo,helo,authuser) struct qmail *qqt; char *protocol; char *local; @@ -45,6 +45,7 @@ char *remotehost; char *remoteinfo; char *helo; +char *authuser; { struct datetime dt; @@ -53,6 +54,11 @@ if (helo) { qmail_puts(qqt," (HELO "); safeput(qqt,helo); + qmail_puts(qqt,")"); + } + if (authuser) { + qmail_puts(qqt," (AUTH "); + safeput(qqt,authuser); qmail_puts(qqt,")"); } qmail_puts(qqt," ("); =================================================================== RCS file: syncdir.c diff -N syncdir.c --- syncdir.c.orig 1 Jan 1970 00:00:00 -0000 +++ syncdir.c 27 Dec 2001 18:08:09 -0000 1.1 @@ -0,0 +1,110 @@ +/* syncdir -- emulate synchronous directories + Copyright (C) 1998 Bruce Guenter + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + You can reach me at bruce.guenter@qcc.sk.ca +*/ + +#include +#include +#define open XXX_open +#include +#undef open +#include +#include +#include +#include +#include + +#define SYS_OPEN(FILE,FLAG,MODE) syscall(SYS_open, FILE, FLAG, MODE) +#define SYS_CLOSE(FD) syscall(SYS_close, FD) +#define SYS_LINK(OLD,NEW) syscall(SYS_link, OLD, NEW) +#define SYS_UNLINK(PATH) syscall(SYS_unlink, PATH) +#define SYS_RENAME(OLD,NEW) syscall(SYS_rename, OLD, NEW) +#define SYS_FSYNC(FD) syscall(SYS_fsync, FD) + +static int fdirsync(const char* filename, unsigned length) +{ + char dirname[length+1]; + /* This could also be: + * char* dirname = alloca(length+1); */ + int dirfd; + int retval; + memcpy(dirname, filename, length); + dirname[length] = 0; + if((dirfd = SYS_OPEN(dirname,O_RDONLY,0)) == -1) + return -1; + retval = (SYS_FSYNC(dirfd) == -1 && errno == EIO) ? -1 : 0; + SYS_CLOSE(dirfd); + return retval; +} + +static int fdirsyncfn(const char *filename) +{ + const char *slash = filename+strlen(filename)-1; + + /* Skip over trailing slashes, which would be ignored by some + * operations */ + while(slash > filename && *slash == '/') + --slash; + + /* Skip back to the next slash */ + while(slash > filename && *slash != '/') + --slash; + + /* slash now either points to a '/' character, or no slash was found */ + if(*slash == '/') + return fdirsync(filename, + (slash == filename) ? 1 : slash-filename); + else + return fdirsync(".", 1); +} + +int open(const char *file,int oflag,mode_t mode) +{ + int fd = SYS_OPEN(file, oflag, mode); + if(fd == -1) + return fd; + if (oflag & (O_WRONLY | O_RDWR)) + if (fdirsyncfn(file) == -1) { + SYS_CLOSE(fd); + return -1; + } + return fd; +} + +int link(const char *oldpath,const char *newpath) +{ + if(SYS_LINK(oldpath,newpath) == -1) + return -1; + return fdirsyncfn(newpath); +} + +int unlink(const char *path) +{ + if(SYS_UNLINK(path) == -1) + return -1; + return fdirsyncfn(path); +} + +int rename(const char *oldpath,const char *newpath) +{ + if (SYS_RENAME(oldpath,newpath) == -1) + return -1; + if (fdirsyncfn(newpath) == -1) + return -1; + return fdirsyncfn(oldpath); +}