This patch is intended to be applied on top of John Simpson's combined patch, see: http://qmail.jms1.net/patches/combined.shtml This patch does three things: 1. Allows a TLS connection to be made without clientcert.pem. The use of a client certificate when connecting via TLS is rarely required. If a clientcert.pem file does not exist in qmail/control, qmail-remote will perform a simple TLS handshake. If a clientcert.pem file does exist, qmail-remote will use it to perform a client authenticated handshake. A default clientcert.pem file is no longer installed when running make cert. 2. Adds support for the treating port 465 as a special smtps port. When port 465 is specified in the smtproutes file, qmail-remote will connect via SSL before issuing any SMTP commands. 3. Replaces calls to ERR_error_string with calls to ERR_error_string_n. This may not be required, but it is safer and solves some issues I was seeing in which the ERR_error_string function returned an incorrect pointer. This may be a 64-bit processor issue, but I was unable to locate the problem. Switching to ERR_error_string_n fixes the problem. diff -Naur qmail-1.03.orig/Makefile qmail-1.03/Makefile --- qmail-1.03.orig/Makefile 2009-03-09 08:53:22.000000000 -0500 +++ qmail-1.03/Makefile 2009-03-09 08:53:37.000000000 -0500 @@ -2226,7 +2226,6 @@ -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: openssl req -new -nodes \ @@ -2234,7 +2233,6 @@ -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" diff -Naur qmail-1.03.orig/qmail-remote.8 qmail-1.03/qmail-remote.8 --- qmail-1.03.orig/qmail-remote.8 2009-03-09 08:53:22.000000000 -0500 +++ qmail-1.03/qmail-remote.8 2009-03-09 08:53:37.000000000 -0500 @@ -114,6 +114,10 @@ always exits zero. .SH "CONTROL FILES" .TP 5 +.I clientcert.pem +SSL certificate that is used to authenticate with the remote server +during a TLS session. +.TP 5 .I helohost Current host name, for use solely in saying hello to the remote SMTP server. @@ -181,6 +185,9 @@ any other address is artificially routed to .BR heaven.af.mil . +A SMTP port of 465 (deprecated smtps port) causes a TLS session to be started +before any SMTP commands are transmitted or received. + .I user and .I pass diff -Naur qmail-1.03.orig/qmail-remote.c qmail-1.03/qmail-remote.c --- qmail-1.03.orig/qmail-remote.c 2009-03-09 08:53:22.000000000 -0500 +++ qmail-1.03/qmail-remote.c 2009-03-09 08:54:11.000000000 -0500 @@ -45,6 +45,10 @@ #define HUGESMTPTEXT 5000 #define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */ +#ifdef TLS +#define PORT_SMTPS 465 +int smtps = 0; +#endif unsigned long port = PORT_SMTP; int allow_insecure_auth = 0; @@ -145,7 +149,7 @@ out("ZTLS connection to "); outhost(); out(" died: "); SSL_load_error_strings(); - out(ERR_error_string(ERR_get_error(), buf)); out("\n"); + ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); out(buf); out("\n"); SSL_shutdown(ssl); zerodie(); } @@ -170,7 +174,7 @@ out("ZTLS connection to "); outhost(); out(" died: "); SSL_load_error_strings(); - out(ERR_error_string(ERR_get_error(), buf)); out("\n"); + ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); out(buf); out("\n"); SSL_shutdown(ssl); zerodie(); } @@ -367,6 +371,10 @@ if(!stralloc_0(&servercert)) temp_nomem(); if (stat(servercert.s,&st) == 0) needtlsauth = 1; } + + if (port == PORT_SMTPS) smtps = 1; + if(!smtps) + { #endif code = smtpcode(); @@ -386,15 +394,22 @@ } #ifdef TLS - if(can_tls) { - i = 0; + } + if(can_tls || smtps) { + i = 0; + if(!smtps) { 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) + } + else + can_tls = 0; + } + + if(smtps || (can_tls && smtpcode() == 220)) { SSL_library_init(); if(!(ctx=SSL_CTX_new(SSLv23_client_method()))) @@ -402,7 +417,7 @@ out("ZTLS not available: error initializing ctx: "); SSL_load_error_strings(); - out(ERR_error_string(ERR_get_error(), buf)); + ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); out(buf); out("\n"); SSL_shutdown(ssl); zerodie(); @@ -430,7 +445,7 @@ out("ZTLS not available: error initializing ssl: "); SSL_load_error_strings(); - out(ERR_error_string(ERR_get_error(), buf)); + ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); out(buf); out("\n"); SSL_shutdown(ssl); zerodie(); @@ -449,7 +464,7 @@ out("ZTLS not available: connect failed: "); SSL_load_error_strings(); - out(ERR_error_string(ERR_get_error(), buf)); + ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); out(buf); out("\n"); SSL_shutdown(ssl); zerodie(); @@ -473,6 +488,12 @@ out(" received\n"); zerodie();} } + + if(smtps) { + code = smtpcode(); + if (code >= 400 && code < 600) return; /* try next MX, see RFC 2821 */ + if (code != 220) quit("ZConnected to "," but greeting failed"); + } substdio_puts(&smtpto,"EHLO "); substdio_put(&smtpto,helohost.s,helohost.len); @@ -484,8 +505,7 @@ quit("ZTLS connected to "," but my name was rejected"); } } - } - if ((!ssl) && needtlsauth) + if ((!ssl) && (needtlsauth || smtps)) {out("ZNo TLS achieved while "); out(servercert.s); out(" exists.\n"); quit();} } /* ends if(can_tls) block */ @@ -662,8 +682,9 @@ getcontrols(); #ifdef TLS - if(-1==(zfd=open("control/clientcert.pem",O_RDONLY))) can_tls=0; - else close(zfd); +/* Don't require a client certificate for remote TLS */ +// if(-1==(zfd=open("control/clientcert.pem",O_RDONLY))) can_tls=0; +// else close(zfd); #endif if (!stralloc_copys(&host,argv[1])) temp_nomem();