Lines 23-36
Link Here
|
23 |
#include "timeoutread.h" |
23 |
#include "timeoutread.h" |
24 |
#include "timeoutwrite.h" |
24 |
#include "timeoutwrite.h" |
25 |
#include "commands.h" |
25 |
#include "commands.h" |
|
|
26 |
#include "wait.h" |
27 |
|
28 |
#define CRAM_MD5 |
29 |
#define AUTHSLEEP 5 |
26 |
|
30 |
|
27 |
#define MAXHOPS 100 |
31 |
#define MAXHOPS 100 |
28 |
unsigned int databytes = 0; |
32 |
unsigned int databytes = 0; |
29 |
int timeout = 1200; |
33 |
int timeout = 1200; |
30 |
|
34 |
|
|
|
35 |
#ifdef TLS |
36 |
#include <sys/stat.h> |
37 |
#include "tls.h" |
38 |
#include "ssl_timeoutio.h" |
39 |
|
40 |
void tls_init(); |
41 |
int tls_verify(); |
42 |
void tls_nogateway(); |
43 |
int ssl_rfd = -1, ssl_wfd = -1; /* SSL_get_Xfd() are broken */ |
44 |
#endif |
45 |
|
31 |
int safewrite(fd,buf,len) int fd; char *buf; int len; |
46 |
int safewrite(fd,buf,len) int fd; char *buf; int len; |
32 |
{ |
47 |
{ |
33 |
int r; |
48 |
int r; |
|
|
49 |
#ifdef TLS |
50 |
if (ssl && fd == ssl_wfd) |
51 |
r = ssl_timeoutwrite(timeout, ssl_rfd, ssl_wfd, ssl, buf, len); |
52 |
else |
53 |
#endif |
34 |
r = timeoutwrite(timeout,fd,buf,len); |
54 |
r = timeoutwrite(timeout,fd,buf,len); |
35 |
if (r <= 0) _exit(1); |
55 |
if (r <= 0) _exit(1); |
36 |
return r; |
56 |
return r; |
Lines 49-56
Link Here
|
49 |
void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); } |
69 |
void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); } |
50 |
void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); } |
70 |
void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); } |
51 |
|
71 |
|
|
|
72 |
void err_size() { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); } |
52 |
void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); } |
73 |
void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); } |
|
|
74 |
#ifndef TLS |
53 |
void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } |
75 |
void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } |
|
|
76 |
#else |
77 |
void err_nogateway() |
78 |
{ |
79 |
out("553 sorry, that domain isn't in my list of allowed rcpthosts"); |
80 |
tls_nogateway(); |
81 |
out(" (#5.7.1)\r\n"); |
82 |
} |
83 |
#endif |
54 |
void err_unimpl(arg) char *arg; { out("502 unimplemented (#5.5.1)\r\n"); } |
84 |
void err_unimpl(arg) char *arg; { out("502 unimplemented (#5.5.1)\r\n"); } |
55 |
void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); } |
85 |
void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); } |
56 |
void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); } |
86 |
void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); } |
Lines 59-64
Link Here
|
59 |
void err_vrfy(arg) char *arg; { out("252 send some mail, i'll try my best\r\n"); } |
89 |
void err_vrfy(arg) char *arg; { out("252 send some mail, i'll try my best\r\n"); } |
60 |
void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); } |
90 |
void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); } |
61 |
|
91 |
|
|
|
92 |
int err_child() { out("454 oops, problem with child and I can't auth (#4.3.0)\r\n"); return -1; } |
93 |
int err_fork() { out("454 oops, child won't start and I can't auth (#4.3.0)\r\n"); return -1; } |
94 |
int err_pipe() { out("454 oops, unable to open pipe and I can't auth (#4.3.0)\r\n"); return -1; } |
95 |
int err_write() { out("454 oops, unable to write pipe and I can't auth (#4.3.0)\r\n"); return -1; } |
96 |
void err_authd() { out("503 you're already authenticated (#5.5.0)\r\n"); } |
97 |
void err_authmail() { out("503 no auth during mail transaction (#5.5.0)\r\n"); } |
98 |
int err_noauth() { out("504 auth type unimplemented (#5.5.1)\r\n"); return -1; } |
99 |
int err_authabrt() { out("501 auth exchange canceled (#5.0.0)\r\n"); return -1; } |
100 |
int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; } |
101 |
void err_authfail() { out("535 authentication failed (#5.7.1)\r\n"); } |
62 |
|
102 |
|
63 |
stralloc greeting = {0}; |
103 |
stralloc greeting = {0}; |
64 |
|
104 |
|
Lines 76-81
Link Here
|
76 |
smtp_greet("221 "); out("\r\n"); flush(); _exit(0); |
116 |
smtp_greet("221 "); out("\r\n"); flush(); _exit(0); |
77 |
} |
117 |
} |
78 |
|
118 |
|
|
|
119 |
char *protocol; |
79 |
char *remoteip; |
120 |
char *remoteip; |
80 |
char *remotehost; |
121 |
char *remotehost; |
81 |
char *remoteinfo; |
122 |
char *remoteinfo; |
Lines 109-115
Link Here
|
109 |
if (liphostok == -1) die_control(); |
150 |
if (liphostok == -1) die_control(); |
110 |
if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control(); |
151 |
if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control(); |
111 |
if (timeout <= 0) timeout = 1; |
152 |
if (timeout <= 0) timeout = 1; |
112 |
|
|
|
113 |
if (rcpthosts_init() == -1) die_control(); |
153 |
if (rcpthosts_init() == -1) die_control(); |
114 |
|
154 |
|
115 |
bmfok = control_readfile(&bmf,"control/badmailfrom",0); |
155 |
bmfok = control_readfile(&bmf,"control/badmailfrom",0); |
Lines 122-127
Link Here
|
122 |
if (x) { scan_ulong(x,&u); databytes = u; } |
162 |
if (x) { scan_ulong(x,&u); databytes = u; } |
123 |
if (!(databytes + 1)) --databytes; |
163 |
if (!(databytes + 1)) --databytes; |
124 |
|
164 |
|
|
|
165 |
protocol = "SMTP"; |
125 |
remoteip = env_get("TCPREMOTEIP"); |
166 |
remoteip = env_get("TCPREMOTEIP"); |
126 |
if (!remoteip) remoteip = "unknown"; |
167 |
if (!remoteip) remoteip = "unknown"; |
127 |
local = env_get("TCPLOCALHOST"); |
168 |
local = env_get("TCPLOCALHOST"); |
Lines 131-136
Link Here
|
131 |
if (!remotehost) remotehost = "unknown"; |
172 |
if (!remotehost) remotehost = "unknown"; |
132 |
remoteinfo = env_get("TCPREMOTEINFO"); |
173 |
remoteinfo = env_get("TCPREMOTEINFO"); |
133 |
relayclient = env_get("RELAYCLIENT"); |
174 |
relayclient = env_get("RELAYCLIENT"); |
|
|
175 |
|
176 |
#ifdef TLS |
177 |
if (env_get("SMTPS")) { smtps = 1; tls_init(); } |
178 |
else |
179 |
#endif |
134 |
dohelo(remotehost); |
180 |
dohelo(remotehost); |
135 |
} |
181 |
} |
136 |
|
182 |
|
Lines 213-235
Link Here
|
213 |
int r; |
259 |
int r; |
214 |
r = rcpthosts(addr.s,str_len(addr.s)); |
260 |
r = rcpthosts(addr.s,str_len(addr.s)); |
215 |
if (r == -1) die_control(); |
261 |
if (r == -1) die_control(); |
|
|
262 |
#ifdef TLS |
263 |
if (r == 0) if (tls_verify()) r = -2; |
264 |
#endif |
216 |
return r; |
265 |
return r; |
217 |
} |
266 |
} |
218 |
|
267 |
|
219 |
|
268 |
|
220 |
int seenmail = 0; |
269 |
int seenmail = 0; |
221 |
int flagbarf; /* defined if seenmail */ |
270 |
int flagbarf; /* defined if seenmail */ |
|
|
271 |
int flagsize; |
222 |
stralloc mailfrom = {0}; |
272 |
stralloc mailfrom = {0}; |
223 |
stralloc rcptto = {0}; |
273 |
stralloc rcptto = {0}; |
|
|
274 |
stralloc fuser = {0}; |
275 |
stralloc mfparms = {0}; |
276 |
|
277 |
int mailfrom_size(arg) char *arg; |
278 |
{ |
279 |
long r; |
280 |
unsigned long sizebytes = 0; |
281 |
|
282 |
scan_ulong(arg,&r); |
283 |
sizebytes = r; |
284 |
if (databytes) if (sizebytes > databytes) return 1; |
285 |
return 0; |
286 |
} |
287 |
|
288 |
void mailfrom_auth(arg,len) |
289 |
char *arg; |
290 |
int len; |
291 |
{ |
292 |
int j; |
293 |
|
294 |
if (!stralloc_copys(&fuser,"")) die_nomem(); |
295 |
if (case_starts(arg,"<>")) { if (!stralloc_cats(&fuser,"unknown")) die_nomem(); } |
296 |
else |
297 |
while (len) { |
298 |
if (*arg == '+') { |
299 |
if (case_starts(arg,"+3D")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"=")) die_nomem(); } |
300 |
if (case_starts(arg,"+2B")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"+")) die_nomem(); } |
301 |
} |
302 |
else |
303 |
if (!stralloc_catb(&fuser,arg,1)) die_nomem(); |
304 |
arg++; len--; |
305 |
} |
306 |
if(!stralloc_0(&fuser)) die_nomem(); |
307 |
if (!remoteinfo) { |
308 |
remoteinfo = fuser.s; |
309 |
if (!env_unset("TCPREMOTEINFO")) die_read(); |
310 |
if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem(); |
311 |
} |
312 |
} |
313 |
|
314 |
void mailfrom_parms(arg) char *arg; |
315 |
{ |
316 |
int i; |
317 |
int len; |
318 |
|
319 |
len = str_len(arg); |
320 |
if (!stralloc_copys(&mfparms,"")) die_nomem; |
321 |
i = byte_chr(arg,len,'>'); |
322 |
if (i > 4 && i < len) { |
323 |
while (len) { |
324 |
arg++; len--; |
325 |
if (*arg == ' ' || *arg == '\0' ) { |
326 |
if (case_starts(mfparms.s,"SIZE=")) if (mailfrom_size(mfparms.s+5)) { flagsize = 1; return; } |
327 |
if (case_starts(mfparms.s,"AUTH=")) mailfrom_auth(mfparms.s+5,mfparms.len-5); |
328 |
if (!stralloc_copys(&mfparms,"")) die_nomem; |
329 |
} |
330 |
else |
331 |
if (!stralloc_catb(&mfparms,arg,1)) die_nomem; |
332 |
} |
333 |
} |
334 |
} |
224 |
|
335 |
|
225 |
void smtp_helo(arg) char *arg; |
336 |
void smtp_helo(arg) char *arg; |
226 |
{ |
337 |
{ |
227 |
smtp_greet("250 "); out("\r\n"); |
338 |
smtp_greet("250 "); out("\r\n"); |
228 |
seenmail = 0; dohelo(arg); |
339 |
seenmail = 0; dohelo(arg); |
229 |
} |
340 |
} |
|
|
341 |
/* ESMTP extensions are published here */ |
230 |
void smtp_ehlo(arg) char *arg; |
342 |
void smtp_ehlo(arg) char *arg; |
231 |
{ |
343 |
{ |
232 |
smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); |
344 |
#ifdef TLS |
|
|
345 |
struct stat st; |
346 |
#endif |
347 |
char size[FMT_ULONG]; |
348 |
smtp_greet("250-"); |
349 |
#ifdef TLS |
350 |
if (!ssl && (stat("control/servercert.pem",&st) == 0)) |
351 |
out("\r\n250-STARTTLS"); |
352 |
#endif |
353 |
size[fmt_ulong(size,(unsigned int) databytes)] = 0; |
354 |
out("\r\n250-PIPELINING\r\n250-8BITMIME\r\n"); |
355 |
out("250-SIZE "); out(size); out("\r\n"); |
356 |
#ifdef CRAM_MD5 |
357 |
out("250 AUTH LOGIN PLAIN CRAM-MD5\r\n"); |
358 |
#else |
359 |
out("250 AUTH LOGIN PLAIN\r\n"); |
360 |
#endif |
233 |
seenmail = 0; dohelo(arg); |
361 |
seenmail = 0; dohelo(arg); |
234 |
} |
362 |
} |
235 |
void smtp_rset(arg) char *arg; |
363 |
void smtp_rset(arg) char *arg; |
Lines 240-245
Link Here
|
240 |
void smtp_mail(arg) char *arg; |
368 |
void smtp_mail(arg) char *arg; |
241 |
{ |
369 |
{ |
242 |
if (!addrparse(arg)) { err_syntax(); return; } |
370 |
if (!addrparse(arg)) { err_syntax(); return; } |
|
|
371 |
flagsize = 0; |
372 |
mailfrom_parms(arg); |
373 |
if (flagsize) { err_size(); return; } |
243 |
flagbarf = bmfcheck(); |
374 |
flagbarf = bmfcheck(); |
244 |
seenmail = 1; |
375 |
seenmail = 1; |
245 |
if (!stralloc_copys(&rcptto,"")) die_nomem(); |
376 |
if (!stralloc_copys(&rcptto,"")) die_nomem(); |
Lines 269-274
Link Here
|
269 |
{ |
400 |
{ |
270 |
int r; |
401 |
int r; |
271 |
flush(); |
402 |
flush(); |
|
|
403 |
#ifdef TLS |
404 |
if (ssl && fd == ssl_rfd) |
405 |
r = ssl_timeoutread(timeout, ssl_rfd, ssl_wfd, ssl, buf, len); |
406 |
else |
407 |
#endif |
272 |
r = timeoutread(timeout,fd,buf,len); |
408 |
r = timeoutread(timeout,fd,buf,len); |
273 |
if (r == -1) if (errno == error_timeout) die_alarm(); |
409 |
if (r == -1) if (errno == error_timeout) die_alarm(); |
274 |
if (r <= 0) die_read(); |
410 |
if (r <= 0) die_read(); |
Lines 378-384
Link Here
|
378 |
qp = qmail_qp(&qqt); |
514 |
qp = qmail_qp(&qqt); |
379 |
out("354 go ahead\r\n"); |
515 |
out("354 go ahead\r\n"); |
380 |
|
516 |
|
381 |
received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); |
517 |
received(&qqt,protocol,local,remoteip,remotehost,remoteinfo,fakehelo); |
382 |
blast(&hops); |
518 |
blast(&hops); |
383 |
hops = (hops >= MAXHOPS); |
519 |
hops = (hops >= MAXHOPS); |
384 |
if (hops) qmail_fail(&qqt); |
520 |
if (hops) qmail_fail(&qqt); |
Lines 388-415
Link Here
|
388 |
qqx = qmail_close(&qqt); |
524 |
qqx = qmail_close(&qqt); |
389 |
if (!*qqx) { acceptmessage(qp); return; } |
525 |
if (!*qqx) { acceptmessage(qp); return; } |
390 |
if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; } |
526 |
if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; } |
391 |
if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; } |
527 |
if (databytes) if (!bytestooverflow) { err_size(); return; } |
392 |
if (*qqx == 'D') out("554 "); else out("451 "); |
528 |
if (*qqx == 'D') out("554 "); else out("451 "); |
393 |
out(qqx + 1); |
529 |
out(qqx + 1); |
394 |
out("\r\n"); |
530 |
out("\r\n"); |
395 |
} |
531 |
} |
396 |
|
532 |
|
|
|
533 |
/* this file is too long ----------------------------------------- SMTP AUTH */ |
534 |
|
535 |
char unique[FMT_ULONG + FMT_ULONG + 3]; |
536 |
static stralloc authin = {0}; /* input from SMTP client */ |
537 |
static stralloc user = {0}; /* authorization user-id */ |
538 |
static stralloc pass = {0}; /* plain passwd or digest */ |
539 |
static stralloc resp = {0}; /* b64 response */ |
540 |
#ifdef CRAM_MD5 |
541 |
static stralloc chal = {0}; /* plain challenge */ |
542 |
static stralloc slop = {0}; /* b64 challenge */ |
543 |
#endif |
544 |
|
545 |
int flagauth = 0; |
546 |
char **childargs; |
547 |
char ssauthbuf[512]; |
548 |
substdio ssauth = SUBSTDIO_FDBUF(safewrite,3,ssauthbuf,sizeof(ssauthbuf)); |
549 |
|
550 |
int authgetl(void) { |
551 |
int i; |
552 |
|
553 |
if (!stralloc_copys(&authin,"")) die_nomem(); |
554 |
for (;;) { |
555 |
if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */ |
556 |
i = substdio_get(&ssin,authin.s + authin.len,1); |
557 |
if (i != 1) die_read(); |
558 |
if (authin.s[authin.len] == '\n') break; |
559 |
++authin.len; |
560 |
} |
561 |
|
562 |
if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len; |
563 |
authin.s[authin.len] = 0; |
564 |
if (*authin.s == '*' && *(authin.s + 1) == 0) { return err_authabrt(); } |
565 |
if (authin.len == 0) { return err_input(); } |
566 |
return authin.len; |
567 |
} |
568 |
|
569 |
int authenticate(void) |
570 |
{ |
571 |
int child; |
572 |
int wstat; |
573 |
int pi[2]; |
574 |
|
575 |
if (!stralloc_0(&user)) die_nomem(); |
576 |
if (!stralloc_0(&pass)) die_nomem(); |
577 |
#ifdef CRAM_MD5 |
578 |
if (!stralloc_0(&chal)) die_nomem(); |
579 |
#endif |
580 |
|
581 |
if (pipe(pi) == -1) return err_pipe(); |
582 |
switch(child = fork()) { |
583 |
case -1: |
584 |
return err_fork(); |
585 |
case 0: |
586 |
close(pi[1]); |
587 |
if(fd_copy(3,pi[0]) == -1) return err_pipe(); |
588 |
sig_pipedefault(); |
589 |
execvp(*childargs, childargs); |
590 |
_exit(1); |
591 |
} |
592 |
close(pi[0]); |
593 |
|
594 |
substdio_fdbuf(&ssauth,write,pi[1],ssauthbuf,sizeof ssauthbuf); |
595 |
if (substdio_put(&ssauth,user.s,user.len) == -1) return err_write(); |
596 |
if (substdio_put(&ssauth,pass.s,pass.len) == -1) return err_write(); |
597 |
#ifdef CRAM_MD5 |
598 |
if (substdio_put(&ssauth,chal.s,chal.len) == -1) return err_write(); |
599 |
#endif |
600 |
if (substdio_flush(&ssauth) == -1) return err_write(); |
601 |
|
602 |
close(pi[1]); |
603 |
#ifdef CRAM_MD5 |
604 |
if (!stralloc_copys(&chal,"")) die_nomem(); |
605 |
if (!stralloc_copys(&slop,"")) die_nomem(); |
606 |
#endif |
607 |
byte_zero(ssauthbuf,sizeof ssauthbuf); |
608 |
if (wait_pid(&wstat,child) == -1) return err_child(); |
609 |
if (wait_crashed(wstat)) return err_child(); |
610 |
if (wait_exitcode(wstat)) { sleep(AUTHSLEEP); return 1; } /* no */ |
611 |
return 0; /* yes */ |
612 |
} |
613 |
|
614 |
int auth_login(arg) char *arg; |
615 |
{ |
616 |
int r; |
617 |
|
618 |
if (*arg) { |
619 |
if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input(); |
620 |
} |
621 |
else { |
622 |
out("334 VXNlcm5hbWU6\r\n"); flush(); /* Username: */ |
623 |
if (authgetl() < 0) return -1; |
624 |
if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input(); |
625 |
} |
626 |
if (r == -1) die_nomem(); |
627 |
|
628 |
out("334 UGFzc3dvcmQ6\r\n"); flush(); /* Password: */ |
629 |
|
630 |
if (authgetl() < 0) return -1; |
631 |
if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input(); |
632 |
if (r == -1) die_nomem(); |
633 |
|
634 |
if (!user.len || !pass.len) return err_input(); |
635 |
return authenticate(); |
636 |
} |
637 |
|
638 |
int auth_plain(arg) char *arg; |
639 |
{ |
640 |
int r, id = 0; |
641 |
|
642 |
if (*arg) { |
643 |
if (r = b64decode(arg,str_len(arg),&resp) == 1) return err_input(); |
644 |
} |
645 |
else { |
646 |
out("334 \r\n"); flush(); |
647 |
if (authgetl() < 0) return -1; |
648 |
if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input(); |
649 |
} |
650 |
if (r == -1 || !stralloc_0(&resp)) die_nomem(); |
651 |
while (resp.s[id]) id++; /* "authorize-id\0userid\0passwd\0" */ |
652 |
|
653 |
if (resp.len > id + 1) |
654 |
if (!stralloc_copys(&user,resp.s + id + 1)) die_nomem(); |
655 |
if (resp.len > id + user.len + 2) |
656 |
if (!stralloc_copys(&pass,resp.s + id + user.len + 2)) die_nomem(); |
657 |
|
658 |
if (!user.len || !pass.len) return err_input(); |
659 |
return authenticate(); |
660 |
} |
661 |
|
662 |
#ifdef CRAM_MD5 |
663 |
int auth_cram() |
664 |
{ |
665 |
int i, r; |
666 |
char *s; |
667 |
|
668 |
s = unique; /* generate challenge */ |
669 |
s += fmt_uint(s,getpid()); |
670 |
*s++ = '.'; |
671 |
s += fmt_ulong(s,(unsigned long) now()); |
672 |
*s++ = '@'; |
673 |
*s++ = 0; |
674 |
if (!stralloc_copys(&chal,"<")) die_nomem(); |
675 |
if (!stralloc_cats(&chal,unique)) die_nomem(); |
676 |
if (!stralloc_cats(&chal,local)) die_nomem(); |
677 |
if (!stralloc_cats(&chal,">")) die_nomem(); |
678 |
if (b64encode(&chal,&slop) < 0) die_nomem(); |
679 |
if (!stralloc_0(&slop)) die_nomem(); |
680 |
|
681 |
out("334 "); /* "334 base64_challenge \r\n" */ |
682 |
out(slop.s); |
683 |
out("\r\n"); |
684 |
flush(); |
685 |
|
686 |
if (authgetl() < 0) return -1; /* got response */ |
687 |
if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input(); |
688 |
if (r == -1 || !stralloc_0(&resp)) die_nomem(); |
689 |
|
690 |
i = str_chr(resp.s,' '); |
691 |
s = resp.s + i; |
692 |
while (*s == ' ') ++s; |
693 |
resp.s[i] = 0; |
694 |
if (!stralloc_copys(&user,resp.s)) die_nomem(); /* userid */ |
695 |
if (!stralloc_copys(&pass,s)) die_nomem(); /* digest */ |
696 |
|
697 |
if (!user.len || !pass.len) return err_input(); |
698 |
return authenticate(); |
699 |
} |
700 |
#endif |
701 |
|
702 |
struct authcmd { |
703 |
char *text; |
704 |
int (*fun)(); |
705 |
} authcmds[] = { |
706 |
{ "login",auth_login } |
707 |
, { "plain",auth_plain } |
708 |
#ifdef CRAM_MD5 |
709 |
, { "cram-md5",auth_cram } |
710 |
#endif |
711 |
, { 0,err_noauth } |
712 |
}; |
713 |
|
714 |
void smtp_auth(arg) |
715 |
char *arg; |
716 |
{ |
717 |
int i; |
718 |
char *cmd = arg; |
719 |
|
720 |
if (!*childargs) { out("503 auth not available (#5.3.3)\r\n"); return; } |
721 |
if (flagauth) { err_authd(); return; } |
722 |
if (seenmail) { err_authmail(); return; } |
723 |
|
724 |
if (!stralloc_copys(&user,"")) die_nomem(); |
725 |
if (!stralloc_copys(&pass,"")) die_nomem(); |
726 |
if (!stralloc_copys(&resp,"")) die_nomem(); |
727 |
#ifdef CRAM_MD5 |
728 |
if (!stralloc_copys(&chal,"")) die_nomem(); |
729 |
#endif |
730 |
|
731 |
i = str_chr(cmd,' '); |
732 |
arg = cmd + i; |
733 |
while (*arg == ' ') ++arg; |
734 |
cmd[i] = 0; |
735 |
|
736 |
for (i = 0;authcmds[i].text;++i) |
737 |
if (case_equals(authcmds[i].text,cmd)) break; |
738 |
|
739 |
switch (authcmds[i].fun(arg)) { |
740 |
case 0: |
741 |
flagauth = 1; |
742 |
protocol = "ESMTPA"; |
743 |
relayclient = ""; |
744 |
remoteinfo = user.s; |
745 |
if (!env_unset("TCPREMOTEINFO")) die_read(); |
746 |
if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem(); |
747 |
if (!env_put2("RELAYCLIENT",relayclient)) die_nomem(); |
748 |
out("235 ok, go ahead (#2.0.0)\r\n"); |
749 |
break; |
750 |
case 1: |
751 |
err_authfail(user.s,authcmds[i].text); |
752 |
} |
753 |
} |
754 |
|
755 |
|
756 |
/* this file is too long --------------------------------------------- GO ON */ |
757 |
|
758 |
#ifdef TLS |
759 |
stralloc proto = {0}; |
760 |
int ssl_verified = 0; |
761 |
const char *ssl_verify_err = 0; |
762 |
|
763 |
void smtp_tls(char *arg) |
764 |
{ |
765 |
if (ssl) err_unimpl(); |
766 |
else if (*arg) out("501 Syntax error (no parameters allowed) (#5.5.4)\r\n"); |
767 |
else tls_init(); |
768 |
} |
769 |
|
770 |
RSA *tmp_rsa_cb(SSL *ssl, int export, int keylen) |
771 |
{ |
772 |
if (!export) keylen = 512; |
773 |
if (keylen == 512) { |
774 |
FILE *in = fopen("control/rsa512.pem", "r"); |
775 |
if (in) { |
776 |
RSA *rsa = PEM_read_RSAPrivateKey(in, NULL, NULL, NULL); |
777 |
fclose(in); |
778 |
if (rsa) return rsa; |
779 |
} |
780 |
} |
781 |
return RSA_generate_key(keylen, RSA_F4, NULL, NULL); |
782 |
} |
783 |
|
784 |
DH *tmp_dh_cb(SSL *ssl, int export, int keylen) |
785 |
{ |
786 |
if (!export) keylen = 1024; |
787 |
if (keylen == 512) { |
788 |
FILE *in = fopen("control/dh512.pem", "r"); |
789 |
if (in) { |
790 |
DH *dh = PEM_read_DHparams(in, NULL, NULL, NULL); |
791 |
fclose(in); |
792 |
if (dh) return dh; |
793 |
} |
794 |
} |
795 |
if (keylen == 1024) { |
796 |
FILE *in = fopen("control/dh1024.pem", "r"); |
797 |
if (in) { |
798 |
DH *dh = PEM_read_DHparams(in, NULL, NULL, NULL); |
799 |
fclose(in); |
800 |
if (dh) return dh; |
801 |
} |
802 |
} |
803 |
return DH_generate_parameters(keylen, DH_GENERATOR_2, NULL, NULL); |
804 |
} |
805 |
|
806 |
/* don't want to fail handshake if cert isn't verifiable */ |
807 |
int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; } |
808 |
|
809 |
void tls_nogateway() |
810 |
{ |
811 |
/* there may be cases when relayclient is set */ |
812 |
if (!ssl || relayclient) return; |
813 |
out("; no valid cert for gatewaying"); |
814 |
if (ssl_verify_err) { out(": "); out(ssl_verify_err); } |
815 |
} |
816 |
void tls_out(const char *s1, const char *s2) |
817 |
{ |
818 |
out("454 TLS "); out(s1); |
819 |
if (s2) { out(": "); out(s2); } |
820 |
out(" (#4.3.0)\r\n"); flush(); |
821 |
} |
822 |
void tls_err(const char *s) { tls_out(s, ssl_error()); if (smtps) die_read(); } |
823 |
|
824 |
# define CLIENTCA "control/clientca.pem" |
825 |
# define CLIENTCRL "control/clientcrl.pem" |
826 |
# define SERVERCERT "control/servercert.pem" |
827 |
|
828 |
int tls_verify() |
829 |
{ |
830 |
stralloc clients = {0}; |
831 |
struct constmap mapclients; |
832 |
|
833 |
if (!ssl || relayclient || ssl_verified) return 0; |
834 |
ssl_verified = 1; /* don't do this twice */ |
835 |
|
836 |
/* request client cert to see if it can be verified by one of our CAs |
837 |
* and the associated email address matches an entry in tlsclients */ |
838 |
switch (control_readfile(&clients, "control/tlsclients", 0)) |
839 |
{ |
840 |
case 1: |
841 |
if (constmap_init(&mapclients, clients.s, clients.len, 0)) { |
842 |
/* if CLIENTCA contains all the standard root certificates, a |
843 |
* 0.9.6b client might fail with SSL_R_EXCESSIVE_MESSAGE_SIZE; |
844 |
* it is probably due to 0.9.6b supporting only 8k key exchange |
845 |
* data while the 0.9.6c release increases that limit to 100k */ |
846 |
STACK_OF(X509_NAME) *sk = SSL_load_client_CA_file(CLIENTCA); |
847 |
if (sk) { |
848 |
SSL_set_client_CA_list(ssl, sk); |
849 |
SSL_set_verify(ssl, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, NULL); |
850 |
break; |
851 |
} |
852 |
constmap_free(&mapclients); |
853 |
} |
854 |
case 0: alloc_free(clients.s); return 0; |
855 |
case -1: die_control(); |
856 |
} |
857 |
|
858 |
if (ssl_timeoutrehandshake(timeout, ssl_rfd, ssl_wfd, ssl) <= 0) { |
859 |
const char *err = ssl_error_str(); |
860 |
tls_out("rehandshake failed", err); die_read(); |
861 |
} |
862 |
|
863 |
do { /* one iteration */ |
864 |
X509 *peercert; |
865 |
X509_NAME *subj; |
866 |
stralloc email = {0}; |
867 |
|
868 |
int n = SSL_get_verify_result(ssl); |
869 |
if (n != X509_V_OK) |
870 |
{ ssl_verify_err = X509_verify_cert_error_string(n); break; } |
871 |
peercert = SSL_get_peer_certificate(ssl); |
872 |
if (!peercert) break; |
873 |
|
874 |
subj = X509_get_subject_name(peercert); |
875 |
n = X509_NAME_get_index_by_NID(subj, NID_pkcs9_emailAddress, -1); |
876 |
if (n >= 0) { |
877 |
const ASN1_STRING *s = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subj, n)); |
878 |
if (s) { email.len = s->length; email.s = s->data; } |
879 |
} |
880 |
|
881 |
if (email.len <= 0) |
882 |
ssl_verify_err = "contains no email address"; |
883 |
else if (!constmap(&mapclients, email.s, email.len)) |
884 |
ssl_verify_err = "email address not in my list of tlsclients"; |
885 |
else { |
886 |
/* add the cert email to the proto if it helped allow relaying */ |
887 |
--proto.len; |
888 |
if (!stralloc_cats(&proto, "\n (cert ") /* continuation line */ |
889 |
|| !stralloc_catb(&proto, email.s, email.len) |
890 |
|| !stralloc_cats(&proto, ")") |
891 |
|| !stralloc_0(&proto)) die_nomem(); |
892 |
relayclient = ""; |
893 |
protocol = proto.s; |
894 |
} |
895 |
|
896 |
X509_free(peercert); |
897 |
} while (0); |
898 |
constmap_free(&mapclients); alloc_free(clients.s); |
899 |
|
900 |
/* we are not going to need this anymore: free the memory */ |
901 |
SSL_set_client_CA_list(ssl, NULL); |
902 |
SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); |
903 |
|
904 |
return relayclient ? 1 : 0; |
905 |
} |
906 |
|
907 |
void tls_init() |
908 |
{ |
909 |
SSL *myssl; |
910 |
SSL_CTX *ctx; |
911 |
const char *ciphers; |
912 |
stralloc saciphers = {0}; |
913 |
X509_STORE *store; |
914 |
X509_LOOKUP *lookup; |
915 |
|
916 |
SSL_library_init(); |
917 |
|
918 |
/* a new SSL context with the bare minimum of options */ |
919 |
ctx = SSL_CTX_new(SSLv23_server_method()); |
920 |
if (!ctx) { tls_err("unable to initialize ctx"); return; } |
921 |
|
922 |
if (!SSL_CTX_use_certificate_chain_file(ctx, SERVERCERT)) |
923 |
{ SSL_CTX_free(ctx); tls_err("missing certificate"); return; } |
924 |
SSL_CTX_load_verify_locations(ctx, CLIENTCA, NULL); |
925 |
|
926 |
#if OPENSSL_VERSION_NUMBER >= 0x00907000L |
927 |
/* crl checking */ |
928 |
store = SSL_CTX_get_cert_store(ctx); |
929 |
if ((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) && |
930 |
(X509_load_crl_file(lookup, CLIENTCRL, X509_FILETYPE_PEM) == 1)) |
931 |
X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | |
932 |
X509_V_FLAG_CRL_CHECK_ALL); |
933 |
#endif |
934 |
|
935 |
/* set the callback here; SSL_set_verify didn't work before 0.9.6c */ |
936 |
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_cb); |
937 |
|
938 |
/* a new SSL object, with the rest added to it directly to avoid copying */ |
939 |
myssl = SSL_new(ctx); |
940 |
SSL_CTX_free(ctx); |
941 |
if (!myssl) { tls_err("unable to initialize ssl"); return; } |
942 |
|
943 |
/* this will also check whether public and private keys match */ |
944 |
if (!SSL_use_RSAPrivateKey_file(myssl, SERVERCERT, SSL_FILETYPE_PEM)) |
945 |
{ SSL_free(myssl); tls_err("no valid RSA private key"); return; } |
946 |
|
947 |
ciphers = env_get("TLSCIPHERS"); |
948 |
if (!ciphers) { |
949 |
if (control_readfile(&saciphers, "control/tlsserverciphers", 0) == -1) |
950 |
{ SSL_free(myssl); die_control(); } |
951 |
if (saciphers.len) { /* convert all '\0's except the last one to ':' */ |
952 |
int i; |
953 |
for (i = 0; i < saciphers.len - 1; ++i) |
954 |
if (!saciphers.s[i]) saciphers.s[i] = ':'; |
955 |
ciphers = saciphers.s; |
956 |
} |
957 |
} |
958 |
if (!ciphers || !*ciphers) ciphers = "DEFAULT"; |
959 |
SSL_set_cipher_list(myssl, ciphers); |
960 |
alloc_free(saciphers.s); |
961 |
|
962 |
SSL_set_tmp_rsa_callback(myssl, tmp_rsa_cb); |
963 |
SSL_set_tmp_dh_callback(myssl, tmp_dh_cb); |
964 |
SSL_set_rfd(myssl, ssl_rfd = substdio_fileno(&ssin)); |
965 |
SSL_set_wfd(myssl, ssl_wfd = substdio_fileno(&ssout)); |
966 |
|
967 |
if (!smtps) { out("220 ready for tls\r\n"); flush(); } |
968 |
|
969 |
if (ssl_timeoutaccept(timeout, ssl_rfd, ssl_wfd, myssl) <= 0) { |
970 |
/* neither cleartext nor any other response here is part of a standard */ |
971 |
const char *err = ssl_error_str(); |
972 |
ssl_free(myssl); tls_out("connection failed", err); die_read(); |
973 |
} |
974 |
ssl = myssl; |
975 |
|
976 |
/* populate the protocol string, used in Received */ |
977 |
if (!stralloc_copys(&proto, "ESMTPS (") |
978 |
|| !stralloc_cats(&proto, SSL_get_cipher(ssl)) |
979 |
|| !stralloc_cats(&proto, " encrypted)")) die_nomem(); |
980 |
if (!stralloc_0(&proto)) die_nomem(); |
981 |
protocol = proto.s; |
982 |
|
983 |
/* have to discard the pre-STARTTLS HELO/EHLO argument, if any */ |
984 |
dohelo(remotehost); |
985 |
} |
986 |
|
987 |
# undef SERVERCERT |
988 |
# undef CLIENTCA |
989 |
|
990 |
#endif |
991 |
|
397 |
struct commands smtpcommands[] = { |
992 |
struct commands smtpcommands[] = { |
398 |
{ "rcpt", smtp_rcpt, 0 } |
993 |
{ "rcpt", smtp_rcpt, 0 } |
399 |
, { "mail", smtp_mail, 0 } |
994 |
, { "mail", smtp_mail, 0 } |
400 |
, { "data", smtp_data, flush } |
995 |
, { "data", smtp_data, flush } |
|
|
996 |
, { "auth", smtp_auth, flush } |
401 |
, { "quit", smtp_quit, flush } |
997 |
, { "quit", smtp_quit, flush } |
402 |
, { "helo", smtp_helo, flush } |
998 |
, { "helo", smtp_helo, flush } |
403 |
, { "ehlo", smtp_ehlo, flush } |
999 |
, { "ehlo", smtp_ehlo, flush } |
404 |
, { "rset", smtp_rset, 0 } |
1000 |
, { "rset", smtp_rset, 0 } |
405 |
, { "help", smtp_help, flush } |
1001 |
, { "help", smtp_help, flush } |
|
|
1002 |
#ifdef TLS |
1003 |
, { "starttls", smtp_tls, flush } |
1004 |
#endif |
406 |
, { "noop", err_noop, flush } |
1005 |
, { "noop", err_noop, flush } |
407 |
, { "vrfy", err_vrfy, flush } |
1006 |
, { "vrfy", err_vrfy, flush } |
408 |
, { 0, err_unimpl, flush } |
1007 |
, { 0, err_unimpl, flush } |
409 |
} ; |
1008 |
} ; |
410 |
|
1009 |
|
411 |
void main() |
1010 |
void main(argc,argv) |
|
|
1011 |
int argc; |
1012 |
char **argv; |
412 |
{ |
1013 |
{ |
|
|
1014 |
childargs = argv + 1; |
413 |
sig_pipeignore(); |
1015 |
sig_pipeignore(); |
414 |
if (chdir(auto_qmail) == -1) die_control(); |
1016 |
if (chdir(auto_qmail) == -1) die_control(); |
415 |
setup(); |
1017 |
setup(); |