1 |
jpp |
1.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 |
|
|
|
212 |
|
|
--- netqmail-1.06-orig/qmail-remote.c 2019-04-08 15:26:13.100123364 +0000 |
213 |
|
|
+++ netqmail-1.06/qmail-remote.c 2019-04-02 08:01:50.176790637 +0000 |
214 |
|
|
@@ -48,6 +48,17 @@ saa reciplist = {0}; |
215 |
|
|
|
216 |
|
|
struct ip_address partner; |
217 |
|
|
|
218 |
|
|
+#ifdef TLS |
219 |
|
|
+# include <sys/stat.h> |
220 |
|
|
+# include "tls.h" |
221 |
|
|
+# include "ssl_timeoutio.h" |
222 |
|
|
+# include <openssl/x509v3.h> |
223 |
|
|
+# define EHLO 1 |
224 |
|
|
+ |
225 |
|
|
+int tls_init(); |
226 |
|
|
+const char *ssl_err_str = 0; |
227 |
|
|
+#endif |
228 |
|
|
+ |
229 |
|
|
void out(s) char *s; { if (substdio_puts(subfdoutsmall,s) == -1) _exit(0); } |
230 |
|
|
void zero() { if (substdio_put(subfdoutsmall,"\0",1) == -1) _exit(0); } |
231 |
|
|
void zerodie() { zero(); substdio_flush(subfdoutsmall); _exit(0); } |
232 |
|
|
@@ -99,6 +110,9 @@ void dropped() { |
233 |
|
|
outhost(); |
234 |
|
|
out(" but connection died. "); |
235 |
|
|
if (flagcritical) out("Possible duplicate! "); |
236 |
|
|
+#ifdef TLS |
237 |
|
|
+ if (ssl_err_str) { out((char *)ssl_err_str); out(" "); } |
238 |
|
|
+#endif |
239 |
|
|
out("(#4.4.2)\n"); |
240 |
|
|
zerodie(); |
241 |
|
|
} |
242 |
|
|
@@ -110,6 +124,12 @@ int timeout = 1200; |
243 |
|
|
int saferead(fd,buf,len) int fd; char *buf; int len; |
244 |
|
|
{ |
245 |
|
|
int r; |
246 |
|
|
+#ifdef TLS |
247 |
|
|
+ if (ssl) { |
248 |
|
|
+ r = ssl_timeoutread(timeout, smtpfd, smtpfd, ssl, buf, len); |
249 |
|
|
+ if (r < 0) ssl_err_str = ssl_error_str(); |
250 |
|
|
+ } else |
251 |
|
|
+#endif |
252 |
|
|
r = timeoutread(timeout,smtpfd,buf,len); |
253 |
|
|
if (r <= 0) dropped(); |
254 |
|
|
return r; |
255 |
|
|
@@ -117,6 +137,12 @@ int saferead(fd,buf,len) int fd; char *b |
256 |
|
|
int safewrite(fd,buf,len) int fd; char *buf; int len; |
257 |
|
|
{ |
258 |
|
|
int r; |
259 |
|
|
+#ifdef TLS |
260 |
|
|
+ if (ssl) { |
261 |
|
|
+ r = ssl_timeoutwrite(timeout, smtpfd, smtpfd, ssl, buf, len); |
262 |
|
|
+ if (r < 0) ssl_err_str = ssl_error_str(); |
263 |
|
|
+ } else |
264 |
|
|
+#endif |
265 |
|
|
r = timeoutwrite(timeout,smtpfd,buf,len); |
266 |
|
|
if (r <= 0) dropped(); |
267 |
|
|
return r; |
268 |
|
|
@@ -194,19 +220,25 @@ unsigned long ehlo() |
269 |
|
|
e = smtptext.s + smtptext.len - 6; /* 250-?\n */ |
270 |
|
|
while (s <= e) |
271 |
|
|
{ |
272 |
|
|
+ int wasspace = 0; |
273 |
|
|
+ |
274 |
|
|
if (!saa_readyplus(&ehlokw, 1)) temp_nomem(); |
275 |
|
|
sa = ehlokw.sa + ehlokw.len++; |
276 |
|
|
if (ehlokw.len > maxehlokwlen) *sa = sauninit; else sa->len = 0; |
277 |
|
|
|
278 |
|
|
- /* smtptext is known to end in a '\n' */ |
279 |
|
|
- for (p = (s += 4); ; ++p) |
280 |
|
|
- if (*p == '\n' || *p == ' ' || *p == '\t') { |
281 |
|
|
- if (!stralloc_catb(sa, s, p - s) || !stralloc_0(sa)) temp_nomem(); |
282 |
|
|
- if (*p++ == '\n') break; |
283 |
|
|
- while (*p == ' ' || *p == '\t') ; |
284 |
|
|
- s = p; |
285 |
|
|
- } |
286 |
|
|
- s = p; |
287 |
|
|
+ /* smtptext is known to end in a '\n' */ |
288 |
|
|
+ for (p = (s += 4); ; ++p) |
289 |
|
|
+ if (*p == '\n' || *p == ' ' || *p == '\t') { |
290 |
|
|
+ if (!wasspace) |
291 |
|
|
+ if (!stralloc_catb(sa, s, p - s) || !stralloc_0(sa)) temp_nomem(); |
292 |
|
|
+ if (*p == '\n') break; |
293 |
|
|
+ wasspace = 1; |
294 |
|
|
+ } else if (wasspace == 1) { |
295 |
|
|
+ wasspace = 0; |
296 |
|
|
+ s = p; |
297 |
|
|
+ } |
298 |
|
|
+ s = ++p; |
299 |
|
|
+ |
300 |
|
|
/* keyword should consist of alpha-num and '-' |
301 |
|
|
* broken AUTH might use '=' instead of space */ |
302 |
|
|
for (p = sa->s; *p; ++p) if (*p == '=') { *p = 0; break; } |
303 |
|
|
@@ -232,6 +264,17 @@ void quit(prepend,append) |
304 |
|
|
char *prepend; |
305 |
|
|
char *append; |
306 |
|
|
{ |
307 |
|
|
+#ifdef TLS |
308 |
|
|
+ /* shouldn't talk to the client unless in an appropriate state */ |
309 |
|
|
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L |
310 |
|
|
+ OSSL_HANDSHAKE_STATE state = ssl ? SSL_get_state(ssl) : TLS_ST_BEFORE; |
311 |
|
|
+ if (state & TLS_ST_OK || (!smtps && state & TLS_ST_BEFORE)) |
312 |
|
|
+ |
313 |
|
|
+#else |
314 |
|
|
+ int state = ssl ? ssl->state : SSL_ST_BEFORE; |
315 |
|
|
+ if (state & SSL_ST_OK || (!smtps && state & SSL_ST_BEFORE)) |
316 |
|
|
+#endif |
317 |
|
|
+#endif |
318 |
|
|
substdio_putsflush(&smtpto,"QUIT\r\n"); |
319 |
|
|
/* waiting for remote side is just too ridiculous */ |
320 |
|
|
out(prepend); |
321 |
|
|
@@ -239,6 +282,30 @@ char *append; |
322 |
|
|
out(append); |
323 |
|
|
out(".\n"); |
324 |
|
|
outsmtptext(); |
325 |
|
|
+ |
326 |
|
|
+#if defined(TLS) && defined(DEBUG) |
327 |
|
|
+ if (ssl) { |
328 |
|
|
+ X509 *peercert; |
329 |
|
|
+ |
330 |
|
|
+ out("STARTTLS proto="); out(SSL_get_version(ssl)); |
331 |
|
|
+ out("; cipher="); out(SSL_get_cipher(ssl)); |
332 |
|
|
+ |
333 |
|
|
+ /* we want certificate details */ |
334 |
|
|
+ if (peercert = SSL_get_peer_certificate(ssl)) { |
335 |
|
|
+ char *str; |
336 |
|
|
+ |
337 |
|
|
+ str = X509_NAME_oneline(X509_get_subject_name(peercert), NULL, 0); |
338 |
|
|
+ out("; subject="); out(str); OPENSSL_free(str); |
339 |
|
|
+ |
340 |
|
|
+ str = X509_NAME_oneline(X509_get_issuer_name(peercert), NULL, 0); |
341 |
|
|
+ out("; issuer="); out(str); OPENSSL_free(str); |
342 |
|
|
+ |
343 |
|
|
+ X509_free(peercert); |
344 |
|
|
+ } |
345 |
|
|
+ out(";\n"); |
346 |
|
|
+ } |
347 |
|
|
+#endif |
348 |
|
|
+ |
349 |
|
|
zerodie(); |
350 |
|
|
} |
351 |
|
|
|
352 |
|
|
@@ -267,6 +334,206 @@ void blast() |
353 |
|
|
substdio_flush(&smtpto); |
354 |
|
|
} |
355 |
|
|
|
356 |
|
|
+#ifdef TLS |
357 |
|
|
+char *partner_fqdn = 0; |
358 |
|
|
+ |
359 |
|
|
+# define TLS_QUIT quit(ssl ? "; connected to " : "; connecting to ", "") |
360 |
|
|
+void tls_quit(const char *s1, const char *s2) |
361 |
|
|
+{ |
362 |
|
|
+ out((char *)s1); if (s2) { out(": "); out((char *)s2); } TLS_QUIT; |
363 |
|
|
+} |
364 |
|
|
+# define tls_quit_error(s) tls_quit(s, ssl_error()) |
365 |
|
|
+ |
366 |
|
|
+int match_partner(const char *s, int len) |
367 |
|
|
+{ |
368 |
|
|
+ if (!case_diffb(partner_fqdn, len, s) && !partner_fqdn[len]) return 1; |
369 |
|
|
+ /* we also match if the name is *.domainname */ |
370 |
|
|
+ if (*s == '*') { |
371 |
|
|
+ const char *domain = partner_fqdn + str_chr(partner_fqdn, '.'); |
372 |
|
|
+ if (!case_diffb(domain, --len, ++s) && !domain[len]) return 1; |
373 |
|
|
+ } |
374 |
|
|
+ return 0; |
375 |
|
|
+} |
376 |
|
|
+ |
377 |
|
|
+/* don't want to fail handshake if certificate can't be verified */ |
378 |
|
|
+int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; } |
379 |
|
|
+ |
380 |
|
|
+int tls_init() |
381 |
|
|
+{ |
382 |
|
|
+ int i; |
383 |
|
|
+ SSL *myssl; |
384 |
|
|
+ SSL_CTX *ctx; |
385 |
|
|
+ stralloc saciphers = {0}; |
386 |
|
|
+ const char *ciphers, *servercert = 0; |
387 |
|
|
+ |
388 |
|
|
+ if (partner_fqdn) { |
389 |
|
|
+ struct stat st; |
390 |
|
|
+ stralloc tmp = {0}; |
391 |
|
|
+ if (!stralloc_copys(&tmp, "control/tlshosts/") |
392 |
|
|
+ || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn)) |
393 |
|
|
+ || !stralloc_catb(&tmp, ".pem", 5)) temp_nomem(); |
394 |
|
|
+ if (stat(tmp.s, &st) == 0) |
395 |
|
|
+ servercert = tmp.s; |
396 |
|
|
+ else { |
397 |
|
|
+ if (!stralloc_copys(&tmp, "control/notlshosts/") |
398 |
|
|
+ || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn)+1)) |
399 |
|
|
+ temp_nomem(); |
400 |
|
|
+ if ((stat("control/tlshosts/exhaustivelist", &st) == 0) || |
401 |
|
|
+ (stat(tmp.s, &st) == 0)) { |
402 |
|
|
+ alloc_free(tmp.s); |
403 |
|
|
+ return 0; |
404 |
|
|
+ } |
405 |
|
|
+ alloc_free(tmp.s); |
406 |
|
|
+ } |
407 |
|
|
+ } |
408 |
|
|
+ |
409 |
|
|
+ if (!smtps) { |
410 |
|
|
+ stralloc *sa = ehlokw.sa; |
411 |
|
|
+ unsigned int len = ehlokw.len; |
412 |
|
|
+ /* look for STARTTLS among EHLO keywords */ |
413 |
|
|
+ for ( ; len && case_diffs(sa->s, "STARTTLS"); ++sa, --len) ; |
414 |
|
|
+ if (!len) { |
415 |
|
|
+ if (!servercert) return 0; |
416 |
|
|
+ out("ZNo TLS achieved while "); out((char *)servercert); |
417 |
|
|
+ out(" exists"); smtptext.len = 0; TLS_QUIT; |
418 |
|
|
+ } |
419 |
|
|
+ } |
420 |
|
|
+ |
421 |
|
|
+ SSL_library_init(); |
422 |
|
|
+ ctx = SSL_CTX_new(SSLv23_client_method()); |
423 |
|
|
+ if (!ctx) { |
424 |
|
|
+ if (!smtps && !servercert) return 0; |
425 |
|
|
+ smtptext.len = 0; |
426 |
|
|
+ tls_quit_error("ZTLS error initializing ctx"); |
427 |
|
|
+ } |
428 |
|
|
+ |
429 |
|
|
+ /* POODLE vulnerability */ |
430 |
|
|
+ SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); |
431 |
|
|
+ |
432 |
|
|
+ if (servercert) { |
433 |
|
|
+ if (!SSL_CTX_load_verify_locations(ctx, servercert, NULL)) { |
434 |
|
|
+ SSL_CTX_free(ctx); |
435 |
|
|
+ smtptext.len = 0; |
436 |
|
|
+ out("ZTLS unable to load "); tls_quit_error(servercert); |
437 |
|
|
+ } |
438 |
|
|
+ /* set the callback here; SSL_set_verify didn't work before 0.9.6c */ |
439 |
|
|
+ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb); |
440 |
|
|
+ } |
441 |
|
|
+ |
442 |
|
|
+ /* let the other side complain if it needs a cert and we don't have one */ |
443 |
|
|
+# define CLIENTCERT "control/clientcert.pem" |
444 |
|
|
+ if (SSL_CTX_use_certificate_chain_file(ctx, CLIENTCERT)) |
445 |
|
|
+ SSL_CTX_use_RSAPrivateKey_file(ctx, CLIENTCERT, SSL_FILETYPE_PEM); |
446 |
|
|
+# undef CLIENTCERT |
447 |
|
|
+ |
448 |
|
|
+ myssl = SSL_new(ctx); |
449 |
|
|
+ SSL_CTX_free(ctx); |
450 |
|
|
+ if (!myssl) { |
451 |
|
|
+ if (!smtps && !servercert) return 0; |
452 |
|
|
+ smtptext.len = 0; |
453 |
|
|
+ tls_quit_error("ZTLS error initializing ssl"); |
454 |
|
|
+ } |
455 |
|
|
+ |
456 |
|
|
+ if (!smtps) substdio_putsflush(&smtpto, "STARTTLS\r\n"); |
457 |
|
|
+ |
458 |
|
|
+ /* while the server is preparing a response, do something else */ |
459 |
|
|
+ if (control_readfile(&saciphers, "control/tlsclientciphers", 0) == -1) |
460 |
|
|
+ { SSL_free(myssl); temp_control(); } |
461 |
|
|
+ if (saciphers.len) { |
462 |
|
|
+ for (i = 0; i < saciphers.len - 1; ++i) |
463 |
|
|
+ if (!saciphers.s[i]) saciphers.s[i] = ':'; |
464 |
|
|
+ ciphers = saciphers.s; |
465 |
|
|
+ } |
466 |
|
|
+ else ciphers = "DEFAULT"; |
467 |
|
|
+ SSL_set_cipher_list(myssl, ciphers); |
468 |
|
|
+ alloc_free(saciphers.s); |
469 |
|
|
+ |
470 |
|
|
+ SSL_set_fd(myssl, smtpfd); |
471 |
|
|
+ |
472 |
|
|
+ /* read the response to STARTTLS */ |
473 |
|
|
+ if (!smtps) { |
474 |
|
|
+ if (smtpcode() != 220) { |
475 |
|
|
+ SSL_free(myssl); |
476 |
|
|
+ if (!servercert) return 0; |
477 |
|
|
+ out("ZSTARTTLS rejected while "); |
478 |
|
|
+ out((char *)servercert); out(" exists"); TLS_QUIT; |
479 |
|
|
+ } |
480 |
|
|
+ smtptext.len = 0; |
481 |
|
|
+ } |
482 |
|
|
+ |
483 |
|
|
+ ssl = myssl; |
484 |
|
|
+ if (ssl_timeoutconn(timeout, smtpfd, smtpfd, ssl) <= 0) |
485 |
|
|
+ tls_quit("ZTLS connect failed", ssl_error_str()); |
486 |
|
|
+ |
487 |
|
|
+ if (servercert) { |
488 |
|
|
+ X509 *peercert; |
489 |
|
|
+ STACK_OF(GENERAL_NAME) *gens; |
490 |
|
|
+ int found_gen_dns = 0; |
491 |
|
|
+ int matched_gen_dns = 0; |
492 |
|
|
+ |
493 |
|
|
+ int r = SSL_get_verify_result(ssl); |
494 |
|
|
+ if (r != X509_V_OK) { |
495 |
|
|
+ out("ZTLS unable to verify server with "); |
496 |
|
|
+ tls_quit(servercert, X509_verify_cert_error_string(r)); |
497 |
|
|
+ } |
498 |
|
|
+ alloc_free(servercert); |
499 |
|
|
+ |
500 |
|
|
+ peercert = SSL_get_peer_certificate(ssl); |
501 |
|
|
+ if (!peercert) { |
502 |
|
|
+ out("ZTLS unable to verify server "); |
503 |
|
|
+ tls_quit(partner_fqdn, "no certificate provided"); |
504 |
|
|
+ } |
505 |
|
|
+ |
506 |
|
|
+ /* RFC 2595 section 2.4: find a matching name |
507 |
|
|
+ * first find a match among alternative names */ |
508 |
|
|
+ gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0); |
509 |
|
|
+ if (gens) { |
510 |
|
|
+ for (i = 0, r = sk_GENERAL_NAME_num(gens); i < r; ++i) |
511 |
|
|
+ { |
512 |
|
|
+ const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i); |
513 |
|
|
+ if (gn->type == GEN_DNS){ |
514 |
|
|
+ found_gen_dns = 1; |
515 |
|
|
+ if (match_partner(gn->d.ia5->data, gn->d.ia5->length)){ |
516 |
|
|
+ matched_gen_dns = 1; |
517 |
|
|
+ break; |
518 |
|
|
+ } |
519 |
|
|
+ } |
520 |
|
|
+ } |
521 |
|
|
+ sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); |
522 |
|
|
+ } |
523 |
|
|
+ |
524 |
|
|
+ /* no SubjectAltName of type DNS found, look up commonName */ |
525 |
|
|
+ if (!found_gen_dns) { |
526 |
|
|
+ stralloc peer = {0}; |
527 |
|
|
+ X509_NAME *subj = X509_get_subject_name(peercert); |
528 |
|
|
+ i = X509_NAME_get_index_by_NID(subj, NID_commonName, -1); |
529 |
|
|
+ if (i >= 0) { |
530 |
|
|
+ const ASN1_STRING *s = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subj, i)); |
531 |
|
|
+ if (s) { peer.len = s->length; peer.s = s->data; } |
532 |
|
|
+ } |
533 |
|
|
+ if (peer.len <= 0) { |
534 |
|
|
+ out("ZTLS unable to verify server "); |
535 |
|
|
+ tls_quit(partner_fqdn, "certificate contains no valid commonName"); |
536 |
|
|
+ } |
537 |
|
|
+ if (!match_partner(peer.s, peer.len)) { |
538 |
|
|
+ out("ZTLS unable to verify server "); out(partner_fqdn); |
539 |
|
|
+ out(": received certificate for "); outsafe(&peer); TLS_QUIT; |
540 |
|
|
+ } |
541 |
|
|
+ } else if (!matched_gen_dns) { |
542 |
|
|
+ out("ZTLS unable to verify server "); |
543 |
|
|
+ tls_quit(partner_fqdn, "certificate contains no matching dNSNnames"); |
544 |
|
|
+ } |
545 |
|
|
+ |
546 |
|
|
+ X509_free(peercert); |
547 |
|
|
+ } |
548 |
|
|
+ |
549 |
|
|
+ if (smtps) if (smtpcode() != 220) |
550 |
|
|
+ quit("ZTLS Connected to "," but greeting failed"); |
551 |
|
|
+ |
552 |
|
|
+ return 1; |
553 |
|
|
+} |
554 |
|
|
+#endif |
555 |
|
|
+ |
556 |
|
|
stralloc recip = {0}; |
557 |
|
|
|
558 |
|
|
void smtp() |
559 |
|
|
@@ -274,12 +541,37 @@ void smtp() |
560 |
|
|
unsigned long code; |
561 |
|
|
int flagbother; |
562 |
|
|
int i; |
563 |
|
|
+ |
564 |
|
|
+#ifndef PORT_SMTP |
565 |
|
|
+ /* the qmtpc patch uses smtp_port and undefines PORT_SMTP */ |
566 |
|
|
+# define port smtp_port |
567 |
|
|
+#endif |
568 |
|
|
+ |
569 |
|
|
+#ifdef TLS |
570 |
|
|
+# ifdef MXPS |
571 |
|
|
+ if (type == 'S') smtps = 1; |
572 |
|
|
+ else if (type != 's') |
573 |
|
|
+# endif |
574 |
|
|
+ if (port == 465) smtps = 1; |
575 |
|
|
+ if (!smtps) |
576 |
|
|
+#endif |
577 |
|
|
|
578 |
|
|
if (smtpcode() != 220) quit("ZConnected to "," but greeting failed"); |
579 |
|
|
|
580 |
|
|
#ifdef EHLO |
581 |
|
|
+# ifdef TLS |
582 |
|
|
+ if (!smtps) |
583 |
|
|
+# endif |
584 |
|
|
code = ehlo(); |
585 |
|
|
|
586 |
|
|
+# ifdef TLS |
587 |
|
|
+ if (tls_init()) |
588 |
|
|
+ /* RFC2487 says we should issue EHLO (even if we might not need |
589 |
|
|
+ * extensions); at the same time, it does not prohibit a server |
590 |
|
|
+ * to reject the EHLO and make us fallback to HELO */ |
591 |
|
|
+ code = ehlo(); |
592 |
|
|
+# endif |
593 |
|
|
+ |
594 |
|
|
if (code == 250) { |
595 |
|
|
/* add EHLO response checks here */ |
596 |
|
|
|
597 |
|
|
@@ -484,6 +776,9 @@ char **argv; |
598 |
|
|
if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) { |
599 |
|
|
tcpto_err(&ip.ix[i].ip,0); |
600 |
|
|
partner = ip.ix[i].ip; |
601 |
|
|
+#ifdef TLS |
602 |
|
|
+ partner_fqdn = ip.ix[i].fqdn; |
603 |
|
|
+#endif |
604 |
|
|
smtp(); /* does not return */ |
605 |
|
|
} |
606 |
|
|
tcpto_err(&ip.ix[i].ip,errno == error_timeout); |
607 |
|
|
--- netqmail-1.06-orig/qmail-remote.8 1998-06-15 10:53:16.000000000 +0000 |
608 |
|
|
+++ netqmail-1.06/qmail-remote.8 2015-12-01 15:54:59.029940779 +0000 |
609 |
|
|
@@ -114,6 +114,10 @@ arguments. |
610 |
|
|
always exits zero. |
611 |
|
|
.SH "CONTROL FILES" |
612 |
|
|
.TP 5 |
613 |
|
|
+.I clientcert.pem |
614 |
|
|
+SSL certificate that is used to authenticate with the remote server |
615 |
|
|
+during a TLS session. |
616 |
|
|
+.TP 5 |
617 |
|
|
.I helohost |
618 |
|
|
Current host name, |
619 |
|
|
for use solely in saying hello to the remote SMTP server. |
620 |
|
|
@@ -123,6 +127,16 @@ if that is supplied; |
621 |
|
|
otherwise |
622 |
|
|
.B qmail-remote |
623 |
|
|
refuses to run. |
624 |
|
|
+ |
625 |
|
|
+.TP 5 |
626 |
|
|
+.I notlshosts/<FQDN> |
627 |
|
|
+.B qmail-remote |
628 |
|
|
+will not try TLS on servers for which this file exists |
629 |
|
|
+.RB ( <FQDN> |
630 |
|
|
+is the fully-qualified domain name of the server). |
631 |
|
|
+.IR (tlshosts/<FQDN>.pem |
632 |
|
|
+takes precedence over this file however). |
633 |
|
|
+ |
634 |
|
|
.TP 5 |
635 |
|
|
.I smtproutes |
636 |
|
|
Artificial SMTP routes. |
637 |
|
|
@@ -156,6 +170,8 @@ may be empty; |
638 |
|
|
this tells |
639 |
|
|
.B qmail-remote |
640 |
|
|
to look up MX records as usual. |
641 |
|
|
+.I port |
642 |
|
|
+value of 465 (deprecated smtps port) causes TLS session to be started. |
643 |
|
|
.I smtproutes |
644 |
|
|
may include wildcards: |
645 |
|
|
|
646 |
|
|
@@ -195,6 +211,33 @@ Number of seconds |
647 |
|
|
.B qmail-remote |
648 |
|
|
will wait for each response from the remote SMTP server. |
649 |
|
|
Default: 1200. |
650 |
|
|
+ |
651 |
|
|
+.TP 5 |
652 |
|
|
+.I tlsclientciphers |
653 |
|
|
+A set of OpenSSL client cipher strings. Multiple ciphers |
654 |
|
|
+contained in a string should be separated by a colon. |
655 |
|
|
+ |
656 |
|
|
+.TP 5 |
657 |
|
|
+.I tlshosts/<FQDN>.pem |
658 |
|
|
+.B qmail-remote |
659 |
|
|
+requires TLS authentication from servers for which this file exists |
660 |
|
|
+.RB ( <FQDN> |
661 |
|
|
+is the fully-qualified domain name of the server). One of the |
662 |
|
|
+.I dNSName |
663 |
|
|
+or the |
664 |
|
|
+.I CommonName |
665 |
|
|
+attributes have to match. The file contains the trusted CA certificates. |
666 |
|
|
+ |
667 |
|
|
+.B WARNING: |
668 |
|
|
+this option may cause mail to be delayed, bounced, doublebounced, or lost. |
669 |
|
|
+ |
670 |
|
|
+.TP 5 |
671 |
|
|
+.I tlshosts/exhaustivelist |
672 |
|
|
+if this file exists |
673 |
|
|
+no TLS will be tried on hosts other than those for which a file |
674 |
|
|
+.B tlshosts/<FQDN>.pem |
675 |
|
|
+exists. |
676 |
|
|
+ |
677 |
|
|
.SH "SEE ALSO" |
678 |
|
|
addresses(5), |
679 |
|
|
envelopes(5), |
680 |
|
|
--- netqmail-1.06-orig/qmail-control.9 1998-06-15 10:53:16.000000000 +0000 |
681 |
|
|
+++ netqmail-1.06/qmail-control.9 2015-12-08 00:33:06.248714330 +0000 |
682 |
|
|
@@ -43,11 +43,14 @@ control default used by |
683 |
|
|
.I badmailfrom \fR(none) \fRqmail-smtpd |
684 |
|
|
.I bouncefrom \fRMAILER-DAEMON \fRqmail-send |
685 |
|
|
.I bouncehost \fIme \fRqmail-send |
686 |
|
|
+.I clientca.pem \fR(none) \fRqmail-smtpd |
687 |
|
|
+.I clientcert.pem \fR(none) \fRqmail-remote |
688 |
|
|
.I concurrencylocal \fR10 \fRqmail-send |
689 |
|
|
.I concurrencyremote \fR20 \fRqmail-send |
690 |
|
|
.I defaultdomain \fIme \fRqmail-inject |
691 |
|
|
.I defaulthost \fIme \fRqmail-inject |
692 |
|
|
.I databytes \fR0 \fRqmail-smtpd |
693 |
|
|
+.I dh2048.pem \fR(none) \fRqmail-smtpd |
694 |
|
|
.I doublebouncehost \fIme \fRqmail-send |
695 |
|
|
.I doublebounceto \fRpostmaster \fRqmail-send |
696 |
|
|
.I envnoathost \fIme \fRqmail-send |
697 |
|
|
@@ -61,11 +64,17 @@ control default used by |
698 |
|
|
.I qmqpservers \fR(none) \fRqmail-qmqpc |
699 |
|
|
.I queuelifetime \fR604800 \fRqmail-send |
700 |
|
|
.I rcpthosts \fR(none) \fRqmail-smtpd |
701 |
|
|
+.I rsa2048.pem \fR(none) \fRqmail-smtpd |
702 |
|
|
+.I servercert.pem \fR(none) \fRqmail-smtpd |
703 |
|
|
.I smtpgreeting \fIme \fRqmail-smtpd |
704 |
|
|
.I smtproutes \fR(none) \fRqmail-remote |
705 |
|
|
.I timeoutconnect \fR60 \fRqmail-remote |
706 |
|
|
.I timeoutremote \fR1200 \fRqmail-remote |
707 |
|
|
.I timeoutsmtpd \fR1200 \fRqmail-smtpd |
708 |
|
|
+.I tlsclients \fR(none) \fRqmail-smtpd |
709 |
|
|
+.I tlsclientciphers \fR(none) \fRqmail-remote |
710 |
|
|
+.I tlshosts/FQDN.pem \fR(none) \fRqmail-remote |
711 |
|
|
+.I tlsserverciphers \fR(none) \fRqmail-smtpd |
712 |
|
|
.I virtualdomains \fR(none) \fRqmail-send |
713 |
|
|
.fi |
714 |
|
|
.RE |
715 |
|
|
--- netqmail-1.06-orig/dns.c 2007-11-30 20:22:54.000000000 +0000 |
716 |
|
|
+++ netqmail-1.06/dns.c 2019-04-08 15:22:04.390598941 +0000 |
717 |
|
|
@@ -267,12 +267,14 @@ stralloc *sa; |
718 |
|
|
int pref; |
719 |
|
|
{ |
720 |
|
|
int r; |
721 |
|
|
- struct ip_mx ix; |
722 |
|
|
+ struct ip_mx ix = {0}; |
723 |
|
|
|
724 |
|
|
if (!stralloc_copy(&glue,sa)) return DNS_MEM; |
725 |
|
|
if (!stralloc_0(&glue)) return DNS_MEM; |
726 |
|
|
if (glue.s[0]) { |
727 |
|
|
+#ifndef IX_FQDN |
728 |
|
|
ix.pref = 0; |
729 |
|
|
+#endif |
730 |
|
|
if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) |
731 |
|
|
{ |
732 |
|
|
if (!ipalloc_append(ia,&ix)) return DNS_MEM; |
733 |
|
|
@@ -291,9 +293,16 @@ int pref; |
734 |
|
|
ix.ip = ip; |
735 |
|
|
ix.pref = pref; |
736 |
|
|
if (r == DNS_SOFT) return DNS_SOFT; |
737 |
|
|
- if (r == 1) |
738 |
|
|
+ if (r == 1) { |
739 |
|
|
+#ifdef IX_FQDN |
740 |
|
|
+ ix.fqdn = glue.s; |
741 |
|
|
+#endif |
742 |
|
|
if (!ipalloc_append(ia,&ix)) return DNS_MEM; |
743 |
|
|
} |
744 |
|
|
+ } |
745 |
|
|
+#ifdef IX_FQDN |
746 |
|
|
+ glue.s = 0; |
747 |
|
|
+#endif |
748 |
|
|
return 0; |
749 |
|
|
} |
750 |
|
|
|
751 |
|
|
@@ -313,7 +322,7 @@ unsigned long random; |
752 |
|
|
{ |
753 |
|
|
int r; |
754 |
|
|
struct mx { stralloc sa; unsigned short p; } *mx; |
755 |
|
|
- struct ip_mx ix; |
756 |
|
|
+ struct ip_mx ix = {0}; |
757 |
|
|
int nummx; |
758 |
|
|
int i; |
759 |
|
|
int j; |
760 |
|
|
@@ -325,7 +334,9 @@ unsigned long random; |
761 |
|
|
if (!stralloc_copy(&glue,sa)) return DNS_MEM; |
762 |
|
|
if (!stralloc_0(&glue)) return DNS_MEM; |
763 |
|
|
if (glue.s[0]) { |
764 |
|
|
+#ifndef IX_FQDN |
765 |
|
|
ix.pref = 0; |
766 |
|
|
+#endif |
767 |
|
|
if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) |
768 |
|
|
{ |
769 |
|
|
if (!ipalloc_append(ia,&ix)) return DNS_MEM; |
770 |
|
|
--- netqmail-1.06-orig/hier.c 1998-06-15 10:53:16.000000000 +0000 |
771 |
|
|
+++ netqmail-1.06/hier.c 2015-12-01 15:54:59.033940812 +0000 |
772 |
|
|
@@ -143,6 +143,9 @@ void hier() |
773 |
|
|
c(auto_qmail,"bin","qail",auto_uido,auto_gidq,0755); |
774 |
|
|
c(auto_qmail,"bin","elq",auto_uido,auto_gidq,0755); |
775 |
|
|
c(auto_qmail,"bin","pinq",auto_uido,auto_gidq,0755); |
776 |
|
|
+#ifdef TLS |
777 |
|
|
+ c(auto_qmail,"bin","update_tmprsadh",auto_uido,auto_gidq,0755); |
778 |
|
|
+#endif |
779 |
|
|
|
780 |
|
|
c(auto_qmail,"man/man5","addresses.5",auto_uido,auto_gidq,0644); |
781 |
|
|
c(auto_qmail,"man/cat5","addresses.0",auto_uido,auto_gidq,0644); |
782 |
|
|
--- netqmail-1.06-orig/ipalloc.h 1998-06-15 10:53:16.000000000 +0000 |
783 |
|
|
+++ netqmail-1.06/ipalloc.h 2015-12-01 15:54:59.033940812 +0000 |
784 |
|
|
@@ -3,7 +3,15 @@ |
785 |
|
|
|
786 |
|
|
#include "ip.h" |
787 |
|
|
|
788 |
|
|
+#ifdef TLS |
789 |
|
|
+# define IX_FQDN 1 |
790 |
|
|
+#endif |
791 |
|
|
+ |
792 |
|
|
+#ifdef IX_FQDN |
793 |
|
|
+struct ip_mx { struct ip_address ip; int pref; char *fqdn; } ; |
794 |
|
|
+#else |
795 |
|
|
struct ip_mx { struct ip_address ip; int pref; } ; |
796 |
|
|
+#endif |
797 |
|
|
|
798 |
|
|
#include "gen_alloc.h" |
799 |
|
|
|
800 |
|
|
--- netqmail-1.06-orig/tls.c 2019-04-08 15:26:13.112123436 +0000 |
801 |
|
|
+++ netqmail-1.06/tls.c 2019-04-08 15:17:31.924930725 +0000 |
802 |
|
|
@@ -0,0 +1,27 @@ |
803 |
|
|
+#ifdef TLS |
804 |
|
|
+#include "exit.h" |
805 |
|
|
+#include "error.h" |
806 |
|
|
+#include <openssl/ssl.h> |
807 |
|
|
+#include <openssl/err.h> |
808 |
|
|
+ |
809 |
|
|
+int smtps = 0; |
810 |
|
|
+SSL *ssl = NULL; |
811 |
|
|
+ |
812 |
|
|
+void ssl_free(SSL *myssl) { SSL_shutdown(myssl); SSL_free(myssl); } |
813 |
|
|
+void ssl_exit(int status) { if (ssl) ssl_free(ssl); _exit(status); } |
814 |
|
|
+ |
815 |
|
|
+const char *ssl_error() |
816 |
|
|
+{ |
817 |
|
|
+ int r = ERR_get_error(); |
818 |
|
|
+ if (!r) return NULL; |
819 |
|
|
+ SSL_load_error_strings(); |
820 |
|
|
+ return ERR_error_string(r, NULL); |
821 |
|
|
+} |
822 |
|
|
+const char *ssl_error_str() |
823 |
|
|
+{ |
824 |
|
|
+ const char *err = ssl_error(); |
825 |
|
|
+ if (err) return err; |
826 |
|
|
+ if (!errno) return 0; |
827 |
|
|
+ return (errno == error_timeout) ? "timed out" : error_str(errno); |
828 |
|
|
+} |
829 |
|
|
+#endif |
830 |
|
|
--- netqmail-1.06-orig/tls.h 2019-04-08 15:26:13.112123436 +0000 |
831 |
|
|
+++ netqmail-1.06/tls.h 2015-12-01 15:54:59.033940812 +0000 |
832 |
|
|
@@ -0,0 +1,16 @@ |
833 |
|
|
+#ifndef TLS_H |
834 |
|
|
+#define TLS_H |
835 |
|
|
+ |
836 |
|
|
+#include <openssl/ssl.h> |
837 |
|
|
+ |
838 |
|
|
+extern int smtps; |
839 |
|
|
+extern SSL *ssl; |
840 |
|
|
+ |
841 |
|
|
+void ssl_free(SSL *myssl); |
842 |
|
|
+void ssl_exit(int status); |
843 |
|
|
+# define _exit ssl_exit |
844 |
|
|
+ |
845 |
|
|
+const char *ssl_error(); |
846 |
|
|
+const char *ssl_error_str(); |
847 |
|
|
+ |
848 |
|
|
+#endif |
849 |
|
|
--- netqmail-1.06-orig/ssl_timeoutio.c 2019-04-08 15:26:13.112123436 +0000 |
850 |
|
|
+++ netqmail-1.06/ssl_timeoutio.c 2019-04-08 15:17:14.324823036 +0000 |
851 |
|
|
@@ -0,0 +1,114 @@ |
852 |
|
|
+#ifdef TLS |
853 |
|
|
+#include "select.h" |
854 |
|
|
+#include "error.h" |
855 |
|
|
+#include "ndelay.h" |
856 |
|
|
+#include "now.h" |
857 |
|
|
+#include "ssl_timeoutio.h" |
858 |
|
|
+ |
859 |
|
|
+int ssl_timeoutio(int (*fun)(), |
860 |
|
|
+ int t, int rfd, int wfd, SSL *ssl, char *buf, int len) |
861 |
|
|
+{ |
862 |
|
|
+ int n; |
863 |
|
|
+ const datetime_sec end = (datetime_sec)t + now(); |
864 |
|
|
+ |
865 |
|
|
+ do { |
866 |
|
|
+ fd_set fds; |
867 |
|
|
+ struct timeval tv; |
868 |
|
|
+ |
869 |
|
|
+ const int r = buf ? fun(ssl, buf, len) : fun(ssl); |
870 |
|
|
+ if (r > 0) return r; |
871 |
|
|
+ |
872 |
|
|
+ t = end - now(); |
873 |
|
|
+ if (t < 0) break; |
874 |
|
|
+ tv.tv_sec = (time_t)t; tv.tv_usec = 0; |
875 |
|
|
+ |
876 |
|
|
+ FD_ZERO(&fds); |
877 |
|
|
+ switch (SSL_get_error(ssl, r)) |
878 |
|
|
+ { |
879 |
|
|
+ default: return r; /* some other error */ |
880 |
|
|
+ case SSL_ERROR_WANT_READ: |
881 |
|
|
+ FD_SET(rfd, &fds); n = select(rfd + 1, &fds, NULL, NULL, &tv); |
882 |
|
|
+ break; |
883 |
|
|
+ case SSL_ERROR_WANT_WRITE: |
884 |
|
|
+ FD_SET(wfd, &fds); n = select(wfd + 1, NULL, &fds, NULL, &tv); |
885 |
|
|
+ break; |
886 |
|
|
+ } |
887 |
|
|
+ |
888 |
|
|
+ /* n is the number of descriptors that changed status */ |
889 |
|
|
+ } while (n > 0); |
890 |
|
|
+ |
891 |
|
|
+ if (n != -1) errno = error_timeout; |
892 |
|
|
+ return -1; |
893 |
|
|
+} |
894 |
|
|
+ |
895 |
|
|
+int ssl_timeoutaccept(int t, int rfd, int wfd, SSL *ssl) |
896 |
|
|
+{ |
897 |
|
|
+ int r; |
898 |
|
|
+ |
899 |
|
|
+ /* if connection is established, keep NDELAY */ |
900 |
|
|
+ if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1; |
901 |
|
|
+ r = ssl_timeoutio(SSL_accept, t, rfd, wfd, ssl, NULL, 0); |
902 |
|
|
+ |
903 |
|
|
+ if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); } |
904 |
|
|
+ else SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); |
905 |
|
|
+ |
906 |
|
|
+ return r; |
907 |
|
|
+} |
908 |
|
|
+ |
909 |
|
|
+int ssl_timeoutconn(int t, int rfd, int wfd, SSL *ssl) |
910 |
|
|
+{ |
911 |
|
|
+ int r; |
912 |
|
|
+ |
913 |
|
|
+ /* if connection is established, keep NDELAY */ |
914 |
|
|
+ if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1; |
915 |
|
|
+ r = ssl_timeoutio(SSL_connect, t, rfd, wfd, ssl, NULL, 0); |
916 |
|
|
+ |
917 |
|
|
+ if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); } |
918 |
|
|
+ else SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); |
919 |
|
|
+ |
920 |
|
|
+ return r; |
921 |
|
|
+} |
922 |
|
|
+ |
923 |
|
|
+int ssl_timeoutrehandshake(int t, int rfd, int wfd, SSL *ssl) |
924 |
|
|
+{ |
925 |
|
|
+ int r=0; |
926 |
|
|
+ |
927 |
|
|
+ SSL_renegotiate(ssl); |
928 |
|
|
+ |
929 |
|
|
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L |
930 |
|
|
+ char buf[1]; /* dummy read buffer */ |
931 |
|
|
+ struct timeval tv; |
932 |
|
|
+ fd_set fds; |
933 |
|
|
+ r = ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0); |
934 |
|
|
+ if (r <=0) return r; |
935 |
|
|
+ |
936 |
|
|
+ tv.tv_sec = (time_t)t; tv.tv_usec = 0; |
937 |
|
|
+ FD_ZERO(&fds); FD_SET(rfd, &fds); |
938 |
|
|
+ if ((r = select(rfd + 1, &fds, NULL, NULL, &tv)>0) && FD_ISSET(rfd, &fds)){ |
939 |
|
|
+ r = SSL_read(ssl, buf, 1); |
940 |
|
|
+ if (SSL_get_error(ssl, r) == SSL_ERROR_WANT_READ) r = 1; /*ignore */ |
941 |
|
|
+ } |
942 |
|
|
+ if (r <=0) return r; |
943 |
|
|
+#else |
944 |
|
|
+ r = ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0); |
945 |
|
|
+ if (r <= 0 || ssl->type == SSL_ST_CONNECT) return r; |
946 |
|
|
+ |
947 |
|
|
+ /* this is for the server only */ |
948 |
|
|
+ ssl->state = SSL_ST_ACCEPT; |
949 |
|
|
+#endif |
950 |
|
|
+ return ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0); |
951 |
|
|
+} |
952 |
|
|
+ |
953 |
|
|
+int ssl_timeoutread(int t, int rfd, int wfd, SSL *ssl, char *buf, int len) |
954 |
|
|
+{ |
955 |
|
|
+ if (!buf) return 0; |
956 |
|
|
+ if (SSL_pending(ssl)) return SSL_read(ssl, buf, len); |
957 |
|
|
+ return ssl_timeoutio(SSL_read, t, rfd, wfd, ssl, buf, len); |
958 |
|
|
+} |
959 |
|
|
+ |
960 |
|
|
+int ssl_timeoutwrite(int t, int rfd, int wfd, SSL *ssl, char *buf, int len) |
961 |
|
|
+{ |
962 |
|
|
+ if (!buf) return 0; |
963 |
|
|
+ return ssl_timeoutio(SSL_write, t, rfd, wfd, ssl, buf, len); |
964 |
|
|
+} |
965 |
|
|
+#endif |
966 |
|
|
--- netqmail-1.06-orig/ssl_timeoutio.h 2019-04-08 15:26:13.112123436 +0000 |
967 |
|
|
+++ netqmail-1.06/ssl_timeoutio.h 2019-03-22 21:11:16.610440636 +0000 |
968 |
|
|
@@ -0,0 +1,21 @@ |
969 |
|
|
+#ifndef SSL_TIMEOUTIO_H |
970 |
|
|
+#define SSL_TIMEOUTIO_H |
971 |
|
|
+ |
972 |
|
|
+#include <openssl/ssl.h> |
973 |
|
|
+ |
974 |
|
|
+/* the version is like this: 0xMNNFFPPS: major minor fix patch status */ |
975 |
|
|
+#if OPENSSL_VERSION_NUMBER < 0x00908000L |
976 |
|
|
+# error "Need OpenSSL version at least 0.9.8" |
977 |
|
|
+#endif |
978 |
|
|
+ |
979 |
|
|
+int ssl_timeoutconn(int t, int rfd, int wfd, SSL *ssl); |
980 |
|
|
+int ssl_timeoutaccept(int t, int rfd, int wfd, SSL *ssl); |
981 |
|
|
+int ssl_timeoutrehandshake(int t, int rfd, int wfd, SSL *ssl); |
982 |
|
|
+ |
983 |
|
|
+int ssl_timeoutread(int t, int rfd, int wfd, SSL *ssl, char *buf, int len); |
984 |
|
|
+int ssl_timeoutwrite(int t, int rfd, int wfd, SSL *ssl, char *buf, int len); |
985 |
|
|
+ |
986 |
|
|
+int ssl_timeoutio( |
987 |
|
|
+ int (*fun)(), int t, int rfd, int wfd, SSL *ssl, char *buf, int len); |
988 |
|
|
+ |
989 |
|
|
+#endif |
990 |
|
|
--- netqmail-1.06-orig/TARGETS 1998-06-15 10:53:16.000000000 +0000 |
991 |
|
|
+++ netqmail-1.06/TARGETS 2015-12-01 15:54:59.033940812 +0000 |
992 |
|
|
@@ -168,6 +168,8 @@ control.o |
993 |
|
|
constmap.o |
994 |
|
|
timeoutread.o |
995 |
|
|
timeoutwrite.o |
996 |
|
|
+tls.o |
997 |
|
|
+ssl_timeoutio.o |
998 |
|
|
timeoutconn.o |
999 |
|
|
tcpto.o |
1000 |
|
|
dns.o |
1001 |
|
|
@@ -320,6 +322,7 @@ binm2 |
1002 |
|
|
binm2+df |
1003 |
|
|
binm3 |
1004 |
|
|
binm3+df |
1005 |
|
|
+Makefile-cert |
1006 |
|
|
it |
1007 |
|
|
qmail-local.0 |
1008 |
|
|
qmail-lspawn.0 |
1009 |
|
|
@@ -385,3 +388,4 @@ forgeries.0 |
1010 |
|
|
man |
1011 |
|
|
setup |
1012 |
|
|
check |
1013 |
|
|
+update_tmprsadh |
1014 |
|
|
--- netqmail-1.06-orig/Makefile-cert.mk 2019-04-08 15:26:13.112123436 +0000 |
1015 |
|
|
+++ netqmail-1.06/Makefile-cert.mk 2015-12-01 15:54:59.033940812 +0000 |
1016 |
|
|
@@ -0,0 +1,21 @@ |
1017 |
|
|
+cert-req: req.pem |
1018 |
|
|
+cert cert-req: QMAIL/control/clientcert.pem |
1019 |
|
|
+ @: |
1020 |
|
|
+ |
1021 |
|
|
+QMAIL/control/clientcert.pem: QMAIL/control/servercert.pem |
1022 |
|
|
+ ln -s $< $@ |
1023 |
|
|
+ |
1024 |
|
|
+QMAIL/control/servercert.pem: |
1025 |
|
|
+ PATH=$$PATH:/usr/local/ssl/bin \ |
1026 |
|
|
+ openssl req -new -x509 -nodes -days 366 -out $@ -keyout $@ |
1027 |
|
|
+ chmod 640 $@ |
1028 |
|
|
+ chown `head -2 conf-users | tail -1`:`head -1 conf-groups` $@ |
1029 |
|
|
+ |
1030 |
|
|
+req.pem: |
1031 |
|
|
+ PATH=$$PATH:/usr/local/ssl/bin openssl req \ |
1032 |
|
|
+ -new -nodes -out $@ -keyout QMAIL/control/servercert.pem |
1033 |
|
|
+ chmod 640 QMAIL/control/servercert.pem |
1034 |
|
|
+ chown `head -2 conf-users | tail -1`:`head -1 conf-groups` QMAIL/control/servercert.pem |
1035 |
|
|
+ @echo |
1036 |
|
|
+ @echo "Send req.pem to your CA to obtain signed_req.pem, and do:" |
1037 |
|
|
+ @echo "cat signed_req.pem >> QMAIL/control/servercert.pem" |
1038 |
|
|
--- netqmail-1.06-orig/conf-cc 1998-06-15 10:53:16.000000000 +0000 |
1039 |
|
|
+++ netqmail-1.06/conf-cc 2019-04-08 15:25:56.312020413 +0000 |
1040 |
|
|
@@ -1,3 +1,3 @@ |
1041 |
|
|
-cc -O2 |
1042 |
|
|
+cc -O2 -DTLS=20190517 -I/usr/local/ssl/include |
1043 |
|
|
|
1044 |
|
|
This will be used to compile .c files. |
1045 |
|
|
--- netqmail-1.06-orig/Makefile 2007-11-30 20:22:54.000000000 +0000 |
1046 |
|
|
+++ netqmail-1.06/Makefile 2015-12-01 15:54:59.033940812 +0000 |
1047 |
|
|
@@ -808,7 +808,7 @@ dnsptr dnsip dnsmxip dnsfq hostname ipme |
1048 |
|
|
forward preline condredirect bouncesaying except maildirmake \ |
1049 |
|
|
maildir2mbox maildirwatch qail elq pinq idedit install-big install \ |
1050 |
|
|
instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \ |
1051 |
|
|
-binm3 binm3+df |
1052 |
|
|
+binm3 binm3+df update_tmprsadh |
1053 |
|
|
|
1054 |
|
|
load: \ |
1055 |
|
|
make-load warn-auto.sh systype |
1056 |
|
|
@@ -1444,6 +1444,7 @@ ndelay.a case.a sig.a open.a lock.a seek |
1057 |
|
|
substdio.a error.a str.a fs.a auto_qmail.o dns.lib socket.lib |
1058 |
|
|
./load qmail-remote control.o constmap.o timeoutread.o \ |
1059 |
|
|
timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \ |
1060 |
|
|
+ tls.o ssl_timeoutio.o -L/usr/local/ssl/lib -lssl -lcrypto \ |
1061 |
|
|
ipalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \ |
1062 |
|
|
lock.a seek.a getln.a stralloc.a alloc.a substdio.a error.a \ |
1063 |
|
|
str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib` |
1064 |
|
|
@@ -1539,6 +1540,7 @@ open.a sig.a case.a env.a stralloc.a all |
1065 |
|
|
fs.a auto_qmail.o socket.lib |
1066 |
|
|
./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ |
1067 |
|
|
timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ |
1068 |
|
|
+ tls.o ssl_timeoutio.o ndelay.a -L/usr/local/ssl/lib -lssl -lcrypto \ |
1069 |
|
|
received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ |
1070 |
|
|
datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ |
1071 |
|
|
alloc.a substdio.a error.a str.a fs.a auto_qmail.o `cat \ |
1072 |
|
|
@@ -1827,7 +1829,8 @@ date822fmt.h date822fmt.c dns.h dns.c tr |
1073 |
|
|
ipalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \ |
1074 |
|
|
ndelay_off.c direntry.3 direntry.h1 direntry.h2 trydrent.c prot.h \ |
1075 |
|
|
prot.c chkshsgr.c warn-shsgr tryshsgr.c ipme.h ipme.c trysalen.c \ |
1076 |
|
|
-maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c |
1077 |
|
|
+maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c \ |
1078 |
|
|
+update_tmprsadh |
1079 |
|
|
shar -m `cat FILES` > shar |
1080 |
|
|
chmod 400 shar |
1081 |
|
|
|
1082 |
|
|
@@ -2108,6 +2111,19 @@ timeoutwrite.o: \ |
1083 |
|
|
compile timeoutwrite.c timeoutwrite.h select.h error.h readwrite.h |
1084 |
|
|
./compile timeoutwrite.c |
1085 |
|
|
|
1086 |
|
|
+qmail-smtpd: tls.o ssl_timeoutio.o ndelay.a |
1087 |
|
|
+qmail-remote: tls.o ssl_timeoutio.o |
1088 |
|
|
+qmail-smtpd.o: tls.h ssl_timeoutio.h |
1089 |
|
|
+qmail-remote.o: tls.h ssl_timeoutio.h |
1090 |
|
|
+ |
1091 |
|
|
+tls.o: \ |
1092 |
|
|
+compile tls.c exit.h error.h |
1093 |
|
|
+ ./compile tls.c |
1094 |
|
|
+ |
1095 |
|
|
+ssl_timeoutio.o: \ |
1096 |
|
|
+compile ssl_timeoutio.c ssl_timeoutio.h select.h error.h ndelay.h |
1097 |
|
|
+ ./compile ssl_timeoutio.c |
1098 |
|
|
+ |
1099 |
|
|
token822.o: \ |
1100 |
|
|
compile token822.c stralloc.h gen_alloc.h alloc.h str.h token822.h \ |
1101 |
|
|
gen_alloc.h gen_allocdefs.h |
1102 |
|
|
@@ -2139,3 +2155,26 @@ compile wait_nohang.c haswaitp.h |
1103 |
|
|
wait_pid.o: \ |
1104 |
|
|
compile wait_pid.c error.h haswaitp.h |
1105 |
|
|
./compile wait_pid.c |
1106 |
|
|
+ |
1107 |
|
|
+cert cert-req: \ |
1108 |
|
|
+Makefile-cert |
1109 |
|
|
+ @$(MAKE) -sf $< $@ |
1110 |
|
|
+ |
1111 |
|
|
+Makefile-cert: \ |
1112 |
|
|
+conf-qmail conf-users conf-groups Makefile-cert.mk |
1113 |
|
|
+ @cat Makefile-cert.mk \ |
1114 |
|
|
+ | sed s}QMAIL}"`head -1 conf-qmail`"}g \ |
1115 |
|
|
+ > $@ |
1116 |
|
|
+ |
1117 |
|
|
+update_tmprsadh: \ |
1118 |
|
|
+conf-qmail conf-users conf-groups update_tmprsadh.sh |
1119 |
|
|
+ @cat update_tmprsadh.sh\ |
1120 |
|
|
+ | sed s}UGQMAILD}"`head -2 conf-users|tail -1`:`head -1 conf-groups`"}g \ |
1121 |
|
|
+ | sed s}QMAIL}"`head -1 conf-qmail`"}g \ |
1122 |
|
|
+ > $@ |
1123 |
|
|
+ chmod 755 update_tmprsadh |
1124 |
|
|
+ |
1125 |
|
|
+tmprsadh: \ |
1126 |
|
|
+update_tmprsadh |
1127 |
|
|
+ echo "Creating new temporary RSA and DH parameters" |
1128 |
|
|
+ ./update_tmprsadh |
1129 |
|
|
--- netqmail-1.06-orig/update_tmprsadh.sh 2019-04-08 15:26:13.112123436 +0000 |
1130 |
|
|
+++ netqmail-1.06/update_tmprsadh.sh 2015-12-08 00:32:33.936474103 +0000 |
1131 |
|
|
@@ -0,0 +1,19 @@ |
1132 |
|
|
+#!/bin/sh |
1133 |
|
|
+ |
1134 |
|
|
+# Update temporary RSA and DH keys |
1135 |
|
|
+# Frederik Vermeulen 2004-05-31 GPL |
1136 |
|
|
+ |
1137 |
|
|
+umask 0077 || exit 0 |
1138 |
|
|
+ |
1139 |
|
|
+export PATH="$PATH:/usr/local/bin/ssl:/usr/sbin" |
1140 |
|
|
+ |
1141 |
|
|
+openssl genrsa -out QMAIL/control/rsa2048.new 2048 && |
1142 |
|
|
+chmod 600 QMAIL/control/rsa2048.new && |
1143 |
|
|
+chown UGQMAILD QMAIL/control/rsa2048.new && |
1144 |
|
|
+mv -f QMAIL/control/rsa2048.new QMAIL/control/rsa2048.pem |
1145 |
|
|
+echo |
1146 |
|
|
+ |
1147 |
|
|
+openssl dhparam -2 -out QMAIL/control/dh2048.new 2048 && |
1148 |
|
|
+chmod 600 QMAIL/control/dh2048.new && |
1149 |
|
|
+chown UGQMAILD QMAIL/control/dh2048.new && |
1150 |
|
|
+mv -f QMAIL/control/dh2048.new QMAIL/control/dh2048.pem |