diff options
| author | manuel <manuel@mausz.at> | 2013-02-04 02:32:40 +0100 |
|---|---|---|
| committer | manuel <manuel@mausz.at> | 2013-02-04 02:32:40 +0100 |
| commit | 8514473287c9594137c6fbc39f5619672ebc2430 (patch) | |
| tree | a5b965d8c7b60dee396bf8ebe25dd3eddfaa6753 /qmail-smtpd.c | |
| parent | 35ddb916045abafaa4ae2c778b9383059fa06726 (diff) | |
| download | qmail-8514473287c9594137c6fbc39f5619672ebc2430.tar.gz qmail-8514473287c9594137c6fbc39f5619672ebc2430.tar.bz2 qmail-8514473287c9594137c6fbc39f5619672ebc2430.zip | |
[PATCH] qregex-starttls-2way-auth-20060423-mm
Diffstat (limited to 'qmail-smtpd.c')
| -rw-r--r-- | qmail-smtpd.c | 758 |
1 files changed, 739 insertions, 19 deletions
diff --git a/qmail-smtpd.c b/qmail-smtpd.c index 1e28c88..c77c6cc 100644 --- a/qmail-smtpd.c +++ b/qmail-smtpd.c | |||
| @@ -23,14 +23,44 @@ | |||
| 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 | #include "qregex.h" | ||
| 28 | #include "strerr.h" | ||
| 29 | |||
| 30 | #define BMCHECK_BMF 0 | ||
| 31 | #define BMCHECK_BMFNR 1 | ||
| 32 | #define BMCHECK_BMT 2 | ||
| 33 | #define BMCHECK_BMTNR 3 | ||
| 34 | #define BMCHECK_BHELO 4 | ||
| 35 | |||
| 36 | #define CRAM_MD5 | ||
| 37 | #define AUTHSLEEP 5 | ||
| 26 | 38 | ||
| 27 | #define MAXHOPS 100 | 39 | #define MAXHOPS 100 |
| 28 | unsigned int databytes = 0; | 40 | unsigned int databytes = 0; |
| 29 | int timeout = 1200; | 41 | int timeout = 1200; |
| 30 | 42 | ||
| 43 | const char *protocol = "SMTP"; | ||
| 44 | |||
| 45 | #ifdef TLS | ||
| 46 | #include <sys/stat.h> | ||
| 47 | #include "tls.h" | ||
| 48 | #include "ssl_timeoutio.h" | ||
| 49 | |||
| 50 | void tls_init(); | ||
| 51 | int tls_verify(); | ||
| 52 | void tls_nogateway(); | ||
| 53 | int ssl_rfd = -1, ssl_wfd = -1; /* SSL_get_Xfd() are broken */ | ||
| 54 | #endif | ||
| 55 | |||
| 31 | int safewrite(fd,buf,len) int fd; char *buf; int len; | 56 | int safewrite(fd,buf,len) int fd; char *buf; int len; |
| 32 | { | 57 | { |
| 33 | int r; | 58 | int r; |
| 59 | #ifdef TLS | ||
| 60 | if (ssl && fd == ssl_wfd) | ||
| 61 | r = ssl_timeoutwrite(timeout, ssl_rfd, ssl_wfd, ssl, buf, len); | ||
| 62 | else | ||
| 63 | #endif | ||
| 34 | r = timeoutwrite(timeout,fd,buf,len); | 64 | r = timeoutwrite(timeout,fd,buf,len); |
| 35 | if (r <= 0) _exit(1); | 65 | if (r <= 0) _exit(1); |
| 36 | return r; | 66 | return r; |
| @@ -49,8 +79,20 @@ void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _ | |||
| 49 | void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); } | 79 | 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); } | 80 | void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); } |
| 51 | 81 | ||
| 52 | void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); } | 82 | void err_size() { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); } |
| 83 | void err_bmf() { out("553 sorry, your envelope sender has been denied (#5.7.1)\r\n"); } | ||
| 84 | void err_bmt() { out("553 sorry, your envelope recipient has been denied (#5.7.1)\r\n"); } | ||
| 85 | void err_bhelo() { out("553 sorry, your HELO host name has been denied (#5.7.1)\r\n"); } | ||
| 86 | #ifndef TLS | ||
| 53 | void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } | 87 | void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } |
| 88 | #else | ||
| 89 | void err_nogateway() | ||
| 90 | { | ||
| 91 | out("553 sorry, that domain isn't in my list of allowed rcpthosts"); | ||
| 92 | tls_nogateway(); | ||
| 93 | out(" (#5.7.1)\r\n"); | ||
| 94 | } | ||
| 95 | #endif | ||
| 54 | void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); } | 96 | void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); } |
| 55 | void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); } | 97 | 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"); } | 98 | void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); } |
| @@ -59,6 +101,16 @@ void err_noop() { out("250 ok\r\n"); } | |||
| 59 | void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); } | 101 | void err_vrfy() { 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"); } | 102 | void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); } |
| 61 | 103 | ||
| 104 | int err_child() { out("454 oops, problem with child and I can't auth (#4.3.0)\r\n"); return -1; } | ||
| 105 | int err_fork() { out("454 oops, child won't start and I can't auth (#4.3.0)\r\n"); return -1; } | ||
| 106 | int err_pipe() { out("454 oops, unable to open pipe and I can't auth (#4.3.0)\r\n"); return -1; } | ||
| 107 | int err_write() { out("454 oops, unable to write pipe and I can't auth (#4.3.0)\r\n"); return -1; } | ||
| 108 | void err_authd() { out("503 you're already authenticated (#5.5.0)\r\n"); } | ||
| 109 | void err_authmail() { out("503 no auth during mail transaction (#5.5.0)\r\n"); } | ||
| 110 | int err_noauth() { out("504 auth type unimplemented (#5.5.1)\r\n"); return -1; } | ||
| 111 | int err_authabrt() { out("501 auth exchange canceled (#5.0.0)\r\n"); return -1; } | ||
| 112 | int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; } | ||
| 113 | void err_authfail() { out("535 authentication failed (#5.7.1)\r\n"); } | ||
| 62 | 114 | ||
| 63 | stralloc greeting = {0}; | 115 | stralloc greeting = {0}; |
| 64 | 116 | ||
| @@ -93,9 +145,24 @@ void dohelo(arg) char *arg; { | |||
| 93 | 145 | ||
| 94 | int liphostok = 0; | 146 | int liphostok = 0; |
| 95 | stralloc liphost = {0}; | 147 | stralloc liphost = {0}; |
| 148 | |||
| 96 | int bmfok = 0; | 149 | int bmfok = 0; |
| 97 | stralloc bmf = {0}; | 150 | stralloc bmf = {0}; |
| 98 | struct constmap mapbmf; | 151 | |
| 152 | int bmfnrok = 0; | ||
| 153 | stralloc bmfnr = {0}; | ||
| 154 | |||
| 155 | int bmtok = 0; | ||
| 156 | stralloc bmt = {0}; | ||
| 157 | |||
| 158 | int bmtnrok = 0; | ||
| 159 | stralloc bmtnr = {0}; | ||
| 160 | |||
| 161 | int bhelook = 0; | ||
| 162 | stralloc bhelo = {0}; | ||
| 163 | |||
| 164 | int logregex = 0; | ||
| 165 | stralloc matchedregex = {0}; | ||
| 99 | 166 | ||
| 100 | void setup() | 167 | void setup() |
| 101 | { | 168 | { |
| @@ -109,13 +176,25 @@ void setup() | |||
| 109 | if (liphostok == -1) die_control(); | 176 | if (liphostok == -1) die_control(); |
| 110 | if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control(); | 177 | if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control(); |
| 111 | if (timeout <= 0) timeout = 1; | 178 | if (timeout <= 0) timeout = 1; |
| 112 | |||
| 113 | if (rcpthosts_init() == -1) die_control(); | 179 | if (rcpthosts_init() == -1) die_control(); |
| 114 | 180 | ||
| 115 | bmfok = control_readfile(&bmf,"control/badmailfrom",0); | 181 | bmfok = control_readfile(&bmf,"control/badmailfrom",0); |
| 116 | if (bmfok == -1) die_control(); | 182 | if (bmfok == -1) die_control(); |
| 117 | if (bmfok) | 183 | |
| 118 | if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem(); | 184 | bmfnrok = control_readfile(&bmfnr,"control/badmailfromnorelay",0); |
| 185 | if (bmfnrok == -1) die_control(); | ||
| 186 | |||
| 187 | bmtok = control_readfile(&bmt,"control/badmailto",0); | ||
| 188 | if (bmtok == -1) die_control(); | ||
| 189 | |||
| 190 | bmtnrok = control_readfile(&bmtnr,"control/badmailtonorelay",0); | ||
| 191 | if (bmtnrok == -1) die_control(); | ||
| 192 | |||
| 193 | bhelook = control_readfile(&bhelo, "control/badhelo",0); | ||
| 194 | if (bhelook == -1) die_control(); | ||
| 195 | if (env_get("NOBADHELO")) bhelook = 0; | ||
| 196 | |||
| 197 | if (env_get("LOGREGEX")) logregex = 1; | ||
| 119 | 198 | ||
| 120 | if (control_readint(&databytes,"control/databytes") == -1) die_control(); | 199 | if (control_readint(&databytes,"control/databytes") == -1) die_control(); |
| 121 | x = env_get("DATABYTES"); | 200 | x = env_get("DATABYTES"); |
| @@ -131,6 +210,11 @@ void setup() | |||
| 131 | if (!remotehost) remotehost = "unknown"; | 210 | if (!remotehost) remotehost = "unknown"; |
| 132 | remoteinfo = env_get("TCPREMOTEINFO"); | 211 | remoteinfo = env_get("TCPREMOTEINFO"); |
| 133 | relayclient = env_get("RELAYCLIENT"); | 212 | relayclient = env_get("RELAYCLIENT"); |
| 213 | |||
| 214 | #ifdef TLS | ||
| 215 | if (env_get("SMTPS")) { smtps = 1; tls_init(); } | ||
| 216 | else | ||
| 217 | #endif | ||
| 134 | dohelo(remotehost); | 218 | dohelo(remotehost); |
| 135 | } | 219 | } |
| 136 | 220 | ||
| @@ -197,14 +281,56 @@ char *arg; | |||
| 197 | return 1; | 281 | return 1; |
| 198 | } | 282 | } |
| 199 | 283 | ||
| 200 | int bmfcheck() | 284 | int bmcheck(which) int which; |
| 201 | { | 285 | { |
| 202 | int j; | 286 | int i = 0; |
| 203 | if (!bmfok) return 0; | 287 | int j = 0; |
| 204 | if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1; | 288 | int x = 0; |
| 205 | j = byte_rchr(addr.s,addr.len,'@'); | 289 | int negate = 0; |
| 206 | if (j < addr.len) | 290 | static stralloc bmb = {0}; |
| 207 | if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1; | 291 | static stralloc curregex = {0}; |
| 292 | |||
| 293 | if (which == BMCHECK_BMF) { | ||
| 294 | if (!stralloc_copy(&bmb,&bmf)) die_nomem(); | ||
| 295 | } else if (which == BMCHECK_BMFNR) { | ||
| 296 | if (!stralloc_copy(&bmb,&bmfnr)) die_nomem(); | ||
| 297 | } else if (which == BMCHECK_BMT) { | ||
| 298 | if (!stralloc_copy(&bmb,&bmt)) die_nomem(); | ||
| 299 | } else if (which == BMCHECK_BMTNR) { | ||
| 300 | if (!stralloc_copy(&bmb,&bmtnr)) die_nomem(); | ||
| 301 | } else if (which == BMCHECK_BHELO) { | ||
| 302 | if (!stralloc_copy(&bmb,&bhelo)) die_nomem(); | ||
| 303 | } else { | ||
| 304 | die_control(); | ||
| 305 | } | ||
| 306 | |||
| 307 | while (j < bmb.len) { | ||
| 308 | i = j; | ||
| 309 | while ((bmb.s[i] != '\0') && (i < bmb.len)) i++; | ||
| 310 | if (bmb.s[j] == '!') { | ||
| 311 | negate = 1; | ||
| 312 | j++; | ||
| 313 | } | ||
| 314 | if (!stralloc_copyb(&curregex,bmb.s + j,(i - j))) die_nomem(); | ||
| 315 | if (!stralloc_0(&curregex)) die_nomem(); | ||
| 316 | if (which == BMCHECK_BHELO) { | ||
| 317 | x = matchregex(helohost.s, curregex.s); | ||
| 318 | } else { | ||
| 319 | x = matchregex(addr.s, curregex.s); | ||
| 320 | } | ||
| 321 | if ((negate) && (x == 0)) { | ||
| 322 | if (!stralloc_copyb(&matchedregex,bmb.s + j - 1,(i - j + 1))) die_nomem(); | ||
| 323 | if (!stralloc_0(&matchedregex)) die_nomem(); | ||
| 324 | return 1; | ||
| 325 | } | ||
| 326 | if (!(negate) && (x > 0)) { | ||
| 327 | if (!stralloc_copyb(&matchedregex,bmb.s + j,(i - j))) die_nomem(); | ||
| 328 | if (!stralloc_0(&matchedregex)) die_nomem(); | ||
| 329 | return 1; | ||
| 330 | } | ||
| 331 | j = i + 1; | ||
| 332 | negate = 0; | ||
| 333 | } | ||
| 208 | return 0; | 334 | return 0; |
| 209 | } | 335 | } |
| 210 | 336 | ||
| @@ -213,24 +339,110 @@ int addrallowed() | |||
| 213 | int r; | 339 | int r; |
| 214 | r = rcpthosts(addr.s,str_len(addr.s)); | 340 | r = rcpthosts(addr.s,str_len(addr.s)); |
| 215 | if (r == -1) die_control(); | 341 | if (r == -1) die_control(); |
| 342 | #ifdef TLS | ||
| 343 | if (r == 0) if (tls_verify()) r = -2; | ||
| 344 | #endif | ||
| 216 | return r; | 345 | return r; |
| 217 | } | 346 | } |
| 218 | 347 | ||
| 219 | 348 | ||
| 220 | int seenmail = 0; | 349 | int seenmail = 0; |
| 221 | int flagbarf; /* defined if seenmail */ | 350 | int flagbarfbmf; /* defined if seenmail */ |
| 351 | int flagbarfbmt; | ||
| 352 | int flagbarfbhelo; | ||
| 353 | int flagsize; | ||
| 222 | stralloc mailfrom = {0}; | 354 | stralloc mailfrom = {0}; |
| 223 | stralloc rcptto = {0}; | 355 | stralloc rcptto = {0}; |
| 356 | stralloc fuser = {0}; | ||
| 357 | stralloc mfparms = {0}; | ||
| 358 | |||
| 359 | int mailfrom_size(arg) char *arg; | ||
| 360 | { | ||
| 361 | long r; | ||
| 362 | unsigned long sizebytes = 0; | ||
| 363 | |||
| 364 | scan_ulong(arg,&r); | ||
| 365 | sizebytes = r; | ||
| 366 | if (databytes) if (sizebytes > databytes) return 1; | ||
| 367 | return 0; | ||
| 368 | } | ||
| 369 | |||
| 370 | void mailfrom_auth(arg,len) | ||
| 371 | char *arg; | ||
| 372 | int len; | ||
| 373 | { | ||
| 374 | int j; | ||
| 375 | |||
| 376 | if (!stralloc_copys(&fuser,"")) die_nomem(); | ||
| 377 | if (case_starts(arg,"<>")) { if (!stralloc_cats(&fuser,"unknown")) die_nomem(); } | ||
| 378 | else | ||
| 379 | while (len) { | ||
| 380 | if (*arg == '+') { | ||
| 381 | if (case_starts(arg,"+3D")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"=")) die_nomem(); } | ||
| 382 | if (case_starts(arg,"+2B")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"+")) die_nomem(); } | ||
| 383 | } | ||
| 384 | else | ||
| 385 | if (!stralloc_catb(&fuser,arg,1)) die_nomem(); | ||
| 386 | arg++; len--; | ||
| 387 | } | ||
| 388 | if(!stralloc_0(&fuser)) die_nomem(); | ||
| 389 | if (!remoteinfo) { | ||
| 390 | remoteinfo = fuser.s; | ||
| 391 | if (!env_unset("TCPREMOTEINFO")) die_read(); | ||
| 392 | if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem(); | ||
| 393 | } | ||
| 394 | } | ||
| 395 | |||
| 396 | void mailfrom_parms(arg) char *arg; | ||
| 397 | { | ||
| 398 | int i; | ||
| 399 | int len; | ||
| 400 | |||
| 401 | len = str_len(arg); | ||
| 402 | if (!stralloc_copys(&mfparms,"")) die_nomem; | ||
| 403 | i = byte_chr(arg,len,'>'); | ||
| 404 | if (i > 4 && i < len) { | ||
| 405 | while (len) { | ||
| 406 | arg++; len--; | ||
| 407 | if (*arg == ' ' || *arg == '\0' ) { | ||
| 408 | if (case_starts(mfparms.s,"SIZE=")) if (mailfrom_size(mfparms.s+5)) { flagsize = 1; return; } | ||
| 409 | if (case_starts(mfparms.s,"AUTH=")) mailfrom_auth(mfparms.s+5,mfparms.len-5); | ||
| 410 | if (!stralloc_copys(&mfparms,"")) die_nomem; | ||
| 411 | } | ||
| 412 | else | ||
| 413 | if (!stralloc_catb(&mfparms,arg,1)) die_nomem; | ||
| 414 | } | ||
| 415 | } | ||
| 416 | } | ||
| 224 | 417 | ||
| 225 | void smtp_helo(arg) char *arg; | 418 | void smtp_helo(arg) char *arg; |
| 226 | { | 419 | { |
| 227 | smtp_greet("250 "); out("\r\n"); | 420 | smtp_greet("250 "); out("\r\n"); |
| 228 | seenmail = 0; dohelo(arg); | 421 | seenmail = 0; dohelo(arg); |
| 422 | if (bhelook) flagbarfbhelo = bmcheck(BMCHECK_BHELO); | ||
| 229 | } | 423 | } |
| 424 | /* ESMTP extensions are published here */ | ||
| 230 | void smtp_ehlo(arg) char *arg; | 425 | void smtp_ehlo(arg) char *arg; |
| 231 | { | 426 | { |
| 232 | smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); | 427 | #ifdef TLS |
| 428 | struct stat st; | ||
| 429 | #endif | ||
| 430 | char size[FMT_ULONG]; | ||
| 431 | size[fmt_ulong(size,(unsigned int) databytes)] = 0; | ||
| 432 | smtp_greet("250-"); | ||
| 433 | #ifdef TLS | ||
| 434 | if (!ssl && (stat("control/servercert.pem",&st) == 0)) | ||
| 435 | out("\r\n250-STARTTLS"); | ||
| 436 | #endif | ||
| 437 | out("\r\n250-PIPELINING\r\n250-8BITMIME\r\n"); | ||
| 438 | out("250-SIZE "); out(size); out("\r\n"); | ||
| 439 | #ifdef CRAM_MD5 | ||
| 440 | out("250 AUTH LOGIN PLAIN CRAM-MD5\r\n"); | ||
| 441 | #else | ||
| 442 | out("250 AUTH LOGIN PLAIN\r\n"); | ||
| 443 | #endif | ||
| 233 | seenmail = 0; dohelo(arg); | 444 | seenmail = 0; dohelo(arg); |
| 445 | if (bhelook) flagbarfbhelo = bmcheck(BMCHECK_BHELO); | ||
| 234 | } | 446 | } |
| 235 | void smtp_rset() | 447 | void smtp_rset() |
| 236 | { | 448 | { |
| @@ -240,7 +452,14 @@ void smtp_rset() | |||
| 240 | void smtp_mail(arg) char *arg; | 452 | void smtp_mail(arg) char *arg; |
| 241 | { | 453 | { |
| 242 | if (!addrparse(arg)) { err_syntax(); return; } | 454 | if (!addrparse(arg)) { err_syntax(); return; } |
| 243 | flagbarf = bmfcheck(); | 455 | flagsize = 0; |
| 456 | mailfrom_parms(arg); | ||
| 457 | if (flagsize) { err_size(); return; } | ||
| 458 | flagbarfbmf = 0; /* bmcheck is skipped for empty envelope senders */ | ||
| 459 | if ((bmfok) && (addr.len != 1)) flagbarfbmf = bmcheck(BMCHECK_BMF); | ||
| 460 | if ((!flagbarfbmf) && (bmfnrok) && (addr.len != 1) && (!relayclient)) { | ||
| 461 | flagbarfbmf = bmcheck(BMCHECK_BMFNR); | ||
| 462 | } | ||
| 244 | seenmail = 1; | 463 | seenmail = 1; |
| 245 | if (!stralloc_copys(&rcptto,"")) die_nomem(); | 464 | if (!stralloc_copys(&rcptto,"")) die_nomem(); |
| 246 | if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); | 465 | if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); |
| @@ -250,7 +469,37 @@ void smtp_mail(arg) char *arg; | |||
| 250 | void smtp_rcpt(arg) char *arg; { | 469 | void smtp_rcpt(arg) char *arg; { |
| 251 | if (!seenmail) { err_wantmail(); return; } | 470 | if (!seenmail) { err_wantmail(); return; } |
| 252 | if (!addrparse(arg)) { err_syntax(); return; } | 471 | if (!addrparse(arg)) { err_syntax(); return; } |
| 253 | if (flagbarf) { err_bmf(); return; } | 472 | if (flagbarfbhelo) { |
| 473 | if (logregex) { | ||
| 474 | strerr_warn6("qmail-smtpd: badhelo: <",helohost.s,"> at ",remoteip," matches pattern: ",matchedregex.s,0); | ||
| 475 | } else { | ||
| 476 | strerr_warn4("qmail-smtpd: badhelo: <",helohost.s,"> at ",remoteip,0); | ||
| 477 | } | ||
| 478 | err_bhelo(); | ||
| 479 | return; | ||
| 480 | } | ||
| 481 | if (flagbarfbmf) { | ||
| 482 | if (logregex) { | ||
| 483 | strerr_warn6("qmail-smtpd: badmailfrom: <",mailfrom.s,"> at ",remoteip," matches pattern: ",matchedregex.s,0); | ||
| 484 | } else { | ||
| 485 | strerr_warn4("qmail-smtpd: badmailfrom: <",mailfrom.s,"> at ",remoteip,0); | ||
| 486 | } | ||
| 487 | err_bmf(); | ||
| 488 | return; | ||
| 489 | } | ||
| 490 | if (bmtok) flagbarfbmt = bmcheck(BMCHECK_BMT); | ||
| 491 | if ((!flagbarfbmt) && (bmtnrok) && (!relayclient)) { | ||
| 492 | flagbarfbmt = bmcheck(BMCHECK_BMTNR); | ||
| 493 | } | ||
| 494 | if (flagbarfbmt) { | ||
| 495 | if (logregex) { | ||
| 496 | strerr_warn6("qmail-smtpd: badmailto: <",addr.s,"> at ",remoteip," matches pattern: ",matchedregex.s,0); | ||
| 497 | } else { | ||
| 498 | strerr_warn4("qmail-smtpd: badmailto: <",addr.s,"> at ",remoteip,0); | ||
| 499 | } | ||
| 500 | err_bmt(); | ||
| 501 | return; | ||
| 502 | } | ||
| 254 | if (relayclient) { | 503 | if (relayclient) { |
| 255 | --addr.len; | 504 | --addr.len; |
| 256 | if (!stralloc_cats(&addr,relayclient)) die_nomem(); | 505 | if (!stralloc_cats(&addr,relayclient)) die_nomem(); |
| @@ -269,6 +518,11 @@ int saferead(fd,buf,len) int fd; char *buf; int len; | |||
| 269 | { | 518 | { |
| 270 | int r; | 519 | int r; |
| 271 | flush(); | 520 | flush(); |
| 521 | #ifdef TLS | ||
| 522 | if (ssl && fd == ssl_rfd) | ||
| 523 | r = ssl_timeoutread(timeout, ssl_rfd, ssl_wfd, ssl, buf, len); | ||
| 524 | else | ||
| 525 | #endif | ||
| 272 | r = timeoutread(timeout,fd,buf,len); | 526 | r = timeoutread(timeout,fd,buf,len); |
| 273 | if (r == -1) if (errno == error_timeout) die_alarm(); | 527 | if (r == -1) if (errno == error_timeout) die_alarm(); |
| 274 | if (r <= 0) die_read(); | 528 | if (r <= 0) die_read(); |
| @@ -378,7 +632,7 @@ void smtp_data() { | |||
| 378 | qp = qmail_qp(&qqt); | 632 | qp = qmail_qp(&qqt); |
| 379 | out("354 go ahead\r\n"); | 633 | out("354 go ahead\r\n"); |
| 380 | 634 | ||
| 381 | received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); | 635 | received(&qqt,protocol,local,remoteip,remotehost,remoteinfo,fakehelo); |
| 382 | blast(&hops); | 636 | blast(&hops); |
| 383 | hops = (hops >= MAXHOPS); | 637 | hops = (hops >= MAXHOPS); |
| 384 | if (hops) qmail_fail(&qqt); | 638 | if (hops) qmail_fail(&qqt); |
| @@ -388,28 +642,494 @@ void smtp_data() { | |||
| 388 | qqx = qmail_close(&qqt); | 642 | qqx = qmail_close(&qqt); |
| 389 | if (!*qqx) { acceptmessage(qp); return; } | 643 | if (!*qqx) { acceptmessage(qp); return; } |
| 390 | if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; } | 644 | 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; } | 645 | if (databytes) if (!bytestooverflow) { err_size(); return; } |
| 392 | if (*qqx == 'D') out("554 "); else out("451 "); | 646 | if (*qqx == 'D') out("554 "); else out("451 "); |
| 393 | out(qqx + 1); | 647 | out(qqx + 1); |
| 394 | out("\r\n"); | 648 | out("\r\n"); |
| 395 | } | 649 | } |
| 396 | 650 | ||
| 651 | /* this file is too long ----------------------------------------- SMTP AUTH */ | ||
| 652 | |||
| 653 | char unique[FMT_ULONG + FMT_ULONG + 3]; | ||
| 654 | static stralloc authin = {0}; /* input from SMTP client */ | ||
| 655 | static stralloc user = {0}; /* authorization user-id */ | ||
| 656 | static stralloc pass = {0}; /* plain passwd or digest */ | ||
| 657 | static stralloc resp = {0}; /* b64 response */ | ||
| 658 | #ifdef CRAM_MD5 | ||
| 659 | static stralloc chal = {0}; /* plain challenge */ | ||
| 660 | static stralloc slop = {0}; /* b64 challenge */ | ||
| 661 | #endif | ||
| 662 | |||
| 663 | int flagauth = 0; | ||
| 664 | char **childargs; | ||
| 665 | char ssauthbuf[512]; | ||
| 666 | substdio ssauth = SUBSTDIO_FDBUF(safewrite,3,ssauthbuf,sizeof(ssauthbuf)); | ||
| 667 | |||
| 668 | int authgetl(void) { | ||
| 669 | int i; | ||
| 670 | |||
| 671 | if (!stralloc_copys(&authin,"")) die_nomem(); | ||
| 672 | for (;;) { | ||
| 673 | if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */ | ||
| 674 | i = substdio_get(&ssin,authin.s + authin.len,1); | ||
| 675 | if (i != 1) die_read(); | ||
| 676 | if (authin.s[authin.len] == '\n') break; | ||
| 677 | ++authin.len; | ||
| 678 | } | ||
| 679 | |||
| 680 | if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len; | ||
| 681 | authin.s[authin.len] = 0; | ||
| 682 | if (*authin.s == '*' && *(authin.s + 1) == 0) { return err_authabrt(); } | ||
| 683 | if (authin.len == 0) { return err_input(); } | ||
| 684 | return authin.len; | ||
| 685 | } | ||
| 686 | |||
| 687 | int authenticate(void) | ||
| 688 | { | ||
| 689 | int child; | ||
| 690 | int wstat; | ||
| 691 | int pi[2]; | ||
| 692 | |||
| 693 | if (!stralloc_0(&user)) die_nomem(); | ||
| 694 | if (!stralloc_0(&pass)) die_nomem(); | ||
| 695 | #ifdef CRAM_MD5 | ||
| 696 | if (!stralloc_0(&chal)) die_nomem(); | ||
| 697 | #endif | ||
| 698 | |||
| 699 | if (pipe(pi) == -1) return err_pipe(); | ||
| 700 | switch(child = fork()) { | ||
| 701 | case -1: | ||
| 702 | return err_fork(); | ||
| 703 | case 0: | ||
| 704 | close(pi[1]); | ||
| 705 | if(fd_copy(3,pi[0]) == -1) return err_pipe(); | ||
| 706 | sig_pipedefault(); | ||
| 707 | execvp(*childargs, childargs); | ||
| 708 | _exit(1); | ||
| 709 | } | ||
| 710 | close(pi[0]); | ||
| 711 | |||
| 712 | substdio_fdbuf(&ssauth,write,pi[1],ssauthbuf,sizeof ssauthbuf); | ||
| 713 | if (substdio_put(&ssauth,user.s,user.len) == -1) return err_write(); | ||
| 714 | if (substdio_put(&ssauth,pass.s,pass.len) == -1) return err_write(); | ||
| 715 | #ifdef CRAM_MD5 | ||
| 716 | if (substdio_put(&ssauth,chal.s,chal.len) == -1) return err_write(); | ||
| 717 | #endif | ||
| 718 | if (substdio_flush(&ssauth) == -1) return err_write(); | ||
| 719 | |||
| 720 | close(pi[1]); | ||
| 721 | #ifdef CRAM_MD5 | ||
| 722 | if (!stralloc_copys(&chal,"")) die_nomem(); | ||
| 723 | if (!stralloc_copys(&slop,"")) die_nomem(); | ||
| 724 | #endif | ||
| 725 | byte_zero(ssauthbuf,sizeof ssauthbuf); | ||
| 726 | if (wait_pid(&wstat,child) == -1) return err_child(); | ||
| 727 | if (wait_crashed(wstat)) return err_child(); | ||
| 728 | if (wait_exitcode(wstat)) { sleep(AUTHSLEEP); return 1; } /* no */ | ||
| 729 | return 0; /* yes */ | ||
| 730 | } | ||
| 731 | |||
| 732 | int auth_login(arg) char *arg; | ||
| 733 | { | ||
| 734 | int r; | ||
| 735 | |||
| 736 | if (*arg) { | ||
| 737 | if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input(); | ||
| 738 | } | ||
| 739 | else { | ||
| 740 | out("334 VXNlcm5hbWU6\r\n"); flush(); /* Username: */ | ||
| 741 | if (authgetl() < 0) return -1; | ||
| 742 | if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input(); | ||
| 743 | } | ||
| 744 | if (r == -1) die_nomem(); | ||
| 745 | |||
| 746 | out("334 UGFzc3dvcmQ6\r\n"); flush(); /* Password: */ | ||
| 747 | |||
| 748 | if (authgetl() < 0) return -1; | ||
| 749 | if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input(); | ||
| 750 | if (r == -1) die_nomem(); | ||
| 751 | |||
| 752 | if (!user.len || !pass.len) return err_input(); | ||
| 753 | return authenticate(); | ||
| 754 | } | ||
| 755 | |||
| 756 | int auth_plain(arg) char *arg; | ||
| 757 | { | ||
| 758 | int r, id = 0; | ||
| 759 | |||
| 760 | if (*arg) { | ||
| 761 | if (r = b64decode(arg,str_len(arg),&resp) == 1) return err_input(); | ||
| 762 | } | ||
| 763 | else { | ||
| 764 | out("334 \r\n"); flush(); | ||
| 765 | if (authgetl() < 0) return -1; | ||
| 766 | if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input(); | ||
| 767 | } | ||
| 768 | if (r == -1 || !stralloc_0(&resp)) die_nomem(); | ||
| 769 | while (resp.s[id]) id++; /* "authorize-id\0userid\0passwd\0" */ | ||
| 770 | |||
| 771 | if (resp.len > id + 1) | ||
| 772 | if (!stralloc_copys(&user,resp.s + id + 1)) die_nomem(); | ||
| 773 | if (resp.len > id + user.len + 2) | ||
| 774 | if (!stralloc_copys(&pass,resp.s + id + user.len + 2)) die_nomem(); | ||
| 775 | |||
| 776 | if (!user.len || !pass.len) return err_input(); | ||
| 777 | return authenticate(); | ||
| 778 | } | ||
| 779 | |||
| 780 | #ifdef CRAM_MD5 | ||
| 781 | int auth_cram() | ||
| 782 | { | ||
| 783 | int i, r; | ||
| 784 | char *s; | ||
| 785 | |||
| 786 | s = unique; /* generate challenge */ | ||
| 787 | s += fmt_uint(s,getpid()); | ||
| 788 | *s++ = '.'; | ||
| 789 | s += fmt_ulong(s,(unsigned long) now()); | ||
| 790 | *s++ = '@'; | ||
| 791 | *s++ = 0; | ||
| 792 | if (!stralloc_copys(&chal,"<")) die_nomem(); | ||
| 793 | if (!stralloc_cats(&chal,unique)) die_nomem(); | ||
| 794 | if (!stralloc_cats(&chal,local)) die_nomem(); | ||
| 795 | if (!stralloc_cats(&chal,">")) die_nomem(); | ||
| 796 | if (b64encode(&chal,&slop) < 0) die_nomem(); | ||
| 797 | if (!stralloc_0(&slop)) die_nomem(); | ||
| 798 | |||
| 799 | out("334 "); /* "334 base64_challenge \r\n" */ | ||
| 800 | out(slop.s); | ||
| 801 | out("\r\n"); | ||
| 802 | flush(); | ||
| 803 | |||
| 804 | if (authgetl() < 0) return -1; /* got response */ | ||
| 805 | if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input(); | ||
| 806 | if (r == -1 || !stralloc_0(&resp)) die_nomem(); | ||
| 807 | |||
| 808 | i = str_chr(resp.s,' '); | ||
| 809 | s = resp.s + i; | ||
| 810 | while (*s == ' ') ++s; | ||
| 811 | resp.s[i] = 0; | ||
| 812 | if (!stralloc_copys(&user,resp.s)) die_nomem(); /* userid */ | ||
| 813 | if (!stralloc_copys(&pass,s)) die_nomem(); /* digest */ | ||
| 814 | |||
| 815 | if (!user.len || !pass.len) return err_input(); | ||
| 816 | return authenticate(); | ||
| 817 | } | ||
| 818 | #endif | ||
| 819 | |||
| 820 | struct authcmd { | ||
| 821 | char *text; | ||
| 822 | int (*fun)(); | ||
| 823 | } authcmds[] = { | ||
| 824 | { "login",auth_login } | ||
| 825 | , { "plain",auth_plain } | ||
| 826 | #ifdef CRAM_MD5 | ||
| 827 | , { "cram-md5",auth_cram } | ||
| 828 | #endif | ||
| 829 | , { 0,err_noauth } | ||
| 830 | }; | ||
| 831 | |||
| 832 | void smtp_auth(arg) | ||
| 833 | char *arg; | ||
| 834 | { | ||
| 835 | int i; | ||
| 836 | char *cmd = arg; | ||
| 837 | |||
| 838 | if (!*childargs) { out("503 auth not available (#5.3.3)\r\n"); return; } | ||
| 839 | if (flagauth) { err_authd(); return; } | ||
| 840 | if (seenmail) { err_authmail(); return; } | ||
| 841 | |||
| 842 | if (!stralloc_copys(&user,"")) die_nomem(); | ||
| 843 | if (!stralloc_copys(&pass,"")) die_nomem(); | ||
| 844 | if (!stralloc_copys(&resp,"")) die_nomem(); | ||
| 845 | #ifdef CRAM_MD5 | ||
| 846 | if (!stralloc_copys(&chal,"")) die_nomem(); | ||
| 847 | #endif | ||
| 848 | |||
| 849 | i = str_chr(cmd,' '); | ||
| 850 | arg = cmd + i; | ||
| 851 | while (*arg == ' ') ++arg; | ||
| 852 | cmd[i] = 0; | ||
| 853 | |||
| 854 | for (i = 0;authcmds[i].text;++i) | ||
| 855 | if (case_equals(authcmds[i].text,cmd)) break; | ||
| 856 | |||
| 857 | switch (authcmds[i].fun(arg)) { | ||
| 858 | case 0: | ||
| 859 | flagauth = 1; | ||
| 860 | protocol = "ESMTPA"; | ||
| 861 | relayclient = ""; | ||
| 862 | remoteinfo = user.s; | ||
| 863 | if (!env_unset("TCPREMOTEINFO")) die_read(); | ||
| 864 | if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem(); | ||
| 865 | if (!env_put2("RELAYCLIENT",relayclient)) die_nomem(); | ||
| 866 | out("235 ok, go ahead (#2.0.0)\r\n"); | ||
| 867 | break; | ||
| 868 | case 1: | ||
| 869 | err_authfail(user.s,authcmds[i].text); | ||
| 870 | } | ||
| 871 | } | ||
| 872 | |||
| 873 | |||
| 874 | /* this file is too long --------------------------------------------- GO ON */ | ||
| 875 | |||
| 876 | #ifdef TLS | ||
| 877 | stralloc proto = {0}; | ||
| 878 | int ssl_verified = 0; | ||
| 879 | const char *ssl_verify_err = 0; | ||
| 880 | |||
| 881 | void smtp_tls(char *arg) | ||
| 882 | { | ||
| 883 | if (ssl) err_unimpl(); | ||
| 884 | else if (*arg) out("501 Syntax error (no parameters allowed) (#5.5.4)\r\n"); | ||
| 885 | else tls_init(); | ||
| 886 | } | ||
| 887 | |||
| 888 | RSA *tmp_rsa_cb(SSL *ssl, int export, int keylen) | ||
| 889 | { | ||
| 890 | if (!export) keylen = 512; | ||
| 891 | if (keylen == 512) { | ||
| 892 | FILE *in = fopen("control/rsa512.pem", "r"); | ||
| 893 | if (in) { | ||
| 894 | RSA *rsa = PEM_read_RSAPrivateKey(in, NULL, NULL, NULL); | ||
| 895 | fclose(in); | ||
| 896 | if (rsa) return rsa; | ||
| 897 | } | ||
| 898 | } | ||
| 899 | return RSA_generate_key(keylen, RSA_F4, NULL, NULL); | ||
| 900 | } | ||
| 901 | |||
| 902 | DH *tmp_dh_cb(SSL *ssl, int export, int keylen) | ||
| 903 | { | ||
| 904 | if (!export) keylen = 1024; | ||
| 905 | if (keylen == 512) { | ||
| 906 | FILE *in = fopen("control/dh512.pem", "r"); | ||
| 907 | if (in) { | ||
| 908 | DH *dh = PEM_read_DHparams(in, NULL, NULL, NULL); | ||
| 909 | fclose(in); | ||
| 910 | if (dh) return dh; | ||
| 911 | } | ||
| 912 | } | ||
| 913 | if (keylen == 1024) { | ||
| 914 | FILE *in = fopen("control/dh1024.pem", "r"); | ||
| 915 | if (in) { | ||
| 916 | DH *dh = PEM_read_DHparams(in, NULL, NULL, NULL); | ||
| 917 | fclose(in); | ||
| 918 | if (dh) return dh; | ||
| 919 | } | ||
| 920 | } | ||
| 921 | return DH_generate_parameters(keylen, DH_GENERATOR_2, NULL, NULL); | ||
| 922 | } | ||
| 923 | |||
| 924 | /* don't want to fail handshake if cert isn't verifiable */ | ||
| 925 | int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; } | ||
| 926 | |||
| 927 | void tls_nogateway() | ||
| 928 | { | ||
| 929 | /* there may be cases when relayclient is set */ | ||
| 930 | if (!ssl || relayclient) return; | ||
| 931 | out("; no valid cert for gatewaying"); | ||
| 932 | if (ssl_verify_err) { out(": "); out(ssl_verify_err); } | ||
| 933 | } | ||
| 934 | void tls_out(const char *s1, const char *s2) | ||
| 935 | { | ||
| 936 | out("454 TLS "); out(s1); | ||
| 937 | if (s2) { out(": "); out(s2); } | ||
| 938 | out(" (#4.3.0)\r\n"); flush(); | ||
| 939 | } | ||
| 940 | void tls_err(const char *s) { tls_out(s, ssl_error()); if (smtps) die_read(); } | ||
| 941 | |||
| 942 | # define CLIENTCA "control/clientca.pem" | ||
| 943 | # define CLIENTCRL "control/clientcrl.pem" | ||
| 944 | # define SERVERCERT "control/servercert.pem" | ||
| 945 | |||
| 946 | int tls_verify() | ||
| 947 | { | ||
| 948 | stralloc clients = {0}; | ||
| 949 | struct constmap mapclients; | ||
| 950 | |||
| 951 | if (!ssl || relayclient || ssl_verified) return 0; | ||
| 952 | ssl_verified = 1; /* don't do this twice */ | ||
| 953 | |||
| 954 | /* request client cert to see if it can be verified by one of our CAs | ||
| 955 | * and the associated email address matches an entry in tlsclients */ | ||
| 956 | switch (control_readfile(&clients, "control/tlsclients", 0)) | ||
| 957 | { | ||
| 958 | case 1: | ||
| 959 | if (constmap_init(&mapclients, clients.s, clients.len, 0)) { | ||
| 960 | /* if CLIENTCA contains all the standard root certificates, a | ||
| 961 | * 0.9.6b client might fail with SSL_R_EXCESSIVE_MESSAGE_SIZE; | ||
| 962 | * it is probably due to 0.9.6b supporting only 8k key exchange | ||
| 963 | * data while the 0.9.6c release increases that limit to 100k */ | ||
| 964 | STACK_OF(X509_NAME) *sk = SSL_load_client_CA_file(CLIENTCA); | ||
| 965 | if (sk) { | ||
| 966 | SSL_set_client_CA_list(ssl, sk); | ||
| 967 | SSL_set_verify(ssl, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, NULL); | ||
| 968 | break; | ||
| 969 | } | ||
| 970 | constmap_free(&mapclients); | ||
| 971 | } | ||
| 972 | case 0: alloc_free(clients.s); return 0; | ||
| 973 | case -1: die_control(); | ||
| 974 | } | ||
| 975 | |||
| 976 | if (ssl_timeoutrehandshake(timeout, ssl_rfd, ssl_wfd, ssl) <= 0) { | ||
| 977 | const char *err = ssl_error_str(); | ||
| 978 | tls_out("rehandshake failed", err); die_read(); | ||
| 979 | } | ||
| 980 | |||
| 981 | do { /* one iteration */ | ||
| 982 | X509 *peercert; | ||
| 983 | X509_NAME *subj; | ||
| 984 | stralloc email = {0}; | ||
| 985 | |||
| 986 | int n = SSL_get_verify_result(ssl); | ||
| 987 | if (n != X509_V_OK) | ||
| 988 | { ssl_verify_err = X509_verify_cert_error_string(n); break; } | ||
| 989 | peercert = SSL_get_peer_certificate(ssl); | ||
| 990 | if (!peercert) break; | ||
| 991 | |||
| 992 | subj = X509_get_subject_name(peercert); | ||
| 993 | n = X509_NAME_get_index_by_NID(subj, NID_pkcs9_emailAddress, -1); | ||
| 994 | if (n >= 0) { | ||
| 995 | const ASN1_STRING *s = X509_NAME_get_entry(subj, n)->value; | ||
| 996 | if (s) { email.len = s->length; email.s = s->data; } | ||
| 997 | } | ||
| 998 | |||
| 999 | if (email.len <= 0) | ||
| 1000 | ssl_verify_err = "contains no email address"; | ||
| 1001 | else if (!constmap(&mapclients, email.s, email.len)) | ||
| 1002 | ssl_verify_err = "email address not in my list of tlsclients"; | ||
| 1003 | else { | ||
| 1004 | /* add the cert email to the proto if it helped allow relaying */ | ||
| 1005 | --proto.len; | ||
| 1006 | if (!stralloc_cats(&proto, "\n (cert ") /* continuation line */ | ||
| 1007 | || !stralloc_catb(&proto, email.s, email.len) | ||
| 1008 | || !stralloc_cats(&proto, ")") | ||
| 1009 | || !stralloc_0(&proto)) die_nomem(); | ||
| 1010 | relayclient = ""; | ||
| 1011 | protocol = proto.s; | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | X509_free(peercert); | ||
| 1015 | } while (0); | ||
| 1016 | constmap_free(&mapclients); alloc_free(clients.s); | ||
| 1017 | |||
| 1018 | /* we are not going to need this anymore: free the memory */ | ||
| 1019 | SSL_set_client_CA_list(ssl, NULL); | ||
| 1020 | SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); | ||
| 1021 | |||
| 1022 | return relayclient ? 1 : 0; | ||
| 1023 | } | ||
| 1024 | |||
| 1025 | void tls_init() | ||
| 1026 | { | ||
| 1027 | SSL *myssl; | ||
| 1028 | SSL_CTX *ctx; | ||
| 1029 | const char *ciphers; | ||
| 1030 | stralloc saciphers = {0}; | ||
| 1031 | X509_STORE *store; | ||
| 1032 | X509_LOOKUP *lookup; | ||
| 1033 | |||
| 1034 | SSL_library_init(); | ||
| 1035 | |||
| 1036 | /* a new SSL context with the bare minimum of options */ | ||
| 1037 | ctx = SSL_CTX_new(SSLv23_server_method()); | ||
| 1038 | if (!ctx) { tls_err("unable to initialize ctx"); return; } | ||
| 1039 | |||
| 1040 | if (!SSL_CTX_use_certificate_chain_file(ctx, SERVERCERT)) | ||
| 1041 | { SSL_CTX_free(ctx); tls_err("missing certificate"); return; } | ||
| 1042 | SSL_CTX_load_verify_locations(ctx, CLIENTCA, NULL); | ||
| 1043 | |||
| 1044 | #if OPENSSL_VERSION_NUMBER >= 0x00907000L | ||
| 1045 | /* crl checking */ | ||
| 1046 | store = SSL_CTX_get_cert_store(ctx); | ||
| 1047 | if ((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) && | ||
| 1048 | (X509_load_crl_file(lookup, CLIENTCRL, X509_FILETYPE_PEM) == 1)) | ||
| 1049 | X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | | ||
| 1050 | X509_V_FLAG_CRL_CHECK_ALL); | ||
| 1051 | #endif | ||
| 1052 | |||
| 1053 | /* set the callback here; SSL_set_verify didn't work before 0.9.6c */ | ||
| 1054 | SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_cb); | ||
| 1055 | |||
| 1056 | /* a new SSL object, with the rest added to it directly to avoid copying */ | ||
| 1057 | myssl = SSL_new(ctx); | ||
| 1058 | SSL_CTX_free(ctx); | ||
| 1059 | if (!myssl) { tls_err("unable to initialize ssl"); return; } | ||
| 1060 | |||
| 1061 | /* this will also check whether public and private keys match */ | ||
| 1062 | if (!SSL_use_RSAPrivateKey_file(myssl, SERVERCERT, SSL_FILETYPE_PEM)) | ||
| 1063 | { SSL_free(myssl); tls_err("no valid RSA private key"); return; } | ||
| 1064 | |||
| 1065 | ciphers = env_get("TLSCIPHERS"); | ||
| 1066 | if (!ciphers) { | ||
| 1067 | if (control_readfile(&saciphers, "control/tlsserverciphers", 0) == -1) | ||
| 1068 | { SSL_free(myssl); die_control(); } | ||
| 1069 | if (saciphers.len) { /* convert all '\0's except the last one to ':' */ | ||
| 1070 | int i; | ||
| 1071 | for (i = 0; i < saciphers.len - 1; ++i) | ||
| 1072 | if (!saciphers.s[i]) saciphers.s[i] = ':'; | ||
| 1073 | ciphers = saciphers.s; | ||
| 1074 | } | ||
| 1075 | } | ||
| 1076 | if (!ciphers || !*ciphers) ciphers = "DEFAULT"; | ||
| 1077 | SSL_set_cipher_list(myssl, ciphers); | ||
| 1078 | alloc_free(saciphers.s); | ||
| 1079 | |||
| 1080 | SSL_set_tmp_rsa_callback(myssl, tmp_rsa_cb); | ||
| 1081 | SSL_set_tmp_dh_callback(myssl, tmp_dh_cb); | ||
| 1082 | SSL_set_rfd(myssl, ssl_rfd = substdio_fileno(&ssin)); | ||
| 1083 | SSL_set_wfd(myssl, ssl_wfd = substdio_fileno(&ssout)); | ||
| 1084 | |||
| 1085 | if (!smtps) { out("220 ready for tls\r\n"); flush(); } | ||
| 1086 | |||
| 1087 | if (ssl_timeoutaccept(timeout, ssl_rfd, ssl_wfd, myssl) <= 0) { | ||
| 1088 | /* neither cleartext nor any other response here is part of a standard */ | ||
| 1089 | const char *err = ssl_error_str(); | ||
| 1090 | ssl_free(myssl); tls_out("connection failed", err); die_read(); | ||
| 1091 | } | ||
| 1092 | ssl = myssl; | ||
| 1093 | |||
| 1094 | /* populate the protocol string, used in Received */ | ||
| 1095 | if (!stralloc_copys(&proto, "ESMTPS (") | ||
| 1096 | || !stralloc_cats(&proto, SSL_get_cipher(ssl)) | ||
| 1097 | || !stralloc_cats(&proto, " encrypted)")) die_nomem(); | ||
| 1098 | if (!stralloc_0(&proto)) die_nomem(); | ||
| 1099 | protocol = proto.s; | ||
| 1100 | |||
| 1101 | /* have to discard the pre-STARTTLS HELO/EHLO argument, if any */ | ||
| 1102 | dohelo(remotehost); | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | # undef SERVERCERT | ||
| 1106 | # undef CLIENTCA | ||
| 1107 | |||
| 1108 | #endif | ||
| 1109 | |||
| 397 | struct commands smtpcommands[] = { | 1110 | struct commands smtpcommands[] = { |
| 398 | { "rcpt", smtp_rcpt, 0 } | 1111 | { "rcpt", smtp_rcpt, 0 } |
| 399 | , { "mail", smtp_mail, 0 } | 1112 | , { "mail", smtp_mail, 0 } |
| 400 | , { "data", smtp_data, flush } | 1113 | , { "data", smtp_data, flush } |
| 1114 | , { "auth", smtp_auth, flush } | ||
| 401 | , { "quit", smtp_quit, flush } | 1115 | , { "quit", smtp_quit, flush } |
| 402 | , { "helo", smtp_helo, flush } | 1116 | , { "helo", smtp_helo, flush } |
| 403 | , { "ehlo", smtp_ehlo, flush } | 1117 | , { "ehlo", smtp_ehlo, flush } |
| 404 | , { "rset", smtp_rset, 0 } | 1118 | , { "rset", smtp_rset, 0 } |
| 405 | , { "help", smtp_help, flush } | 1119 | , { "help", smtp_help, flush } |
| 1120 | #ifdef TLS | ||
| 1121 | , { "starttls", smtp_tls, flush } | ||
| 1122 | #endif | ||
| 406 | , { "noop", err_noop, flush } | 1123 | , { "noop", err_noop, flush } |
| 407 | , { "vrfy", err_vrfy, flush } | 1124 | , { "vrfy", err_vrfy, flush } |
| 408 | , { 0, err_unimpl, flush } | 1125 | , { 0, err_unimpl, flush } |
| 409 | } ; | 1126 | } ; |
| 410 | 1127 | ||
| 411 | void main() | 1128 | void main(argc,argv) |
| 1129 | int argc; | ||
| 1130 | char **argv; | ||
| 412 | { | 1131 | { |
| 1132 | childargs = argv + 1; | ||
| 413 | sig_pipeignore(); | 1133 | sig_pipeignore(); |
| 414 | if (chdir(auto_qmail) == -1) die_control(); | 1134 | if (chdir(auto_qmail) == -1) die_control(); |
| 415 | setup(); | 1135 | setup(); |
