Download | Plain Text | No Line Numbers


  1. Frederik Vermeulen <qmail-tls akrul inoa.net> 20060104
  2. http://inoa.net/qmail-tls/
  3.  
  4. This patch implements RFC 3207 (was RFC 2487) in qmail.
  5. This means you can get SSL or TLS encrypted and
  6. authenticated SMTP between the MTAs and from MUA to MTA.
  7. The code is considered experimental (but has worked for
  8. many since its first release on 1999-03-21).
  9.  
  10. Usage: - install OpenSSL-0.9.8 http://www.openssl.org/
  11. (any 0.9.6 to 0.9.8 version is presumed to work)
  12. - apply patch to netqmail-1.05 http://qmail.org/netqmail
  13. (should work on qmail-1.03 too). The patches to
  14. qmail-remote.c and qmail-smtpd.c can be applied separately.
  15. - provide a server certificate in /var/qmail/control/servercert.pem.
  16. "make cert" makes a self-signed certificate.
  17. "make cert-req" makes a certificate request.
  18. Note: you can add the CA certificate and intermediate
  19. certs to the end of servercert.pem.
  20. - replace qmail-smtpd and/or qmail-remote binary
  21. - verify operation (header information should show
  22. something like
  23. "Received [..] with (DHE-RSA-AES256-SHA encrypted) SMTP;")
  24. If you don't have a server to test with, you can test
  25. by sending mail to tag-ping@tbs-internet.com,
  26. which will bounce your mail.
  27.  
  28. Optional: - when DEBUG is defined, some extra TLS info will be logged
  29. - qmail-remote will authenticate with the certificate in
  30. /var/qmail/control/clientcert.pem. By preference this is
  31. the same as servercert.pem, where nsCertType should be
  32. == server,client or be a generic certificate (no usage specified).
  33. - when a 512 bit RSA key is provided in /var/qmail/control/rsa512.pem,
  34. this key will be used instead of (slow) on-the-fly generation by
  35. qmail-smtpd. Idem for 512 and 1024 DH params in control/dh512.pem
  36. and control/dh1024.pem. `make tmprsadh` does this.
  37. Periodical replacement can be done by crontab:
  38. 01 01 * * * /var/qmail/bin/update_tmprsadh > /dev/null 2>&1
  39. - server authentication:
  40. qmail-remote requires authentication from servers for which
  41. /var/qmail/control/tlshosts/host.dom.ain.pem exists.
  42. The .pem file contains the validating CA certificates
  43. (or self-signed server certificate). CommonName has to match.
  44. WARNING: this option may cause mail to be delayed, bounced,
  45. doublebounced, and lost.
  46. If /var/qmail/control/tlshosts/exhaustivelist is present,
  47. the lists of hosts in /var/qmail/control/tlshosts is
  48. an exhaustive list of hosts TLS is tried on.
  49. If /var/qmail/control/notlshosts/host.dom.ain is present,
  50. no TLS is tried on this host.
  51. - client authentication:
  52. when relay rules would reject an incoming mail,
  53. qmail-smtpd can allow the mail based on a presented cert.
  54. Certs are verified against a CA list in
  55. /var/qmail/control/clientca.pem (eg. http://www.modssl.org/
  56. source/cvs/exp/mod_ssl/pkg.mod_ssl/pkg.sslcfg/ca-bundle.crt)
  57. and the cert email-address has to match a line in
  58. /var/qmail/control/tlsclients. This email-address is logged
  59. in the headers. CRLs can be provided through
  60. /var/qmail/control/clientcrl.pem.
  61. - cipher selection:
  62. qmail-remote:
  63. openssl cipher string (`man ciphers`) read from
  64. /var/qmail/control/tlsclientciphers
  65. qmail-smtpd:
  66. openssl cipher string read from TLSCIPHERS environment variable
  67. (can vary based on client IP address e.g.)
  68. or if that is not available /var/qmail/control/tlsserverciphers
  69. - smtps (deprecated SMTP over TLS via port 465):
  70. qmail-remote: when connecting to port 465
  71. qmail-smtpd: when SMTPS environment variable is not empty
  72.  
  73. Caveats: - do a `make clean` after patching
  74. - binaries dynamically linked with current openssl versions need
  75. recompilation when the shared openssl libs are upgraded.
  76. - this patch could conflict with other patches (notably those
  77. replacing \n with \r\n, which is a bad idea on encrypted links).
  78. - some broken servers have a problem with TLSv1 compatibility.
  79. Uncomment the line where we set the SSL_OP_NO_TLSv1 option.
  80. - needs working /dev/urandom (or EGD for openssl versions >0.9.7)
  81. for seeding random number generator.
  82. - packagers should make sure that installing without a valid
  83. servercert is impossible
  84. - when applied in combination with AUTH patch, AUTH patch
  85. should be applied first and first part of this patch
  86. will fail. This error can be ignored. Packagers should
  87. cut the first 12 lines of this patch to make a happy
  88. patch
  89. - `make tmprsadh` is recommended (or should I say required),
  90. otherwise DH generation can be unpredictably slow
  91. - some need "-I/usr/kerberos/include" to be added in conf-cc
  92.  
  93. Copyright: GPL
  94. Links with OpenSSL
  95. Inspiration and code from examples in SSLeay (E. Young
  96. <eay@cryptsoft.com> and T. Hudson <tjh@cryptsoft.com>),
  97. stunnel (M. Trojnara <mtrojnar@ddc.daewoo.com.pl>),
  98. Postfix/TLS (L. Jaenicke <Lutz.Jaenicke@aet.tu-cottbus.de>),
  99. modssl (R. Engelschall <rse@engelschall.com>),
  100. openssl examples of E. Rescorla <ekr@rtfm.com>.
  101.  
  102. Bug reports: mailto:<qmail-tls akrul inoa.net>
  103.  
  104.  
  105. >----< Cut the next 12 lines if applying over AUTH server patch >---<
  106. --- qmail-1.03/qmail-smtpd.c Mon Jun 15 03:53:16 1998
  107. +++ qmail-1.03-tls/qmail-smtpd.c Tue Jun 18 09:49:38 2002
  108. @@ -229,7 +229,8 @@
  109. }
  110. void smtp_ehlo(arg) char *arg;
  111. {
  112. - smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
  113. + smtp_greet("250-");
  114. + out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
  115. seenmail = 0; dohelo(arg);
  116. }
  117. void smtp_rset()
  118. >----< Cut previous 12 lines if applying over AUTH server patch >---<
  119.  
  120.  
  121.  
  122. >----< The next 89 lines are the qmail-remote EHLO patch >---<
  123. --- qmail-1.03/qmail-remote.c Mon Jun 15 03:53:16 1998
  124. +++ qmail-1.03-tls/qmail-remote.c Sun Nov 24 13:05:20 2002
  125. @@ -163,6 +163,59 @@ unsigned long smtpcode()
  126. return code;
  127. }
  128.  
  129. +#ifdef EHLO
  130. +saa ehlokw = {0}; /* list of EHLO keywords and parameters */
  131. +int maxehlokwlen = 0;
  132. +
  133. +unsigned long ehlo()
  134. +{
  135. + stralloc *sa;
  136. + char *s, *e, *p;
  137. + unsigned long code;
  138. +
  139. + if (ehlokw.len > maxehlokwlen) maxehlokwlen = ehlokw.len;
  140. + ehlokw.len = 0;
  141. +
  142. +# ifdef MXPS
  143. + if (type == 's') return 0;
  144. +# endif
  145. +
  146. + substdio_puts(&smtpto, "EHLO ");
  147. + substdio_put(&smtpto, helohost.s, helohost.len);
  148. + substdio_puts(&smtpto, "\r\n");
  149. + substdio_flush(&smtpto);
  150. +
  151. + code = smtpcode();
  152. + if (code != 250) return code;
  153. +
  154. + s = smtptext.s;
  155. + while (*s++ != '\n') ; /* skip the first line: contains the domain */
  156. +
  157. + e = smtptext.s + smtptext.len - 6; /* 250-?\n */
  158. + while (s <= e)
  159. + {
  160. + if (!saa_readyplus(&ehlokw, 1)) temp_nomem();
  161. + sa = ehlokw.sa + ehlokw.len++;
  162. + if (ehlokw.len > maxehlokwlen) *sa = sauninit; else sa->len = 0;
  163. +
  164. + /* smtptext is known to end in a '\n' */
  165. + for (p = (s += 4); ; ++p)
  166. + if (*p == '\n' || *p == ' ' || *p == '\t') {
  167. + if (!stralloc_catb(sa, s, p - s) || !stralloc_0(sa)) temp_nomem();
  168. + if (*p++ == '\n') break;
  169. + while (*p == ' ' || *p == '\t') ;
  170. + s = p;
  171. + }
  172. + s = p;
  173. + /* keyword should consist of alpha-num and '-'
  174. + * broken AUTH might use '=' instead of space */
  175. + for (p = sa->s; *p; ++p) if (*p == '=') { *p = 0; break; }
  176. + }
  177. +
  178. + return 250;
  179. +}
  180. +#endif
  181. +
  182. void outsmtptext()
  183. {
  184. int i;
  185. @@ -224,12 +277,26 @@ void smtp()
  186.  
  187. if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
  188.  
  189. +#ifdef EHLO
  190. + code = ehlo();
  191. +
  192. + if (code == 250) {
  193. + /* add EHLO response checks here */
  194. +
  195. + /* and if EHLO failed, use HELO */
  196. + } else {
  197. +#endif
  198. +
  199. substdio_puts(&smtpto,"HELO ");
  200. substdio_put(&smtpto,helohost.s,helohost.len);
  201. substdio_puts(&smtpto,"\r\n");
  202. substdio_flush(&smtpto);
  203. if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
  204.  
  205. +#ifdef EHLO
  206. + }
  207. +#endif
  208. +
  209. substdio_puts(&smtpto,"MAIL FROM:<");
  210. substdio_put(&smtpto,sender.s,sender.len);
  211. substdio_puts(&smtpto,">\r\n");
  212. >----< Previous 89 lines are the qmail-remote EHLO patch >---<
  213.  
  214.  
  215.  
  216. --- qmail-1.03/qmail-smtpd.c Mon Jun 15 03:53:16 1998
  217. +++ qmail-1.03-tls/qmail-smtpd.c Mon Jul 1 10:47:54 2002
  218. @@ -227,6 +227,7 @@ void smtp_helo(arg) char *arg;
  219. smtp_greet("250 "); out("\r\n");
  220. seenmail = 0; dohelo(arg);
  221. }
  222. +/* ESMTP extensions are published here */
  223. void smtp_ehlo(arg) char *arg;
  224. {
  225. smtp_greet("250-");
  226. @@ -231,2 +232,5 @@ void smtp_ehlo(arg) char *arg;
  227. {
  228. smtp_greet("250-");
  229. +#ifdef TLS
  230. + if (!ssl) out("\r\n250-STARTTLS");
  231. +#endif
  232. --- qmail-1.03-orig/qmail-smtpd.c Thu Jan 5 21:31:41 2006
  233. +++ qmail-1.03/qmail-smtpd.c Sun Jul 10 08:46:58 2005
  234. @@ -28,9 +28,27 @@
  235. unsigned int databytes = 0;
  236. int timeout = 1200;
  237.  
  238. +const char *protocol = "SMTP";
  239. +
  240. +#ifdef TLS
  241. +#include <sys/stat.h>
  242. +#include "tls.h"
  243. +#include "ssl_timeoutio.h"
  244. +
  245. +void tls_init();
  246. +int tls_verify();
  247. +void tls_nogateway();
  248. +int ssl_rfd = -1, ssl_wfd = -1; /* SSL_get_Xfd() are broken */
  249. +#endif
  250. +
  251. int safewrite(fd,buf,len) int fd; char *buf; int len;
  252. {
  253. int r;
  254. +#ifdef TLS
  255. + if (ssl && fd == ssl_wfd)
  256. + r = ssl_timeoutwrite(timeout, ssl_rfd, ssl_wfd, ssl, buf, len);
  257. + else
  258. +#endif
  259. r = timeoutwrite(timeout,fd,buf,len);
  260. if (r <= 0) _exit(1);
  261. return r;
  262. @@ -50,7 +68,16 @@ void die_ipme() { out("421 unable to fig
  263. void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); }
  264.  
  265. void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
  266. +#ifndef TLS
  267. void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); }
  268. +#else
  269. +void err_nogateway()
  270. +{
  271. + out("553 sorry, that domain isn't in my list of allowed rcpthosts");
  272. + tls_nogateway();
  273. + out(" (#5.7.1)\r\n");
  274. +}
  275. +#endif
  276. void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); }
  277. void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); }
  278. void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); }
  279. @@ -131,6 +158,11 @@ void setup()
  280. if (!remotehost) remotehost = "unknown";
  281. remoteinfo = env_get("TCPREMOTEINFO");
  282. relayclient = env_get("RELAYCLIENT");
  283. +
  284. +#ifdef TLS
  285. + if (env_get("SMTPS")) { smtps = 1; tls_init(); }
  286. + else
  287. +#endif
  288. dohelo(remotehost);
  289. }
  290.  
  291. @@ -213,6 +245,9 @@ int addrallowed()
  292. int r;
  293. r = rcpthosts(addr.s,str_len(addr.s));
  294. if (r == -1) die_control();
  295. +#ifdef TLS
  296. + if (r == 0) if (tls_verify()) r = -2;
  297. +#endif
  298. return r;
  299. }
  300.  
  301. @@ -230,9 +265,13 @@ void smtp_helo(arg) char *arg;
  302. /* ESMTP extensions are published here */
  303. void smtp_ehlo(arg) char *arg;
  304. {
  305. +#ifdef TLS
  306. + struct stat st;
  307. +#endif
  308. smtp_greet("250-");
  309. #ifdef TLS
  310. - if (!ssl) out("\r\n250-STARTTLS");
  311. + if (!ssl && (stat("control/servercert.pem",&st) == 0))
  312. + out("\r\n250-STARTTLS");
  313. #endif
  314. out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
  315. seenmail = 0; dohelo(arg);
  316. @@ -274,6 +313,11 @@ int saferead(fd,buf,len) int fd; char *b
  317. {
  318. int r;
  319. flush();
  320. +#ifdef TLS
  321. + if (ssl && fd == ssl_rfd)
  322. + r = ssl_timeoutread(timeout, ssl_rfd, ssl_wfd, ssl, buf, len);
  323. + else
  324. +#endif
  325. r = timeoutread(timeout,fd,buf,len);
  326. if (r == -1) if (errno == error_timeout) die_alarm();
  327. if (r <= 0) die_read();
  328. @@ -383,7 +427,7 @@ void smtp_data() {
  329. qp = qmail_qp(&qqt);
  330. out("354 go ahead\r\n");
  331.  
  332. - received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo);
  333. + received(&qqt,protocol,local,remoteip,remotehost,remoteinfo,fakehelo);
  334. blast(&hops);
  335. hops = (hops >= MAXHOPS);
  336. if (hops) qmail_fail(&qqt);
  337. @@ -399,6 +443,240 @@ void smtp_data() {
  338. out("\r\n");
  339. }
  340.  
  341. +#ifdef TLS
  342. +stralloc proto = {0};
  343. +int ssl_verified = 0;
  344. +const char *ssl_verify_err = 0;
  345. +
  346. +void smtp_tls(char *arg)
  347. +{
  348. + if (ssl) err_unimpl();
  349. + else if (*arg) out("501 Syntax error (no parameters allowed) (#5.5.4)\r\n");
  350. + else tls_init();
  351. +}
  352. +
  353. +RSA *tmp_rsa_cb(SSL *ssl, int export, int keylen)
  354. +{
  355. + if (!export) keylen = 512;
  356. + if (keylen == 512) {
  357. + FILE *in = fopen("control/rsa512.pem", "r");
  358. + if (in) {
  359. + RSA *rsa = PEM_read_RSAPrivateKey(in, NULL, NULL, NULL);
  360. + fclose(in);
  361. + if (rsa) return rsa;
  362. + }
  363. + }
  364. + return RSA_generate_key(keylen, RSA_F4, NULL, NULL);
  365. +}
  366. +
  367. +DH *tmp_dh_cb(SSL *ssl, int export, int keylen)
  368. +{
  369. + if (!export) keylen = 1024;
  370. + if (keylen == 512) {
  371. + FILE *in = fopen("control/dh512.pem", "r");
  372. + if (in) {
  373. + DH *dh = PEM_read_DHparams(in, NULL, NULL, NULL);
  374. + fclose(in);
  375. + if (dh) return dh;
  376. + }
  377. + }
  378. + if (keylen == 1024) {
  379. + FILE *in = fopen("control/dh1024.pem", "r");
  380. + if (in) {
  381. + DH *dh = PEM_read_DHparams(in, NULL, NULL, NULL);
  382. + fclose(in);
  383. + if (dh) return dh;
  384. + }
  385. + }
  386. + return DH_generate_parameters(keylen, DH_GENERATOR_2, NULL, NULL);
  387. +}
  388. +
  389. +/* don't want to fail handshake if cert isn't verifiable */
  390. +int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; }
  391. +
  392. +void tls_nogateway()
  393. +{
  394. + /* there may be cases when relayclient is set */
  395. + if (!ssl || relayclient) return;
  396. + out("; no valid cert for gatewaying");
  397. + if (ssl_verify_err) { out(": "); out(ssl_verify_err); }
  398. +}
  399. +void tls_out(const char *s1, const char *s2)
  400. +{
  401. + out("454 TLS "); out(s1);
  402. + if (s2) { out(": "); out(s2); }
  403. + out(" (#4.3.0)\r\n"); flush();
  404. +}
  405. +void tls_err(const char *s) { tls_out(s, ssl_error()); if (smtps) die_read(); }
  406. +
  407. +# define CLIENTCA "control/clientca.pem"
  408. +# define CLIENTCRL "control/clientcrl.pem"
  409. +# define SERVERCERT "control/servercert.pem"
  410. +
  411. +int tls_verify()
  412. +{
  413. + stralloc clients = {0};
  414. + struct constmap mapclients;
  415. +
  416. + if (!ssl || relayclient || ssl_verified) return 0;
  417. + ssl_verified = 1; /* don't do this twice */
  418. +
  419. + /* request client cert to see if it can be verified by one of our CAs
  420. + * and the associated email address matches an entry in tlsclients */
  421. + switch (control_readfile(&clients, "control/tlsclients", 0))
  422. + {
  423. + case 1:
  424. + if (constmap_init(&mapclients, clients.s, clients.len, 0)) {
  425. + /* if CLIENTCA contains all the standard root certificates, a
  426. + * 0.9.6b client might fail with SSL_R_EXCESSIVE_MESSAGE_SIZE;
  427. + * it is probably due to 0.9.6b supporting only 8k key exchange
  428. + * data while the 0.9.6c release increases that limit to 100k */
  429. + STACK_OF(X509_NAME) *sk = SSL_load_client_CA_file(CLIENTCA);
  430. + if (sk) {
  431. + SSL_set_client_CA_list(ssl, sk);
  432. + SSL_set_verify(ssl, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, NULL);
  433. + break;
  434. + }
  435. + constmap_free(&mapclients);
  436. + }
  437. + case 0: alloc_free(clients.s); return 0;
  438. + case -1: die_control();
  439. + }
  440. +
  441. + if (ssl_timeoutrehandshake(timeout, ssl_rfd, ssl_wfd, ssl) <= 0) {
  442. + const char *err = ssl_error_str();
  443. + tls_out("rehandshake failed", err); die_read();
  444. + }
  445. +
  446. + do { /* one iteration */
  447. + X509 *peercert;
  448. + X509_NAME *subj;
  449. + stralloc email = {0};
  450. +
  451. + int n = SSL_get_verify_result(ssl);
  452. + if (n != X509_V_OK)
  453. + { ssl_verify_err = X509_verify_cert_error_string(n); break; }
  454. + peercert = SSL_get_peer_certificate(ssl);
  455. + if (!peercert) break;
  456. +
  457. + subj = X509_get_subject_name(peercert);
  458. + n = X509_NAME_get_index_by_NID(subj, NID_pkcs9_emailAddress, -1);
  459. + if (n >= 0) {
  460. + const ASN1_STRING *s = X509_NAME_get_entry(subj, n)->value;
  461. + if (s) { email.len = s->length; email.s = s->data; }
  462. + }
  463. +
  464. + if (email.len <= 0)
  465. + ssl_verify_err = "contains no email address";
  466. + else if (!constmap(&mapclients, email.s, email.len))
  467. + ssl_verify_err = "email address not in my list of tlsclients";
  468. + else {
  469. + /* add the cert email to the proto if it helped allow relaying */
  470. + --proto.len;
  471. + if (!stralloc_cats(&proto, "\n (cert ") /* continuation line */
  472. + || !stralloc_catb(&proto, email.s, email.len)
  473. + || !stralloc_cats(&proto, ")")
  474. + || !stralloc_0(&proto)) die_nomem();
  475. + relayclient = "";
  476. + protocol = proto.s;
  477. + }
  478. +
  479. + X509_free(peercert);
  480. + } while (0);
  481. + constmap_free(&mapclients); alloc_free(clients.s);
  482. +
  483. + /* we are not going to need this anymore: free the memory */
  484. + SSL_set_client_CA_list(ssl, NULL);
  485. + SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL);
  486. +
  487. + return relayclient ? 1 : 0;
  488. +}
  489. +
  490. +void tls_init()
  491. +{
  492. + SSL *myssl;
  493. + SSL_CTX *ctx;
  494. + const char *ciphers;
  495. + stralloc saciphers = {0};
  496. + X509_STORE *store;
  497. + X509_LOOKUP *lookup;
  498. +
  499. + SSL_library_init();
  500. +
  501. + /* a new SSL context with the bare minimum of options */
  502. + ctx = SSL_CTX_new(SSLv23_server_method());
  503. + if (!ctx) { tls_err("unable to initialize ctx"); return; }
  504. +
  505. + if (!SSL_CTX_use_certificate_chain_file(ctx, SERVERCERT))
  506. + { SSL_CTX_free(ctx); tls_err("missing certificate"); return; }
  507. + SSL_CTX_load_verify_locations(ctx, CLIENTCA, NULL);
  508. +
  509. +#if OPENSSL_VERSION_NUMBER >= 0x00907000L
  510. + /* crl checking */
  511. + store = SSL_CTX_get_cert_store(ctx);
  512. + if ((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) &&
  513. + (X509_load_crl_file(lookup, CLIENTCRL, X509_FILETYPE_PEM) == 1))
  514. + X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK |
  515. + X509_V_FLAG_CRL_CHECK_ALL);
  516. +#endif
  517. +
  518. + /* set the callback here; SSL_set_verify didn't work before 0.9.6c */
  519. + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_cb);
  520. +
  521. + /* a new SSL object, with the rest added to it directly to avoid copying */
  522. + myssl = SSL_new(ctx);
  523. + SSL_CTX_free(ctx);
  524. + if (!myssl) { tls_err("unable to initialize ssl"); return; }
  525. +
  526. + /* this will also check whether public and private keys match */
  527. + if (!SSL_use_RSAPrivateKey_file(myssl, SERVERCERT, SSL_FILETYPE_PEM))
  528. + { SSL_free(myssl); tls_err("no valid RSA private key"); return; }
  529. +
  530. + ciphers = env_get("TLSCIPHERS");
  531. + if (!ciphers) {
  532. + if (control_readfile(&saciphers, "control/tlsserverciphers") == -1)
  533. + { SSL_free(myssl); die_control(); }
  534. + if (saciphers.len) { /* convert all '\0's except the last one to ':' */
  535. + int i;
  536. + for (i = 0; i < saciphers.len - 1; ++i)
  537. + if (!saciphers.s[i]) saciphers.s[i] = ':';
  538. + ciphers = saciphers.s;
  539. + }
  540. + }
  541. + if (!ciphers || !*ciphers) ciphers = "DEFAULT";
  542. + SSL_set_cipher_list(myssl, ciphers);
  543. + alloc_free(saciphers.s);
  544. +
  545. + SSL_set_tmp_rsa_callback(myssl, tmp_rsa_cb);
  546. + SSL_set_tmp_dh_callback(myssl, tmp_dh_cb);
  547. + SSL_set_rfd(myssl, ssl_rfd = substdio_fileno(&ssin));
  548. + SSL_set_wfd(myssl, ssl_wfd = substdio_fileno(&ssout));
  549. +
  550. + if (!smtps) { out("220 ready for tls\r\n"); flush(); }
  551. +
  552. + if (ssl_timeoutaccept(timeout, ssl_rfd, ssl_wfd, myssl) <= 0) {
  553. + /* neither cleartext nor any other response here is part of a standard */
  554. + const char *err = ssl_error_str();
  555. + ssl_free(myssl); tls_out("connection failed", err); die_read();
  556. + }
  557. + ssl = myssl;
  558. +
  559. + /* populate the protocol string, used in Received */
  560. + if (!stralloc_copys(&proto, "(")
  561. + || !stralloc_cats(&proto, SSL_get_cipher(ssl))
  562. + || !stralloc_cats(&proto, " encrypted) SMTP")) die_nomem();
  563. + if (!stralloc_0(&proto)) die_nomem();
  564. + protocol = proto.s;
  565. +
  566. + /* have to discard the pre-STARTTLS HELO/EHLO argument, if any */
  567. + dohelo(remotehost);
  568. +}
  569. +
  570. +# undef SERVERCERT
  571. +# undef CLIENTCA
  572. +
  573. +#endif
  574. +
  575. struct commands smtpcommands[] = {
  576. { "rcpt", smtp_rcpt, 0 }
  577. , { "mail", smtp_mail, 0 }
  578. @@ -408,6 +686,9 @@ struct commands smtpcommands[] = {
  579. , { "ehlo", smtp_ehlo, flush }
  580. , { "rset", smtp_rset, 0 }
  581. , { "help", smtp_help, flush }
  582. +#ifdef TLS
  583. +, { "starttls", smtp_tls, flush }
  584. +#endif
  585. , { "noop", err_noop, flush }
  586. , { "vrfy", err_vrfy, flush }
  587. , { 0, err_unimpl, flush }
  588. --- qmail-1.03-orig/qmail-remote.c Thu Jan 5 21:31:41 2006
  589. +++ qmail-1.03/qmail-remote.c Sun Dec 18 09:40:58 2005
  590. @@ -48,6 +48,17 @@ saa reciplist = {0};
  591.  
  592. struct ip_address partner;
  593.  
  594. +#ifdef TLS
  595. +# include <sys/stat.h>
  596. +# include "tls.h"
  597. +# include "ssl_timeoutio.h"
  598. +# include <openssl/x509v3.h>
  599. +# define EHLO 1
  600. +
  601. +int tls_init();
  602. +const char *ssl_err_str = 0;
  603. +#endif
  604. +
  605. void out(s) char *s; { if (substdio_puts(subfdoutsmall,s) == -1) _exit(0); }
  606. void zero() { if (substdio_put(subfdoutsmall,"\0",1) == -1) _exit(0); }
  607. void zerodie() { zero(); substdio_flush(subfdoutsmall); _exit(0); }
  608. @@ -99,6 +110,9 @@ void dropped() {
  609. outhost();
  610. out(" but connection died. ");
  611. if (flagcritical) out("Possible duplicate! ");
  612. +#ifdef TLS
  613. + if (ssl_err_str) { out(ssl_err_str); out(" "); }
  614. +#endif
  615. out("(#4.4.2)\n");
  616. zerodie();
  617. }
  618. @@ -110,6 +124,12 @@ int timeout = 1200;
  619. int saferead(fd,buf,len) int fd; char *buf; int len;
  620. {
  621. int r;
  622. +#ifdef TLS
  623. + if (ssl) {
  624. + r = ssl_timeoutread(timeout, smtpfd, smtpfd, ssl, buf, len);
  625. + if (r < 0) ssl_err_str = ssl_error_str();
  626. + } else
  627. +#endif
  628. r = timeoutread(timeout,smtpfd,buf,len);
  629. if (r <= 0) dropped();
  630. return r;
  631. @@ -117,6 +137,12 @@ int saferead(fd,buf,len) int fd; char *b
  632. int safewrite(fd,buf,len) int fd; char *buf; int len;
  633. {
  634. int r;
  635. +#ifdef TLS
  636. + if (ssl) {
  637. + r = ssl_timeoutwrite(timeout, smtpfd, smtpfd, ssl, buf, len);
  638. + if (r < 0) ssl_err_str = ssl_error_str();
  639. + } else
  640. +#endif
  641. r = timeoutwrite(timeout,smtpfd,buf,len);
  642. if (r <= 0) dropped();
  643. return r;
  644. @@ -194,19 +220,25 @@ unsigned long ehlo()
  645. e = smtptext.s + smtptext.len - 6; /* 250-?\n */
  646. while (s <= e)
  647. {
  648. + int wasspace = 0;
  649. +
  650. if (!saa_readyplus(&ehlokw, 1)) temp_nomem();
  651. sa = ehlokw.sa + ehlokw.len++;
  652. if (ehlokw.len > maxehlokwlen) *sa = sauninit; else sa->len = 0;
  653.  
  654. - /* smtptext is known to end in a '\n' */
  655. - for (p = (s += 4); ; ++p)
  656. - if (*p == '\n' || *p == ' ' || *p == '\t') {
  657. - if (!stralloc_catb(sa, s, p - s) || !stralloc_0(sa)) temp_nomem();
  658. - if (*p++ == '\n') break;
  659. - while (*p == ' ' || *p == '\t') ;
  660. - s = p;
  661. - }
  662. - s = p;
  663. + /* smtptext is known to end in a '\n' */
  664. + for (p = (s += 4); ; ++p)
  665. + if (*p == '\n' || *p == ' ' || *p == '\t') {
  666. + if (!wasspace)
  667. + if (!stralloc_catb(sa, s, p - s) || !stralloc_0(sa)) temp_nomem();
  668. + if (*p == '\n') break;
  669. + wasspace = 1;
  670. + } else if (wasspace == 1) {
  671. + wasspace = 0;
  672. + s = p;
  673. + }
  674. + s = ++p;
  675. +
  676. /* keyword should consist of alpha-num and '-'
  677. * broken AUTH might use '=' instead of space */
  678. for (p = sa->s; *p; ++p) if (*p == '=') { *p = 0; break; }
  679. @@ -232,6 +264,11 @@ void quit(prepend,append)
  680. char *prepend;
  681. char *append;
  682. {
  683. +#ifdef TLS
  684. + /* shouldn't talk to the client unless in an appropriate state */
  685. + int state = ssl ? ssl->state : SSL_ST_BEFORE;
  686. + if (state & SSL_ST_OK || (!smtps && state & SSL_ST_BEFORE))
  687. +#endif
  688. substdio_putsflush(&smtpto,"QUIT\r\n");
  689. /* waiting for remote side is just too ridiculous */
  690. out(prepend);
  691. @@ -239,6 +276,30 @@ char *append;
  692. out(append);
  693. out(".\n");
  694. outsmtptext();
  695. +
  696. +#if defined(TLS) && defined(DEBUG)
  697. + if (ssl) {
  698. + X509 *peercert;
  699. +
  700. + out("STARTTLS proto="); out(SSL_get_version(ssl));
  701. + out("; cipher="); out(SSL_get_cipher(ssl));
  702. +
  703. + /* we want certificate details */
  704. + if (peercert = SSL_get_peer_certificate(ssl)) {
  705. + char *str;
  706. +
  707. + str = X509_NAME_oneline(X509_get_subject_name(peercert), NULL, 0);
  708. + out("; subject="); out(str); OPENSSL_free(str);
  709. +
  710. + str = X509_NAME_oneline(X509_get_issuer_name(peercert), NULL, 0);
  711. + out("; issuer="); out(str); OPENSSL_free(str);
  712. +
  713. + X509_free(peercert);
  714. + }
  715. + out(";\n");
  716. + }
  717. +#endif
  718. +
  719. zerodie();
  720. }
  721.  
  722. @@ -267,6 +328,194 @@ void blast()
  723. substdio_flush(&smtpto);
  724. }
  725.  
  726. +#ifdef TLS
  727. +char *partner_fqdn = 0;
  728. +
  729. +# define TLS_QUIT quit(ssl ? "; connected to " : "; connecting to ", "")
  730. +void tls_quit(const char *s1, const char *s2)
  731. +{
  732. + out(s1); if (s2) { out(": "); out(s2); } TLS_QUIT;
  733. +}
  734. +# define tls_quit_error(s) tls_quit(s, ssl_error())
  735. +
  736. +int match_partner(const char *s, int len)
  737. +{
  738. + if (!case_diffb(partner_fqdn, len, s) && !partner_fqdn[len]) return 1;
  739. + /* we also match if the name is *.domainname */
  740. + if (*s == '*') {
  741. + const char *domain = partner_fqdn + str_chr(partner_fqdn, '.');
  742. + if (!case_diffb(domain, --len, ++s) && !domain[len]) return 1;
  743. + }
  744. + return 0;
  745. +}
  746. +
  747. +/* don't want to fail handshake if certificate can't be verified */
  748. +int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; }
  749. +
  750. +int tls_init()
  751. +{
  752. + int i;
  753. + SSL *myssl;
  754. + SSL_CTX *ctx;
  755. + stralloc saciphers = {0};
  756. + const char *ciphers, *servercert = 0;
  757. +
  758. + if (partner_fqdn) {
  759. + struct stat st;
  760. + stralloc tmp = {0};
  761. + if (!stralloc_copys(&tmp, "control/tlshosts/")
  762. + || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn))
  763. + || !stralloc_catb(&tmp, ".pem", 5)) temp_nomem();
  764. + if (stat(tmp.s, &st) == 0)
  765. + servercert = tmp.s;
  766. + else {
  767. + if (!stralloc_copys(&tmp, "control/notlshosts/")
  768. + || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn)+1))
  769. + temp_nomem();
  770. + if ((stat("control/tlshosts/exhaustivelist", &st) == 0) ||
  771. + (stat(tmp.s, &st) == 0)) {
  772. + alloc_free(tmp.s);
  773. + return 0;
  774. + }
  775. + alloc_free(tmp.s);
  776. + }
  777. + }
  778. +
  779. + if (!smtps) {
  780. + stralloc *sa = ehlokw.sa;
  781. + unsigned int len = ehlokw.len;
  782. + /* look for STARTTLS among EHLO keywords */
  783. + for ( ; len && case_diffs(sa->s, "STARTTLS"); ++sa, --len) ;
  784. + if (!len) {
  785. + if (!servercert) return 0;
  786. + out("ZNo TLS achieved while "); out(servercert);
  787. + out(" exists"); smtptext.len = 0; TLS_QUIT;
  788. + }
  789. + }
  790. +
  791. + SSL_library_init();
  792. + ctx = SSL_CTX_new(SSLv23_client_method());
  793. + if (!ctx) {
  794. + if (!smtps && !servercert) return 0;
  795. + smtptext.len = 0;
  796. + tls_quit_error("ZTLS error initializing ctx");
  797. + }
  798. +
  799. + if (servercert) {
  800. + if (!SSL_CTX_load_verify_locations(ctx, servercert, NULL)) {
  801. + SSL_CTX_free(ctx);
  802. + smtptext.len = 0;
  803. + out("ZTLS unable to load "); tls_quit_error(servercert);
  804. + }
  805. + /* set the callback here; SSL_set_verify didn't work before 0.9.6c */
  806. + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb);
  807. + }
  808. +
  809. + /* let the other side complain if it needs a cert and we don't have one */
  810. +# define CLIENTCERT "control/clientcert.pem"
  811. + if (SSL_CTX_use_certificate_chain_file(ctx, CLIENTCERT))
  812. + SSL_CTX_use_RSAPrivateKey_file(ctx, CLIENTCERT, SSL_FILETYPE_PEM);
  813. +# undef CLIENTCERT
  814. +
  815. + myssl = SSL_new(ctx);
  816. + SSL_CTX_free(ctx);
  817. + if (!myssl) {
  818. + if (!smtps && !servercert) return 0;
  819. + smtptext.len = 0;
  820. + tls_quit_error("ZTLS error initializing ssl");
  821. + }
  822. +
  823. + if (!smtps) substdio_putsflush(&smtpto, "STARTTLS\r\n");
  824. +
  825. + /* while the server is preparing a responce, do something else */
  826. + if (control_readfile(&saciphers, "control/tlsclientciphers", 0) == -1)
  827. + { SSL_free(myssl); temp_control(); }
  828. + if (saciphers.len) {
  829. + for (i = 0; i < saciphers.len - 1; ++i)
  830. + if (!saciphers.s[i]) saciphers.s[i] = ':';
  831. + ciphers = saciphers.s;
  832. + }
  833. + else ciphers = "DEFAULT";
  834. + SSL_set_cipher_list(myssl, ciphers);
  835. + alloc_free(saciphers.s);
  836. +
  837. + /* SSL_set_options(myssl, SSL_OP_NO_TLSv1); */
  838. + SSL_set_fd(myssl, smtpfd);
  839. +
  840. + /* read the responce to STARTTLS */
  841. + if (!smtps) {
  842. + if (smtpcode() != 220) {
  843. + SSL_free(myssl);
  844. + if (!servercert) return 0;
  845. + out("ZSTARTTLS rejected while ");
  846. + out(servercert); out(" exists"); TLS_QUIT;
  847. + }
  848. + smtptext.len = 0;
  849. + }
  850. +
  851. + ssl = myssl;
  852. + if (ssl_timeoutconn(timeout, smtpfd, smtpfd, ssl) <= 0)
  853. + tls_quit("ZTLS connect failed", ssl_error_str());
  854. +
  855. + if (servercert) {
  856. + X509 *peercert;
  857. + STACK_OF(GENERAL_NAME) *gens;
  858. +
  859. + int r = SSL_get_verify_result(ssl);
  860. + if (r != X509_V_OK) {
  861. + out("ZTLS unable to verify server with ");
  862. + tls_quit(servercert, X509_verify_cert_error_string(r));
  863. + }
  864. + alloc_free(servercert);
  865. +
  866. + peercert = SSL_get_peer_certificate(ssl);
  867. + if (!peercert) {
  868. + out("ZTLS unable to verify server ");
  869. + tls_quit(partner_fqdn, "no certificate provided");
  870. + }
  871. +
  872. + /* RFC 2595 section 2.4: find a matching name
  873. + * first find a match among alternative names */
  874. + gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0);
  875. + if (gens) {
  876. + for (i = 0, r = sk_GENERAL_NAME_num(gens); i < r; ++i)
  877. + {
  878. + const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i);
  879. + if (gn->type == GEN_DNS)
  880. + if (match_partner(gn->d.ia5->data, gn->d.ia5->length)) break;
  881. + }
  882. + sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
  883. + }
  884. +
  885. + /* no alternative name matched, look up commonName */
  886. + if (!gens || i >= r) {
  887. + stralloc peer = {0};
  888. + X509_NAME *subj = X509_get_subject_name(peercert);
  889. + i = X509_NAME_get_index_by_NID(subj, NID_commonName, -1);
  890. + if (i >= 0) {
  891. + const ASN1_STRING *s = X509_NAME_get_entry(subj, i)->value;
  892. + if (s) { peer.len = s->length; peer.s = s->data; }
  893. + }
  894. + if (peer.len <= 0) {
  895. + out("ZTLS unable to verify server ");
  896. + tls_quit(partner_fqdn, "certificate contains no valid commonName");
  897. + }
  898. + if (!match_partner(peer.s, peer.len)) {
  899. + out("ZTLS unable to verify server "); out(partner_fqdn);
  900. + out(": received certificate for "); outsafe(&peer); TLS_QUIT;
  901. + }
  902. + }
  903. +
  904. + X509_free(peercert);
  905. + }
  906. +
  907. + if (smtps) if (smtpcode() != 220)
  908. + quit("ZTLS Connected to "," but greeting failed");
  909. +
  910. + return 1;
  911. +}
  912. +#endif
  913. +
  914. stralloc recip = {0};
  915.  
  916. void smtp()
  917. @@ -274,12 +523,37 @@ void smtp()
  918. unsigned long code;
  919. int flagbother;
  920. int i;
  921. +
  922. +#ifndef PORT_SMTP
  923. + /* the qmtpc patch uses smtp_port and undefines PORT_SMTP */
  924. +# define port smtp_port
  925. +#endif
  926. +
  927. +#ifdef TLS
  928. +# ifdef MXPS
  929. + if (type == 'S') smtps = 1;
  930. + else if (type != 's')
  931. +# endif
  932. + if (port == 465) smtps = 1;
  933. + if (!smtps)
  934. +#endif
  935.  
  936. if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
  937.  
  938. #ifdef EHLO
  939. +# ifdef TLS
  940. + if (!smtps)
  941. +# endif
  942. code = ehlo();
  943.  
  944. +# ifdef TLS
  945. + if (tls_init())
  946. + /* RFC2487 says we should issue EHLO (even if we might not need
  947. + * extensions); at the same time, it does not prohibit a server
  948. + * to reject the EHLO and make us fallback to HELO */
  949. + code = ehlo();
  950. +# endif
  951. +
  952. if (code == 250) {
  953. /* add EHLO response checks here */
  954.  
  955. @@ -484,6 +758,9 @@ char **argv;
  956. if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) {
  957. tcpto_err(&ip.ix[i].ip,0);
  958. partner = ip.ix[i].ip;
  959. +#ifdef TLS
  960. + partner_fqdn = ip.ix[i].fqdn;
  961. +#endif
  962. smtp(); /* does not return */
  963. }
  964. tcpto_err(&ip.ix[i].ip,errno == error_timeout);
  965. --- qmail-1.03-orig/qmail-smtpd.8 Mon Jun 15 10:53:16 1998
  966. +++ qmail-1.03/qmail-smtpd.8 Tue Mar 29 18:14:11 2005
  967. @@ -14,6 +14,15 @@ must be supplied several environment var
  968. see
  969. .BR tcp-environ(5) .
  970.  
  971. +If the environment variable
  972. +.B SMTPS
  973. +is non-empty,
  974. +.B qmail-smtpd
  975. +starts a TLS session (to support the deprecated SMTPS protocol,
  976. +normally on port 465). Otherwise,
  977. +.B qmail-smtpd
  978. +offers the STARTTLS extension to ESMTP.
  979. +
  980. .B qmail-smtpd
  981. is responsible for counting hops.
  982. It rejects any message with 100 or more
  983. @@ -49,6 +58,19 @@ may be of the form
  984. .BR @\fIhost ,
  985. meaning every address at
  986. .IR host .
  987. +
  988. +.TP 5
  989. +.I clientca.pem
  990. +A list of Certifying Authority (CA) certificates that are used to verify
  991. +the client-presented certificates during a TLS-encrypted session.
  992. +
  993. +.TP 5
  994. +.I clientcrl.pem
  995. +A list of Certificate Revocation Lists (CRLs). If present it
  996. +should contain the CRLs of the CAs in
  997. +.I clientca.pem
  998. +and client certs will be checked for revocation.
  999. +
  1000. .TP 5
  1001. .I databytes
  1002. Maximum number of bytes allowed in a message,
  1003. @@ -76,6 +98,18 @@ If the environment variable
  1004. .B DATABYTES
  1005. is set, it overrides
  1006. .IR databytes .
  1007. +
  1008. +.TP 5
  1009. +.I dh1024.pem
  1010. +If these 1024 bit DH parameters are provided,
  1011. +.B qmail-smtpd
  1012. +will use them for TLS sessions instead of generating one on-the-fly
  1013. +(which is very timeconsuming).
  1014. +.TP 5
  1015. +.I dh512.pem
  1016. +512 bit counterpart for
  1017. +.B dh1024.pem.
  1018. +
  1019. .TP 5
  1020. .I localiphost
  1021. Replacement host name for local IP addresses.
  1022. @@ -151,6 +185,19 @@ may include wildcards:
  1023.  
  1024. Envelope recipient addresses without @ signs are
  1025. always allowed through.
  1026. +
  1027. +.TP 5
  1028. +.I rsa512.pem
  1029. +If this 512 bit RSA key is provided,
  1030. +.B qmail-smtpd
  1031. +will use it for TLS sessions instead of generating one on-the-fly.
  1032. +
  1033. +.TP 5
  1034. +.I servercert.pem
  1035. +SSL certificate to be presented to clients in TLS-encrypted sessions.
  1036. +Should contain both the certificate and the private key. Certifying Authority
  1037. +(CA) and intermediate certificates can be added at the end of the file.
  1038. +
  1039. .TP 5
  1040. .I smtpgreeting
  1041. SMTP greeting message.
  1042. @@ -169,6 +216,24 @@ Number of seconds
  1043. .B qmail-smtpd
  1044. will wait for each new buffer of data from the remote SMTP client.
  1045. Default: 1200.
  1046. +
  1047. +.TP 5
  1048. +.I tlsclients
  1049. +A list of email addresses. When relay rules would reject an incoming message,
  1050. +.B qmail-smtpd
  1051. +can allow it if the client presents a certificate that can be verified against
  1052. +the CA list in
  1053. +.I clientca.pem
  1054. +and the certificate email address is in
  1055. +.IR tlsclients .
  1056. +
  1057. +.TP 5
  1058. +.I tlsserverciphers
  1059. +A set of OpenSSL cipher strings. Multiple ciphers contained in a
  1060. +string should be separated by a colon. If the environment variable
  1061. +.B TLSCIPHERS
  1062. +is set to such a string, it takes precedence.
  1063. +
  1064. .SH "SEE ALSO"
  1065. tcp-env(1),
  1066. tcp-environ(5),
  1067. --- qmail-1.03-orig/qmail-remote.8 Mon Jun 15 10:53:16 1998
  1068. +++ qmail-1.03/qmail-remote.8 Tue Mar 29 19:29:44 2005
  1069. @@ -114,6 +114,10 @@ arguments.
  1070. always exits zero.
  1071. .SH "CONTROL FILES"
  1072. .TP 5
  1073. +.I clientcert.pem
  1074. +SSL certificate that is used to authenticate with the remote server
  1075. +during a TLS session.
  1076. +.TP 5
  1077. .I helohost
  1078. Current host name,
  1079. for use solely in saying hello to the remote SMTP server.
  1080. @@ -123,6 +127,16 @@ if that is supplied;
  1081. otherwise
  1082. .B qmail-remote
  1083. refuses to run.
  1084. +
  1085. +.TP 5
  1086. +.I notlshosts/<FQDN>
  1087. +.B qmail-remote
  1088. +will not try TLS on servers for which this file exists
  1089. +.RB ( <FQDN>
  1090. +is the fully-qualified domain name of the server).
  1091. +.IR (tlshosts/<FQDN>.pem
  1092. +takes precedence over this file however).
  1093. +
  1094. .TP 5
  1095. .I smtproutes
  1096. Artificial SMTP routes.
  1097. @@ -156,6 +170,8 @@ may be empty;
  1098. this tells
  1099. .B qmail-remote
  1100. to look up MX records as usual.
  1101. +.I port
  1102. +value of 465 (deprecated smtps port) causes TLS session to be started.
  1103. .I smtproutes
  1104. may include wildcards:
  1105.  
  1106. @@ -195,6 +211,33 @@ Number of seconds
  1107. .B qmail-remote
  1108. will wait for each response from the remote SMTP server.
  1109. Default: 1200.
  1110. +
  1111. +.TP 5
  1112. +.I tlsclientciphers
  1113. +A set of OpenSSL client cipher strings. Multiple ciphers
  1114. +contained in a string should be separated by a colon.
  1115. +
  1116. +.TP 5
  1117. +.I tlshosts/<FQDN>.pem
  1118. +.B qmail-remote
  1119. +requires TLS authentication from servers for which this certificate exists
  1120. +.RB ( <FQDN>
  1121. +is the fully-qualified domain name of the server). One of the
  1122. +.I dNSName
  1123. +or the
  1124. +.I CommonName
  1125. +attributes have to match.
  1126. +
  1127. +.B WARNING:
  1128. +this option may cause mail to be delayed, bounced, doublebounced, or lost.
  1129. +
  1130. +.TP 5
  1131. +.I tlshosts/exhaustivelist
  1132. +if this file exists
  1133. +no TLS will be tried on hosts other than those for which a file
  1134. +.B tlshosts/<FQDN>.pem
  1135. +exists.
  1136. +
  1137. .SH "SEE ALSO"
  1138. addresses(5),
  1139. envelopes(5),
  1140. --- qmail-1.03-orig/qmail-control.9 Mon Jun 15 10:53:16 1998
  1141. +++ qmail-1.03/qmail-control.9 Sat Apr 17 09:58:55 2004
  1142. @@ -43,11 +43,15 @@ control default used by
  1143. .I badmailfrom \fR(none) \fRqmail-smtpd
  1144. .I bouncefrom \fRMAILER-DAEMON \fRqmail-send
  1145. .I bouncehost \fIme \fRqmail-send
  1146. +.I clientca.pem \fR(none) \fRqmail-smtpd
  1147. +.I clientcert.pem \fR(none) \fRqmail-remote
  1148. .I concurrencylocal \fR10 \fRqmail-send
  1149. .I concurrencyremote \fR20 \fRqmail-send
  1150. .I defaultdomain \fIme \fRqmail-inject
  1151. .I defaulthost \fIme \fRqmail-inject
  1152. .I databytes \fR0 \fRqmail-smtpd
  1153. +.I dh1024.pem \fR(none) \fRqmail-smtpd
  1154. +.I dh512.pem \fR(none) \fRqmail-smtpd
  1155. .I doublebouncehost \fIme \fRqmail-send
  1156. .I doublebounceto \fRpostmaster \fRqmail-send
  1157. .I envnoathost \fIme \fRqmail-send
  1158. @@ -61,11 +65,17 @@ control default used by
  1159. .I qmqpservers \fR(none) \fRqmail-qmqpc
  1160. .I queuelifetime \fR604800 \fRqmail-send
  1161. .I rcpthosts \fR(none) \fRqmail-smtpd
  1162. +.I rsa512.pem \fR(none) \fRqmail-smtpd
  1163. +.I servercert.pem \fR(none) \fRqmail-smtpd
  1164. .I smtpgreeting \fIme \fRqmail-smtpd
  1165. .I smtproutes \fR(none) \fRqmail-remote
  1166. .I timeoutconnect \fR60 \fRqmail-remote
  1167. .I timeoutremote \fR1200 \fRqmail-remote
  1168. .I timeoutsmtpd \fR1200 \fRqmail-smtpd
  1169. +.I tlsclients \fR(none) \fRqmail-smtpd
  1170. +.I tlsclientciphers \fR(none) \fRqmail-remote
  1171. +.I tlshosts/FQDN.pem \fR(none) \fRqmail-remote
  1172. +.I tlsserverciphers \fR(none) \fRqmail-smtpd
  1173. .I virtualdomains \fR(none) \fRqmail-send
  1174. .fi
  1175. .RE
  1176. --- qmail-1.03-orig/dns.c Sat Apr 17 09:57:24 2004
  1177. +++ qmail-1.03/dns.c Sat Apr 17 09:58:55 2004
  1178. @@ -267,12 +267,11 @@ stralloc *sa;
  1179. int pref;
  1180. {
  1181. int r;
  1182. - struct ip_mx ix;
  1183. + struct ip_mx ix = {0};
  1184.  
  1185. if (!stralloc_copy(&glue,sa)) return DNS_MEM;
  1186. if (!stralloc_0(&glue)) return DNS_MEM;
  1187. if (glue.s[0]) {
  1188. - ix.pref = 0;
  1189. if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)])
  1190. {
  1191. if (!ipalloc_append(ia,&ix)) return DNS_MEM;
  1192. @@ -291,9 +290,16 @@ int pref;
  1193. ix.ip = ip;
  1194. ix.pref = pref;
  1195. if (r == DNS_SOFT) return DNS_SOFT;
  1196. - if (r == 1)
  1197. + if (r == 1) {
  1198. +#ifdef IX_FQDN
  1199. + ix.fqdn = glue.s;
  1200. +#endif
  1201. if (!ipalloc_append(ia,&ix)) return DNS_MEM;
  1202. }
  1203. + }
  1204. +#ifdef IX_FQDN
  1205. + glue.s = 0;
  1206. +#endif
  1207. return 0;
  1208. }
  1209.  
  1210. @@ -313,7 +319,7 @@ unsigned long random;
  1211. {
  1212. int r;
  1213. struct mx { stralloc sa; unsigned short p; } *mx;
  1214. - struct ip_mx ix;
  1215. + struct ip_mx ix = {0};
  1216. int nummx;
  1217. int i;
  1218. int j;
  1219. @@ -325,7 +331,6 @@ unsigned long random;
  1220. if (!stralloc_copy(&glue,sa)) return DNS_MEM;
  1221. if (!stralloc_0(&glue)) return DNS_MEM;
  1222. if (glue.s[0]) {
  1223. - ix.pref = 0;
  1224. if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)])
  1225. {
  1226. if (!ipalloc_append(ia,&ix)) return DNS_MEM;
  1227. --- qmail-1.03-orig/ipalloc.h Mon Jun 15 10:53:16 1998
  1228. +++ qmail-1.03/ipalloc.h Sat Apr 17 09:58:55 2004
  1229. @@ -3,7 +3,15 @@
  1230.  
  1231. #include "ip.h"
  1232.  
  1233. +#ifdef TLS
  1234. +# define IX_FQDN 1
  1235. +#endif
  1236. +
  1237. +#ifdef IX_FQDN
  1238. +struct ip_mx { struct ip_address ip; int pref; char *fqdn; } ;
  1239. +#else
  1240. struct ip_mx { struct ip_address ip; int pref; } ;
  1241. +#endif
  1242.  
  1243. #include "gen_alloc.h"
  1244.  
  1245. --- qmail-1.03-orig/tls.c Thu Jan 5 21:31:41 2006
  1246. +++ qmail-1.03/tls.c Sun Dec 18 09:32:28 2005
  1247. @@ -0,0 +1,25 @@
  1248. +#include "exit.h"
  1249. +#include "error.h"
  1250. +#include <openssl/ssl.h>
  1251. +#include <openssl/err.h>
  1252. +
  1253. +int smtps = 0;
  1254. +SSL *ssl = NULL;
  1255. +
  1256. +void ssl_free(SSL *myssl) { SSL_shutdown(myssl); SSL_free(myssl); }
  1257. +void ssl_exit(int status) { if (ssl) ssl_free(ssl); _exit(status); }
  1258. +
  1259. +const char *ssl_error()
  1260. +{
  1261. + int r = ERR_get_error();
  1262. + if (!r) return NULL;
  1263. + SSL_load_error_strings();
  1264. + return ERR_error_string(r, NULL);
  1265. +}
  1266. +const char *ssl_error_str()
  1267. +{
  1268. + const char *err = ssl_error();
  1269. + if (err) return err;
  1270. + if (!errno) return 0;
  1271. + return (errno == error_timeout) ? "timed out" : error_str(errno);
  1272. +}
  1273. --- qmail-1.03-orig/tls.h Thu Jan 5 21:31:41 2006
  1274. +++ qmail-1.03/tls.h Sun Jul 10 08:46:14 2005
  1275. @@ -0,0 +1,16 @@
  1276. +#ifndef TLS_H
  1277. +#define TLS_H
  1278. +
  1279. +#include <openssl/ssl.h>
  1280. +
  1281. +extern int smtps;
  1282. +extern SSL *ssl;
  1283. +
  1284. +void ssl_free(SSL *myssl);
  1285. +void ssl_exit(int status);
  1286. +# define _exit ssl_exit
  1287. +
  1288. +const char *ssl_error();
  1289. +const char *ssl_error_str();
  1290. +
  1291. +#endif
  1292. --- qmail-1.03-orig/ssl_timeoutio.c Thu Jan 5 21:31:41 2006
  1293. +++ qmail-1.03/ssl_timeoutio.c Sat Apr 17 09:58:55 2004
  1294. @@ -0,0 +1,94 @@
  1295. +#include "select.h"
  1296. +#include "error.h"
  1297. +#include "ndelay.h"
  1298. +#include "ssl_timeoutio.h"
  1299. +
  1300. +int ssl_timeoutio(int (*fun)(),
  1301. + long t, int rfd, int wfd, SSL *ssl, char *buf, int len)
  1302. +{
  1303. + int n;
  1304. + const long end = t + time(NULL);
  1305. +
  1306. + do {
  1307. + fd_set fds;
  1308. + struct timeval tv;
  1309. +
  1310. + const int r = buf ? fun(ssl, buf, len) : fun(ssl);
  1311. + if (r > 0) return r;
  1312. +
  1313. + t = end - time(NULL);
  1314. + if (t < 0) break;
  1315. + tv.tv_sec = t; tv.tv_usec = 0;
  1316. +
  1317. + FD_ZERO(&fds);
  1318. + switch (SSL_get_error(ssl, r))
  1319. + {
  1320. + default: return r; /* some other error */
  1321. + case SSL_ERROR_WANT_READ:
  1322. + FD_SET(rfd, &fds); n = select(rfd + 1, &fds, NULL, NULL, &tv);
  1323. + break;
  1324. + case SSL_ERROR_WANT_WRITE:
  1325. + FD_SET(wfd, &fds); n = select(wfd + 1, NULL, &fds, NULL, &tv);
  1326. + break;
  1327. + }
  1328. +
  1329. + /* n is the number of descriptors that changed status */
  1330. + } while (n > 0);
  1331. +
  1332. + if (n != -1) errno = error_timeout;
  1333. + return -1;
  1334. +}
  1335. +
  1336. +int ssl_timeoutaccept(long t, int rfd, int wfd, SSL *ssl)
  1337. +{
  1338. + int r;
  1339. +
  1340. + /* if connection is established, keep NDELAY */
  1341. + if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1;
  1342. + r = ssl_timeoutio(SSL_accept, t, rfd, wfd, ssl, NULL, 0);
  1343. +
  1344. + if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); }
  1345. + else SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
  1346. +
  1347. + return r;
  1348. +}
  1349. +
  1350. +int ssl_timeoutconn(long t, int rfd, int wfd, SSL *ssl)
  1351. +{
  1352. + int r;
  1353. +
  1354. + /* if connection is established, keep NDELAY */
  1355. + if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1;
  1356. + r = ssl_timeoutio(SSL_connect, t, rfd, wfd, ssl, NULL, 0);
  1357. +
  1358. + if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); }
  1359. + else SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
  1360. +
  1361. + return r;
  1362. +}
  1363. +
  1364. +int ssl_timeoutrehandshake(long t, int rfd, int wfd, SSL *ssl)
  1365. +{
  1366. + int r;
  1367. +
  1368. + SSL_renegotiate(ssl);
  1369. + r = ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0);
  1370. + if (r <= 0 || ssl->type == SSL_ST_CONNECT) return r;
  1371. +
  1372. + /* this is for the server only */
  1373. + ssl->state = SSL_ST_ACCEPT;
  1374. + return ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0);
  1375. +}
  1376. +
  1377. +int ssl_timeoutread(long t, int rfd, int wfd, SSL *ssl, char *buf, int len)
  1378. +{
  1379. + if (!buf) return 0;
  1380. + if (SSL_pending(ssl)) return SSL_read(ssl, buf, len);
  1381. + return ssl_timeoutio(SSL_read, t, rfd, wfd, ssl, buf, len);
  1382. +}
  1383. +
  1384. +int ssl_timeoutwrite(long t, int rfd, int wfd, SSL *ssl, char *buf, int len)
  1385. +{
  1386. + if (!buf) return 0;
  1387. + return ssl_timeoutio(SSL_write, t, rfd, wfd, ssl, buf, len);
  1388. +}
  1389. --- qmail-1.03-orig/ssl_timeoutio.h Thu Jan 5 21:31:41 2006
  1390. +++ qmail-1.03/ssl_timeoutio.h Sat Apr 17 09:58:55 2004
  1391. @@ -0,0 +1,21 @@
  1392. +#ifndef SSL_TIMEOUTIO_H
  1393. +#define SSL_TIMEOUTIO_H
  1394. +
  1395. +#include <openssl/ssl.h>
  1396. +
  1397. +/* the version is like this: 0xMNNFFPPS: major minor fix patch status */
  1398. +#if OPENSSL_VERSION_NUMBER < 0x00906000L
  1399. +# error "Need OpenSSL version at least 0.9.6"
  1400. +#endif
  1401. +
  1402. +int ssl_timeoutconn(long t, int rfd, int wfd, SSL *ssl);
  1403. +int ssl_timeoutaccept(long t, int rfd, int wfd, SSL *ssl);
  1404. +int ssl_timeoutrehandshake(long t, int rfd, int wfd, SSL *ssl);
  1405. +
  1406. +int ssl_timeoutread(long t, int rfd, int wfd, SSL *ssl, char *buf, int len);
  1407. +int ssl_timeoutwrite(long t, int rfd, int wfd, SSL *ssl, char *buf, int len);
  1408. +
  1409. +int ssl_timeoutio(
  1410. + int (*fun)(), long t, int rfd, int wfd, SSL *ssl, char *buf, int len);
  1411. +
  1412. +#endif
  1413. --- qmail-1.03-orig/TARGETS Mon Jun 15 10:53:16 1998
  1414. +++ qmail-1.03/TARGETS Sat Apr 17 09:58:55 2004
  1415. @@ -168,6 +168,8 @@ control.o
  1416. constmap.o
  1417. timeoutread.o
  1418. timeoutwrite.o
  1419. +tls.o
  1420. +ssl_timeoutio.o
  1421. timeoutconn.o
  1422. tcpto.o
  1423. dns.o
  1424. @@ -320,6 +322,7 @@ binm2
  1425. binm2+df
  1426. binm3
  1427. binm3+df
  1428. +Makefile-cert
  1429. it
  1430. qmail-local.0
  1431. qmail-lspawn.0
  1432. --- qmail-1.03-orig/Makefile-cert.mk Thu Jan 5 21:31:41 2006
  1433. +++ qmail-1.03/Makefile-cert.mk Tue Dec 20 19:38:05 2005
  1434. @@ -0,0 +1,21 @@
  1435. +cert-req: req.pem
  1436. +cert cert-req: QMAIL/control/clientcert.pem
  1437. + @:
  1438. +
  1439. +QMAIL/control/clientcert.pem: QMAIL/control/servercert.pem
  1440. + ln -s $< $@
  1441. +
  1442. +QMAIL/control/servercert.pem:
  1443. + PATH=$$PATH:/usr/local/ssl/bin \
  1444. + openssl req -new -x509 -nodes -days 366 -out $@ -keyout $@
  1445. + chmod 640 $@
  1446. + chown `head -2 conf-users | tail -1`:`head -1 conf-groups` $@
  1447. +
  1448. +req.pem:
  1449. + PATH=$$PATH:/usr/local/ssl/bin openssl req \
  1450. + -new -nodes -out $@ -keyout QMAIL/control/servercert.pem
  1451. + chmod 640 QMAIL/control/servercert.pem
  1452. + chown `head -2 conf-users | tail -1`:`head -1 conf-groups` QMAIL/control/servercert.pem
  1453. + @echo
  1454. + @echo "Send req.pem to your CA to obtain signed_req.pem, and do:"
  1455. + @echo "cat signed_req.pem >> QMAIL/control/servercert.pem"
  1456. --- qmail-1.03-orig/conf-cc Mon Jun 15 10:53:16 1998
  1457. +++ qmail-1.03/conf-cc Thu Jan 5 21:30:51 2006
  1458. @@ -1,3 +1,3 @@
  1459. -cc -O2
  1460. +cc -O2 -DTLS=20060104 -I/usr/local/ssl/include
  1461.  
  1462. This will be used to compile .c files.
  1463. --- qmail-1.03-orig/Makefile Sat Apr 17 09:57:24 2004
  1464. +++ qmail-1.03/Makefile Thu Jan 5 21:28:56 2006
  1465. @@ -1444,6 +1444,7 @@ ndelay.a case.a sig.a open.a lock.a seek
  1466. substdio.a error.a str.a fs.a auto_qmail.o dns.lib socket.lib
  1467. ./load qmail-remote control.o constmap.o timeoutread.o \
  1468. timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \
  1469. + tls.o ssl_timeoutio.o -L/usr/local/ssl/lib -lssl -lcrypto \
  1470. ipalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \
  1471. lock.a seek.a getln.a stralloc.a alloc.a substdio.a error.a \
  1472. str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib`
  1473. @@ -1539,6 +1540,7 @@ open.a sig.a case.a env.a stralloc.a all
  1474. fs.a auto_qmail.o socket.lib
  1475. ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \
  1476. timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \
  1477. + tls.o ssl_timeoutio.o ndelay.a -L/usr/local/ssl/lib -lssl -lcrypto \
  1478. received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
  1479. datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
  1480. alloc.a substdio.a error.a str.a fs.a auto_qmail.o `cat \
  1481. @@ -2108,6 +2111,19 @@ timeoutwrite.o: \
  1482. compile timeoutwrite.c timeoutwrite.h select.h error.h readwrite.h
  1483. ./compile timeoutwrite.c
  1484.  
  1485. +qmail-smtpd: tls.o ssl_timeoutio.o ndelay.a
  1486. +qmail-remote: tls.o ssl_timeoutio.o
  1487. +qmail-smtpd.o: tls.h ssl_timeoutio.h
  1488. +qmail-remote.o: tls.h ssl_timeoutio.h
  1489. +
  1490. +tls.o: \
  1491. +compile tls.c exit.h error.h
  1492. + ./compile tls.c
  1493. +
  1494. +ssl_timeoutio.o: \
  1495. +compile ssl_timeoutio.c ssl_timeoutio.h select.h error.h ndelay.h
  1496. + ./compile ssl_timeoutio.c
  1497. +
  1498. token822.o: \
  1499. compile token822.c stralloc.h gen_alloc.h alloc.h str.h token822.h \
  1500. gen_alloc.h gen_allocdefs.h
  1501. @@ -2139,3 +2155,13 @@ compile wait_nohang.c haswaitp.h
  1502. wait_pid.o: \
  1503. compile wait_pid.c error.h haswaitp.h
  1504. ./compile wait_pid.c
  1505. +
  1506. +cert cert-req: \
  1507. +Makefile-cert
  1508. + @$(MAKE) -sf $< $@
  1509. +
  1510. +Makefile-cert: \
  1511. +conf-qmail conf-users conf-groups Makefile-cert.mk
  1512. + @cat Makefile-cert.mk \
  1513. + | sed s}QMAIL}"`head -1 conf-qmail`"}g \
  1514. + > $@
  1515.