diff options
Diffstat (limited to 'qmail-remote.c')
| -rw-r--r-- | qmail-remote.c | 444 |
1 files changed, 435 insertions, 9 deletions
diff --git a/qmail-remote.c b/qmail-remote.c index 7d65473..dae884d 100644 --- a/qmail-remote.c +++ b/qmail-remote.c | |||
| @@ -28,6 +28,7 @@ | |||
| 28 | #include "timeoutconn.h" | 28 | #include "timeoutconn.h" |
| 29 | #include "timeoutread.h" | 29 | #include "timeoutread.h" |
| 30 | #include "timeoutwrite.h" | 30 | #include "timeoutwrite.h" |
| 31 | #include "base64.h" | ||
| 31 | 32 | ||
| 32 | #define HUGESMTPTEXT 5000 | 33 | #define HUGESMTPTEXT 5000 |
| 33 | 34 | ||
| @@ -43,11 +44,27 @@ stralloc routes = {0}; | |||
| 43 | struct constmap maproutes; | 44 | struct constmap maproutes; |
| 44 | stralloc host = {0}; | 45 | stralloc host = {0}; |
| 45 | stralloc sender = {0}; | 46 | stralloc sender = {0}; |
| 47 | stralloc auth_smtp_user = {0}; | ||
| 48 | stralloc auth_smtp_pass = {0}; | ||
| 49 | stralloc auth_b64_user = {0}; | ||
| 50 | stralloc auth_b64_pass = {0}; | ||
| 51 | stralloc auth_status = {0}; | ||
| 46 | 52 | ||
| 47 | saa reciplist = {0}; | 53 | saa reciplist = {0}; |
| 48 | 54 | ||
| 49 | struct ip_address partner; | 55 | struct ip_address partner; |
| 50 | 56 | ||
| 57 | #ifdef TLS | ||
| 58 | # include <sys/stat.h> | ||
| 59 | # include "tls.h" | ||
| 60 | # include "ssl_timeoutio.h" | ||
| 61 | # include <openssl/x509v3.h> | ||
| 62 | # define EHLO 1 | ||
| 63 | |||
| 64 | int tls_init(); | ||
| 65 | const char *ssl_err_str = 0; | ||
| 66 | #endif | ||
| 67 | |||
| 51 | void out(s) char *s; { if (substdio_puts(subfdoutsmall,s) == -1) _exit(0); } | 68 | void out(s) char *s; { if (substdio_puts(subfdoutsmall,s) == -1) _exit(0); } |
| 52 | void zero() { if (substdio_put(subfdoutsmall,"\0",1) == -1) _exit(0); } | 69 | void zero() { if (substdio_put(subfdoutsmall,"\0",1) == -1) _exit(0); } |
| 53 | void zerodie() { zero(); substdio_flush(subfdoutsmall); _exit(0); } | 70 | void zerodie() { zero(); substdio_flush(subfdoutsmall); _exit(0); } |
| @@ -85,6 +102,18 @@ void perm_ambigmx() { out("D\ | |||
| 85 | Sorry. Although I'm listed as a best-preference MX or A for that host,\n\ | 102 | Sorry. Although I'm listed as a best-preference MX or A for that host,\n\ |
| 86 | it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)\n"); | 103 | it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)\n"); |
| 87 | zerodie(); } | 104 | zerodie(); } |
| 105 | void auth_user_not_set() { | ||
| 106 | if(!stralloc_copys(&auth_status, \ | ||
| 107 | "User and password not set, continuing without authentication.\n")) | ||
| 108 | temp_nomem(); | ||
| 109 | if(!stralloc_0(&auth_status)) temp_nomem(); | ||
| 110 | } | ||
| 111 | void no_supported_auth() { | ||
| 112 | if(!stralloc_copys(&auth_status, \ | ||
| 113 | "No supported AUTH method found, continuing without authentication.\n")) | ||
| 114 | temp_nomem(); | ||
| 115 | if(!stralloc_0(&auth_status)) temp_nomem(); | ||
| 116 | } | ||
| 88 | 117 | ||
| 89 | void outhost() | 118 | void outhost() |
| 90 | { | 119 | { |
| @@ -99,6 +128,9 @@ void dropped() { | |||
| 99 | outhost(); | 128 | outhost(); |
| 100 | out(" but connection died. "); | 129 | out(" but connection died. "); |
| 101 | if (flagcritical) out("Possible duplicate! "); | 130 | if (flagcritical) out("Possible duplicate! "); |
| 131 | #ifdef TLS | ||
| 132 | if (ssl_err_str) { out(ssl_err_str); out(" "); } | ||
| 133 | #endif | ||
| 102 | out("(#4.4.2)\n"); | 134 | out("(#4.4.2)\n"); |
| 103 | zerodie(); | 135 | zerodie(); |
| 104 | } | 136 | } |
| @@ -110,6 +142,12 @@ int timeout = 1200; | |||
| 110 | int saferead(fd,buf,len) int fd; char *buf; int len; | 142 | int saferead(fd,buf,len) int fd; char *buf; int len; |
| 111 | { | 143 | { |
| 112 | int r; | 144 | int r; |
| 145 | #ifdef TLS | ||
| 146 | if (ssl) { | ||
| 147 | r = ssl_timeoutread(timeout, smtpfd, smtpfd, ssl, buf, len); | ||
| 148 | if (r < 0) ssl_err_str = ssl_error_str(); | ||
| 149 | } else | ||
| 150 | #endif | ||
| 113 | r = timeoutread(timeout,smtpfd,buf,len); | 151 | r = timeoutread(timeout,smtpfd,buf,len); |
| 114 | if (r <= 0) dropped(); | 152 | if (r <= 0) dropped(); |
| 115 | return r; | 153 | return r; |
| @@ -117,6 +155,12 @@ int saferead(fd,buf,len) int fd; char *buf; int len; | |||
| 117 | int safewrite(fd,buf,len) int fd; char *buf; int len; | 155 | int safewrite(fd,buf,len) int fd; char *buf; int len; |
| 118 | { | 156 | { |
| 119 | int r; | 157 | int r; |
| 158 | #ifdef TLS | ||
| 159 | if (ssl) { | ||
| 160 | r = ssl_timeoutwrite(timeout, smtpfd, smtpfd, ssl, buf, len); | ||
| 161 | if (r < 0) ssl_err_str = ssl_error_str(); | ||
| 162 | } else | ||
| 163 | #endif | ||
| 120 | r = timeoutwrite(timeout,smtpfd,buf,len); | 164 | r = timeoutwrite(timeout,smtpfd,buf,len); |
| 121 | if (r <= 0) dropped(); | 165 | if (r <= 0) dropped(); |
| 122 | return r; | 166 | return r; |
| @@ -163,6 +207,65 @@ unsigned long smtpcode() | |||
| 163 | return code; | 207 | return code; |
| 164 | } | 208 | } |
| 165 | 209 | ||
| 210 | #ifdef EHLO | ||
| 211 | saa ehlokw = {0}; /* list of EHLO keywords and parameters */ | ||
| 212 | int maxehlokwlen = 0; | ||
| 213 | |||
| 214 | unsigned long ehlo() | ||
| 215 | { | ||
| 216 | stralloc *sa; | ||
| 217 | char *s, *e, *p; | ||
| 218 | unsigned long code; | ||
| 219 | |||
| 220 | if (ehlokw.len > maxehlokwlen) maxehlokwlen = ehlokw.len; | ||
| 221 | ehlokw.len = 0; | ||
| 222 | |||
| 223 | # ifdef MXPS | ||
| 224 | if (type == 's') return 0; | ||
| 225 | # endif | ||
| 226 | |||
| 227 | substdio_puts(&smtpto, "EHLO "); | ||
| 228 | substdio_put(&smtpto, helohost.s, helohost.len); | ||
| 229 | substdio_puts(&smtpto, "\r\n"); | ||
| 230 | substdio_flush(&smtpto); | ||
| 231 | |||
| 232 | code = smtpcode(); | ||
| 233 | if (code != 250) return code; | ||
| 234 | |||
| 235 | s = smtptext.s; | ||
| 236 | while (*s++ != '\n') ; /* skip the first line: contains the domain */ | ||
| 237 | |||
| 238 | e = smtptext.s + smtptext.len - 6; /* 250-?\n */ | ||
| 239 | while (s <= e) | ||
| 240 | { | ||
| 241 | int wasspace = 0; | ||
| 242 | |||
| 243 | if (!saa_readyplus(&ehlokw, 1)) temp_nomem(); | ||
| 244 | sa = ehlokw.sa + ehlokw.len++; | ||
| 245 | if (ehlokw.len > maxehlokwlen) *sa = sauninit; else sa->len = 0; | ||
| 246 | |||
| 247 | /* smtptext is known to end in a '\n' */ | ||
| 248 | for (p = (s += 4); ; ++p) | ||
| 249 | if (*p == '\n' || *p == ' ' || *p == '\t') { | ||
| 250 | if (!wasspace) | ||
| 251 | if (!stralloc_catb(sa, s, p - s) || !stralloc_0(sa)) temp_nomem(); | ||
| 252 | if (*p == '\n') break; | ||
| 253 | wasspace = 1; | ||
| 254 | } else if (wasspace == 1) { | ||
| 255 | wasspace = 0; | ||
| 256 | s = p; | ||
| 257 | } | ||
| 258 | s = ++p; | ||
| 259 | |||
| 260 | /* keyword should consist of alpha-num and '-' | ||
| 261 | * broken AUTH might use '=' instead of space */ | ||
| 262 | for (p = sa->s; *p; ++p) if (*p == '=') { *p = 0; break; } | ||
| 263 | } | ||
| 264 | |||
| 265 | return 250; | ||
| 266 | } | ||
| 267 | #endif | ||
| 268 | |||
| 166 | void outsmtptext() | 269 | void outsmtptext() |
| 167 | { | 270 | { |
| 168 | int i; | 271 | int i; |
| @@ -179,6 +282,11 @@ void quit(prepend,append) | |||
| 179 | char *prepend; | 282 | char *prepend; |
| 180 | char *append; | 283 | char *append; |
| 181 | { | 284 | { |
| 285 | #ifdef TLS | ||
| 286 | /* shouldn't talk to the client unless in an appropriate state */ | ||
| 287 | int state = ssl ? ssl->state : SSL_ST_BEFORE; | ||
| 288 | if (state & SSL_ST_OK || (!smtps && state & SSL_ST_BEFORE)) | ||
| 289 | #endif | ||
| 182 | substdio_putsflush(&smtpto,"QUIT\r\n"); | 290 | substdio_putsflush(&smtpto,"QUIT\r\n"); |
| 183 | /* waiting for remote side is just too ridiculous */ | 291 | /* waiting for remote side is just too ridiculous */ |
| 184 | out(prepend); | 292 | out(prepend); |
| @@ -186,6 +294,30 @@ char *append; | |||
| 186 | out(append); | 294 | out(append); |
| 187 | out(".\n"); | 295 | out(".\n"); |
| 188 | outsmtptext(); | 296 | outsmtptext(); |
| 297 | |||
| 298 | #if defined(TLS) && defined(DEBUG) | ||
| 299 | if (ssl) { | ||
| 300 | X509 *peercert; | ||
| 301 | |||
| 302 | out("STARTTLS proto="); out(SSL_get_version(ssl)); | ||
| 303 | out("; cipher="); out(SSL_get_cipher(ssl)); | ||
| 304 | |||
| 305 | /* we want certificate details */ | ||
| 306 | if (peercert = SSL_get_peer_certificate(ssl)) { | ||
| 307 | char *str; | ||
| 308 | |||
| 309 | str = X509_NAME_oneline(X509_get_subject_name(peercert), NULL, 0); | ||
| 310 | out("; subject="); out(str); OPENSSL_free(str); | ||
| 311 | |||
| 312 | str = X509_NAME_oneline(X509_get_issuer_name(peercert), NULL, 0); | ||
| 313 | out("; issuer="); out(str); OPENSSL_free(str); | ||
| 314 | |||
| 315 | X509_free(peercert); | ||
| 316 | } | ||
| 317 | out(";\n"); | ||
| 318 | } | ||
| 319 | #endif | ||
| 320 | |||
| 189 | zerodie(); | 321 | zerodie(); |
| 190 | } | 322 | } |
| 191 | 323 | ||
| @@ -214,26 +346,297 @@ void blast() | |||
| 214 | substdio_flush(&smtpto); | 346 | substdio_flush(&smtpto); |
| 215 | } | 347 | } |
| 216 | 348 | ||
| 349 | #ifdef TLS | ||
| 350 | char *partner_fqdn = 0; | ||
| 351 | |||
| 352 | # define TLS_QUIT quit(ssl ? "; connected to " : "; connecting to ", "") | ||
| 353 | void tls_quit(const char *s1, const char *s2) | ||
| 354 | { | ||
| 355 | out(s1); if (s2) { out(": "); out(s2); } TLS_QUIT; | ||
| 356 | } | ||
| 357 | # define tls_quit_error(s) tls_quit(s, ssl_error()) | ||
| 358 | |||
| 359 | int match_partner(const char *s, int len) | ||
| 360 | { | ||
| 361 | if (!case_diffb(partner_fqdn, len, s) && !partner_fqdn[len]) return 1; | ||
| 362 | /* we also match if the name is *.domainname */ | ||
| 363 | if (*s == '*') { | ||
| 364 | const char *domain = partner_fqdn + str_chr(partner_fqdn, '.'); | ||
| 365 | if (!case_diffb(domain, --len, ++s) && !domain[len]) return 1; | ||
| 366 | } | ||
| 367 | return 0; | ||
| 368 | } | ||
| 369 | |||
| 370 | /* don't want to fail handshake if certificate can't be verified */ | ||
| 371 | int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; } | ||
| 372 | |||
| 373 | int tls_init() | ||
| 374 | { | ||
| 375 | int i; | ||
| 376 | SSL *myssl; | ||
| 377 | SSL_CTX *ctx; | ||
| 378 | stralloc saciphers = {0}; | ||
| 379 | const char *ciphers, *servercert = 0; | ||
| 380 | |||
| 381 | if (partner_fqdn) { | ||
| 382 | struct stat st; | ||
| 383 | stralloc tmp = {0}; | ||
| 384 | if (!stralloc_copys(&tmp, "control/tlshosts/") | ||
| 385 | || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn)) | ||
| 386 | || !stralloc_catb(&tmp, ".pem", 5)) temp_nomem(); | ||
| 387 | if (stat(tmp.s, &st) == 0) | ||
| 388 | servercert = tmp.s; | ||
| 389 | else { | ||
| 390 | if (!stralloc_copys(&tmp, "control/notlshosts/") | ||
| 391 | || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn)+1)) | ||
| 392 | temp_nomem(); | ||
| 393 | if ((stat("control/tlshosts/exhaustivelist", &st) == 0) || | ||
| 394 | (stat(tmp.s, &st) == 0)) { | ||
| 395 | alloc_free(tmp.s); | ||
| 396 | return 0; | ||
| 397 | } | ||
| 398 | alloc_free(tmp.s); | ||
| 399 | } | ||
| 400 | } | ||
| 401 | |||
| 402 | if (!smtps) { | ||
| 403 | stralloc *sa = ehlokw.sa; | ||
| 404 | unsigned int len = ehlokw.len; | ||
| 405 | /* look for STARTTLS among EHLO keywords */ | ||
| 406 | for ( ; len && case_diffs(sa->s, "STARTTLS"); ++sa, --len) ; | ||
| 407 | if (!len) { | ||
| 408 | if (!servercert) return 0; | ||
| 409 | out("ZNo TLS achieved while "); out(servercert); | ||
| 410 | out(" exists"); smtptext.len = 0; TLS_QUIT; | ||
| 411 | } | ||
| 412 | } | ||
| 413 | |||
| 414 | SSL_library_init(); | ||
| 415 | ctx = SSL_CTX_new(SSLv23_client_method()); | ||
| 416 | if (!ctx) { | ||
| 417 | if (!smtps && !servercert) return 0; | ||
| 418 | smtptext.len = 0; | ||
| 419 | tls_quit_error("ZTLS error initializing ctx"); | ||
| 420 | } | ||
| 421 | |||
| 422 | if (servercert) { | ||
| 423 | if (!SSL_CTX_load_verify_locations(ctx, servercert, NULL)) { | ||
| 424 | SSL_CTX_free(ctx); | ||
| 425 | smtptext.len = 0; | ||
| 426 | out("ZTLS unable to load "); tls_quit_error(servercert); | ||
| 427 | } | ||
| 428 | /* set the callback here; SSL_set_verify didn't work before 0.9.6c */ | ||
| 429 | SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb); | ||
| 430 | } | ||
| 431 | |||
| 432 | /* let the other side complain if it needs a cert and we don't have one */ | ||
| 433 | # define CLIENTCERT "control/clientcert.pem" | ||
| 434 | if (SSL_CTX_use_certificate_chain_file(ctx, CLIENTCERT)) | ||
| 435 | SSL_CTX_use_RSAPrivateKey_file(ctx, CLIENTCERT, SSL_FILETYPE_PEM); | ||
| 436 | # undef CLIENTCERT | ||
| 437 | |||
| 438 | myssl = SSL_new(ctx); | ||
| 439 | SSL_CTX_free(ctx); | ||
| 440 | if (!myssl) { | ||
| 441 | if (!smtps && !servercert) return 0; | ||
| 442 | smtptext.len = 0; | ||
| 443 | tls_quit_error("ZTLS error initializing ssl"); | ||
| 444 | } | ||
| 445 | |||
| 446 | if (!smtps) substdio_putsflush(&smtpto, "STARTTLS\r\n"); | ||
| 447 | |||
| 448 | /* while the server is preparing a responce, do something else */ | ||
| 449 | if (control_readfile(&saciphers, "control/tlsclientciphers", 0) == -1) | ||
| 450 | { SSL_free(myssl); temp_control(); } | ||
| 451 | if (saciphers.len) { | ||
| 452 | for (i = 0; i < saciphers.len - 1; ++i) | ||
| 453 | if (!saciphers.s[i]) saciphers.s[i] = ':'; | ||
| 454 | ciphers = saciphers.s; | ||
| 455 | } | ||
| 456 | else ciphers = "DEFAULT"; | ||
| 457 | SSL_set_cipher_list(myssl, ciphers); | ||
| 458 | alloc_free(saciphers.s); | ||
| 459 | |||
| 460 | /* SSL_set_options(myssl, SSL_OP_NO_TLSv1); */ | ||
| 461 | SSL_set_fd(myssl, smtpfd); | ||
| 462 | |||
| 463 | /* read the responce to STARTTLS */ | ||
| 464 | if (!smtps) { | ||
| 465 | if (smtpcode() != 220) { | ||
| 466 | SSL_free(myssl); | ||
| 467 | if (!servercert) return 0; | ||
| 468 | out("ZSTARTTLS rejected while "); | ||
| 469 | out(servercert); out(" exists"); TLS_QUIT; | ||
| 470 | } | ||
| 471 | smtptext.len = 0; | ||
| 472 | } | ||
| 473 | |||
| 474 | ssl = myssl; | ||
| 475 | if (ssl_timeoutconn(timeout, smtpfd, smtpfd, ssl) <= 0) | ||
| 476 | tls_quit("ZTLS connect failed", ssl_error_str()); | ||
| 477 | |||
| 478 | if (servercert) { | ||
| 479 | X509 *peercert; | ||
| 480 | STACK_OF(GENERAL_NAME) *gens; | ||
| 481 | |||
| 482 | int r = SSL_get_verify_result(ssl); | ||
| 483 | if (r != X509_V_OK) { | ||
| 484 | out("ZTLS unable to verify server with "); | ||
| 485 | tls_quit(servercert, X509_verify_cert_error_string(r)); | ||
| 486 | } | ||
| 487 | alloc_free(servercert); | ||
| 488 | |||
| 489 | peercert = SSL_get_peer_certificate(ssl); | ||
| 490 | if (!peercert) { | ||
| 491 | out("ZTLS unable to verify server "); | ||
| 492 | tls_quit(partner_fqdn, "no certificate provided"); | ||
| 493 | } | ||
| 494 | |||
| 495 | /* RFC 2595 section 2.4: find a matching name | ||
| 496 | * first find a match among alternative names */ | ||
| 497 | gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0); | ||
| 498 | if (gens) { | ||
| 499 | for (i = 0, r = sk_GENERAL_NAME_num(gens); i < r; ++i) | ||
| 500 | { | ||
| 501 | const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i); | ||
| 502 | if (gn->type == GEN_DNS) | ||
| 503 | if (match_partner(gn->d.ia5->data, gn->d.ia5->length)) break; | ||
| 504 | } | ||
| 505 | sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); | ||
| 506 | } | ||
| 507 | |||
| 508 | /* no alternative name matched, look up commonName */ | ||
| 509 | if (!gens || i >= r) { | ||
| 510 | stralloc peer = {0}; | ||
| 511 | X509_NAME *subj = X509_get_subject_name(peercert); | ||
| 512 | i = X509_NAME_get_index_by_NID(subj, NID_commonName, -1); | ||
| 513 | if (i >= 0) { | ||
| 514 | const ASN1_STRING *s = X509_NAME_get_entry(subj, i)->value; | ||
| 515 | if (s) { peer.len = s->length; peer.s = s->data; } | ||
| 516 | } | ||
| 517 | if (peer.len <= 0) { | ||
| 518 | out("ZTLS unable to verify server "); | ||
| 519 | tls_quit(partner_fqdn, "certificate contains no valid commonName"); | ||
| 520 | } | ||
| 521 | if (!match_partner(peer.s, peer.len)) { | ||
| 522 | out("ZTLS unable to verify server "); out(partner_fqdn); | ||
| 523 | out(": received certificate for "); outsafe(&peer); TLS_QUIT; | ||
| 524 | } | ||
| 525 | } | ||
| 526 | |||
| 527 | X509_free(peercert); | ||
| 528 | } | ||
| 529 | |||
| 530 | if (smtps) if (smtpcode() != 220) | ||
| 531 | quit("ZTLS Connected to "," but greeting failed"); | ||
| 532 | |||
| 533 | return 1; | ||
| 534 | } | ||
| 535 | #endif | ||
| 536 | |||
| 217 | stralloc recip = {0}; | 537 | stralloc recip = {0}; |
| 218 | 538 | ||
| 539 | void mail_without_auth() | ||
| 540 | { | ||
| 541 | substdio_puts(&smtpto,"MAIL FROM:<"); | ||
| 542 | substdio_put(&smtpto,sender.s,sender.len); | ||
| 543 | substdio_puts(&smtpto,">\r\n"); | ||
| 544 | substdio_flush(&smtpto); | ||
| 545 | } | ||
| 546 | |||
| 219 | void smtp() | 547 | void smtp() |
| 220 | { | 548 | { |
| 221 | unsigned long code; | 549 | unsigned long code; |
| 222 | int flagbother; | 550 | int flagbother; |
| 223 | int i; | 551 | int i, j; |
| 552 | |||
| 553 | #ifndef PORT_SMTP | ||
| 554 | /* the qmtpc patch uses smtp_port and undefines PORT_SMTP */ | ||
| 555 | # define port smtp_port | ||
| 556 | #endif | ||
| 557 | |||
| 558 | #ifdef TLS | ||
| 559 | # ifdef MXPS | ||
| 560 | if (type == 'S') smtps = 1; | ||
| 561 | else if (type != 's') | ||
| 562 | # endif | ||
| 563 | if (port == 465) smtps = 1; | ||
| 564 | if (!smtps) | ||
| 565 | #endif | ||
| 224 | 566 | ||
| 225 | if (smtpcode() != 220) quit("ZConnected to "," but greeting failed"); | 567 | if (smtpcode() != 220) quit("ZConnected to "," but greeting failed"); |
| 226 | 568 | ||
| 569 | #ifdef EHLO | ||
| 570 | # ifdef TLS | ||
| 571 | if (!smtps) | ||
| 572 | # endif | ||
| 573 | code = ehlo(); | ||
| 574 | |||
| 575 | # ifdef TLS | ||
| 576 | if (tls_init()) | ||
| 577 | /* RFC2487 says we should issue EHLO (even if we might not need | ||
| 578 | * extensions); at the same time, it does not prohibit a server | ||
| 579 | * to reject the EHLO and make us fallback to HELO */ | ||
| 580 | code = ehlo(); | ||
| 581 | # endif | ||
| 582 | |||
| 583 | if (code == 250) { | ||
| 584 | /* add EHLO response checks here */ | ||
| 585 | |||
| 586 | /* and if EHLO failed, use HELO */ | ||
| 587 | } else { | ||
| 588 | #endif | ||
| 589 | |||
| 227 | substdio_puts(&smtpto,"HELO "); | 590 | substdio_puts(&smtpto,"HELO "); |
| 228 | substdio_put(&smtpto,helohost.s,helohost.len); | 591 | substdio_put(&smtpto,helohost.s,helohost.len); |
| 229 | substdio_puts(&smtpto,"\r\n"); | 592 | substdio_puts(&smtpto,"\r\n"); |
| 230 | substdio_flush(&smtpto); | 593 | substdio_flush(&smtpto); |
| 231 | if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected"); | 594 | if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected"); |
| 232 | 595 | ||
| 233 | substdio_puts(&smtpto,"MAIL FROM:<"); | 596 | #ifdef EHLO |
| 234 | substdio_put(&smtpto,sender.s,sender.len); | 597 | } |
| 235 | substdio_puts(&smtpto,">\r\n"); | 598 | #endif |
| 236 | substdio_flush(&smtpto); | 599 | i = 0; |
| 600 | if (auth_smtp_user.len && auth_smtp_pass.len) { | ||
| 601 | while((i += str_chr(smtptext.s+i,'\n') + 1) && | ||
| 602 | (i+8 < smtptext.len) && | ||
| 603 | str_diffn(smtptext.s+i+4,"AUTH",4)); | ||
| 604 | if (((i+9 < smtptext.len) && | ||
| 605 | (str_diffn(smtptext.s+i+9," ",1) || | ||
| 606 | str_diffn(smtptext.s+i+9,"=",1))) && | ||
| 607 | ( i += str_chr(smtptext.s+i,'L') + 1 ) && | ||
| 608 | str_diffn(smtptext.s+i+1,"OGIN",4)) { | ||
| 609 | |||
| 610 | if (b64encode(&auth_smtp_user,&auth_b64_user)) quit("ZConnected to "," but unable to base64encode user"); | ||
| 611 | if (b64encode(&auth_smtp_pass,&auth_b64_pass)) quit("ZConnected to "," but unable to base64encode pass"); | ||
| 612 | |||
| 613 | substdio_puts(&smtpto,"AUTH LOGIN\r\n"); | ||
| 614 | substdio_flush(&smtpto); | ||
| 615 | if (smtpcode() != 334) quit("ZConnected to "," but authentication was rejected (AUTH LOGIN)"); | ||
| 616 | substdio_put(&smtpto,auth_b64_user.s,auth_b64_user.len); | ||
| 617 | substdio_puts(&smtpto,"\r\n"); | ||
| 618 | substdio_flush(&smtpto); | ||
| 619 | if (smtpcode() != 334) quit("ZConnected to "," but authentication was rejected (username)"); | ||
| 620 | substdio_put(&smtpto,auth_b64_pass.s,auth_b64_pass.len); | ||
| 621 | substdio_puts(&smtpto,"\r\n"); | ||
| 622 | substdio_flush(&smtpto); | ||
| 623 | if (smtpcode() != 235) quit("ZConnected to "," but authentication was rejected (password)"); | ||
| 624 | substdio_puts(&smtpto,"MAIL FROM:<"); | ||
| 625 | substdio_put(&smtpto,sender.s,sender.len); | ||
| 626 | substdio_puts(&smtpto,"> AUTH=<"); | ||
| 627 | substdio_put(&smtpto,sender.s,sender.len); | ||
| 628 | substdio_puts(&smtpto,">\r\n"); | ||
| 629 | substdio_flush(&smtpto); | ||
| 630 | if(!stralloc_copys(&auth_status, "Delivered with authenticated connection to \n")) temp_nomem(); | ||
| 631 | if(!stralloc_0(&auth_status)) temp_nomem(); | ||
| 632 | } else { | ||
| 633 | no_supported_auth(); | ||
| 634 | mail_without_auth(); | ||
| 635 | } | ||
| 636 | } else { | ||
| 637 | auth_user_not_set(); | ||
| 638 | mail_without_auth(); | ||
| 639 | } | ||
| 237 | code = smtpcode(); | 640 | code = smtpcode(); |
| 238 | if (code >= 500) quit("DConnected to "," but sender was rejected"); | 641 | if (code >= 500) quit("DConnected to "," but sender was rejected"); |
| 239 | if (code >= 400) quit("ZConnected to "," but sender was rejected"); | 642 | if (code >= 400) quit("ZConnected to "," but sender was rejected"); |
| @@ -246,15 +649,22 @@ void smtp() | |||
| 246 | substdio_flush(&smtpto); | 649 | substdio_flush(&smtpto); |
| 247 | code = smtpcode(); | 650 | code = smtpcode(); |
| 248 | if (code >= 500) { | 651 | if (code >= 500) { |
| 249 | out("h"); outhost(); out(" does not like recipient.\n"); | 652 | out("h"); out(auth_status.s); outhost(); |
| 653 | out(" does not like recipient.\n"); | ||
| 250 | outsmtptext(); zero(); | 654 | outsmtptext(); zero(); |
| 251 | } | 655 | } |
| 252 | else if (code >= 400) { | 656 | else if (code >= 400) { |
| 253 | out("s"); outhost(); out(" does not like recipient.\n"); | 657 | out("s"); out(auth_status.s); outhost(); |
| 658 | out(" does not like recipient.\n"); | ||
| 254 | outsmtptext(); zero(); | 659 | outsmtptext(); zero(); |
| 255 | } | 660 | } |
| 256 | else { | 661 | else { |
| 257 | out("r"); zero(); | 662 | /* |
| 663 | * James Raftery <james@now.ie> | ||
| 664 | * Log _real_ envelope recipient, post canonicalisation. | ||
| 665 | */ | ||
| 666 | out("r"); out(auth_status.s); | ||
| 667 | out("<"); outsafe(&reciplist.sa[i]); out("> "); zero(); | ||
| 258 | flagbother = 1; | 668 | flagbother = 1; |
| 259 | } | 669 | } |
| 260 | } | 670 | } |
| @@ -331,7 +741,7 @@ int argc; | |||
| 331 | char **argv; | 741 | char **argv; |
| 332 | { | 742 | { |
| 333 | static ipalloc ip = {0}; | 743 | static ipalloc ip = {0}; |
| 334 | int i; | 744 | int i,j; |
| 335 | unsigned long random; | 745 | unsigned long random; |
| 336 | char **recips; | 746 | char **recips; |
| 337 | unsigned long prefme; | 747 | unsigned long prefme; |
| @@ -347,6 +757,9 @@ char **argv; | |||
| 347 | 757 | ||
| 348 | if (!stralloc_copys(&host,argv[1])) temp_nomem(); | 758 | if (!stralloc_copys(&host,argv[1])) temp_nomem(); |
| 349 | 759 | ||
| 760 | if (!stralloc_copys(&auth_smtp_user,"")) temp_nomem(); | ||
| 761 | if (!stralloc_copys(&auth_smtp_pass,"")) temp_nomem(); | ||
| 762 | |||
| 350 | relayhost = 0; | 763 | relayhost = 0; |
| 351 | for (i = 0;i <= host.len;++i) | 764 | for (i = 0;i <= host.len;++i) |
| 352 | if ((i == 0) || (i == host.len) || (host.s[i] == '.')) | 765 | if ((i == 0) || (i == host.len) || (host.s[i] == '.')) |
| @@ -355,6 +768,16 @@ char **argv; | |||
| 355 | if (relayhost && !*relayhost) relayhost = 0; | 768 | if (relayhost && !*relayhost) relayhost = 0; |
| 356 | 769 | ||
| 357 | if (relayhost) { | 770 | if (relayhost) { |
| 771 | i = str_chr(relayhost,' '); | ||
| 772 | if (relayhost[i]) { | ||
| 773 | j = str_chr(relayhost + i + 1,' '); | ||
| 774 | if (relayhost[j]) { | ||
| 775 | relayhost[i] = 0; | ||
| 776 | relayhost[i + j + 1] = 0; | ||
| 777 | if (!stralloc_copys(&auth_smtp_user,relayhost + i + 1)) temp_nomem(); | ||
| 778 | if (!stralloc_copys(&auth_smtp_pass,relayhost + i + j + 2)) temp_nomem(); | ||
| 779 | } | ||
| 780 | } | ||
| 358 | i = str_chr(relayhost,':'); | 781 | i = str_chr(relayhost,':'); |
| 359 | if (relayhost[i]) { | 782 | if (relayhost[i]) { |
| 360 | scan_ulong(relayhost + i + 1,&port); | 783 | scan_ulong(relayhost + i + 1,&port); |
| @@ -417,6 +840,9 @@ char **argv; | |||
| 417 | if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) { | 840 | if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) { |
| 418 | tcpto_err(&ip.ix[i].ip,0); | 841 | tcpto_err(&ip.ix[i].ip,0); |
| 419 | partner = ip.ix[i].ip; | 842 | partner = ip.ix[i].ip; |
| 843 | #ifdef TLS | ||
| 844 | partner_fqdn = ip.ix[i].fqdn; | ||
| 845 | #endif | ||
| 420 | smtp(); /* does not return */ | 846 | smtp(); /* does not return */ |
| 421 | } | 847 | } |
| 422 | tcpto_err(&ip.ix[i].ip,errno == error_timeout); | 848 | tcpto_err(&ip.ix[i].ip,errno == error_timeout); |
