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