Index: qmail/.cvsignore diff -c /dev/null qmail/.cvsignore:1.1 *** /dev/null Fri Dec 28 11:05:38 2001 --- qmail/.cvsignore Wed Dec 26 20:36:30 2001 *************** *** 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 Index: qmail/ChangeLog.arctic.org diff -c /dev/null qmail/ChangeLog.arctic.org:1.2 *** /dev/null Fri Dec 28 11:05:39 2001 --- qmail/ChangeLog.arctic.org Thu Dec 27 10:08:09 2001 *************** *** 0 **** --- 1,19 ---- + 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. Index: qmail/Makefile diff -c qmail/Makefile:1.1.1.1 qmail/Makefile:1.4 *** qmail/Makefile:1.1.1.1 Mon Nov 26 18:20:07 2001 --- qmail/Makefile Thu Dec 27 10:08:09 2001 *************** *** 810,816 **** instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \ binm3 binm3+df ! load: \ make-load warn-auto.sh systype ( cat warn-auto.sh; ./make-load "`cat systype`" ) > load chmod 755 load --- 810,820 ---- instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \ binm3 binm3+df ! 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,1452 **** 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` qmail-remote.0: \ qmail-remote.8 --- 1450,1457 ---- 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` \ ! -L/usr/local/ssl/lib -lssl -lcrypto qmail-remote.0: \ qmail-remote.8 *************** *** 1542,1548 **** 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` qmail-smtpd.0: \ qmail-smtpd.8 --- 1547,1553 ---- 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` -L/usr/local/ssl/lib -lssl -lcrypto qmail-smtpd.0: \ qmail-smtpd.8 *************** *** 2053,2058 **** --- 2058,2067 ---- 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,2141 **** --- 2148,2169 ---- 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" Index: qmail/README-starttls-requireauth diff -c /dev/null qmail/README-starttls-requireauth:1.2 *** /dev/null Fri Dec 28 11:05:39 2001 --- qmail/README-starttls-requireauth Wed Dec 26 20:41:14 2001 *************** *** 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(); Index: qmail/conf-cc diff -c qmail/conf-cc:1.1.1.1 qmail/conf-cc:1.2 *** qmail/conf-cc:1.1.1.1 Mon Nov 26 18:20:07 2001 --- qmail/conf-cc Thu Dec 20 19:29:00 2001 *************** *** 1,3 **** ! cc -O2 This will be used to compile .c files. --- 1,3 ---- ! cc -O2 -DTLS -I/usr/local/ssl/include This will be used to compile .c files. Index: qmail/dns.c diff -c qmail/dns.c:1.1.1.1 qmail/dns.c:1.2 *** qmail/dns.c:1.1.1.1 Mon Nov 26 18:20:07 2001 --- qmail/dns.c Thu Dec 20 19:29:00 2001 *************** *** 270,276 **** --- 270,284 ---- { 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; if (glue.s[0]) { *************** *** 330,335 **** --- 338,346 ---- 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; } Index: qmail/ipalloc.h diff -c qmail/ipalloc.h:1.1.1.1 qmail/ipalloc.h:1.2 *** qmail/ipalloc.h:1.1.1.1 Mon Nov 26 18:20:07 2001 --- qmail/ipalloc.h Thu Dec 20 19:29:00 2001 *************** *** 3,9 **** --- 3,14 ---- #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" Index: qmail/make-load.sh diff -c qmail/make-load.sh:1.1.1.1 qmail/make-load.sh:1.2 *** qmail/make-load.sh:1.1.1.1 Mon Nov 26 18:20:07 2001 --- qmail/make-load.sh Thu Dec 27 10:08:09 2001 *************** *** 1,2 **** echo 'main="$1"; shift' ! echo exec "$LD" '-o "$main" "$main".o ${1+"$@"}' --- 1,2 ---- echo 'main="$1"; shift' ! echo exec "$LD" '-o "$main" "$main".o ${1+"$@"} -L. -lsyncdir' Index: qmail/qmail-qmqpd.c diff -c qmail/qmail-qmqpd.c:1.1.1.1 qmail/qmail-qmqpd.c:1.2 *** qmail/qmail-qmqpd.c:1.1.1.1 Mon Nov 26 18:20:07 2001 --- qmail/qmail-qmqpd.c Wed Dec 26 20:55:04 2001 *************** *** 78,84 **** if (!local) local = env_get("TCPLOCALIP"); if (!local) local = "unknown"; ! received(&qq,"QMQP",local,remoteip,remotehost,remoteinfo,(char *) 0); } char buf[1000]; --- 78,84 ---- if (!local) local = env_get("TCPLOCALIP"); if (!local) local = "unknown"; ! received(&qq,"QMQP",local,remoteip,remotehost,remoteinfo,(char *) 0, (char *) 0); } char buf[1000]; Index: qmail/qmail-remote.c diff -c qmail/qmail-remote.c:1.1.1.1 qmail/qmail-remote.c:1.2 *** qmail/qmail-remote.c:1.1.1.1 Mon Nov 26 18:20:07 2001 --- qmail/qmail-remote.c Thu Dec 20 19:29:00 2001 *************** *** 26,34 **** --- 26,44 ---- #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 #define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */ *************** *** 107,123 **** --- 117,210 ---- 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,191 **** --- 273,306 ---- 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,235 **** stralloc recip = {0}; void smtp() { unsigned long code; int flagbother; int i; ! if (smtpcode() != 220) quit("ZConnected to "," but greeting failed"); 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"); ! substdio_puts(&smtpto,"MAIL FROM:<"); substdio_put(&smtpto,sender.s,sender.len); substdio_puts(&smtpto,">\r\n"); --- 331,488 ---- 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,329 **** --- 577,587 ---- 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,344 **** int flagallaliases; int flagalias; char *relayhost; ! sig_pipeignore(); if (argc < 4) perm_usage(); if (chdir(auto_qmail) == -1) temp_chdir(); --- 596,605 ---- 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,423 **** --- 678,688 ---- 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); Index: qmail/qmail-smtpd.c diff -c qmail/qmail-smtpd.c:1.1.1.1 qmail/qmail-smtpd.c:1.4 *** qmail/qmail-smtpd.c:1.1.1.1 Mon Nov 26 18:20:07 2001 --- qmail/qmail-smtpd.c Wed Dec 26 20:55:04 2001 *************** *** 1,4 **** --- 1,7 ---- #include "sig.h" + #include + #include + #include #include "readwrite.h" #include "stralloc.h" #include "substdio.h" *************** *** 16,37 **** --- 19,103 ---- #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 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; } *************** *** 45,56 **** --- 111,128 ---- 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_brt() { out("553 sorry, this recipient is in my badrecipientto 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,93 **** char *remoteinfo; char *local; char *relayclient; 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(); fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0; } --- 153,168 ---- 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(); fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0; } *************** *** 96,107 **** int bmfok = 0; stralloc bmf = {0}; struct constmap mapbmf; void setup() { char *x; unsigned long u; ! if (control_init() == -1) die_control(); if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1) die_control(); --- 171,188 ---- 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(); *************** *** 116,121 **** --- 197,207 ---- 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,136 **** --- 217,233 ---- 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,152 **** struct ip_address ip; int flagesc; int flagquoted; ! terminator = '>'; i = str_chr(arg,'<'); if (arg[i]) --- 243,249 ---- struct ip_address ip; int flagesc; int flagquoted; ! terminator = '>'; i = str_chr(arg,'<'); if (arg[i]) *************** *** 208,213 **** --- 305,321 ---- return 0; } + int brtcheck() + { + int j; + if (!brtok) return 0; + if (constmap(&mapbrt,addr.s,addr.len - 1)) return 1; + j = byte_rchr(addr.s,addr.len,'@'); + if (j < addr.len) + if (constmap(&mapbrt,addr.s + j,addr.len - j - 1)) return 1; + return 0; + } + int addrallowed() { int r; *************** *** 229,235 **** } void smtp_ehlo(arg) char *arg; { ! smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); seenmail = 0; dohelo(arg); } void smtp_rset() --- 337,354 ---- } void smtp_ehlo(arg) char *arg; { ! 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,252 **** --- 366,377 ---- 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,263 **** --- 382,437 ---- 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,275 **** --- 443,453 ---- { 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,306 **** 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; --- 478,484 ---- 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,374 **** --- 547,555 ---- 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,384 **** if (qmail_open(&qqt) == -1) { err_qqt(); return; } qp = qmail_qp(&qqt); out("354 go ahead\r\n"); ! received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); blast(&hops); hops = (hops >= MAXHOPS); if (hops) qmail_fail(&qqt); --- 558,578 ---- 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,415 **** out("\r\n"); } struct commands smtpcommands[] = { { "rcpt", smtp_rcpt, 0 } , { "mail", smtp_mail, 0 } , { "data", smtp_data, flush } , { "quit", smtp_quit, flush } , { "helo", smtp_helo, flush } , { "ehlo", smtp_ehlo, flush } , { "rset", smtp_rset, 0 } , { "help", smtp_help, flush } , { "noop", err_noop, flush } , { "vrfy", err_vrfy, flush } , { 0, err_unimpl, flush } } ; ! void main() { sig_pipeignore(); if (chdir(auto_qmail) == -1) die_control(); setup(); --- 588,1022 ---- 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(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(); Index: qmail/received.c diff -c qmail/received.c:1.1.1.1 qmail/received.c:1.2 *** qmail/received.c:1.1.1.1 Mon Nov 26 18:20:07 2001 --- qmail/received.c Wed Dec 26 20:55:04 2001 *************** *** 37,43 **** /* "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) struct qmail *qqt; char *protocol; char *local; --- 37,43 ---- /* "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,authuser) struct qmail *qqt; char *protocol; char *local; *************** *** 45,50 **** --- 45,51 ---- char *remotehost; char *remoteinfo; char *helo; + char *authuser; { struct datetime dt; *************** *** 53,58 **** --- 54,64 ---- 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," ("); Index: qmail/syncdir.c diff -c /dev/null qmail/syncdir.c:1.1 *** /dev/null Fri Dec 28 11:05:39 2001 --- qmail/syncdir.c Thu Dec 27 10:08:09 2001 *************** *** 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); + }