updated on Thu Jan 12 04:00:44 UTC 2012
[aur-mirror.git] / netqmail / netqmail-1.05-tls-smtpauth-20070417.patch
blob1f1290259d73e271c5d63ff1765d6117281d1aa8
1 VERSION: 20070417
3 This patch for netqmail 1.05 is a composite of the latest versions of Frederik
4 Vermulen's TLS patch (20070408) and Erwin Hoffmann's SMTP-AUTH (0.5.8) update
5 to Eric M. Johnston's and Krzysztof Dabrowski's qmail-smtpd-auth-0.31 patch.
7 To install, get netqmail 1.05, put it in the same directory as this patch, and
8 then set it up:
10 wget http://qmail.org/netqmail-1.05.tar.gz
11 tar -xzf netqmail-1.05.tar.gz
12 cd netqmail-1.05
13 ./collate.sh
14 cd netqmail-1.05
15 patch -p0 < ../../netqmail-1.05-tls-smtpauth-20070417.patch
16 cd netqmail-1.05
17 make
18 make setup check
19 make cert
20 make tmprsadh
22 Voila! You should now have TLS and SMTP-AUTH support working in qmail-smtpd.
24 VPOPMAIL NOTE: This version will only work with vpopmail versions 5.4.0 and
25 later
28 Here are the relevant URLs:
30 Netqmail:
31 http://www.qmail.org/netqmail/
33 TLS:
34 http://inoa.net/qmail-tls/
36 Qmail SMTP-AUTH:
37 http://www.fehcom.de/qmail/smtpauth.html
42 This composite patch was put together by Bill Shupp (hostmaster@shupp.org)
48 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/base64.c ./base64.c
49 --- ../../netqmail-1.05-orig/netqmail-1.05/base64.c 1969-12-31 18:00:00.000000000 -0600
50 +++ ./base64.c 2007-04-17 17:44:12.572978320 -0500
51 @@ -0,0 +1,124 @@
52 +#include "base64.h"
53 +#include "stralloc.h"
54 +#include "substdio.h"
55 +#include "str.h"
57 +static char *b64alpha =
58 + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
59 +#define B64PAD '='
61 +/* returns 0 ok, 1 illegal, -1 problem */
63 +int b64decode(in,l,out)
64 +const unsigned char *in;
65 +int l;
66 +stralloc *out; /* not null terminated */
68 + int p = 0;
69 + int n;
70 + unsigned int x;
71 + int i, j;
72 + char *s;
73 + unsigned char b[3];
75 + if (l == 0)
76 + {
77 + if (!stralloc_copys(out,"")) return -1;
78 + return 0;
79 + }
81 + while(in[l-1] == B64PAD) {
82 + p ++;
83 + l--;
84 + }
86 + n = (l + p) / 4;
87 + i = (n * 3) - p;
88 + if (!stralloc_ready(out,i)) return -1;
89 + out->len = i;
90 + s = out->s;
92 + for(i = 0; i < n - 1 ; i++) {
93 + x = 0;
94 + for(j = 0; j < 4; j++) {
95 + if(in[j] >= 'A' && in[j] <= 'Z')
96 + x = (x << 6) + (unsigned int)(in[j] - 'A' + 0);
97 + else if(in[j] >= 'a' && in[j] <= 'z')
98 + x = (x << 6) + (unsigned int)(in[j] - 'a' + 26);
99 + else if(in[j] >= '0' && in[j] <= '9')
100 + x = (x << 6) + (unsigned int)(in[j] - '0' + 52);
101 + else if(in[j] == '+')
102 + x = (x << 6) + 62;
103 + else if(in[j] == '/')
104 + x = (x << 6) + 63;
105 + else if(in[j] == '=')
106 + x = (x << 6);
109 + s[2] = (unsigned char)(x & 255); x >>= 8;
110 + s[1] = (unsigned char)(x & 255); x >>= 8;
111 + s[0] = (unsigned char)(x & 255); x >>= 8;
112 + s += 3; in += 4;
115 + x = 0;
116 + for(j = 0; j < 4; j++) {
117 + if(in[j] >= 'A' && in[j] <= 'Z')
118 + x = (x << 6) + (unsigned int)(in[j] - 'A' + 0);
119 + else if(in[j] >= 'a' && in[j] <= 'z')
120 + x = (x << 6) + (unsigned int)(in[j] - 'a' + 26);
121 + else if(in[j] >= '0' && in[j] <= '9')
122 + x = (x << 6) + (unsigned int)(in[j] - '0' + 52);
123 + else if(in[j] == '+')
124 + x = (x << 6) + 62;
125 + else if(in[j] == '/')
126 + x = (x << 6) + 63;
127 + else if(in[j] == '=')
128 + x = (x << 6);
131 + b[2] = (unsigned char)(x & 255); x >>= 8;
132 + b[1] = (unsigned char)(x & 255); x >>= 8;
133 + b[0] = (unsigned char)(x & 255); x >>= 8;
135 + for(i = 0; i < 3 - p; i++)
136 + s[i] = b[i];
138 + return 0;
141 +int b64encode(in,out)
142 +stralloc *in;
143 +stralloc *out; /* not null terminated */
145 + unsigned char a, b, c;
146 + int i;
147 + char *s;
149 + if (in->len == 0)
151 + if (!stralloc_copys(out,"")) return -1;
152 + return 0;
155 + i = in->len / 3 * 4 + 4;
156 + if (!stralloc_ready(out,i)) return -1;
157 + s = out->s;
159 + for (i = 0;i < in->len;i += 3) {
160 + a = in->s[i];
161 + b = i + 1 < in->len ? in->s[i + 1] : 0;
162 + c = i + 2 < in->len ? in->s[i + 2] : 0;
164 + *s++ = b64alpha[a >> 2];
165 + *s++ = b64alpha[((a & 3 ) << 4) | (b >> 4)];
167 + if (i + 1 >= in->len) *s++ = B64PAD;
168 + else *s++ = b64alpha[((b & 15) << 2) | (c >> 6)];
170 + if (i + 2 >= in->len) *s++ = B64PAD;
171 + else *s++ = b64alpha[c & 63];
173 + out->len = s - out->s;
174 + return 0;
176 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/base64.h ./base64.h
177 --- ../../netqmail-1.05-orig/netqmail-1.05/base64.h 1969-12-31 18:00:00.000000000 -0600
178 +++ ./base64.h 2007-04-17 17:44:12.572978320 -0500
179 @@ -0,0 +1,7 @@
180 +#ifndef BASE64_H
181 +#define BASE64_H
183 +extern int b64decode();
184 +extern int b64encode();
186 +#endif
187 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/case_startb.c ./case_startb.c
188 --- ../../netqmail-1.05-orig/netqmail-1.05/case_startb.c 1969-12-31 18:00:00.000000000 -0600
189 +++ ./case_startb.c 2007-04-17 17:44:12.573978168 -0500
190 @@ -0,0 +1,21 @@
191 +#include "case.h"
193 +int case_startb(s,len,t)
194 +register char *s;
195 +unsigned int len;
196 +register char *t;
198 + register unsigned char x;
199 + register unsigned char y;
201 + for (;;) {
202 + y = *t++ - 'A';
203 + if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
204 + if (!y) return 1;
205 + if (!len) return 0;
206 + --len;
207 + x = *s++ - 'A';
208 + if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
209 + if (x != y) return 0;
212 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/conf-cc ./conf-cc
213 --- ../../netqmail-1.05-orig/netqmail-1.05/conf-cc 1998-06-15 05:53:16.000000000 -0500
214 +++ ./conf-cc 2007-04-17 17:44:27.666683728 -0500
215 @@ -1,3 +1,3 @@
216 -cc -O2
217 +cc -O2 -DTLS=20070408 -I/usr/local/ssl/include
219 This will be used to compile .c files.
220 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/dns.c ./dns.c
221 --- ../../netqmail-1.05-orig/netqmail-1.05/dns.c 2007-04-17 17:41:58.087423232 -0500
222 +++ ./dns.c 2007-04-17 17:44:12.574978016 -0500
223 @@ -267,12 +267,11 @@
224 int pref;
226 int r;
227 - struct ip_mx ix;
228 + struct ip_mx ix = {0};
230 if (!stralloc_copy(&glue,sa)) return DNS_MEM;
231 if (!stralloc_0(&glue)) return DNS_MEM;
232 if (glue.s[0]) {
233 - ix.pref = 0;
234 if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)])
236 if (!ipalloc_append(ia,&ix)) return DNS_MEM;
237 @@ -291,9 +290,16 @@
238 ix.ip = ip;
239 ix.pref = pref;
240 if (r == DNS_SOFT) return DNS_SOFT;
241 - if (r == 1)
242 + if (r == 1) {
243 +#ifdef IX_FQDN
244 + ix.fqdn = glue.s;
245 +#endif
246 if (!ipalloc_append(ia,&ix)) return DNS_MEM;
249 +#ifdef IX_FQDN
250 + glue.s = 0;
251 +#endif
252 return 0;
255 @@ -313,7 +319,7 @@
257 int r;
258 struct mx { stralloc sa; unsigned short p; } *mx;
259 - struct ip_mx ix;
260 + struct ip_mx ix = {0};
261 int nummx;
262 int i;
263 int j;
264 @@ -325,7 +331,6 @@
265 if (!stralloc_copy(&glue,sa)) return DNS_MEM;
266 if (!stralloc_0(&glue)) return DNS_MEM;
267 if (glue.s[0]) {
268 - ix.pref = 0;
269 if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)])
271 if (!ipalloc_append(ia,&ix)) return DNS_MEM;
272 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/FILES.auth ./FILES.auth
273 --- ../../netqmail-1.05-orig/netqmail-1.05/FILES.auth 1969-12-31 18:00:00.000000000 -0600
274 +++ ./FILES.auth 2007-04-17 17:44:12.575977864 -0500
275 @@ -0,0 +1,18 @@
276 +The qmail-smtpd Auth patch modifies the following QMAIL 1.03 files:
278 += TARGETS
279 += Makefile
280 += qmail-smtpd.c
281 += qmail-smtpd.8
283 +Added files:
285 ++ base64.c
286 ++ base64.h
287 ++ case_startb.c
289 +Informational files:
291 +% install_smtpd-auth.sh (Installation shell script)
292 +% README.auth
293 +% README.auth.old (old description of SMTP Auth)
294 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/hier.c ./hier.c
295 --- ../../netqmail-1.05-orig/netqmail-1.05/hier.c 1998-06-15 05:53:16.000000000 -0500
296 +++ ./hier.c 2007-04-17 17:44:12.576977712 -0500
297 @@ -143,6 +143,9 @@
298 c(auto_qmail,"bin","qail",auto_uido,auto_gidq,0755);
299 c(auto_qmail,"bin","elq",auto_uido,auto_gidq,0755);
300 c(auto_qmail,"bin","pinq",auto_uido,auto_gidq,0755);
301 +#ifdef TLS
302 + c(auto_qmail,"bin","update_tmprsadh",auto_uido,auto_gidq,0755);
303 +#endif
305 c(auto_qmail,"man/man5","addresses.5",auto_uido,auto_gidq,0644);
306 c(auto_qmail,"man/cat5","addresses.0",auto_uido,auto_gidq,0644);
307 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/install_auth.sh ./install_auth.sh
308 --- ../../netqmail-1.05-orig/netqmail-1.05/install_auth.sh 1969-12-31 18:00:00.000000000 -0600
309 +++ ./install_auth.sh 2007-04-17 17:44:12.577977560 -0500
310 @@ -0,0 +1,99 @@
311 +#!/bin/sh
313 +# qmail-smtpd AUTH (UN)INSTALL Script (install_auth.sh)
314 +# -----------------------------------------------------
316 +# Purpose: To install and uninstall the qmail-smtpd Authentication Patch
318 +# Parameters: -u (uninstall)
319 +# VRF (Version to be uninstalled)
321 +# Usage: ./install_auth.sh [-u] [Version]
323 +# Installation: ./install_auth.sh
324 +# Uninstallation: ./install_auth.sh -u 105
326 +# Return Codes: 0 - Patches applied successfully
327 +# 1 - Original QMAIL files not found (Patch not extracted in QMAIL source directory)
328 +# 2 - Patch files not found
330 +# Output: install_auth.log
332 +# History: 1.0.0 - Erwin Hoffmann - Initial release
333 +# 1.0.1 - - grep fix; Gentoo fix
334 +# 1.0.2 - removed '-v' optio for cp
336 +#---------------------------------------------------------------------------------------
338 +DATE=$(date)
339 +LOCDIR=${PWD}
340 +QMAILHOME=$(head -n 1 conf-qmail)
341 +SOLARIS=$(sh ./find-systype.sh | grep -ci "SunOS")
342 +LOGFILE=auth.log
343 +TARGETS=FILES.auth
344 +IFSKEEP=${IFS}
345 +REL=057 # Should be identical to qmail-smtpd AUTH level
346 +BUILD=2005024212941
349 +if [ $# -eq 0 ] ; then
351 + echo "Installing qmail-smtpd AUTH $REL (Build $BUILD) at $DATE <<<" | tee -a $LOGFILE 2>&1
353 + for FILE in $(grep "^= " ${TARGETS} | awk '{print $2}'); do
354 + echo "Targeting file $FILE ..." | tee -a $LOGFILE 2>&1
355 + if [ -s ${FILE} ] ; then
356 + cp ${FILE} ${FILE}.$REL | tee -a $LOGFILE 2>&1
357 + echo "--> ${FILE} copied to ${FILE}.$REL" | tee -a $LOGFILE 2>&1
358 + else
359 + echo "${FILE} not found !"
360 + exit 1
361 + fi
362 + if [ -s ${FILE}.patch ] ; then
363 + if [ ${SOLARIS} -gt 0 ]; then
364 + echo "--> Patching qmail source file ${FILE} for Solaris ...." | tee -a $LOGFILE 2>&1
365 + patch -i ${FILE}.patch ${FILE} 2>&1 | tee -a $LOGFILE
366 + else
367 + echo "--> Patching qmail source file ${FILE} ...." | tee -a $LOGFILE 2>&1
368 + patch ${FILE} ${FILE}.patch 2>&1 | tee -a $LOGFILE
369 + fi
370 + else
371 + echo "!! ${FILE}.patch not found !"
372 + exit 2
373 + fi
374 + done
377 + echo "Copying documentation and samples to ${QMAILHOME}/doc/ ..." | tee -a $LOGFILE 2>&1
379 + cp README.auth* ${QMAILHOME}/doc/ | tee -a $LOGFILE 2>&1
380 + echo ""
381 + echo "If you dont wont CRAM-MD5 suport disable '#define CRAM_MD5' in qmail-smtpd !"
382 + echo "Installation of qmail-smtpd AUTH $REL (Build $BUILD) finished at $DATE <<<" | tee -a $LOGFILE 2>&1
384 +# Now go for the uninstallation....
386 +elif [ "$1" = "-u" ] ; then
388 +# Get the Version Number from INPUT
390 + if [ $# -eq 2 ] ; then
391 + REL=$2
392 + fi
394 + echo "De-installing qmail-smtpd AUTH $REL (Build $BUILD) at $DATE <<<" | tee -a $LOGFILE 2>&1
396 + for FILE in $(grep "^= " ${TARGETS} | awk '{print $2}'); do
397 + echo "Targeting file $FILE ..." | tee -a $LOGFILE 2>&1
398 + if [ -s ${FILE}.$REL ] ; then
399 + mv ${FILE}.$REL ${FILE} | tee -a $LOGFILE 2>&1
400 + touch ${FILE}
401 + echo "--> ${FILE}.$REL moved to ${FILE}" | tee -a $LOGFILE 2>&1
402 + else
403 + echo "!! ${FILE}.$REL not found !"
404 + fi
405 + done
406 + echo "De-installation of qmail-smtpd AUTH $REL (Build $BUILD) finished at $DATE <<<" | tee -a $LOGFILE 2>&1
409 +exit 0
410 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/ipalloc.h ./ipalloc.h
411 --- ../../netqmail-1.05-orig/netqmail-1.05/ipalloc.h 1998-06-15 05:53:16.000000000 -0500
412 +++ ./ipalloc.h 2007-04-17 17:44:12.578977408 -0500
413 @@ -3,7 +3,15 @@
415 #include "ip.h"
417 +#ifdef TLS
418 +# define IX_FQDN 1
419 +#endif
421 +#ifdef IX_FQDN
422 +struct ip_mx { struct ip_address ip; int pref; char *fqdn; } ;
423 +#else
424 struct ip_mx { struct ip_address ip; int pref; } ;
425 +#endif
427 #include "gen_alloc.h"
429 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/Makefile ./Makefile
430 --- ../../netqmail-1.05-orig/netqmail-1.05/Makefile 2007-04-17 17:41:58.083423840 -0500
431 +++ ./Makefile 2007-04-17 17:44:12.581976952 -0500
432 @@ -136,6 +136,10 @@
433 compile auto_usera.c
434 ./compile auto_usera.c
436 +base64.o: \
437 +compile base64.c base64.h stralloc.h substdio.h str.h
438 + ./compile base64.c
440 binm1: \
441 binm1.sh conf-qmail
442 cat binm1.sh \
443 @@ -808,7 +812,7 @@
444 forward preline condredirect bouncesaying except maildirmake \
445 maildir2mbox maildirwatch qail elq pinq idedit install-big install \
446 instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \
447 -binm3 binm3+df
448 +binm3 binm3+df update_tmprsadh
450 load: \
451 make-load warn-auto.sh systype
452 @@ -1444,6 +1448,7 @@
453 substdio.a error.a str.a fs.a auto_qmail.o dns.lib socket.lib
454 ./load qmail-remote control.o constmap.o timeoutread.o \
455 timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \
456 + tls.o ssl_timeoutio.o -L/usr/local/ssl/lib -lssl -lcrypto \
457 ipalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \
458 lock.a seek.a getln.a stralloc.a alloc.a substdio.a error.a \
459 str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib`
460 @@ -1536,12 +1541,13 @@
461 timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \
462 date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \
463 open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \
464 -fs.a auto_qmail.o socket.lib
465 +fs.a auto_qmail.o base64.o socket.lib
466 ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \
467 timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \
468 + tls.o ssl_timeoutio.o ndelay.a -L/usr/local/ssl/lib -lssl -lcrypto \
469 received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
470 datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
471 - alloc.a substdio.a error.a str.a fs.a auto_qmail.o `cat \
472 + alloc.a substdio.a error.a str.a fs.a auto_qmail.o base64.o `cat \
473 socket.lib`
475 qmail-smtpd.0: \
476 @@ -1553,7 +1559,7 @@
477 substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \
478 error.h ipme.h ip.h ipalloc.h ip.h gen_alloc.h ip.h qmail.h \
479 substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \
480 -exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h
481 +exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h base64.h
482 ./compile qmail-smtpd.c
484 qmail-start: \
485 @@ -1827,7 +1833,8 @@
486 ipalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \
487 ndelay_off.c direntry.3 direntry.h1 direntry.h2 trydrent.c prot.h \
488 prot.c chkshsgr.c warn-shsgr tryshsgr.c ipme.h ipme.c trysalen.c \
489 -maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c
490 +maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c \
491 +update_tmprsadh
492 shar -m `cat FILES` > shar
493 chmod 400 shar
495 @@ -2108,6 +2115,19 @@
496 compile timeoutwrite.c timeoutwrite.h select.h error.h readwrite.h
497 ./compile timeoutwrite.c
499 +qmail-smtpd: tls.o ssl_timeoutio.o ndelay.a
500 +qmail-remote: tls.o ssl_timeoutio.o
501 +qmail-smtpd.o: tls.h ssl_timeoutio.h
502 +qmail-remote.o: tls.h ssl_timeoutio.h
504 +tls.o: \
505 +compile tls.c exit.h error.h
506 + ./compile tls.c
508 +ssl_timeoutio.o: \
509 +compile ssl_timeoutio.c ssl_timeoutio.h select.h error.h ndelay.h
510 + ./compile ssl_timeoutio.c
512 token822.o: \
513 compile token822.c stralloc.h gen_alloc.h alloc.h str.h token822.h \
514 gen_alloc.h gen_allocdefs.h
515 @@ -2139,3 +2159,26 @@
516 wait_pid.o: \
517 compile wait_pid.c error.h haswaitp.h
518 ./compile wait_pid.c
520 +cert cert-req: \
521 +Makefile-cert
522 + @$(MAKE) -sf $< $@
524 +Makefile-cert: \
525 +conf-qmail conf-users conf-groups Makefile-cert.mk
526 + @cat Makefile-cert.mk \
527 + | sed s}QMAIL}"`head -1 conf-qmail`"}g \
528 + > $@
530 +update_tmprsadh: \
531 +conf-qmail conf-users conf-groups update_tmprsadh.sh
532 + @cat update_tmprsadh.sh\
533 + | sed s}UGQMAILD}"`head -2 conf-users|tail -1`:`head -1 conf-groups`"}g \
534 + | sed s}QMAIL}"`head -1 conf-qmail`"}g \
535 + > $@
536 + chmod 755 update_tmprsadh
538 +tmprsadh: \
539 +update_tmprsadh
540 + echo "Creating new temporary RSA and DH parameters"
541 + ./update_tmprsadh
542 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/Makefile-cert.mk ./Makefile-cert.mk
543 --- ../../netqmail-1.05-orig/netqmail-1.05/Makefile-cert.mk 1969-12-31 18:00:00.000000000 -0600
544 +++ ./Makefile-cert.mk 2007-04-17 17:44:12.582976800 -0500
545 @@ -0,0 +1,21 @@
546 +cert-req: req.pem
547 +cert cert-req: QMAIL/control/clientcert.pem
548 + @:
550 +QMAIL/control/clientcert.pem: QMAIL/control/servercert.pem
551 + ln -s $< $@
553 +QMAIL/control/servercert.pem:
554 + PATH=$$PATH:/usr/local/ssl/bin \
555 + openssl req -new -x509 -nodes -days 366 -out $@ -keyout $@
556 + chmod 640 $@
557 + chown `head -2 conf-users | tail -1`:`head -1 conf-groups` $@
559 +req.pem:
560 + PATH=$$PATH:/usr/local/ssl/bin openssl req \
561 + -new -nodes -out $@ -keyout QMAIL/control/servercert.pem
562 + chmod 640 QMAIL/control/servercert.pem
563 + chown `head -2 conf-users | tail -1`:`head -1 conf-groups` QMAIL/control/servercert.pem
564 + @echo
565 + @echo "Send req.pem to your CA to obtain signed_req.pem, and do:"
566 + @echo "cat signed_req.pem >> QMAIL/control/servercert.pem"
567 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/qmail-control.9 ./qmail-control.9
568 --- ../../netqmail-1.05-orig/netqmail-1.05/qmail-control.9 1998-06-15 05:53:16.000000000 -0500
569 +++ ./qmail-control.9 2007-04-17 17:44:12.583976648 -0500
570 @@ -43,11 +43,15 @@
571 .I badmailfrom \fR(none) \fRqmail-smtpd
572 .I bouncefrom \fRMAILER-DAEMON \fRqmail-send
573 .I bouncehost \fIme \fRqmail-send
574 +.I clientca.pem \fR(none) \fRqmail-smtpd
575 +.I clientcert.pem \fR(none) \fRqmail-remote
576 .I concurrencylocal \fR10 \fRqmail-send
577 .I concurrencyremote \fR20 \fRqmail-send
578 .I defaultdomain \fIme \fRqmail-inject
579 .I defaulthost \fIme \fRqmail-inject
580 .I databytes \fR0 \fRqmail-smtpd
581 +.I dh1024.pem \fR(none) \fRqmail-smtpd
582 +.I dh512.pem \fR(none) \fRqmail-smtpd
583 .I doublebouncehost \fIme \fRqmail-send
584 .I doublebounceto \fRpostmaster \fRqmail-send
585 .I envnoathost \fIme \fRqmail-send
586 @@ -61,11 +65,17 @@
587 .I qmqpservers \fR(none) \fRqmail-qmqpc
588 .I queuelifetime \fR604800 \fRqmail-send
589 .I rcpthosts \fR(none) \fRqmail-smtpd
590 +.I rsa512.pem \fR(none) \fRqmail-smtpd
591 +.I servercert.pem \fR(none) \fRqmail-smtpd
592 .I smtpgreeting \fIme \fRqmail-smtpd
593 .I smtproutes \fR(none) \fRqmail-remote
594 .I timeoutconnect \fR60 \fRqmail-remote
595 .I timeoutremote \fR1200 \fRqmail-remote
596 .I timeoutsmtpd \fR1200 \fRqmail-smtpd
597 +.I tlsclients \fR(none) \fRqmail-smtpd
598 +.I tlsclientciphers \fR(none) \fRqmail-remote
599 +.I tlshosts/FQDN.pem \fR(none) \fRqmail-remote
600 +.I tlsserverciphers \fR(none) \fRqmail-smtpd
601 .I virtualdomains \fR(none) \fRqmail-send
604 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/qmail-remote.8 ./qmail-remote.8
605 --- ../../netqmail-1.05-orig/netqmail-1.05/qmail-remote.8 1998-06-15 05:53:16.000000000 -0500
606 +++ ./qmail-remote.8 2007-04-17 17:44:27.666683728 -0500
607 @@ -114,6 +114,10 @@
608 always exits zero.
609 .SH "CONTROL FILES"
610 .TP 5
611 +.I clientcert.pem
612 +SSL certificate that is used to authenticate with the remote server
613 +during a TLS session.
614 +.TP 5
615 .I helohost
616 Current host name,
617 for use solely in saying hello to the remote SMTP server.
618 @@ -123,6 +127,16 @@
619 otherwise
620 .B qmail-remote
621 refuses to run.
623 +.TP 5
624 +.I notlshosts/<FQDN>
625 +.B qmail-remote
626 +will not try TLS on servers for which this file exists
627 +.RB ( <FQDN>
628 +is the fully-qualified domain name of the server).
629 +.IR (tlshosts/<FQDN>.pem
630 +takes precedence over this file however).
632 .TP 5
633 .I smtproutes
634 Artificial SMTP routes.
635 @@ -156,6 +170,8 @@
636 this tells
637 .B qmail-remote
638 to look up MX records as usual.
639 +.I port
640 +value of 465 (deprecated smtps port) causes TLS session to be started.
641 .I smtproutes
642 may include wildcards:
644 @@ -195,6 +211,33 @@
645 .B qmail-remote
646 will wait for each response from the remote SMTP server.
647 Default: 1200.
649 +.TP 5
650 +.I tlsclientciphers
651 +A set of OpenSSL client cipher strings. Multiple ciphers
652 +contained in a string should be separated by a colon.
654 +.TP 5
655 +.I tlshosts/<FQDN>.pem
656 +.B qmail-remote
657 +requires TLS authentication from servers for which this file exists
658 +.RB ( <FQDN>
659 +is the fully-qualified domain name of the server). One of the
660 +.I dNSName
661 +or the
662 +.I CommonName
663 +attributes have to match. The file contains the trusted CA certificates.
665 +.B WARNING:
666 +this option may cause mail to be delayed, bounced, doublebounced, or lost.
668 +.TP 5
669 +.I tlshosts/exhaustivelist
670 +if this file exists
671 +no TLS will be tried on hosts other than those for which a file
672 +.B tlshosts/<FQDN>.pem
673 +exists.
675 .SH "SEE ALSO"
676 addresses(5),
677 envelopes(5),
678 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/qmail-remote.c ./qmail-remote.c
679 --- ../../netqmail-1.05-orig/netqmail-1.05/qmail-remote.c 1998-06-15 05:53:16.000000000 -0500
680 +++ ./qmail-remote.c 2007-04-17 17:44:12.586976192 -0500
681 @@ -48,6 +48,17 @@
683 struct ip_address partner;
685 +#ifdef TLS
686 +# include <sys/stat.h>
687 +# include "tls.h"
688 +# include "ssl_timeoutio.h"
689 +# include <openssl/x509v3.h>
690 +# define EHLO 1
692 +int tls_init();
693 +const char *ssl_err_str = 0;
694 +#endif
696 void out(s) char *s; { if (substdio_puts(subfdoutsmall,s) == -1) _exit(0); }
697 void zero() { if (substdio_put(subfdoutsmall,"\0",1) == -1) _exit(0); }
698 void zerodie() { zero(); substdio_flush(subfdoutsmall); _exit(0); }
699 @@ -99,6 +110,9 @@
700 outhost();
701 out(" but connection died. ");
702 if (flagcritical) out("Possible duplicate! ");
703 +#ifdef TLS
704 + if (ssl_err_str) { out(ssl_err_str); out(" "); }
705 +#endif
706 out("(#4.4.2)\n");
707 zerodie();
709 @@ -110,6 +124,12 @@
710 int saferead(fd,buf,len) int fd; char *buf; int len;
712 int r;
713 +#ifdef TLS
714 + if (ssl) {
715 + r = ssl_timeoutread(timeout, smtpfd, smtpfd, ssl, buf, len);
716 + if (r < 0) ssl_err_str = ssl_error_str();
717 + } else
718 +#endif
719 r = timeoutread(timeout,smtpfd,buf,len);
720 if (r <= 0) dropped();
721 return r;
722 @@ -117,6 +137,12 @@
723 int safewrite(fd,buf,len) int fd; char *buf; int len;
725 int r;
726 +#ifdef TLS
727 + if (ssl) {
728 + r = ssl_timeoutwrite(timeout, smtpfd, smtpfd, ssl, buf, len);
729 + if (r < 0) ssl_err_str = ssl_error_str();
730 + } else
731 +#endif
732 r = timeoutwrite(timeout,smtpfd,buf,len);
733 if (r <= 0) dropped();
734 return r;
735 @@ -163,6 +189,65 @@
736 return code;
739 +#ifdef EHLO
740 +saa ehlokw = {0}; /* list of EHLO keywords and parameters */
741 +int maxehlokwlen = 0;
743 +unsigned long ehlo()
745 + stralloc *sa;
746 + char *s, *e, *p;
747 + unsigned long code;
749 + if (ehlokw.len > maxehlokwlen) maxehlokwlen = ehlokw.len;
750 + ehlokw.len = 0;
752 +# ifdef MXPS
753 + if (type == 's') return 0;
754 +# endif
756 + substdio_puts(&smtpto, "EHLO ");
757 + substdio_put(&smtpto, helohost.s, helohost.len);
758 + substdio_puts(&smtpto, "\r\n");
759 + substdio_flush(&smtpto);
761 + code = smtpcode();
762 + if (code != 250) return code;
764 + s = smtptext.s;
765 + while (*s++ != '\n') ; /* skip the first line: contains the domain */
767 + e = smtptext.s + smtptext.len - 6; /* 250-?\n */
768 + while (s <= e)
770 + int wasspace = 0;
772 + if (!saa_readyplus(&ehlokw, 1)) temp_nomem();
773 + sa = ehlokw.sa + ehlokw.len++;
774 + if (ehlokw.len > maxehlokwlen) *sa = sauninit; else sa->len = 0;
776 + /* smtptext is known to end in a '\n' */
777 + for (p = (s += 4); ; ++p)
778 + if (*p == '\n' || *p == ' ' || *p == '\t') {
779 + if (!wasspace)
780 + if (!stralloc_catb(sa, s, p - s) || !stralloc_0(sa)) temp_nomem();
781 + if (*p == '\n') break;
782 + wasspace = 1;
783 + } else if (wasspace == 1) {
784 + wasspace = 0;
785 + s = p;
787 + s = ++p;
789 + /* keyword should consist of alpha-num and '-'
790 + * broken AUTH might use '=' instead of space */
791 + for (p = sa->s; *p; ++p) if (*p == '=') { *p = 0; break; }
794 + return 250;
796 +#endif
798 void outsmtptext()
800 int i;
801 @@ -179,6 +264,11 @@
802 char *prepend;
803 char *append;
805 +#ifdef TLS
806 + /* shouldn't talk to the client unless in an appropriate state */
807 + int state = ssl ? ssl->state : SSL_ST_BEFORE;
808 + if (state & SSL_ST_OK || (!smtps && state & SSL_ST_BEFORE))
809 +#endif
810 substdio_putsflush(&smtpto,"QUIT\r\n");
811 /* waiting for remote side is just too ridiculous */
812 out(prepend);
813 @@ -186,6 +276,30 @@
814 out(append);
815 out(".\n");
816 outsmtptext();
818 +#if defined(TLS) && defined(DEBUG)
819 + if (ssl) {
820 + X509 *peercert;
822 + out("STARTTLS proto="); out(SSL_get_version(ssl));
823 + out("; cipher="); out(SSL_get_cipher(ssl));
825 + /* we want certificate details */
826 + if (peercert = SSL_get_peer_certificate(ssl)) {
827 + char *str;
829 + str = X509_NAME_oneline(X509_get_subject_name(peercert), NULL, 0);
830 + out("; subject="); out(str); OPENSSL_free(str);
832 + str = X509_NAME_oneline(X509_get_issuer_name(peercert), NULL, 0);
833 + out("; issuer="); out(str); OPENSSL_free(str);
835 + X509_free(peercert);
837 + out(";\n");
839 +#endif
841 zerodie();
844 @@ -214,6 +328,194 @@
845 substdio_flush(&smtpto);
848 +#ifdef TLS
849 +char *partner_fqdn = 0;
851 +# define TLS_QUIT quit(ssl ? "; connected to " : "; connecting to ", "")
852 +void tls_quit(const char *s1, const char *s2)
854 + out(s1); if (s2) { out(": "); out(s2); } TLS_QUIT;
856 +# define tls_quit_error(s) tls_quit(s, ssl_error())
858 +int match_partner(const char *s, int len)
860 + if (!case_diffb(partner_fqdn, len, s) && !partner_fqdn[len]) return 1;
861 + /* we also match if the name is *.domainname */
862 + if (*s == '*') {
863 + const char *domain = partner_fqdn + str_chr(partner_fqdn, '.');
864 + if (!case_diffb(domain, --len, ++s) && !domain[len]) return 1;
866 + return 0;
869 +/* don't want to fail handshake if certificate can't be verified */
870 +int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; }
872 +int tls_init()
874 + int i;
875 + SSL *myssl;
876 + SSL_CTX *ctx;
877 + stralloc saciphers = {0};
878 + const char *ciphers, *servercert = 0;
880 + if (partner_fqdn) {
881 + struct stat st;
882 + stralloc tmp = {0};
883 + if (!stralloc_copys(&tmp, "control/tlshosts/")
884 + || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn))
885 + || !stralloc_catb(&tmp, ".pem", 5)) temp_nomem();
886 + if (stat(tmp.s, &st) == 0)
887 + servercert = tmp.s;
888 + else {
889 + if (!stralloc_copys(&tmp, "control/notlshosts/")
890 + || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn)+1))
891 + temp_nomem();
892 + if ((stat("control/tlshosts/exhaustivelist", &st) == 0) ||
893 + (stat(tmp.s, &st) == 0)) {
894 + alloc_free(tmp.s);
895 + return 0;
897 + alloc_free(tmp.s);
901 + if (!smtps) {
902 + stralloc *sa = ehlokw.sa;
903 + unsigned int len = ehlokw.len;
904 + /* look for STARTTLS among EHLO keywords */
905 + for ( ; len && case_diffs(sa->s, "STARTTLS"); ++sa, --len) ;
906 + if (!len) {
907 + if (!servercert) return 0;
908 + out("ZNo TLS achieved while "); out(servercert);
909 + out(" exists"); smtptext.len = 0; TLS_QUIT;
913 + SSL_library_init();
914 + ctx = SSL_CTX_new(SSLv23_client_method());
915 + if (!ctx) {
916 + if (!smtps && !servercert) return 0;
917 + smtptext.len = 0;
918 + tls_quit_error("ZTLS error initializing ctx");
921 + if (servercert) {
922 + if (!SSL_CTX_load_verify_locations(ctx, servercert, NULL)) {
923 + SSL_CTX_free(ctx);
924 + smtptext.len = 0;
925 + out("ZTLS unable to load "); tls_quit_error(servercert);
927 + /* set the callback here; SSL_set_verify didn't work before 0.9.6c */
928 + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb);
931 + /* let the other side complain if it needs a cert and we don't have one */
932 +# define CLIENTCERT "control/clientcert.pem"
933 + if (SSL_CTX_use_certificate_chain_file(ctx, CLIENTCERT))
934 + SSL_CTX_use_RSAPrivateKey_file(ctx, CLIENTCERT, SSL_FILETYPE_PEM);
935 +# undef CLIENTCERT
937 + myssl = SSL_new(ctx);
938 + SSL_CTX_free(ctx);
939 + if (!myssl) {
940 + if (!smtps && !servercert) return 0;
941 + smtptext.len = 0;
942 + tls_quit_error("ZTLS error initializing ssl");
945 + if (!smtps) substdio_putsflush(&smtpto, "STARTTLS\r\n");
947 + /* while the server is preparing a responce, do something else */
948 + if (control_readfile(&saciphers, "control/tlsclientciphers", 0) == -1)
949 + { SSL_free(myssl); temp_control(); }
950 + if (saciphers.len) {
951 + for (i = 0; i < saciphers.len - 1; ++i)
952 + if (!saciphers.s[i]) saciphers.s[i] = ':';
953 + ciphers = saciphers.s;
955 + else ciphers = "DEFAULT";
956 + SSL_set_cipher_list(myssl, ciphers);
957 + alloc_free(saciphers.s);
959 + /* SSL_set_options(myssl, SSL_OP_NO_TLSv1); */
960 + SSL_set_fd(myssl, smtpfd);
962 + /* read the responce to STARTTLS */
963 + if (!smtps) {
964 + if (smtpcode() != 220) {
965 + SSL_free(myssl);
966 + if (!servercert) return 0;
967 + out("ZSTARTTLS rejected while ");
968 + out(servercert); out(" exists"); TLS_QUIT;
970 + smtptext.len = 0;
973 + ssl = myssl;
974 + if (ssl_timeoutconn(timeout, smtpfd, smtpfd, ssl) <= 0)
975 + tls_quit("ZTLS connect failed", ssl_error_str());
977 + if (servercert) {
978 + X509 *peercert;
979 + STACK_OF(GENERAL_NAME) *gens;
981 + int r = SSL_get_verify_result(ssl);
982 + if (r != X509_V_OK) {
983 + out("ZTLS unable to verify server with ");
984 + tls_quit(servercert, X509_verify_cert_error_string(r));
986 + alloc_free(servercert);
988 + peercert = SSL_get_peer_certificate(ssl);
989 + if (!peercert) {
990 + out("ZTLS unable to verify server ");
991 + tls_quit(partner_fqdn, "no certificate provided");
994 + /* RFC 2595 section 2.4: find a matching name
995 + * first find a match among alternative names */
996 + gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0);
997 + if (gens) {
998 + for (i = 0, r = sk_GENERAL_NAME_num(gens); i < r; ++i)
1000 + const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i);
1001 + if (gn->type == GEN_DNS)
1002 + if (match_partner(gn->d.ia5->data, gn->d.ia5->length)) break;
1004 + sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
1007 + /* no alternative name matched, look up commonName */
1008 + if (!gens || i >= r) {
1009 + stralloc peer = {0};
1010 + X509_NAME *subj = X509_get_subject_name(peercert);
1011 + i = X509_NAME_get_index_by_NID(subj, NID_commonName, -1);
1012 + if (i >= 0) {
1013 + const ASN1_STRING *s = X509_NAME_get_entry(subj, i)->value;
1014 + if (s) { peer.len = s->length; peer.s = s->data; }
1016 + if (peer.len <= 0) {
1017 + out("ZTLS unable to verify server ");
1018 + tls_quit(partner_fqdn, "certificate contains no valid commonName");
1020 + if (!match_partner(peer.s, peer.len)) {
1021 + out("ZTLS unable to verify server "); out(partner_fqdn);
1022 + out(": received certificate for "); outsafe(&peer); TLS_QUIT;
1026 + X509_free(peercert);
1029 + if (smtps) if (smtpcode() != 220)
1030 + quit("ZTLS Connected to "," but greeting failed");
1032 + return 1;
1034 +#endif
1036 stralloc recip = {0};
1038 void smtp()
1039 @@ -221,15 +523,54 @@
1040 unsigned long code;
1041 int flagbother;
1042 int i;
1044 +#ifndef PORT_SMTP
1045 + /* the qmtpc patch uses smtp_port and undefines PORT_SMTP */
1046 +# define port smtp_port
1047 +#endif
1049 +#ifdef TLS
1050 +# ifdef MXPS
1051 + if (type == 'S') smtps = 1;
1052 + else if (type != 's')
1053 +# endif
1054 + if (port == 465) smtps = 1;
1055 + if (!smtps)
1056 +#endif
1058 if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
1060 +#ifdef EHLO
1061 +# ifdef TLS
1062 + if (!smtps)
1063 +# endif
1064 + code = ehlo();
1066 +# ifdef TLS
1067 + if (tls_init())
1068 + /* RFC2487 says we should issue EHLO (even if we might not need
1069 + * extensions); at the same time, it does not prohibit a server
1070 + * to reject the EHLO and make us fallback to HELO */
1071 + code = ehlo();
1072 +# endif
1074 + if (code == 250) {
1075 + /* add EHLO response checks here */
1077 + /* and if EHLO failed, use HELO */
1078 + } else {
1079 +#endif
1081 substdio_puts(&smtpto,"HELO ");
1082 substdio_put(&smtpto,helohost.s,helohost.len);
1083 substdio_puts(&smtpto,"\r\n");
1084 substdio_flush(&smtpto);
1085 if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
1087 +#ifdef EHLO
1089 +#endif
1091 substdio_puts(&smtpto,"MAIL FROM:<");
1092 substdio_put(&smtpto,sender.s,sender.len);
1093 substdio_puts(&smtpto,">\r\n");
1094 @@ -417,6 +758,9 @@
1095 if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) {
1096 tcpto_err(&ip.ix[i].ip,0);
1097 partner = ip.ix[i].ip;
1098 +#ifdef TLS
1099 + partner_fqdn = ip.ix[i].fqdn;
1100 +#endif
1101 smtp(); /* does not return */
1103 tcpto_err(&ip.ix[i].ip,errno == error_timeout);
1104 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/qmail-smtpd.8 ./qmail-smtpd.8
1105 --- ../../netqmail-1.05-orig/netqmail-1.05/qmail-smtpd.8 1998-06-15 05:53:16.000000000 -0500
1106 +++ ./qmail-smtpd.8 2007-04-17 17:44:12.587976040 -0500
1107 @@ -14,6 +14,15 @@
1109 .BR tcp-environ(5) .
1111 +If the environment variable
1112 +.B SMTPS
1113 +is non-empty,
1114 +.B qmail-smtpd
1115 +starts a TLS session (to support the deprecated SMTPS protocol,
1116 +normally on port 465). Otherwise,
1117 +.B qmail-smtpd
1118 +offers the STARTTLS extension to ESMTP.
1120 .B qmail-smtpd
1121 is responsible for counting hops.
1122 It rejects any message with 100 or more
1123 @@ -23,7 +32,30 @@
1124 header fields.
1126 .B qmail-smtpd
1127 -supports ESMTP, including the 8BITMIME and PIPELINING options.
1128 +supports ESMTP, including the 8BITMIME, DATA, PIPELINING, SIZE, and AUTH options.
1129 +.B qmail-smtpd
1130 +includes a \'MAIL FROM:\' parameter parser and obeys \'Auth\' and \'Size\' advertisements.
1131 +.B qmail-smtpd
1132 +can accept LOGIN, PLAIN, and CRAM-MD5 AUTH types. It invokes
1133 +.IR checkprogram ,
1134 +which reads on file descriptor 3 the username, a 0 byte, the password
1135 +or CRAM-MD5 digest/response derived from the SMTP client,
1136 +another 0 byte, a CRAM-MD5 challenge (if applicable to the AUTH type),
1137 +and a final 0 byte.
1138 +.I checkprogram
1139 +invokes
1140 +.I subprogram
1141 +upon successful authentication, which should in turn return 0 to
1142 +.BR qmail-smtpd ,
1143 +effectively setting the environment variables $RELAYCLIENT and $TCPREMOTEINFO
1144 +(any supplied value replaced with the authenticated username).
1145 +.B qmail-smtpd
1146 +will reject the authentication attempt if it receives a nonzero return
1147 +value from
1148 +.I checkprogram
1150 +.IR subprogram .
1152 .SH TRANSPARENCY
1153 .B qmail-smtpd
1154 converts the SMTP newline convention into the UNIX newline convention
1155 @@ -49,6 +81,19 @@
1156 .BR @\fIhost ,
1157 meaning every address at
1158 .IR host .
1160 +.TP 5
1161 +.I clientca.pem
1162 +A list of Certifying Authority (CA) certificates that are used to verify
1163 +the client-presented certificates during a TLS-encrypted session.
1165 +.TP 5
1166 +.I clientcrl.pem
1167 +A list of Certificate Revocation Lists (CRLs). If present it
1168 +should contain the CRLs of the CAs in
1169 +.I clientca.pem
1170 +and client certs will be checked for revocation.
1172 .TP 5
1173 .I databytes
1174 Maximum number of bytes allowed in a message,
1175 @@ -76,6 +121,18 @@
1176 .B DATABYTES
1177 is set, it overrides
1178 .IR databytes .
1180 +.TP 5
1181 +.I dh1024.pem
1182 +If these 1024 bit DH parameters are provided,
1183 +.B qmail-smtpd
1184 +will use them for TLS sessions instead of generating one on-the-fly
1185 +(which is very timeconsuming).
1186 +.TP 5
1187 +.I dh512.pem
1188 +512 bit counterpart for
1189 +.B dh1024.pem.
1191 .TP 5
1192 .I localiphost
1193 Replacement host name for local IP addresses.
1194 @@ -151,6 +208,19 @@
1196 Envelope recipient addresses without @ signs are
1197 always allowed through.
1199 +.TP 5
1200 +.I rsa512.pem
1201 +If this 512 bit RSA key is provided,
1202 +.B qmail-smtpd
1203 +will use it for TLS sessions instead of generating one on-the-fly.
1205 +.TP 5
1206 +.I servercert.pem
1207 +SSL certificate to be presented to clients in TLS-encrypted sessions.
1208 +Should contain both the certificate and the private key. Certifying Authority
1209 +(CA) and intermediate certificates can be added at the end of the file.
1211 .TP 5
1212 .I smtpgreeting
1213 SMTP greeting message.
1214 @@ -169,6 +239,24 @@
1215 .B qmail-smtpd
1216 will wait for each new buffer of data from the remote SMTP client.
1217 Default: 1200.
1219 +.TP 5
1220 +.I tlsclients
1221 +A list of email addresses. When relay rules would reject an incoming message,
1222 +.B qmail-smtpd
1223 +can allow it if the client presents a certificate that can be verified against
1224 +the CA list in
1225 +.I clientca.pem
1226 +and the certificate email address is in
1227 +.IR tlsclients .
1229 +.TP 5
1230 +.I tlsserverciphers
1231 +A set of OpenSSL cipher strings. Multiple ciphers contained in a
1232 +string should be separated by a colon. If the environment variable
1233 +.B TLSCIPHERS
1234 +is set to such a string, it takes precedence.
1236 .SH "SEE ALSO"
1237 tcp-env(1),
1238 tcp-environ(5),
1239 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/qmail-smtpd.c ./qmail-smtpd.c
1240 --- ../../netqmail-1.05-orig/netqmail-1.05/qmail-smtpd.c 2007-04-17 17:41:58.094422168 -0500
1241 +++ ./qmail-smtpd.c 2007-04-17 17:44:27.668683424 -0500
1242 @@ -23,14 +23,34 @@
1243 #include "timeoutread.h"
1244 #include "timeoutwrite.h"
1245 #include "commands.h"
1246 +#include "wait.h"
1248 +#define CRAM_MD5
1249 +#define AUTHSLEEP 5
1251 #define MAXHOPS 100
1252 unsigned int databytes = 0;
1253 int timeout = 1200;
1255 +#ifdef TLS
1256 +#include <sys/stat.h>
1257 +#include "tls.h"
1258 +#include "ssl_timeoutio.h"
1260 +void tls_init();
1261 +int tls_verify();
1262 +void tls_nogateway();
1263 +int ssl_rfd = -1, ssl_wfd = -1; /* SSL_get_Xfd() are broken */
1264 +#endif
1266 int safewrite(fd,buf,len) int fd; char *buf; int len;
1268 int r;
1269 +#ifdef TLS
1270 + if (ssl && fd == ssl_wfd)
1271 + r = ssl_timeoutwrite(timeout, ssl_rfd, ssl_wfd, ssl, buf, len);
1272 + else
1273 +#endif
1274 r = timeoutwrite(timeout,fd,buf,len);
1275 if (r <= 0) _exit(1);
1276 return r;
1277 @@ -49,8 +69,18 @@
1278 void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); }
1279 void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); }
1281 +void err_size() { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); }
1282 void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
1283 +#ifndef TLS
1284 void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); }
1285 +#else
1286 +void err_nogateway()
1288 + out("553 sorry, that domain isn't in my list of allowed rcpthosts");
1289 + tls_nogateway();
1290 + out(" (#5.7.1)\r\n");
1292 +#endif
1293 void err_unimpl(arg) char *arg; { out("502 unimplemented (#5.5.1)\r\n"); }
1294 void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); }
1295 void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); }
1296 @@ -59,6 +89,16 @@
1297 void err_vrfy(arg) char *arg; { out("252 send some mail, i'll try my best\r\n"); }
1298 void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); }
1300 +int err_child() { out("454 oops, problem with child and I can't auth (#4.3.0)\r\n"); return -1; }
1301 +int err_fork() { out("454 oops, child won't start and I can't auth (#4.3.0)\r\n"); return -1; }
1302 +int err_pipe() { out("454 oops, unable to open pipe and I can't auth (#4.3.0)\r\n"); return -1; }
1303 +int err_write() { out("454 oops, unable to write pipe and I can't auth (#4.3.0)\r\n"); return -1; }
1304 +void err_authd() { out("503 you're already authenticated (#5.5.0)\r\n"); }
1305 +void err_authmail() { out("503 no auth during mail transaction (#5.5.0)\r\n"); }
1306 +int err_noauth() { out("504 auth type unimplemented (#5.5.1)\r\n"); return -1; }
1307 +int err_authabrt() { out("501 auth exchange canceled (#5.0.0)\r\n"); return -1; }
1308 +int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; }
1309 +void err_authfail() { out("535 authentication failed (#5.7.1)\r\n"); }
1311 stralloc greeting = {0};
1313 @@ -76,6 +116,7 @@
1314 smtp_greet("221 "); out("\r\n"); flush(); _exit(0);
1317 +char *protocol;
1318 char *remoteip;
1319 char *remotehost;
1320 char *remoteinfo;
1321 @@ -109,7 +150,6 @@
1322 if (liphostok == -1) die_control();
1323 if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control();
1324 if (timeout <= 0) timeout = 1;
1326 if (rcpthosts_init() == -1) die_control();
1328 bmfok = control_readfile(&bmf,"control/badmailfrom",0);
1329 @@ -122,6 +162,7 @@
1330 if (x) { scan_ulong(x,&u); databytes = u; }
1331 if (!(databytes + 1)) --databytes;
1333 + protocol = "SMTP";
1334 remoteip = env_get("TCPREMOTEIP");
1335 if (!remoteip) remoteip = "unknown";
1336 local = env_get("TCPLOCALHOST");
1337 @@ -131,6 +172,11 @@
1338 if (!remotehost) remotehost = "unknown";
1339 remoteinfo = env_get("TCPREMOTEINFO");
1340 relayclient = env_get("RELAYCLIENT");
1342 +#ifdef TLS
1343 + if (env_get("SMTPS")) { smtps = 1; tls_init(); }
1344 + else
1345 +#endif
1346 dohelo(remotehost);
1349 @@ -213,23 +259,105 @@
1350 int r;
1351 r = rcpthosts(addr.s,str_len(addr.s));
1352 if (r == -1) die_control();
1353 +#ifdef TLS
1354 + if (r == 0) if (tls_verify()) r = -2;
1355 +#endif
1356 return r;
1360 int seenmail = 0;
1361 int flagbarf; /* defined if seenmail */
1362 +int flagsize;
1363 stralloc mailfrom = {0};
1364 stralloc rcptto = {0};
1365 +stralloc fuser = {0};
1366 +stralloc mfparms = {0};
1368 +int mailfrom_size(arg) char *arg;
1370 + long r;
1371 + unsigned long sizebytes = 0;
1373 + scan_ulong(arg,&r);
1374 + sizebytes = r;
1375 + if (databytes) if (sizebytes > databytes) return 1;
1376 + return 0;
1379 +void mailfrom_auth(arg,len)
1380 +char *arg;
1381 +int len;
1383 + int j;
1385 + if (!stralloc_copys(&fuser,"")) die_nomem();
1386 + if (case_starts(arg,"<>")) { if (!stralloc_cats(&fuser,"unknown")) die_nomem(); }
1387 + else
1388 + while (len) {
1389 + if (*arg == '+') {
1390 + if (case_starts(arg,"+3D")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"=")) die_nomem(); }
1391 + if (case_starts(arg,"+2B")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"+")) die_nomem(); }
1393 + else
1394 + if (!stralloc_catb(&fuser,arg,1)) die_nomem();
1395 + arg++; len--;
1397 + if(!stralloc_0(&fuser)) die_nomem();
1398 + if (!remoteinfo) {
1399 + remoteinfo = fuser.s;
1400 + if (!env_unset("TCPREMOTEINFO")) die_read();
1401 + if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem();
1405 +void mailfrom_parms(arg) char *arg;
1407 + int i;
1408 + int len;
1410 + len = str_len(arg);
1411 + if (!stralloc_copys(&mfparms,"")) die_nomem;
1412 + i = byte_chr(arg,len,'>');
1413 + if (i > 4 && i < len) {
1414 + while (len) {
1415 + arg++; len--;
1416 + if (*arg == ' ' || *arg == '\0' ) {
1417 + if (case_starts(mfparms.s,"SIZE=")) if (mailfrom_size(mfparms.s+5)) { flagsize = 1; return; }
1418 + if (case_starts(mfparms.s,"AUTH=")) mailfrom_auth(mfparms.s+5,mfparms.len-5);
1419 + if (!stralloc_copys(&mfparms,"")) die_nomem;
1421 + else
1422 + if (!stralloc_catb(&mfparms,arg,1)) die_nomem;
1427 void smtp_helo(arg) char *arg;
1429 smtp_greet("250 "); out("\r\n");
1430 seenmail = 0; dohelo(arg);
1432 +/* ESMTP extensions are published here */
1433 void smtp_ehlo(arg) char *arg;
1435 - smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
1436 +#ifdef TLS
1437 + struct stat st;
1438 +#endif
1439 + char size[FMT_ULONG];
1440 + smtp_greet("250-");
1441 +#ifdef TLS
1442 + if (!ssl && (stat("control/servercert.pem",&st) == 0))
1443 + out("\r\n250-STARTTLS");
1444 +#endif
1445 + size[fmt_ulong(size,(unsigned int) databytes)] = 0;
1446 + out("\r\n250-PIPELINING\r\n250-8BITMIME\r\n");
1447 + out("250-SIZE "); out(size); out("\r\n");
1448 +#ifdef CRAM_MD5
1449 + out("250 AUTH LOGIN PLAIN CRAM-MD5\r\n");
1450 +#else
1451 + out("250 AUTH LOGIN PLAIN\r\n");
1452 +#endif
1453 seenmail = 0; dohelo(arg);
1455 void smtp_rset(arg) char *arg;
1456 @@ -240,6 +368,9 @@
1457 void smtp_mail(arg) char *arg;
1459 if (!addrparse(arg)) { err_syntax(); return; }
1460 + flagsize = 0;
1461 + mailfrom_parms(arg);
1462 + if (flagsize) { err_size(); return; }
1463 flagbarf = bmfcheck();
1464 seenmail = 1;
1465 if (!stralloc_copys(&rcptto,"")) die_nomem();
1466 @@ -269,6 +400,11 @@
1468 int r;
1469 flush();
1470 +#ifdef TLS
1471 + if (ssl && fd == ssl_rfd)
1472 + r = ssl_timeoutread(timeout, ssl_rfd, ssl_wfd, ssl, buf, len);
1473 + else
1474 +#endif
1475 r = timeoutread(timeout,fd,buf,len);
1476 if (r == -1) if (errno == error_timeout) die_alarm();
1477 if (r <= 0) die_read();
1478 @@ -378,7 +514,7 @@
1479 qp = qmail_qp(&qqt);
1480 out("354 go ahead\r\n");
1482 - received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo);
1483 + received(&qqt,protocol,local,remoteip,remotehost,remoteinfo,fakehelo);
1484 blast(&hops);
1485 hops = (hops >= MAXHOPS);
1486 if (hops) qmail_fail(&qqt);
1487 @@ -388,28 +524,494 @@
1488 qqx = qmail_close(&qqt);
1489 if (!*qqx) { acceptmessage(qp); return; }
1490 if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; }
1491 - if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; }
1492 + if (databytes) if (!bytestooverflow) { err_size(); return; }
1493 if (*qqx == 'D') out("554 "); else out("451 ");
1494 out(qqx + 1);
1495 out("\r\n");
1498 +/* this file is too long ----------------------------------------- SMTP AUTH */
1500 +char unique[FMT_ULONG + FMT_ULONG + 3];
1501 +static stralloc authin = {0}; /* input from SMTP client */
1502 +static stralloc user = {0}; /* authorization user-id */
1503 +static stralloc pass = {0}; /* plain passwd or digest */
1504 +static stralloc resp = {0}; /* b64 response */
1505 +#ifdef CRAM_MD5
1506 +static stralloc chal = {0}; /* plain challenge */
1507 +static stralloc slop = {0}; /* b64 challenge */
1508 +#endif
1510 +int flagauth = 0;
1511 +char **childargs;
1512 +char ssauthbuf[512];
1513 +substdio ssauth = SUBSTDIO_FDBUF(safewrite,3,ssauthbuf,sizeof(ssauthbuf));
1515 +int authgetl(void) {
1516 + int i;
1518 + if (!stralloc_copys(&authin,"")) die_nomem();
1519 + for (;;) {
1520 + if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */
1521 + i = substdio_get(&ssin,authin.s + authin.len,1);
1522 + if (i != 1) die_read();
1523 + if (authin.s[authin.len] == '\n') break;
1524 + ++authin.len;
1527 + if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len;
1528 + authin.s[authin.len] = 0;
1529 + if (*authin.s == '*' && *(authin.s + 1) == 0) { return err_authabrt(); }
1530 + if (authin.len == 0) { return err_input(); }
1531 + return authin.len;
1534 +int authenticate(void)
1536 + int child;
1537 + int wstat;
1538 + int pi[2];
1540 + if (!stralloc_0(&user)) die_nomem();
1541 + if (!stralloc_0(&pass)) die_nomem();
1542 +#ifdef CRAM_MD5
1543 + if (!stralloc_0(&chal)) die_nomem();
1544 +#endif
1546 + if (pipe(pi) == -1) return err_pipe();
1547 + switch(child = fork()) {
1548 + case -1:
1549 + return err_fork();
1550 + case 0:
1551 + close(pi[1]);
1552 + if(fd_copy(3,pi[0]) == -1) return err_pipe();
1553 + sig_pipedefault();
1554 + execvp(*childargs, childargs);
1555 + _exit(1);
1557 + close(pi[0]);
1559 + substdio_fdbuf(&ssauth,write,pi[1],ssauthbuf,sizeof ssauthbuf);
1560 + if (substdio_put(&ssauth,user.s,user.len) == -1) return err_write();
1561 + if (substdio_put(&ssauth,pass.s,pass.len) == -1) return err_write();
1562 +#ifdef CRAM_MD5
1563 + if (substdio_put(&ssauth,chal.s,chal.len) == -1) return err_write();
1564 +#endif
1565 + if (substdio_flush(&ssauth) == -1) return err_write();
1567 + close(pi[1]);
1568 +#ifdef CRAM_MD5
1569 + if (!stralloc_copys(&chal,"")) die_nomem();
1570 + if (!stralloc_copys(&slop,"")) die_nomem();
1571 +#endif
1572 + byte_zero(ssauthbuf,sizeof ssauthbuf);
1573 + if (wait_pid(&wstat,child) == -1) return err_child();
1574 + if (wait_crashed(wstat)) return err_child();
1575 + if (wait_exitcode(wstat)) { sleep(AUTHSLEEP); return 1; } /* no */
1576 + return 0; /* yes */
1579 +int auth_login(arg) char *arg;
1581 + int r;
1583 + if (*arg) {
1584 + if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input();
1586 + else {
1587 + out("334 VXNlcm5hbWU6\r\n"); flush(); /* Username: */
1588 + if (authgetl() < 0) return -1;
1589 + if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input();
1591 + if (r == -1) die_nomem();
1593 + out("334 UGFzc3dvcmQ6\r\n"); flush(); /* Password: */
1595 + if (authgetl() < 0) return -1;
1596 + if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input();
1597 + if (r == -1) die_nomem();
1599 + if (!user.len || !pass.len) return err_input();
1600 + return authenticate();
1603 +int auth_plain(arg) char *arg;
1605 + int r, id = 0;
1607 + if (*arg) {
1608 + if (r = b64decode(arg,str_len(arg),&resp) == 1) return err_input();
1610 + else {
1611 + out("334 \r\n"); flush();
1612 + if (authgetl() < 0) return -1;
1613 + if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input();
1615 + if (r == -1 || !stralloc_0(&resp)) die_nomem();
1616 + while (resp.s[id]) id++; /* "authorize-id\0userid\0passwd\0" */
1618 + if (resp.len > id + 1)
1619 + if (!stralloc_copys(&user,resp.s + id + 1)) die_nomem();
1620 + if (resp.len > id + user.len + 2)
1621 + if (!stralloc_copys(&pass,resp.s + id + user.len + 2)) die_nomem();
1623 + if (!user.len || !pass.len) return err_input();
1624 + return authenticate();
1627 +#ifdef CRAM_MD5
1628 +int auth_cram()
1630 + int i, r;
1631 + char *s;
1633 + s = unique; /* generate challenge */
1634 + s += fmt_uint(s,getpid());
1635 + *s++ = '.';
1636 + s += fmt_ulong(s,(unsigned long) now());
1637 + *s++ = '@';
1638 + *s++ = 0;
1639 + if (!stralloc_copys(&chal,"<")) die_nomem();
1640 + if (!stralloc_cats(&chal,unique)) die_nomem();
1641 + if (!stralloc_cats(&chal,local)) die_nomem();
1642 + if (!stralloc_cats(&chal,">")) die_nomem();
1643 + if (b64encode(&chal,&slop) < 0) die_nomem();
1644 + if (!stralloc_0(&slop)) die_nomem();
1646 + out("334 "); /* "334 base64_challenge \r\n" */
1647 + out(slop.s);
1648 + out("\r\n");
1649 + flush();
1651 + if (authgetl() < 0) return -1; /* got response */
1652 + if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input();
1653 + if (r == -1 || !stralloc_0(&resp)) die_nomem();
1655 + i = str_chr(resp.s,' ');
1656 + s = resp.s + i;
1657 + while (*s == ' ') ++s;
1658 + resp.s[i] = 0;
1659 + if (!stralloc_copys(&user,resp.s)) die_nomem(); /* userid */
1660 + if (!stralloc_copys(&pass,s)) die_nomem(); /* digest */
1662 + if (!user.len || !pass.len) return err_input();
1663 + return authenticate();
1665 +#endif
1667 +struct authcmd {
1668 + char *text;
1669 + int (*fun)();
1670 +} authcmds[] = {
1671 + { "login",auth_login }
1672 +, { "plain",auth_plain }
1673 +#ifdef CRAM_MD5
1674 +, { "cram-md5",auth_cram }
1675 +#endif
1676 +, { 0,err_noauth }
1679 +void smtp_auth(arg)
1680 +char *arg;
1682 + int i;
1683 + char *cmd = arg;
1685 + if (!*childargs) { out("503 auth not available (#5.3.3)\r\n"); return; }
1686 + if (flagauth) { err_authd(); return; }
1687 + if (seenmail) { err_authmail(); return; }
1689 + if (!stralloc_copys(&user,"")) die_nomem();
1690 + if (!stralloc_copys(&pass,"")) die_nomem();
1691 + if (!stralloc_copys(&resp,"")) die_nomem();
1692 +#ifdef CRAM_MD5
1693 + if (!stralloc_copys(&chal,"")) die_nomem();
1694 +#endif
1696 + i = str_chr(cmd,' ');
1697 + arg = cmd + i;
1698 + while (*arg == ' ') ++arg;
1699 + cmd[i] = 0;
1701 + for (i = 0;authcmds[i].text;++i)
1702 + if (case_equals(authcmds[i].text,cmd)) break;
1704 + switch (authcmds[i].fun(arg)) {
1705 + case 0:
1706 + flagauth = 1;
1707 + protocol = "ESMTPA";
1708 + relayclient = "";
1709 + remoteinfo = user.s;
1710 + if (!env_unset("TCPREMOTEINFO")) die_read();
1711 + if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem();
1712 + if (!env_put2("RELAYCLIENT",relayclient)) die_nomem();
1713 + out("235 ok, go ahead (#2.0.0)\r\n");
1714 + break;
1715 + case 1:
1716 + err_authfail(user.s,authcmds[i].text);
1721 +/* this file is too long --------------------------------------------- GO ON */
1723 +#ifdef TLS
1724 +stralloc proto = {0};
1725 +int ssl_verified = 0;
1726 +const char *ssl_verify_err = 0;
1728 +void smtp_tls(char *arg)
1730 + if (ssl) err_unimpl();
1731 + else if (*arg) out("501 Syntax error (no parameters allowed) (#5.5.4)\r\n");
1732 + else tls_init();
1735 +RSA *tmp_rsa_cb(SSL *ssl, int export, int keylen)
1737 + if (!export) keylen = 512;
1738 + if (keylen == 512) {
1739 + FILE *in = fopen("control/rsa512.pem", "r");
1740 + if (in) {
1741 + RSA *rsa = PEM_read_RSAPrivateKey(in, NULL, NULL, NULL);
1742 + fclose(in);
1743 + if (rsa) return rsa;
1746 + return RSA_generate_key(keylen, RSA_F4, NULL, NULL);
1749 +DH *tmp_dh_cb(SSL *ssl, int export, int keylen)
1751 + if (!export) keylen = 1024;
1752 + if (keylen == 512) {
1753 + FILE *in = fopen("control/dh512.pem", "r");
1754 + if (in) {
1755 + DH *dh = PEM_read_DHparams(in, NULL, NULL, NULL);
1756 + fclose(in);
1757 + if (dh) return dh;
1760 + if (keylen == 1024) {
1761 + FILE *in = fopen("control/dh1024.pem", "r");
1762 + if (in) {
1763 + DH *dh = PEM_read_DHparams(in, NULL, NULL, NULL);
1764 + fclose(in);
1765 + if (dh) return dh;
1768 + return DH_generate_parameters(keylen, DH_GENERATOR_2, NULL, NULL);
1771 +/* don't want to fail handshake if cert isn't verifiable */
1772 +int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; }
1774 +void tls_nogateway()
1776 + /* there may be cases when relayclient is set */
1777 + if (!ssl || relayclient) return;
1778 + out("; no valid cert for gatewaying");
1779 + if (ssl_verify_err) { out(": "); out(ssl_verify_err); }
1781 +void tls_out(const char *s1, const char *s2)
1783 + out("454 TLS "); out(s1);
1784 + if (s2) { out(": "); out(s2); }
1785 + out(" (#4.3.0)\r\n"); flush();
1787 +void tls_err(const char *s) { tls_out(s, ssl_error()); if (smtps) die_read(); }
1789 +# define CLIENTCA "control/clientca.pem"
1790 +# define CLIENTCRL "control/clientcrl.pem"
1791 +# define SERVERCERT "control/servercert.pem"
1793 +int tls_verify()
1795 + stralloc clients = {0};
1796 + struct constmap mapclients;
1798 + if (!ssl || relayclient || ssl_verified) return 0;
1799 + ssl_verified = 1; /* don't do this twice */
1801 + /* request client cert to see if it can be verified by one of our CAs
1802 + * and the associated email address matches an entry in tlsclients */
1803 + switch (control_readfile(&clients, "control/tlsclients", 0))
1805 + case 1:
1806 + if (constmap_init(&mapclients, clients.s, clients.len, 0)) {
1807 + /* if CLIENTCA contains all the standard root certificates, a
1808 + * 0.9.6b client might fail with SSL_R_EXCESSIVE_MESSAGE_SIZE;
1809 + * it is probably due to 0.9.6b supporting only 8k key exchange
1810 + * data while the 0.9.6c release increases that limit to 100k */
1811 + STACK_OF(X509_NAME) *sk = SSL_load_client_CA_file(CLIENTCA);
1812 + if (sk) {
1813 + SSL_set_client_CA_list(ssl, sk);
1814 + SSL_set_verify(ssl, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, NULL);
1815 + break;
1817 + constmap_free(&mapclients);
1819 + case 0: alloc_free(clients.s); return 0;
1820 + case -1: die_control();
1823 + if (ssl_timeoutrehandshake(timeout, ssl_rfd, ssl_wfd, ssl) <= 0) {
1824 + const char *err = ssl_error_str();
1825 + tls_out("rehandshake failed", err); die_read();
1828 + do { /* one iteration */
1829 + X509 *peercert;
1830 + X509_NAME *subj;
1831 + stralloc email = {0};
1833 + int n = SSL_get_verify_result(ssl);
1834 + if (n != X509_V_OK)
1835 + { ssl_verify_err = X509_verify_cert_error_string(n); break; }
1836 + peercert = SSL_get_peer_certificate(ssl);
1837 + if (!peercert) break;
1839 + subj = X509_get_subject_name(peercert);
1840 + n = X509_NAME_get_index_by_NID(subj, NID_pkcs9_emailAddress, -1);
1841 + if (n >= 0) {
1842 + const ASN1_STRING *s = X509_NAME_get_entry(subj, n)->value;
1843 + if (s) { email.len = s->length; email.s = s->data; }
1846 + if (email.len <= 0)
1847 + ssl_verify_err = "contains no email address";
1848 + else if (!constmap(&mapclients, email.s, email.len))
1849 + ssl_verify_err = "email address not in my list of tlsclients";
1850 + else {
1851 + /* add the cert email to the proto if it helped allow relaying */
1852 + --proto.len;
1853 + if (!stralloc_cats(&proto, "\n (cert ") /* continuation line */
1854 + || !stralloc_catb(&proto, email.s, email.len)
1855 + || !stralloc_cats(&proto, ")")
1856 + || !stralloc_0(&proto)) die_nomem();
1857 + relayclient = "";
1858 + protocol = proto.s;
1861 + X509_free(peercert);
1862 + } while (0);
1863 + constmap_free(&mapclients); alloc_free(clients.s);
1865 + /* we are not going to need this anymore: free the memory */
1866 + SSL_set_client_CA_list(ssl, NULL);
1867 + SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL);
1869 + return relayclient ? 1 : 0;
1872 +void tls_init()
1874 + SSL *myssl;
1875 + SSL_CTX *ctx;
1876 + const char *ciphers;
1877 + stralloc saciphers = {0};
1878 + X509_STORE *store;
1879 + X509_LOOKUP *lookup;
1881 + SSL_library_init();
1883 + /* a new SSL context with the bare minimum of options */
1884 + ctx = SSL_CTX_new(SSLv23_server_method());
1885 + if (!ctx) { tls_err("unable to initialize ctx"); return; }
1887 + if (!SSL_CTX_use_certificate_chain_file(ctx, SERVERCERT))
1888 + { SSL_CTX_free(ctx); tls_err("missing certificate"); return; }
1889 + SSL_CTX_load_verify_locations(ctx, CLIENTCA, NULL);
1891 +#if OPENSSL_VERSION_NUMBER >= 0x00907000L
1892 + /* crl checking */
1893 + store = SSL_CTX_get_cert_store(ctx);
1894 + if ((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) &&
1895 + (X509_load_crl_file(lookup, CLIENTCRL, X509_FILETYPE_PEM) == 1))
1896 + X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK |
1897 + X509_V_FLAG_CRL_CHECK_ALL);
1898 +#endif
1900 + /* set the callback here; SSL_set_verify didn't work before 0.9.6c */
1901 + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_cb);
1903 + /* a new SSL object, with the rest added to it directly to avoid copying */
1904 + myssl = SSL_new(ctx);
1905 + SSL_CTX_free(ctx);
1906 + if (!myssl) { tls_err("unable to initialize ssl"); return; }
1908 + /* this will also check whether public and private keys match */
1909 + if (!SSL_use_RSAPrivateKey_file(myssl, SERVERCERT, SSL_FILETYPE_PEM))
1910 + { SSL_free(myssl); tls_err("no valid RSA private key"); return; }
1912 + ciphers = env_get("TLSCIPHERS");
1913 + if (!ciphers) {
1914 + if (control_readfile(&saciphers, "control/tlsserverciphers", 0) == -1)
1915 + { SSL_free(myssl); die_control(); }
1916 + if (saciphers.len) { /* convert all '\0's except the last one to ':' */
1917 + int i;
1918 + for (i = 0; i < saciphers.len - 1; ++i)
1919 + if (!saciphers.s[i]) saciphers.s[i] = ':';
1920 + ciphers = saciphers.s;
1923 + if (!ciphers || !*ciphers) ciphers = "DEFAULT";
1924 + SSL_set_cipher_list(myssl, ciphers);
1925 + alloc_free(saciphers.s);
1927 + SSL_set_tmp_rsa_callback(myssl, tmp_rsa_cb);
1928 + SSL_set_tmp_dh_callback(myssl, tmp_dh_cb);
1929 + SSL_set_rfd(myssl, ssl_rfd = substdio_fileno(&ssin));
1930 + SSL_set_wfd(myssl, ssl_wfd = substdio_fileno(&ssout));
1932 + if (!smtps) { out("220 ready for tls\r\n"); flush(); }
1934 + if (ssl_timeoutaccept(timeout, ssl_rfd, ssl_wfd, myssl) <= 0) {
1935 + /* neither cleartext nor any other response here is part of a standard */
1936 + const char *err = ssl_error_str();
1937 + ssl_free(myssl); tls_out("connection failed", err); die_read();
1939 + ssl = myssl;
1941 + /* populate the protocol string, used in Received */
1942 + if (!stralloc_copys(&proto, "ESMTPS (")
1943 + || !stralloc_cats(&proto, SSL_get_cipher(ssl))
1944 + || !stralloc_cats(&proto, " encrypted)")) die_nomem();
1945 + if (!stralloc_0(&proto)) die_nomem();
1946 + protocol = proto.s;
1948 + /* have to discard the pre-STARTTLS HELO/EHLO argument, if any */
1949 + dohelo(remotehost);
1952 +# undef SERVERCERT
1953 +# undef CLIENTCA
1955 +#endif
1957 struct commands smtpcommands[] = {
1958 { "rcpt", smtp_rcpt, 0 }
1959 , { "mail", smtp_mail, 0 }
1960 , { "data", smtp_data, flush }
1961 +, { "auth", smtp_auth, flush }
1962 , { "quit", smtp_quit, flush }
1963 , { "helo", smtp_helo, flush }
1964 , { "ehlo", smtp_ehlo, flush }
1965 , { "rset", smtp_rset, 0 }
1966 , { "help", smtp_help, flush }
1967 +#ifdef TLS
1968 +, { "starttls", smtp_tls, flush }
1969 +#endif
1970 , { "noop", err_noop, flush }
1971 , { "vrfy", err_vrfy, flush }
1972 , { 0, err_unimpl, flush }
1975 -void main()
1976 +void main(argc,argv)
1977 +int argc;
1978 +char **argv;
1980 + childargs = argv + 1;
1981 sig_pipeignore();
1982 if (chdir(auto_qmail) == -1) die_control();
1983 setup();
1984 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/README.auth ./README.auth
1985 --- ../../netqmail-1.05-orig/netqmail-1.05/README.auth 1969-12-31 18:00:00.000000000 -0600
1986 +++ ./README.auth 2007-04-17 17:44:12.590975584 -0500
1987 @@ -0,0 +1,67 @@
1988 +README qmail-smtpd SMTP Authentication
1989 +======================================
1992 +History:
1993 +--------
1995 +This patch is based on Krzysztof Dabrowski's qmail-smtpd-auth-0.31 patch
1996 +which itself uses "Mrs. Brisby's" initial code.
1997 +Version 0.41 of this patch fixes the "CAPS-LOCK" typo announcing
1998 +'CRAM_MD5' instead of 'CRAM-MD5' (german keyboard) - tx to Mike Garrison.
1999 +Version 0.42 fixes the '421 unable to read controls (#4.3.0)' problem
2000 +(can't read control/morercpthosts.cdb) because FD 3 was already closed - tx Richard Lyons.
2001 +Version 0.43 fixes the ba64decode() failure in case CRAM_MD5 is not enabled - tx Vladimir Zidar.
2002 +Version 0.51 includes the evaluation of the 'Auth' and the 'Size' parameter in the 'Mail From:' command.
2003 +Version 0.52 uses DJB functions to copy FDs.
2004 +Version 0.56 corrects some minor mistakes displaying the 'Auth' userid.
2005 +Version 0.57 uses keyword "ESMTPA" in Received header in case of authentication to comply with RFC 3848.
2006 +Version 0.58 fixes a potential problem with cc -O2 optimization within base64.c - tx John Simpson.
2009 +Scope:
2010 +------
2012 +This patch supports RFC 2554 "SMTP Service Extension for Authentication" for qmail-smtpd.
2013 +Additionally, RFC 1870 is honoured ("SMTP Service Extension for Message Size Declaration").
2014 +For more technical details see: http://www.fehcom.de/qmail/docu/smtpauth.html.
2017 +Installation:
2018 +-------------
2020 +* Untar the source in the qmail-1.03 home direcotry.
2021 +* Run ./install_auth.
2022 +* Modify the compile time option "#define CRAM_MD5" to your needs.
2023 +* Re-make qmail.
2026 +Setup:
2027 +------
2029 +In order to use SMTP Authentication you have to use a 'Pluggable Authentication Module'
2030 +PAM to be called by qmail-smtpd; typically
2032 + /var/qmail/bin/qmail-smtpd /bin/checkpassword true 2>&1
2034 +Since qmail-smtpd does not run as root, checkpassword has to be made sticky.
2035 +There is no need to include additionally the hostname in the call.
2036 +In order to compute the CRAM-MD5 challenge, qmail-smtpd uses the 'tcplocalhost' information.
2039 +Changes wrt. Krysztof Dabrowski's patch:
2040 +----------------------------------------
2042 +* Avoid the 'hostname' in the call of the PAM.
2043 +* Confirm to Dan Bernstein's checkpassword interface even for CRAM-MD5.
2044 +* Doesn't close FD 2; thus not inhibiting logging to STDERR.
2045 +* Fixed bugs in base64.c.
2046 +* Modified unconditional close of FD 3 in order to sustain reading of 'control/morecpthosts.cdb'.
2047 +* Evaluation of the (informational) Mail From: < > Auth=username.
2048 +* Additional support for the advertised "Size" via 'Mail From: <return-path> SIZE=123456780' (RFC 1870).
2049 +* RFC 3848 conformance for Received header in case of SMTP Auth.
2052 +Erwin Hoffmann - Cologne 2006-12-28 (www.fehcom.de)
2055 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/ssl_timeoutio.c ./ssl_timeoutio.c
2056 --- ../../netqmail-1.05-orig/netqmail-1.05/ssl_timeoutio.c 1969-12-31 18:00:00.000000000 -0600
2057 +++ ./ssl_timeoutio.c 2007-04-17 17:44:27.669683272 -0500
2058 @@ -0,0 +1,95 @@
2059 +#include "select.h"
2060 +#include "error.h"
2061 +#include "ndelay.h"
2062 +#include "now.h"
2063 +#include "ssl_timeoutio.h"
2065 +int ssl_timeoutio(int (*fun)(),
2066 + int t, int rfd, int wfd, SSL *ssl, char *buf, int len)
2068 + int n;
2069 + const datetime_sec end = (datetime_sec)t + now();
2071 + do {
2072 + fd_set fds;
2073 + struct timeval tv;
2075 + const int r = buf ? fun(ssl, buf, len) : fun(ssl);
2076 + if (r > 0) return r;
2078 + t = end - now();
2079 + if (t < 0) break;
2080 + tv.tv_sec = (time_t)t; tv.tv_usec = 0;
2082 + FD_ZERO(&fds);
2083 + switch (SSL_get_error(ssl, r))
2085 + default: return r; /* some other error */
2086 + case SSL_ERROR_WANT_READ:
2087 + FD_SET(rfd, &fds); n = select(rfd + 1, &fds, NULL, NULL, &tv);
2088 + break;
2089 + case SSL_ERROR_WANT_WRITE:
2090 + FD_SET(wfd, &fds); n = select(wfd + 1, NULL, &fds, NULL, &tv);
2091 + break;
2094 + /* n is the number of descriptors that changed status */
2095 + } while (n > 0);
2097 + if (n != -1) errno = error_timeout;
2098 + return -1;
2101 +int ssl_timeoutaccept(int t, int rfd, int wfd, SSL *ssl)
2103 + int r;
2105 + /* if connection is established, keep NDELAY */
2106 + if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1;
2107 + r = ssl_timeoutio(SSL_accept, t, rfd, wfd, ssl, NULL, 0);
2109 + if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); }
2110 + else SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
2112 + return r;
2115 +int ssl_timeoutconn(int t, int rfd, int wfd, SSL *ssl)
2117 + int r;
2119 + /* if connection is established, keep NDELAY */
2120 + if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1;
2121 + r = ssl_timeoutio(SSL_connect, t, rfd, wfd, ssl, NULL, 0);
2123 + if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); }
2124 + else SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
2126 + return r;
2129 +int ssl_timeoutrehandshake(int t, int rfd, int wfd, SSL *ssl)
2131 + int r;
2133 + SSL_renegotiate(ssl);
2134 + r = ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0);
2135 + if (r <= 0 || ssl->type == SSL_ST_CONNECT) return r;
2137 + /* this is for the server only */
2138 + ssl->state = SSL_ST_ACCEPT;
2139 + return ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0);
2142 +int ssl_timeoutread(int t, int rfd, int wfd, SSL *ssl, char *buf, int len)
2144 + if (!buf) return 0;
2145 + if (SSL_pending(ssl)) return SSL_read(ssl, buf, len);
2146 + return ssl_timeoutio(SSL_read, t, rfd, wfd, ssl, buf, len);
2149 +int ssl_timeoutwrite(int t, int rfd, int wfd, SSL *ssl, char *buf, int len)
2151 + if (!buf) return 0;
2152 + return ssl_timeoutio(SSL_write, t, rfd, wfd, ssl, buf, len);
2154 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/ssl_timeoutio.h ./ssl_timeoutio.h
2155 --- ../../netqmail-1.05-orig/netqmail-1.05/ssl_timeoutio.h 1969-12-31 18:00:00.000000000 -0600
2156 +++ ./ssl_timeoutio.h 2007-04-17 17:44:27.670683120 -0500
2157 @@ -0,0 +1,21 @@
2158 +#ifndef SSL_TIMEOUTIO_H
2159 +#define SSL_TIMEOUTIO_H
2161 +#include <openssl/ssl.h>
2163 +/* the version is like this: 0xMNNFFPPS: major minor fix patch status */
2164 +#if OPENSSL_VERSION_NUMBER < 0x00906000L
2165 +# error "Need OpenSSL version at least 0.9.6"
2166 +#endif
2168 +int ssl_timeoutconn(int t, int rfd, int wfd, SSL *ssl);
2169 +int ssl_timeoutaccept(int t, int rfd, int wfd, SSL *ssl);
2170 +int ssl_timeoutrehandshake(int t, int rfd, int wfd, SSL *ssl);
2172 +int ssl_timeoutread(int t, int rfd, int wfd, SSL *ssl, char *buf, int len);
2173 +int ssl_timeoutwrite(int t, int rfd, int wfd, SSL *ssl, char *buf, int len);
2175 +int ssl_timeoutio(
2176 + int (*fun)(), int t, int rfd, int wfd, SSL *ssl, char *buf, int len);
2178 +#endif
2179 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/TARGETS ./TARGETS
2180 --- ../../netqmail-1.05-orig/netqmail-1.05/TARGETS 1998-06-15 05:53:16.000000000 -0500
2181 +++ ./TARGETS 2007-04-17 17:44:12.593975128 -0500
2182 @@ -10,6 +10,7 @@
2183 qmail.o
2184 quote.o
2185 now.o
2186 +base64.o
2187 gfrom.o
2188 myctime.o
2189 slurpclose.o
2190 @@ -168,6 +169,8 @@
2191 constmap.o
2192 timeoutread.o
2193 timeoutwrite.o
2194 +tls.o
2195 +ssl_timeoutio.o
2196 timeoutconn.o
2197 tcpto.o
2198 dns.o
2199 @@ -320,6 +323,7 @@
2200 binm2+df
2201 binm3
2202 binm3+df
2203 +Makefile-cert
2205 qmail-local.0
2206 qmail-lspawn.0
2207 @@ -385,3 +389,4 @@
2209 setup
2210 check
2211 +update_tmprsadh
2212 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/tls.c ./tls.c
2213 --- ../../netqmail-1.05-orig/netqmail-1.05/tls.c 1969-12-31 18:00:00.000000000 -0600
2214 +++ ./tls.c 2007-04-17 17:44:12.593975128 -0500
2215 @@ -0,0 +1,25 @@
2216 +#include "exit.h"
2217 +#include "error.h"
2218 +#include <openssl/ssl.h>
2219 +#include <openssl/err.h>
2221 +int smtps = 0;
2222 +SSL *ssl = NULL;
2224 +void ssl_free(SSL *myssl) { SSL_shutdown(myssl); SSL_free(myssl); }
2225 +void ssl_exit(int status) { if (ssl) ssl_free(ssl); _exit(status); }
2227 +const char *ssl_error()
2229 + int r = ERR_get_error();
2230 + if (!r) return NULL;
2231 + SSL_load_error_strings();
2232 + return ERR_error_string(r, NULL);
2234 +const char *ssl_error_str()
2236 + const char *err = ssl_error();
2237 + if (err) return err;
2238 + if (!errno) return 0;
2239 + return (errno == error_timeout) ? "timed out" : error_str(errno);
2241 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/tls.h ./tls.h
2242 --- ../../netqmail-1.05-orig/netqmail-1.05/tls.h 1969-12-31 18:00:00.000000000 -0600
2243 +++ ./tls.h 2007-04-17 17:44:12.594974976 -0500
2244 @@ -0,0 +1,16 @@
2245 +#ifndef TLS_H
2246 +#define TLS_H
2248 +#include <openssl/ssl.h>
2250 +extern int smtps;
2251 +extern SSL *ssl;
2253 +void ssl_free(SSL *myssl);
2254 +void ssl_exit(int status);
2255 +# define _exit ssl_exit
2257 +const char *ssl_error();
2258 +const char *ssl_error_str();
2260 +#endif
2261 diff -urN ../../netqmail-1.05-orig/netqmail-1.05/update_tmprsadh.sh ./update_tmprsadh.sh
2262 --- ../../netqmail-1.05-orig/netqmail-1.05/update_tmprsadh.sh 1969-12-31 18:00:00.000000000 -0600
2263 +++ ./update_tmprsadh.sh 2007-04-17 17:44:12.595974824 -0500
2264 @@ -0,0 +1,25 @@
2265 +#!/bin/sh
2267 +# Update temporary RSA and DH keys
2268 +# Frederik Vermeulen 2004-05-31 GPL
2270 +umask 0077 || exit 0
2272 +export PATH="$PATH:/usr/local/bin/ssl:/usr/sbin"
2274 +openssl genrsa -out QMAIL/control/rsa512.new 512 &&
2275 +chmod 600 QMAIL/control/rsa512.new &&
2276 +chown UGQMAILD QMAIL/control/rsa512.new &&
2277 +mv -f QMAIL/control/rsa512.new QMAIL/control/rsa512.pem
2278 +echo
2280 +openssl dhparam -2 -out QMAIL/control/dh512.new 512 &&
2281 +chmod 600 QMAIL/control/dh512.new &&
2282 +chown UGQMAILD QMAIL/control/dh512.new &&
2283 +mv -f QMAIL/control/dh512.new QMAIL/control/dh512.pem
2284 +echo
2286 +openssl dhparam -2 -out QMAIL/control/dh1024.new 1024 &&
2287 +chmod 600 QMAIL/control/dh1024.new &&
2288 +chown UGQMAILD QMAIL/control/dh1024.new &&
2289 +mv -f QMAIL/control/dh1024.new QMAIL/control/dh1024.pem