1 /* 2 * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 5 * Copyright (c) 1988, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * By using this file, you agree to the terms and conditions set 9 * forth in the LICENSE file which can be found at the top level of 10 * the sendmail distribution. 11 * 12 */ 13 14 #include <sendmail.h> 15 16 #ifndef lint 17 # if SMTP 18 static char id[] = "@(#)$Id: usersmtp.c,v 8.245.4.24 2001/02/21 00:59:09 gshapiro Exp $ (with SMTP)"; 19 # else /* SMTP */ 20 static char id[] = "@(#)$Id: usersmtp.c,v 8.245.4.24 2001/02/21 00:59:09 gshapiro Exp $ (without SMTP)"; 21 # endif /* SMTP */ 22 #endif /* ! lint */ 23 24 #include <sysexits.h> 25 26 #if SMTP 27 28 29 static void datatimeout __P((void)); 30 static void esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *)); 31 static void helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *)); 32 33 /* 34 ** USERSMTP -- run SMTP protocol from the user end. 35 ** 36 ** This protocol is described in RFC821. 37 */ 38 39 # define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */ 40 # define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */ 41 # define SMTPCLOSING 421 /* "Service Shutting Down" */ 42 43 #define ENHSCN(e, d) (e) == NULL ? (d) : newstr(e) 44 45 static char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */ 46 static char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ 47 static bool SmtpNeedIntro; /* need "while talking" in transcript */ 48 /* 49 ** SMTPINIT -- initialize SMTP. 50 ** 51 ** Opens the connection and sends the initial protocol. 52 ** 53 ** Parameters: 54 ** m -- mailer to create connection to. 55 ** mci -- the mailer connection info. 56 ** e -- the envelope. 57 ** onlyhelo -- send only helo command? 58 ** 59 ** Returns: 60 ** none. 61 ** 62 ** Side Effects: 63 ** creates connection and sends initial protocol. 64 */ 65 66 void 67 smtpinit(m, mci, e, onlyhelo) 68 MAILER *m; 69 register MCI *mci; 70 ENVELOPE *e; 71 bool onlyhelo; 72 { 73 register int r; 74 register char *p; 75 register char *hn; 76 char *enhsc; 77 78 enhsc = NULL; 79 if (tTd(18, 1)) 80 { 81 dprintf("smtpinit "); 82 mci_dump(mci, FALSE); 83 } 84 85 /* 86 ** Open the connection to the mailer. 87 */ 88 89 SmtpError[0] = '\0'; 90 CurHostName = mci->mci_host; /* XXX UGLY XXX */ 91 if (CurHostName == NULL) 92 CurHostName = MyHostName; 93 SmtpNeedIntro = TRUE; 94 switch (mci->mci_state) 95 { 96 case MCIS_ACTIVE: 97 /* need to clear old information */ 98 smtprset(m, mci, e); 99 /* FALLTHROUGH */ 100 101 case MCIS_OPEN: 102 if (!onlyhelo) 103 return; 104 break; 105 106 case MCIS_ERROR: 107 case MCIS_QUITING: 108 case MCIS_SSD: 109 /* shouldn't happen */ 110 smtpquit(m, mci, e); 111 /* FALLTHROUGH */ 112 113 case MCIS_CLOSED: 114 syserr("451 4.4.0 smtpinit: state CLOSED"); 115 return; 116 117 case MCIS_OPENING: 118 break; 119 } 120 if (onlyhelo) 121 goto helo; 122 123 mci->mci_state = MCIS_OPENING; 124 125 /* 126 ** Get the greeting message. 127 ** This should appear spontaneously. Give it five minutes to 128 ** happen. 129 */ 130 131 SmtpPhase = mci->mci_phase = "client greeting"; 132 sm_setproctitle(TRUE, e, "%s %s: %s", 133 qid_printname(e), CurHostName, mci->mci_phase); 134 r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check, NULL); 135 if (r < 0) 136 goto tempfail1; 137 if (REPLYTYPE(r) == 4) 138 goto tempfail2; 139 if (REPLYTYPE(r) != 2) 140 goto unavailable; 141 142 /* 143 ** Send the HELO command. 144 ** My mother taught me to always introduce myself. 145 */ 146 147 helo: 148 if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags)) 149 mci->mci_flags |= MCIF_ESMTP; 150 hn = mci->mci_heloname ? mci->mci_heloname : MyHostName; 151 152 tryhelo: 153 if (bitnset(M_LMTP, m->m_flags)) 154 { 155 smtpmessage("LHLO %s", m, mci, hn); 156 SmtpPhase = mci->mci_phase = "client LHLO"; 157 } 158 else if (bitset(MCIF_ESMTP, mci->mci_flags)) 159 { 160 smtpmessage("EHLO %s", m, mci, hn); 161 SmtpPhase = mci->mci_phase = "client EHLO"; 162 } 163 else 164 { 165 smtpmessage("HELO %s", m, mci, hn); 166 SmtpPhase = mci->mci_phase = "client HELO"; 167 } 168 sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e), 169 CurHostName, mci->mci_phase); 170 r = reply(m, mci, e, TimeOuts.to_helo, helo_options, NULL); 171 if (r < 0) 172 goto tempfail1; 173 else if (REPLYTYPE(r) == 5) 174 { 175 if (bitset(MCIF_ESMTP, mci->mci_flags) && 176 !bitnset(M_LMTP, m->m_flags)) 177 { 178 /* try old SMTP instead */ 179 mci->mci_flags &= ~MCIF_ESMTP; 180 goto tryhelo; 181 } 182 goto unavailable; 183 } 184 else if (REPLYTYPE(r) != 2) 185 goto tempfail2; 186 187 /* 188 ** Check to see if we actually ended up talking to ourself. 189 ** This means we didn't know about an alias or MX, or we managed 190 ** to connect to an echo server. 191 */ 192 193 p = strchr(&SmtpReplyBuffer[4], ' '); 194 if (p != NULL) 195 *p = '\0'; 196 if (!bitnset(M_NOLOOPCHECK, m->m_flags) && 197 !bitnset(M_LMTP, m->m_flags) && 198 strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0) 199 { 200 syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)", 201 CurHostName); 202 mci_setstat(mci, EX_CONFIG, "5.3.5", "system config error"); 203 mci->mci_errno = 0; 204 smtpquit(m, mci, e); 205 return; 206 } 207 208 /* 209 ** If this is expected to be another sendmail, send some internal 210 ** commands. 211 */ 212 213 if (bitnset(M_INTERNAL, m->m_flags)) 214 { 215 /* tell it to be verbose */ 216 smtpmessage("VERB", m, mci); 217 r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, &enhsc); 218 if (r < 0) 219 goto tempfail1; 220 } 221 222 if (mci->mci_state != MCIS_CLOSED) 223 { 224 mci->mci_state = MCIS_OPEN; 225 return; 226 } 227 228 /* got a 421 error code during startup */ 229 230 tempfail1: 231 if (mci->mci_errno == 0) 232 mci->mci_errno = errno; 233 mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.4.2"), NULL); 234 if (mci->mci_state != MCIS_CLOSED) 235 smtpquit(m, mci, e); 236 return; 237 238 tempfail2: 239 if (mci->mci_errno == 0) 240 mci->mci_errno = errno; 241 /* XXX should use code from other end iff ENHANCEDSTATUSCODES */ 242 mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"), 243 SmtpReplyBuffer); 244 if (mci->mci_state != MCIS_CLOSED) 245 smtpquit(m, mci, e); 246 return; 247 248 unavailable: 249 mci->mci_errno = errno; 250 mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer); 251 smtpquit(m, mci, e); 252 return; 253 } 254 /* 255 ** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol 256 ** 257 ** Parameters: 258 ** line -- the response line. 259 ** firstline -- set if this is the first line of the reply. 260 ** m -- the mailer. 261 ** mci -- the mailer connection info. 262 ** e -- the envelope. 263 ** 264 ** Returns: 265 ** none. 266 */ 267 268 static void 269 esmtp_check(line, firstline, m, mci, e) 270 char *line; 271 bool firstline; 272 MAILER *m; 273 register MCI *mci; 274 ENVELOPE *e; 275 { 276 if (strstr(line, "ESMTP") != NULL) 277 mci->mci_flags |= MCIF_ESMTP; 278 if (strstr(line, "8BIT-OK") != NULL) 279 mci->mci_flags |= MCIF_8BITOK; 280 } 281 # if SASL 282 /* 283 ** STR_UNION -- create the union of two lists 284 ** 285 ** Parameters: 286 ** s1, s2 -- lists of items (separated by single blanks). 287 ** 288 ** Returns: 289 ** the union of both lists. 290 */ 291 292 static char * 293 str_union(s1, s2) 294 char *s1, *s2; 295 { 296 char *hr, *h1, *h, *res; 297 int l1, l2, rl; 298 299 if (s1 == NULL || *s1 == '\0') 300 return s2; 301 if (s2 == NULL || *s2 == '\0') 302 return s1; 303 l1 = strlen(s1); 304 l2 = strlen(s2); 305 rl = l1 + l2; 306 res = (char *)malloc(rl + 2); 307 if (res == NULL) 308 { 309 if (l1 > l2) 310 return s1; 311 return s2; 312 } 313 (void) strlcpy(res, s1, rl); 314 hr = res; 315 h1 = s2; 316 h = s2; 317 318 /* walk through s2 */ 319 while (h != NULL && *h1 != '\0') 320 { 321 /* is there something after the current word? */ 322 if ((h = strchr(h1, ' ')) != NULL) 323 *h = '\0'; 324 l1 = strlen(h1); 325 326 /* does the current word appear in s1 ? */ 327 if (iteminlist(h1, s1, " ") == NULL) 328 { 329 /* add space as delimiter */ 330 *hr++ = ' '; 331 332 /* copy the item */ 333 memcpy(hr, h1, l1); 334 335 /* advance pointer in result list */ 336 hr += l1; 337 *hr = '\0'; 338 } 339 if (h != NULL) 340 { 341 /* there are more items */ 342 *h = ' '; 343 h1 = h + 1; 344 } 345 } 346 return res; 347 } 348 # endif /* SASL */ 349 /* 350 ** HELO_OPTIONS -- process the options on a HELO line. 351 ** 352 ** Parameters: 353 ** line -- the response line. 354 ** firstline -- set if this is the first line of the reply. 355 ** m -- the mailer. 356 ** mci -- the mailer connection info. 357 ** e -- the envelope. 358 ** 359 ** Returns: 360 ** none. 361 */ 362 363 static void 364 helo_options(line, firstline, m, mci, e) 365 char *line; 366 bool firstline; 367 MAILER *m; 368 register MCI *mci; 369 ENVELOPE *e; 370 { 371 register char *p; 372 373 if (firstline) 374 { 375 # if SASL 376 if (mci->mci_saslcap != NULL) 377 free(mci->mci_saslcap); 378 mci->mci_saslcap = NULL; 379 # endif /* SASL */ 380 return; 381 } 382 383 if (strlen(line) < (SIZE_T) 5) 384 return; 385 line += 4; 386 p = strpbrk(line, " ="); 387 if (p != NULL) 388 *p++ = '\0'; 389 if (strcasecmp(line, "size") == 0) 390 { 391 mci->mci_flags |= MCIF_SIZE; 392 if (p != NULL) 393 mci->mci_maxsize = atol(p); 394 } 395 else if (strcasecmp(line, "8bitmime") == 0) 396 { 397 mci->mci_flags |= MCIF_8BITMIME; 398 mci->mci_flags &= ~MCIF_7BIT; 399 } 400 else if (strcasecmp(line, "expn") == 0) 401 mci->mci_flags |= MCIF_EXPN; 402 else if (strcasecmp(line, "dsn") == 0) 403 mci->mci_flags |= MCIF_DSN; 404 else if (strcasecmp(line, "enhancedstatuscodes") == 0) 405 mci->mci_flags |= MCIF_ENHSTAT; 406 # if STARTTLS 407 else if (strcasecmp(line, "starttls") == 0) 408 mci->mci_flags |= MCIF_TLS; 409 # endif /* STARTTLS */ 410 # if SASL 411 else if (strcasecmp(line, "auth") == 0) 412 { 413 if (p != NULL && *p != '\0') 414 { 415 if (mci->mci_saslcap != NULL) 416 { 417 char *h; 418 419 /* 420 ** create the union with previous auth 421 ** offerings because we recognize "auth " 422 ** and "auth=" (old format). 423 */ 424 h = mci->mci_saslcap; 425 mci->mci_saslcap = str_union(h, p); 426 if (h != mci->mci_saslcap) 427 free(h); 428 mci->mci_flags |= MCIF_AUTH; 429 } 430 else 431 { 432 int l; 433 434 l = strlen(p) + 1; 435 mci->mci_saslcap = (char *)malloc(l); 436 437 /* XXX this may be leaked */ 438 if (mci->mci_saslcap != NULL) 439 { 440 (void) strlcpy(mci->mci_saslcap, p, l); 441 mci->mci_flags |= MCIF_AUTH; 442 } 443 } 444 } 445 } 446 # endif /* SASL */ 447 } 448 # if SASL 449 450 /* 451 ** GETSASLDATA -- process the challenges from the SASL protocol 452 ** 453 ** This gets the relevant sasl response data out of the reply 454 ** from the server 455 ** 456 ** Parameters: 457 ** line -- the response line. 458 ** firstline -- set if this is the first line of the reply. 459 ** m -- the mailer. 460 ** mci -- the mailer connection info. 461 ** e -- the envelope. 462 ** 463 ** Returns: 464 ** none. 465 */ 466 467 void 468 getsasldata(line, firstline, m, mci, e) 469 char *line; 470 bool firstline; 471 MAILER *m; 472 register MCI *mci; 473 ENVELOPE *e; 474 { 475 int len; 476 char *out; 477 int result; 478 479 /* if not a continue we don't care about it */ 480 if ((strlen(line) <= 4) || 481 (line[0] != '3') || 482 (line[1] != '3') || 483 (line[2] != '4')) 484 { 485 mci->mci_sasl_string = NULL; 486 return; 487 } 488 489 /* forget about "334 " */ 490 line += 4; 491 len = strlen(line); 492 493 out = xalloc(len + 1); 494 result = sasl_decode64(line, len, out, (u_int *)&len); 495 if (result != SASL_OK) 496 { 497 len = 0; 498 *out = '\0'; 499 } 500 if (mci->mci_sasl_string != NULL) 501 { 502 if (mci->mci_sasl_string_len <= len) 503 { 504 free(mci->mci_sasl_string); 505 mci->mci_sasl_string = xalloc(len + 1); 506 } 507 } 508 else 509 mci->mci_sasl_string = xalloc(len + 1); 510 /* XXX this is probably leaked */ 511 memcpy(mci->mci_sasl_string, out, len); 512 mci->mci_sasl_string[len] = '\0'; 513 mci->mci_sasl_string_len = len; 514 free(out); 515 return; 516 } 517 518 /* 519 ** READAUTH -- read auth value from a file 520 ** 521 ** Parameters: 522 ** l -- line to define. 523 ** filename -- name of file to read. 524 ** safe -- if set, this is a safe read. 525 ** 526 ** Returns: 527 ** line from file 528 ** 529 ** Side Effects: 530 ** overwrites local static buffer. The caller should copy 531 ** the result. 532 ** 533 */ 534 535 /* lines in authinfo file */ 536 # define SASL_USER 1 537 # define SASL_AUTHID 2 538 # define SASL_PASSWORD 3 539 # define SASL_DEFREALM 4 540 # define SASL_MECH 5 541 542 static char *sasl_info_name[] = 543 { 544 "", 545 "user id", 546 "authorization id", 547 "password", 548 "realm", 549 "mechanism" 550 }; 551 552 static char * 553 readauth(l, filename, safe) 554 int l; 555 char *filename; 556 bool safe; 557 { 558 FILE *f; 559 long sff; 560 pid_t pid; 561 int lc; 562 static char buf[MAXLINE]; 563 564 if (filename == NULL || filename[0] == '\0') 565 return ""; 566 #if !_FFR_ALLOW_SASLINFO 567 /* 568 ** make sure we don't use a program that is not 569 ** accesible to the user who specified a different authinfo file. 570 ** However, currently we don't pass this info (authinfo file 571 ** specified by user) around, so we just turn off program access. 572 */ 573 if (filename[0] == '|') 574 { 575 auto int fd; 576 int i; 577 char *p; 578 char *argv[MAXPV + 1]; 579 580 i = 0; 581 for (p = strtok(&filename[1], " \t"); p != NULL; 582 p = strtok(NULL, " \t")) 583 { 584 if (i >= MAXPV) 585 break; 586 argv[i++] = p; 587 } 588 argv[i] = NULL; 589 pid = prog_open(argv, &fd, CurEnv); 590 if (pid < 0) 591 f = NULL; 592 else 593 f = fdopen(fd, "r"); 594 } 595 else 596 #endif /* !_FFR_ALLOW_SASLINFO */ 597 { 598 pid = -1; 599 sff = SFF_REGONLY | SFF_SAFEDIRPATH | SFF_NOWLINK 600 | SFF_NOGWFILES | SFF_NOWWFILES | SFF_NORFILES; 601 if (DontLockReadFiles) 602 sff |= SFF_NOLOCK; 603 #if _FFR_ALLOW_SASLINFO 604 /* 605 ** XXX: make sure we don't read or open files that are not 606 ** accesible to the user who specified a different authinfo 607 ** file. 608 */ 609 sff |= SFF_MUSTOWN; 610 #else /* _FFR_ALLOW_SASLINFO */ 611 if (safe) 612 sff |= SFF_OPENASROOT; 613 #endif /* _FFR_ALLOW_SASLINFO */ 614 615 f = safefopen(filename, O_RDONLY, 0, sff); 616 } 617 if (f == NULL) 618 { 619 syserr("readauth: cannot open %s", filename); 620 return ""; 621 } 622 623 lc = 0; 624 while (lc < l && fgets(buf, sizeof buf, f) != NULL) 625 { 626 if (buf[0] != '#') 627 lc++; 628 } 629 630 (void) fclose(f); 631 if (pid > 0) 632 (void) waitfor(pid); 633 if (lc < l) 634 { 635 if (LogLevel >= 9) 636 sm_syslog(LOG_WARNING, NOQID, "SASL: error: can't read %s from %s", 637 sasl_info_name[l], filename); 638 return ""; 639 } 640 lc = strlen(buf) - 1; 641 if (lc >= 0) 642 buf[lc] = '\0'; 643 if (tTd(95, 6)) 644 dprintf("readauth(%s, %d) = '%s'\n", filename, l, buf); 645 return buf; 646 } 647 648 # ifndef __attribute__ 649 # define __attribute__(x) 650 # endif /* ! __attribute__ */ 651 652 static int getsimple __P((void *, int, const char **, unsigned *)); 653 static int getsecret __P((sasl_conn_t *, void *, int, sasl_secret_t **)); 654 static int saslgetrealm __P((void *, int, const char **, const char **)); 655 656 static sasl_callback_t callbacks[] = 657 { 658 { SASL_CB_GETREALM, &saslgetrealm, NULL }, 659 # define CB_GETREALM_IDX 0 660 { SASL_CB_PASS, &getsecret, NULL }, 661 # define CB_PASS_IDX 1 662 { SASL_CB_USER, &getsimple, NULL }, 663 # define CB_USER_IDX 2 664 { SASL_CB_AUTHNAME, &getsimple, NULL }, 665 # define CB_AUTHNAME_IDX 3 666 { SASL_CB_VERIFYFILE, &safesaslfile, NULL }, 667 { SASL_CB_LIST_END, NULL, NULL } 668 }; 669 670 /* 671 ** GETSIMPLE -- callback to get userid or authid 672 ** 673 ** Parameters: 674 ** context -- unused 675 ** id -- what to do 676 ** result -- (pointer to) result 677 ** len -- (pointer to) length of result 678 ** 679 ** Returns: 680 ** OK/failure values 681 */ 682 683 static int 684 getsimple(context, id, result, len) 685 void *context __attribute__((unused)); 686 int id; 687 const char **result; 688 unsigned *len; 689 { 690 char *h; 691 # if SASL > 10509 692 int addrealm; 693 static int addedrealm = FALSE; 694 # endif /* SASL > 10509 */ 695 static char *user = NULL; 696 static char *authid = NULL; 697 698 if (result == NULL) 699 return SASL_BADPARAM; 700 701 switch (id) 702 { 703 case SASL_CB_USER: 704 if (user == NULL) 705 { 706 h = readauth(SASL_USER, SASLInfo, TRUE); 707 user = newstr(h); 708 } 709 *result = user; 710 if (tTd(95, 5)) 711 dprintf("AUTH username '%s'\n", *result); 712 if (len != NULL) 713 *len = user ? strlen(user) : 0; 714 break; 715 716 case SASL_CB_AUTHNAME: 717 # if SASL > 10509 718 /* XXX maybe other mechanisms too?! */ 719 addrealm = context != NULL && 720 strcasecmp(context, "CRAM-MD5") == 0; 721 if (addedrealm != addrealm && authid != NULL) 722 { 723 # if SASL > 10522 724 /* 725 ** digest-md5 prior to 1.5.23 doesn't copy the 726 ** value it gets from the callback, but free()s 727 ** it later on 728 ** workaround: don't free() it here 729 ** this can cause a memory leak! 730 */ 731 free(authid); 732 # endif /* SASL > 10522 */ 733 authid = NULL; 734 addedrealm = addrealm; 735 } 736 # endif /* SASL > 10509 */ 737 if (authid == NULL) 738 { 739 h = readauth(SASL_AUTHID, SASLInfo, TRUE); 740 # if SASL > 10509 741 if (addrealm && strchr(h, '@') == NULL) 742 { 743 size_t l; 744 char *realm; 745 746 realm = callbacks[CB_GETREALM_IDX].context; 747 l = strlen(h) + strlen(realm) + 2; 748 authid = xalloc(l); 749 snprintf(authid, l, "%s@%s", h, realm); 750 } 751 else 752 # endif /* SASL > 10509 */ 753 authid = newstr(h); 754 } 755 *result = authid; 756 if (tTd(95, 5)) 757 dprintf("AUTH authid '%s'\n", *result); 758 if (len != NULL) 759 *len = authid ? strlen(authid) : 0; 760 break; 761 762 case SASL_CB_LANGUAGE: 763 *result = NULL; 764 if (len != NULL) 765 *len = 0; 766 break; 767 768 default: 769 return SASL_BADPARAM; 770 } 771 return SASL_OK; 772 } 773 774 /* 775 ** GETSECRET -- callback to get password 776 ** 777 ** Parameters: 778 ** conn -- connection information 779 ** context -- unused 780 ** id -- what to do 781 ** psecret -- (pointer to) result 782 ** 783 ** Returns: 784 ** OK/failure values 785 */ 786 787 static int 788 getsecret(conn, context, id, psecret) 789 sasl_conn_t *conn; 790 void *context __attribute__((unused)); 791 int id; 792 sasl_secret_t **psecret; 793 { 794 char *h; 795 int len; 796 static char *authpass = NULL; 797 798 if (conn == NULL || psecret == NULL || id != SASL_CB_PASS) 799 return SASL_BADPARAM; 800 801 if (authpass == NULL) 802 { 803 h = readauth(SASL_PASSWORD, SASLInfo, TRUE); 804 authpass = newstr(h); 805 } 806 len = strlen(authpass); 807 *psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len + 1); 808 if (*psecret == NULL) 809 return SASL_FAIL; 810 (void) strlcpy((*psecret)->data, authpass, len + 1); 811 (*psecret)->len = len; 812 return SASL_OK; 813 } 814 815 /* 816 ** SAFESASLFILE -- callback for sasl: is file safe? 817 ** 818 ** Parameters: 819 ** context -- pointer to context between invocations (unused) 820 ** file -- name of file to check 821 ** type -- type of file to check 822 ** 823 ** Returns: 824 ** SASL_OK: file can be used 825 ** SASL_CONTINUE: don't use file 826 ** SASL_FAIL: failure (not used here) 827 ** 828 */ 829 int 830 # if SASL > 10515 831 safesaslfile(context, file, type) 832 # else /* SASL > 10515 */ 833 safesaslfile(context, file) 834 # endif /* SASL > 10515 */ 835 void *context; 836 char *file; 837 # if SASL > 10515 838 int type; 839 # endif /* SASL > 10515 */ 840 { 841 long sff; 842 int r; 843 char *p; 844 845 if (file == NULL || *file == '\0') 846 return SASL_OK; 847 848 sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOGWFILES|SFF_NOWWFILES|SFF_ROOTOK; 849 if ((p = strrchr(file, '/')) == NULL) 850 p = file; 851 else 852 ++p; 853 854 # if SASL <= 10515 855 /* everything beside libs and .conf files must not be readable */ 856 r = strlen(p); 857 if ((r <= 3 || strncmp(p, "lib", 3) != 0) && 858 (r <= 5 || strncmp(p + r - 5, ".conf", 5) != 0) 859 # if _FFR_UNSAFE_SASL 860 && !bitnset(DBS_GROUPREADABLESASLFILE, DontBlameSendmail) 861 # endif /* _FFR_UNSAFE_SASL */ 862 ) 863 sff |= SFF_NORFILES; 864 # else /* SASL > 10515 */ 865 /* files containing passwords should be not readable */ 866 if (type == SASL_VRFY_PASSWD) 867 { 868 # if _FFR_UNSAFE_SASL 869 if (bitnset(DBS_GROUPREADABLESASLFILE, DontBlameSendmail)) 870 sff |= SFF_NOWRFILES; 871 else 872 # endif /* _FFR_UNSAFE_SASL */ 873 sff |= SFF_NORFILES; 874 } 875 # endif /* SASL <= 10515 */ 876 877 p = file; 878 if ((r = safefile(p, RunAsUid, RunAsGid, RunAsUserName, sff, 879 S_IRUSR, NULL)) == 0) 880 return SASL_OK; 881 if (LogLevel >= 11 || (r != ENOENT && LogLevel >= 9)) 882 sm_syslog(LOG_WARNING, NOQID, "error: safesasl(%s) failed: %s", 883 p, errstring(r)); 884 return SASL_CONTINUE; 885 } 886 887 /* 888 ** SASLGETREALM -- return the realm for SASL 889 ** 890 ** return the realm for the client 891 ** 892 ** Parameters: 893 ** context -- context shared between invocations 894 ** here: realm to return 895 ** availrealms -- list of available realms 896 ** {realm, realm, ...} 897 ** result -- pointer to result 898 ** 899 ** Returns: 900 ** failure/success 901 */ 902 static int 903 saslgetrealm(context, id, availrealms, result) 904 void *context; 905 int id; 906 const char **availrealms; 907 const char **result; 908 { 909 if (LogLevel > 12) 910 sm_syslog(LOG_INFO, NOQID, "saslgetrealm: realm %s available realms %s", 911 context == NULL ? "<No Context>" : (char *) context, 912 (availrealms == NULL || *availrealms == NULL) ? "<No Realms>" : *availrealms); 913 if (context == NULL) 914 return SASL_FAIL; 915 916 /* check whether context is in list? */ 917 if (availrealms != NULL && *availrealms != NULL) 918 { 919 if (iteminlist(context, (char *)(*availrealms + 1), " ,}") == 920 NULL) 921 { 922 if (LogLevel > 8) 923 sm_syslog(LOG_ERR, NOQID, 924 "saslgetrealm: realm %s not in list %s", 925 context, *availrealms); 926 return SASL_FAIL; 927 } 928 } 929 *result = (char *)context; 930 return SASL_OK; 931 } 932 /* 933 ** ITEMINLIST -- does item appear in list? 934 ** 935 ** Check whether item appears in list (which must be separated by a 936 ** character in delim) as a "word", i.e. it must appear at the begin 937 ** of the list or after a space, and it must end with a space or the 938 ** end of the list. 939 ** 940 ** Parameters: 941 ** item -- item to search. 942 ** list -- list of items. 943 ** delim -- list of delimiters. 944 ** 945 ** Returns: 946 ** pointer to occurrence (NULL if not found). 947 */ 948 949 char * 950 iteminlist(item, list, delim) 951 char *item; 952 char *list; 953 char *delim; 954 { 955 char *s; 956 int len; 957 958 if (list == NULL || *list == '\0') 959 return NULL; 960 if (item == NULL || *item == '\0') 961 return NULL; 962 s = list; 963 len = strlen(item); 964 while (s != NULL && *s != '\0') 965 { 966 if (strncasecmp(s, item, len) == 0 && 967 (s[len] == '\0' || strchr(delim, s[len]) != NULL)) 968 return s; 969 s = strpbrk(s, delim); 970 if (s != NULL) 971 while (*++s == ' ') 972 continue; 973 } 974 return NULL; 975 } 976 /* 977 ** REMOVEMECH -- remove item [rem] from list [list] 978 ** 979 ** Parameters: 980 ** rem -- item to remove 981 ** list -- list of items 982 ** 983 ** Returns: 984 ** pointer to new list (NULL in case of error). 985 */ 986 987 char * 988 removemech(rem, list) 989 char *rem; 990 char *list; 991 { 992 char *ret; 993 char *needle; 994 int len; 995 996 if (list == NULL) 997 return NULL; 998 if (rem == NULL || *rem == '\0') 999 { 1000 /* take out what? */ 1001 return NULL; 1002 } 1003 1004 /* find the item in the list */ 1005 if ((needle = iteminlist(rem, list, " ")) == NULL) 1006 { 1007 /* not in there: return original */ 1008 return list; 1009 } 1010 1011 /* length of string without rem */ 1012 len = strlen(list) - strlen(rem); 1013 if (len == 0) 1014 { 1015 ret = xalloc(1); /* XXX leaked */ 1016 *ret = '\0'; 1017 return ret; 1018 } 1019 ret = xalloc(len); /* XXX leaked */ 1020 memset(ret, '\0', len); 1021 1022 /* copy from start to removed item */ 1023 memcpy(ret, list, needle - list); 1024 1025 /* length of rest of string past removed item */ 1026 len = strlen(needle) - strlen(rem) - 1; 1027 if (len > 0) 1028 { 1029 /* not last item -- copy into string */ 1030 memcpy(ret + (needle - list), 1031 list + (needle - list) + strlen(rem) + 1, 1032 len); 1033 } 1034 else 1035 ret[(needle - list) - 1] = '\0'; 1036 return ret; 1037 } 1038 /* 1039 ** INTERSECT -- create the intersection between two lists 1040 ** 1041 ** Parameters: 1042 ** s1, s2 -- lists of items (separated by single blanks). 1043 ** 1044 ** Returns: 1045 ** the intersection of both lists. 1046 */ 1047 1048 char * 1049 intersect(s1, s2) 1050 char *s1, *s2; 1051 { 1052 char *hr, *h1, *h, *res; 1053 int l1, l2, rl; 1054 1055 if (s1 == NULL || s2 == NULL) /* NULL string(s) -> NULL result */ 1056 return NULL; 1057 l1 = strlen(s1); 1058 l2 = strlen(s2); 1059 rl = min(l1, l2); 1060 res = (char *)malloc(rl + 1); 1061 if (res == NULL) 1062 return NULL; 1063 *res = '\0'; 1064 if (rl == 0) /* at least one string empty? */ 1065 return res; 1066 hr = res; 1067 h1 = s1; 1068 h = s1; 1069 1070 /* walk through s1 */ 1071 while (h != NULL && *h1 != '\0') 1072 { 1073 /* is there something after the current word? */ 1074 if ((h = strchr(h1, ' ')) != NULL) 1075 *h = '\0'; 1076 l1 = strlen(h1); 1077 1078 /* does the current word appear in s2 ? */ 1079 if (iteminlist(h1, s2, " ") != NULL) 1080 { 1081 /* add a blank if not first item */ 1082 if (hr != res) 1083 *hr++ = ' '; 1084 1085 /* copy the item */ 1086 memcpy(hr, h1, l1); 1087 1088 /* advance pointer in result list */ 1089 hr += l1; 1090 *hr = '\0'; 1091 } 1092 if (h != NULL) 1093 { 1094 /* there are more items */ 1095 *h = ' '; 1096 h1 = h + 1; 1097 } 1098 } 1099 return res; 1100 } 1101 /* 1102 ** ATTEMPTAUTH -- try to AUTHenticate using one mechanism 1103 ** 1104 ** Parameters: 1105 ** m -- the mailer. 1106 ** mci -- the mailer connection structure. 1107 ** e -- the envelope (including the sender to specify). 1108 ** mechused - filled in with mechanism used 1109 ** 1110 ** Returns: 1111 ** EX_OK/EX_TEMPFAIL 1112 */ 1113 1114 int 1115 attemptauth(m, mci, e, mechused) 1116 MAILER *m; 1117 MCI *mci; 1118 ENVELOPE *e; 1119 char **mechused; 1120 { 1121 int saslresult, smtpresult; 1122 sasl_external_properties_t ssf; 1123 sasl_interact_t *client_interact = NULL; 1124 char *out; 1125 unsigned int outlen; 1126 static char *mechusing; 1127 sasl_security_properties_t ssp; 1128 char in64[MAXOUTLEN]; 1129 # if NETINET 1130 extern SOCKADDR CurHostAddr; 1131 # endif /* NETINET */ 1132 1133 *mechused = NULL; 1134 if (mci->mci_conn != NULL) 1135 { 1136 sasl_dispose(&(mci->mci_conn)); 1137 1138 /* just in case, sasl_dispose() should take care of it */ 1139 mci->mci_conn = NULL; 1140 } 1141 1142 /* make a new client sasl connection */ 1143 saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp" 1144 : "smtp", 1145 CurHostName, NULL, 0, &mci->mci_conn); 1146 1147 /* set properties */ 1148 (void) memset(&ssp, '\0', sizeof ssp); 1149 # if SFIO 1150 /* XXX should these be options settable via .cf ? */ 1151 /* ssp.min_ssf = 0; is default due to memset() */ 1152 { 1153 ssp.max_ssf = INT_MAX; 1154 ssp.maxbufsize = MAXOUTLEN; 1155 # if 0 1156 ssp.security_flags = SASL_SEC_NOPLAINTEXT; 1157 # endif /* 0 */ 1158 } 1159 # endif /* SFIO */ 1160 saslresult = sasl_setprop(mci->mci_conn, SASL_SEC_PROPS, &ssp); 1161 if (saslresult != SASL_OK) 1162 return EX_TEMPFAIL; 1163 1164 /* external security strength factor, authentication id */ 1165 ssf.ssf = 0; 1166 ssf.auth_id = NULL; 1167 # if _FFR_EXT_MECH 1168 out = macvalue(macid("{cert_subject}", NULL), e); 1169 if (out != NULL && *out != '\0') 1170 ssf.auth_id = out; 1171 out = macvalue(macid("{cipher_bits}", NULL), e); 1172 if (out != NULL && *out != '\0') 1173 ssf.ssf = atoi(out); 1174 # endif /* _FFR_EXT_MECH */ 1175 saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf); 1176 if (saslresult != SASL_OK) 1177 return EX_TEMPFAIL; 1178 1179 # if NETINET 1180 /* set local/remote ipv4 addresses */ 1181 if (mci->mci_out != NULL && CurHostAddr.sa.sa_family == AF_INET) 1182 { 1183 SOCKADDR_LEN_T addrsize; 1184 struct sockaddr_in saddr_l; 1185 1186 if (sasl_setprop(mci->mci_conn, SASL_IP_REMOTE, 1187 (struct sockaddr_in *) &CurHostAddr) 1188 != SASL_OK) 1189 return EX_TEMPFAIL; 1190 addrsize = sizeof(struct sockaddr_in); 1191 if (getsockname(fileno(mci->mci_out), 1192 (struct sockaddr *) &saddr_l, &addrsize) == 0) 1193 { 1194 if (sasl_setprop(mci->mci_conn, SASL_IP_LOCAL, 1195 &saddr_l) != SASL_OK) 1196 return EX_TEMPFAIL; 1197 } 1198 } 1199 # endif /* NETINET */ 1200 1201 /* start client side of sasl */ 1202 saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap, 1203 NULL, &client_interact, 1204 &out, &outlen, 1205 (const char **)&mechusing); 1206 callbacks[CB_AUTHNAME_IDX].context = mechusing; 1207 1208 if (saslresult != SASL_OK && saslresult != SASL_CONTINUE) 1209 { 1210 # if SFIO 1211 if (saslresult == SASL_NOMECH && LogLevel > 8) 1212 { 1213 sm_syslog(LOG_NOTICE, e->e_id, 1214 "available AUTH mechanisms do not fulfill requirements"); 1215 } 1216 # endif /* SFIO */ 1217 return EX_TEMPFAIL; 1218 } 1219 1220 *mechused = mechusing; 1221 1222 /* send the info across the wire */ 1223 if (outlen > 0) 1224 { 1225 saslresult = sasl_encode64(out, outlen, in64, MAXOUTLEN, NULL); 1226 if (saslresult != SASL_OK) /* internal error */ 1227 { 1228 if (LogLevel > 8) 1229 sm_syslog(LOG_ERR, e->e_id, 1230 "encode64 for AUTH failed"); 1231 return EX_TEMPFAIL; 1232 } 1233 smtpmessage("AUTH %s %s", m, mci, mechusing, in64); 1234 } 1235 else 1236 { 1237 smtpmessage("AUTH %s", m, mci, mechusing); 1238 } 1239 1240 /* get the reply */ 1241 smtpresult = reply(m, mci, e, TimeOuts.to_datafinal, getsasldata, NULL); 1242 /* which timeout? XXX */ 1243 1244 for (;;) 1245 { 1246 /* check return code from server */ 1247 if (smtpresult == 235) 1248 { 1249 define(macid("{auth_type}", NULL), 1250 newstr(mechusing), e); 1251 # if !SFIO 1252 if (LogLevel > 9) 1253 sm_syslog(LOG_INFO, NOQID, 1254 "SASL: outgoing connection to %.64s: mech=%.16s", 1255 mci->mci_host, mechusing); 1256 # endif /* !SFIO */ 1257 return EX_OK; 1258 } 1259 if (smtpresult == -1) 1260 return EX_IOERR; 1261 if (smtpresult != 334) 1262 return EX_TEMPFAIL; 1263 1264 saslresult = sasl_client_step(mci->mci_conn, 1265 mci->mci_sasl_string, 1266 mci->mci_sasl_string_len, 1267 &client_interact, 1268 &out, &outlen); 1269 1270 if (saslresult != SASL_OK && saslresult != SASL_CONTINUE) 1271 { 1272 if (tTd(95, 5)) 1273 dprintf("AUTH FAIL: %s (%d)\n", 1274 sasl_errstring(saslresult, NULL, NULL), 1275 saslresult); 1276 1277 /* fail deliberately, see RFC 2254 4. */ 1278 smtpmessage("*", m, mci); 1279 1280 /* 1281 ** but we should only fail for this authentication 1282 ** mechanism; how to do that? 1283 */ 1284 1285 smtpresult = reply(m, mci, e, TimeOuts.to_datafinal, 1286 getsasldata, NULL); 1287 return EX_TEMPFAIL; 1288 } 1289 1290 if (outlen > 0) 1291 { 1292 saslresult = sasl_encode64(out, outlen, in64, 1293 MAXOUTLEN, NULL); 1294 if (saslresult != SASL_OK) 1295 { 1296 /* give an error reply to the other side! */ 1297 smtpmessage("*", m, mci); 1298 return EX_TEMPFAIL; 1299 } 1300 } 1301 else 1302 in64[0] = '\0'; 1303 smtpmessage("%s", m, mci, in64); 1304 smtpresult = reply(m, mci, e, TimeOuts.to_datafinal, 1305 getsasldata, NULL); 1306 /* which timeout? XXX */ 1307 } 1308 /* NOTREACHED */ 1309 } 1310 1311 /* 1312 ** SMTPAUTH -- try to AUTHenticate 1313 ** 1314 ** This will try mechanisms in the order the sasl library decided until: 1315 ** - there are no more mechanisms 1316 ** - a mechanism succeeds 1317 ** - the sasl library fails initializing 1318 ** 1319 ** Parameters: 1320 ** m -- the mailer. 1321 ** mci -- the mailer connection info. 1322 ** e -- the envelope. 1323 ** 1324 ** Returns: 1325 ** EX_OK/EX_TEMPFAIL 1326 */ 1327 1328 int 1329 smtpauth(m, mci, e) 1330 MAILER *m; 1331 MCI *mci; 1332 ENVELOPE *e; 1333 { 1334 int result; 1335 char *mechused; 1336 char *h; 1337 static char *defrealm = NULL; 1338 static char *mechs = NULL; 1339 1340 mci->mci_sasl_auth = FALSE; 1341 if (defrealm == NULL) 1342 { 1343 h = readauth(SASL_DEFREALM, SASLInfo, TRUE); 1344 if (h != NULL && *h != '\0') 1345 defrealm = newstr(h); 1346 } 1347 if (defrealm == NULL || *defrealm == '\0') 1348 defrealm = newstr(macvalue('j', CurEnv)); 1349 callbacks[CB_GETREALM_IDX].context = defrealm; 1350 1351 # if _FFR_DEFAUTHINFO_MECHS 1352 if (mechs == NULL) 1353 { 1354 h = readauth(SASL_MECH, SASLInfo, TRUE); 1355 if (h != NULL && *h != '\0') 1356 mechs = newstr(h); 1357 } 1358 # endif /* _FFR_DEFAUTHINFO_MECHS */ 1359 if (mechs == NULL || *mechs == '\0') 1360 mechs = AuthMechanisms; 1361 mci->mci_saslcap = intersect(mechs, mci->mci_saslcap); 1362 1363 /* initialize sasl client library */ 1364 result = sasl_client_init(callbacks); 1365 if (result != SASL_OK) 1366 return EX_TEMPFAIL; 1367 do 1368 { 1369 result = attemptauth(m, mci, e, &mechused); 1370 if (result == EX_OK) 1371 mci->mci_sasl_auth = TRUE; 1372 else if (result == EX_TEMPFAIL) 1373 { 1374 mci->mci_saslcap = removemech(mechused, 1375 mci->mci_saslcap); 1376 if (mci->mci_saslcap == NULL || 1377 *(mci->mci_saslcap) == '\0') 1378 return EX_TEMPFAIL; 1379 } 1380 else /* all others for now */ 1381 return EX_TEMPFAIL; 1382 } while (result != EX_OK); 1383 return result; 1384 } 1385 # endif /* SASL */ 1386 1387 /* 1388 ** SMTPMAILFROM -- send MAIL command 1389 ** 1390 ** Parameters: 1391 ** m -- the mailer. 1392 ** mci -- the mailer connection structure. 1393 ** e -- the envelope (including the sender to specify). 1394 */ 1395 1396 int 1397 smtpmailfrom(m, mci, e) 1398 MAILER *m; 1399 MCI *mci; 1400 ENVELOPE *e; 1401 { 1402 int r; 1403 char *bufp; 1404 char *bodytype; 1405 char buf[MAXNAME + 1]; 1406 char optbuf[MAXLINE]; 1407 char *enhsc; 1408 1409 if (tTd(18, 2)) 1410 dprintf("smtpmailfrom: CurHost=%s\n", CurHostName); 1411 enhsc = NULL; 1412 1413 /* set up appropriate options to include */ 1414 if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0) 1415 { 1416 snprintf(optbuf, sizeof optbuf, " SIZE=%ld", e->e_msgsize); 1417 bufp = &optbuf[strlen(optbuf)]; 1418 } 1419 else 1420 { 1421 optbuf[0] = '\0'; 1422 bufp = optbuf; 1423 } 1424 1425 bodytype = e->e_bodytype; 1426 if (bitset(MCIF_8BITMIME, mci->mci_flags)) 1427 { 1428 if (bodytype == NULL && 1429 bitset(MM_MIME8BIT, MimeMode) && 1430 bitset(EF_HAS8BIT, e->e_flags) && 1431 !bitset(EF_DONT_MIME, e->e_flags) && 1432 !bitnset(M_8BITS, m->m_flags)) 1433 bodytype = "8BITMIME"; 1434 if (bodytype != NULL && 1435 SPACELEFT(optbuf, bufp) > strlen(bodytype) + 7) 1436 { 1437 snprintf(bufp, SPACELEFT(optbuf, bufp), 1438 " BODY=%s", bodytype); 1439 bufp += strlen(bufp); 1440 } 1441 } 1442 else if (bitnset(M_8BITS, m->m_flags) || 1443 !bitset(EF_HAS8BIT, e->e_flags) || 1444 bitset(MCIF_8BITOK, mci->mci_flags)) 1445 { 1446 /* EMPTY */ 1447 /* just pass it through */ 1448 } 1449 # if MIME8TO7 1450 else if (bitset(MM_CVTMIME, MimeMode) && 1451 !bitset(EF_DONT_MIME, e->e_flags) && 1452 (!bitset(MM_PASS8BIT, MimeMode) || 1453 bitset(EF_IS_MIME, e->e_flags))) 1454 { 1455 /* must convert from 8bit MIME format to 7bit encoded */ 1456 mci->mci_flags |= MCIF_CVT8TO7; 1457 } 1458 # endif /* MIME8TO7 */ 1459 else if (!bitset(MM_PASS8BIT, MimeMode)) 1460 { 1461 /* cannot just send a 8-bit version */ 1462 extern char MsgBuf[]; 1463 1464 usrerrenh("5.6.3", "%s does not support 8BITMIME", CurHostName); 1465 mci_setstat(mci, EX_NOTSTICKY, "5.6.3", MsgBuf); 1466 return EX_DATAERR; 1467 } 1468 1469 if (bitset(MCIF_DSN, mci->mci_flags)) 1470 { 1471 if (e->e_envid != NULL && 1472 SPACELEFT(optbuf, bufp) > strlen(e->e_envid) + 7) 1473 { 1474 snprintf(bufp, SPACELEFT(optbuf, bufp), 1475 " ENVID=%s", e->e_envid); 1476 bufp += strlen(bufp); 1477 } 1478 1479 /* RET= parameter */ 1480 if (bitset(EF_RET_PARAM, e->e_flags) && 1481 SPACELEFT(optbuf, bufp) > 9) 1482 { 1483 snprintf(bufp, SPACELEFT(optbuf, bufp), 1484 " RET=%s", 1485 bitset(EF_NO_BODY_RETN, e->e_flags) ? 1486 "HDRS" : "FULL"); 1487 bufp += strlen(bufp); 1488 } 1489 } 1490 1491 if (bitset(MCIF_AUTH, mci->mci_flags) && e->e_auth_param != NULL && 1492 SPACELEFT(optbuf, bufp) > strlen(e->e_auth_param) + 7 1493 # if SASL 1494 && (!bitset(SASL_AUTH_AUTH, SASLOpts) || mci->mci_sasl_auth) 1495 # endif /* SASL */ 1496 ) 1497 { 1498 snprintf(bufp, SPACELEFT(optbuf, bufp), 1499 " AUTH=%s", e->e_auth_param); 1500 bufp += strlen(bufp); 1501 } 1502 1503 /* 1504 ** Send the MAIL command. 1505 ** Designates the sender. 1506 */ 1507 1508 mci->mci_state = MCIS_ACTIVE; 1509 1510 if (bitset(EF_RESPONSE, e->e_flags) && 1511 !bitnset(M_NO_NULL_FROM, m->m_flags)) 1512 buf[0] = '\0'; 1513 else 1514 expand("\201g", buf, sizeof buf, e); 1515 if (buf[0] == '<') 1516 { 1517 /* strip off <angle brackets> (put back on below) */ 1518 bufp = &buf[strlen(buf) - 1]; 1519 if (*bufp == '>') 1520 *bufp = '\0'; 1521 bufp = &buf[1]; 1522 } 1523 else 1524 bufp = buf; 1525 if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) || 1526 !bitnset(M_FROMPATH, m->m_flags)) 1527 { 1528 smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf); 1529 } 1530 else 1531 { 1532 smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName, 1533 *bufp == '@' ? ',' : ':', bufp, optbuf); 1534 } 1535 SmtpPhase = mci->mci_phase = "client MAIL"; 1536 sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e), 1537 CurHostName, mci->mci_phase); 1538 r = reply(m, mci, e, TimeOuts.to_mail, NULL, &enhsc); 1539 if (r < 0) 1540 { 1541 /* communications failure */ 1542 mci->mci_errno = errno; 1543 mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL); 1544 smtpquit(m, mci, e); 1545 return EX_TEMPFAIL; 1546 } 1547 else if (r == SMTPCLOSING) 1548 { 1549 /* service shutting down */ 1550 mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"), 1551 SmtpReplyBuffer); 1552 smtpquit(m, mci, e); 1553 return EX_TEMPFAIL; 1554 } 1555 else if (REPLYTYPE(r) == 4) 1556 { 1557 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, smtptodsn(r)), 1558 SmtpReplyBuffer); 1559 return EX_TEMPFAIL; 1560 } 1561 else if (REPLYTYPE(r) == 2) 1562 { 1563 return EX_OK; 1564 } 1565 else if (r == 501) 1566 { 1567 /* syntax error in arguments */ 1568 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.5.2"), 1569 SmtpReplyBuffer); 1570 return EX_DATAERR; 1571 } 1572 else if (r == 553) 1573 { 1574 /* mailbox name not allowed */ 1575 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.1.3"), 1576 SmtpReplyBuffer); 1577 return EX_DATAERR; 1578 } 1579 else if (r == 552) 1580 { 1581 /* exceeded storage allocation */ 1582 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.3.4"), 1583 SmtpReplyBuffer); 1584 if (bitset(MCIF_SIZE, mci->mci_flags)) 1585 e->e_flags |= EF_NO_BODY_RETN; 1586 return EX_UNAVAILABLE; 1587 } 1588 else if (REPLYTYPE(r) == 5) 1589 { 1590 /* unknown error */ 1591 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.0.0"), 1592 SmtpReplyBuffer); 1593 return EX_UNAVAILABLE; 1594 } 1595 1596 if (LogLevel > 1) 1597 { 1598 sm_syslog(LOG_CRIT, e->e_id, 1599 "%.100s: SMTP MAIL protocol error: %s", 1600 CurHostName, 1601 shortenstring(SmtpReplyBuffer, 403)); 1602 } 1603 1604 /* protocol error -- close up */ 1605 mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"), 1606 SmtpReplyBuffer); 1607 smtpquit(m, mci, e); 1608 return EX_PROTOCOL; 1609 } 1610 /* 1611 ** SMTPRCPT -- designate recipient. 1612 ** 1613 ** Parameters: 1614 ** to -- address of recipient. 1615 ** m -- the mailer we are sending to. 1616 ** mci -- the connection info for this transaction. 1617 ** e -- the envelope for this transaction. 1618 ** 1619 ** Returns: 1620 ** exit status corresponding to recipient status. 1621 ** 1622 ** Side Effects: 1623 ** Sends the mail via SMTP. 1624 */ 1625 1626 int 1627 smtprcpt(to, m, mci, e) 1628 ADDRESS *to; 1629 register MAILER *m; 1630 MCI *mci; 1631 ENVELOPE *e; 1632 { 1633 register int r; 1634 char *bufp; 1635 char optbuf[MAXLINE]; 1636 char *enhsc; 1637 1638 enhsc = NULL; 1639 optbuf[0] = '\0'; 1640 bufp = optbuf; 1641 1642 /* 1643 ** warning: in the following it is assumed that the free space 1644 ** in bufp is sizeof optbuf 1645 */ 1646 if (bitset(MCIF_DSN, mci->mci_flags)) 1647 { 1648 /* NOTIFY= parameter */ 1649 if (bitset(QHASNOTIFY, to->q_flags) && 1650 bitset(QPRIMARY, to->q_flags) && 1651 !bitnset(M_LOCALMAILER, m->m_flags)) 1652 { 1653 bool firstone = TRUE; 1654 1655 (void) strlcat(bufp, " NOTIFY=", sizeof optbuf); 1656 if (bitset(QPINGONSUCCESS, to->q_flags)) 1657 { 1658 (void) strlcat(bufp, "SUCCESS", sizeof optbuf); 1659 firstone = FALSE; 1660 } 1661 if (bitset(QPINGONFAILURE, to->q_flags)) 1662 { 1663 if (!firstone) 1664 (void) strlcat(bufp, ",", 1665 sizeof optbuf); 1666 (void) strlcat(bufp, "FAILURE", sizeof optbuf); 1667 firstone = FALSE; 1668 } 1669 if (bitset(QPINGONDELAY, to->q_flags)) 1670 { 1671 if (!firstone) 1672 (void) strlcat(bufp, ",", 1673 sizeof optbuf); 1674 (void) strlcat(bufp, "DELAY", sizeof optbuf); 1675 firstone = FALSE; 1676 } 1677 if (firstone) 1678 (void) strlcat(bufp, "NEVER", sizeof optbuf); 1679 bufp += strlen(bufp); 1680 } 1681 1682 /* ORCPT= parameter */ 1683 if (to->q_orcpt != NULL && 1684 SPACELEFT(optbuf, bufp) > strlen(to->q_orcpt) + 7) 1685 { 1686 snprintf(bufp, SPACELEFT(optbuf, bufp), 1687 " ORCPT=%s", to->q_orcpt); 1688 bufp += strlen(bufp); 1689 } 1690 } 1691 1692 smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf); 1693 1694 SmtpPhase = mci->mci_phase = "client RCPT"; 1695 sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e), 1696 CurHostName, mci->mci_phase); 1697 r = reply(m, mci, e, TimeOuts.to_rcpt, NULL, &enhsc); 1698 to->q_rstatus = newstr(SmtpReplyBuffer); 1699 to->q_status = ENHSCN(enhsc, smtptodsn(r)); 1700 if (!bitnset(M_LMTP, m->m_flags)) 1701 to->q_statmta = mci->mci_host; 1702 if (r < 0 || REPLYTYPE(r) == 4) 1703 return EX_TEMPFAIL; 1704 else if (REPLYTYPE(r) == 2) 1705 return EX_OK; 1706 else if (r == 550) 1707 { 1708 to->q_status = ENHSCN(enhsc, "5.1.1"); 1709 return EX_NOUSER; 1710 } 1711 else if (r == 551) 1712 { 1713 to->q_status = ENHSCN(enhsc, "5.1.6"); 1714 return EX_NOUSER; 1715 } 1716 else if (r == 553) 1717 { 1718 to->q_status = ENHSCN(enhsc, "5.1.3"); 1719 return EX_NOUSER; 1720 } 1721 else if (REPLYTYPE(r) == 5) 1722 { 1723 return EX_UNAVAILABLE; 1724 } 1725 1726 if (LogLevel > 1) 1727 { 1728 sm_syslog(LOG_CRIT, e->e_id, 1729 "%.100s: SMTP RCPT protocol error: %s", 1730 CurHostName, 1731 shortenstring(SmtpReplyBuffer, 403)); 1732 } 1733 1734 mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"), 1735 SmtpReplyBuffer); 1736 return EX_PROTOCOL; 1737 } 1738 /* 1739 ** SMTPDATA -- send the data and clean up the transaction. 1740 ** 1741 ** Parameters: 1742 ** m -- mailer being sent to. 1743 ** mci -- the mailer connection information. 1744 ** e -- the envelope for this message. 1745 ** 1746 ** Returns: 1747 ** exit status corresponding to DATA command. 1748 ** 1749 ** Side Effects: 1750 ** none. 1751 */ 1752 1753 static jmp_buf CtxDataTimeout; 1754 1755 int 1756 smtpdata(m, mci, e) 1757 MAILER *m; 1758 register MCI *mci; 1759 register ENVELOPE *e; 1760 { 1761 register int r; 1762 register EVENT *ev; 1763 int rstat; 1764 int xstat; 1765 time_t timeout; 1766 char *enhsc; 1767 1768 enhsc = NULL; 1769 /* 1770 ** Send the data. 1771 ** First send the command and check that it is ok. 1772 ** Then send the data. 1773 ** Follow it up with a dot to terminate. 1774 ** Finally get the results of the transaction. 1775 */ 1776 1777 /* send the command and check ok to proceed */ 1778 smtpmessage("DATA", m, mci); 1779 SmtpPhase = mci->mci_phase = "client DATA 354"; 1780 sm_setproctitle(TRUE, e, "%s %s: %s", 1781 qid_printname(e), CurHostName, mci->mci_phase); 1782 r = reply(m, mci, e, TimeOuts.to_datainit, NULL, &enhsc); 1783 if (r < 0 || REPLYTYPE(r) == 4) 1784 { 1785 smtpquit(m, mci, e); 1786 return EX_TEMPFAIL; 1787 } 1788 else if (REPLYTYPE(r) == 5) 1789 { 1790 smtprset(m, mci, e); 1791 return EX_UNAVAILABLE; 1792 } 1793 else if (REPLYTYPE(r) != 3) 1794 { 1795 if (LogLevel > 1) 1796 { 1797 sm_syslog(LOG_CRIT, e->e_id, 1798 "%.100s: SMTP DATA-1 protocol error: %s", 1799 CurHostName, 1800 shortenstring(SmtpReplyBuffer, 403)); 1801 } 1802 smtprset(m, mci, e); 1803 mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"), 1804 SmtpReplyBuffer); 1805 return EX_PROTOCOL; 1806 } 1807 1808 /* 1809 ** Set timeout around data writes. Make it at least large 1810 ** enough for DNS timeouts on all recipients plus some fudge 1811 ** factor. The main thing is that it should not be infinite. 1812 */ 1813 1814 if (setjmp(CtxDataTimeout) != 0) 1815 { 1816 mci->mci_errno = errno; 1817 mci->mci_state = MCIS_ERROR; 1818 mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL); 1819 1820 /* 1821 ** If putbody() couldn't finish due to a timeout, 1822 ** rewind it here in the timeout handler. See 1823 ** comments at the end of putbody() for reasoning. 1824 */ 1825 1826 if (e->e_dfp != NULL) 1827 (void) bfrewind(e->e_dfp); 1828 1829 errno = mci->mci_errno; 1830 syserr("451 4.4.1 timeout writing message to %s", CurHostName); 1831 smtpquit(m, mci, e); 1832 return EX_TEMPFAIL; 1833 } 1834 1835 if (tTd(18, 101)) 1836 { 1837 /* simulate a DATA timeout */ 1838 timeout = 1; 1839 } 1840 else 1841 timeout = DATA_PROGRESS_TIMEOUT; 1842 1843 ev = setevent(timeout, datatimeout, 0); 1844 1845 1846 if (tTd(18, 101)) 1847 { 1848 /* simulate a DATA timeout */ 1849 (void) sleep(1); 1850 } 1851 1852 /* 1853 ** Output the actual message. 1854 */ 1855 1856 (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER); 1857 (*e->e_putbody)(mci, e, NULL); 1858 1859 /* 1860 ** Cleanup after sending message. 1861 */ 1862 1863 clrevent(ev); 1864 1865 # if _FFR_CATCH_BROKEN_MTAS 1866 { 1867 fd_set readfds; 1868 struct timeval timeout; 1869 1870 FD_ZERO(&readfds); 1871 FD_SET(fileno(mci->mci_in), &readfds); 1872 timeout.tv_sec = 0; 1873 timeout.tv_usec = 0; 1874 if (select(fileno(mci->mci_in) + 1, FDSET_CAST &readfds, 1875 NULL, NULL, &timeout) > 0 && 1876 FD_ISSET(fileno(mci->mci_in), &readfds)) 1877 { 1878 /* terminate the message */ 1879 fprintf(mci->mci_out, ".%s", m->m_eol); 1880 if (TrafficLogFile != NULL) 1881 fprintf(TrafficLogFile, "%05d >>> .\n", 1882 (int) getpid()); 1883 if (Verbose) 1884 nmessage(">>> ."); 1885 1886 mci->mci_errno = EIO; 1887 mci->mci_state = MCIS_ERROR; 1888 mci_setstat(mci, EX_PROTOCOL, "5.5.0", NULL); 1889 smtpquit(m, mci, e); 1890 return EX_PROTOCOL; 1891 } 1892 } 1893 # endif /* _FFR_CATCH_BROKEN_MTAS */ 1894 1895 if (ferror(mci->mci_out)) 1896 { 1897 /* error during processing -- don't send the dot */ 1898 mci->mci_errno = EIO; 1899 mci->mci_state = MCIS_ERROR; 1900 mci_setstat(mci, EX_IOERR, "4.4.2", NULL); 1901 smtpquit(m, mci, e); 1902 return EX_IOERR; 1903 } 1904 1905 /* terminate the message */ 1906 fprintf(mci->mci_out, ".%s", m->m_eol); 1907 if (TrafficLogFile != NULL) 1908 fprintf(TrafficLogFile, "%05d >>> .\n", (int) getpid()); 1909 if (Verbose) 1910 nmessage(">>> ."); 1911 1912 /* check for the results of the transaction */ 1913 SmtpPhase = mci->mci_phase = "client DATA status"; 1914 sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e), 1915 CurHostName, mci->mci_phase); 1916 if (bitnset(M_LMTP, m->m_flags)) 1917 return EX_OK; 1918 r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc); 1919 if (r < 0) 1920 { 1921 smtpquit(m, mci, e); 1922 return EX_TEMPFAIL; 1923 } 1924 mci->mci_state = MCIS_OPEN; 1925 xstat = EX_NOTSTICKY; 1926 if (r == 452) 1927 rstat = EX_TEMPFAIL; 1928 else if (REPLYTYPE(r) == 4) 1929 rstat = xstat = EX_TEMPFAIL; 1930 else if (REPLYCLASS(r) != 5) 1931 rstat = xstat = EX_PROTOCOL; 1932 else if (REPLYTYPE(r) == 2) 1933 rstat = xstat = EX_OK; 1934 else if (REPLYTYPE(r) == 5) 1935 rstat = EX_UNAVAILABLE; 1936 else 1937 rstat = EX_PROTOCOL; 1938 mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)), 1939 SmtpReplyBuffer); 1940 if (e->e_statmsg != NULL) 1941 free(e->e_statmsg); 1942 if (bitset(MCIF_ENHSTAT, mci->mci_flags) && 1943 (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0) 1944 r += 5; 1945 else 1946 r = 4; 1947 e->e_statmsg = newstr(&SmtpReplyBuffer[r]); 1948 SmtpPhase = mci->mci_phase = "idle"; 1949 sm_setproctitle(TRUE, e, "%s: %s", CurHostName, mci->mci_phase); 1950 if (rstat != EX_PROTOCOL) 1951 return rstat; 1952 if (LogLevel > 1) 1953 { 1954 sm_syslog(LOG_CRIT, e->e_id, 1955 "%.100s: SMTP DATA-2 protocol error: %s", 1956 CurHostName, 1957 shortenstring(SmtpReplyBuffer, 403)); 1958 } 1959 return rstat; 1960 } 1961 1962 1963 static void 1964 datatimeout() 1965 { 1966 if (DataProgress) 1967 { 1968 time_t timeout; 1969 register EVENT *ev; 1970 1971 /* check back again later */ 1972 if (tTd(18, 101)) 1973 { 1974 /* simulate a DATA timeout */ 1975 timeout = 1; 1976 } 1977 else 1978 timeout = DATA_PROGRESS_TIMEOUT; 1979 1980 DataProgress = FALSE; 1981 ev = setevent(timeout, datatimeout, 0); 1982 } 1983 else 1984 { 1985 /* no progress, give up */ 1986 longjmp(CtxDataTimeout, 1); 1987 } 1988 } 1989 /* 1990 ** SMTPGETSTAT -- get status code from DATA in LMTP 1991 ** 1992 ** Parameters: 1993 ** m -- the mailer to which we are sending the message. 1994 ** mci -- the mailer connection structure. 1995 ** e -- the current envelope. 1996 ** 1997 ** Returns: 1998 ** The exit status corresponding to the reply code. 1999 */ 2000 2001 int 2002 smtpgetstat(m, mci, e) 2003 MAILER *m; 2004 MCI *mci; 2005 ENVELOPE *e; 2006 { 2007 int r; 2008 int status; 2009 char *enhsc; 2010 2011 enhsc = NULL; 2012 /* check for the results of the transaction */ 2013 r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc); 2014 if (r < 0) 2015 { 2016 smtpquit(m, mci, e); 2017 return EX_TEMPFAIL; 2018 } 2019 if (REPLYTYPE(r) == 4) 2020 status = EX_TEMPFAIL; 2021 else if (REPLYCLASS(r) != 5) 2022 status = EX_PROTOCOL; 2023 else if (REPLYTYPE(r) == 2) 2024 status = EX_OK; 2025 else if (REPLYTYPE(r) == 5) 2026 status = EX_UNAVAILABLE; 2027 else 2028 status = EX_PROTOCOL; 2029 if (e->e_statmsg != NULL) 2030 free(e->e_statmsg); 2031 if (bitset(MCIF_ENHSTAT, mci->mci_flags) && 2032 (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0) 2033 r += 5; 2034 else 2035 r = 4; 2036 e->e_statmsg = newstr(&SmtpReplyBuffer[r]); 2037 mci_setstat(mci, status, ENHSCN(enhsc, smtptodsn(r)), 2038 SmtpReplyBuffer); 2039 if (LogLevel > 1 && status == EX_PROTOCOL) 2040 { 2041 sm_syslog(LOG_CRIT, e->e_id, 2042 "%.100s: SMTP DATA-3 protocol error: %s", 2043 CurHostName, 2044 shortenstring(SmtpReplyBuffer, 403)); 2045 } 2046 return status; 2047 } 2048 /* 2049 ** SMTPQUIT -- close the SMTP connection. 2050 ** 2051 ** Parameters: 2052 ** m -- a pointer to the mailer. 2053 ** mci -- the mailer connection information. 2054 ** e -- the current envelope. 2055 ** 2056 ** Returns: 2057 ** none. 2058 ** 2059 ** Side Effects: 2060 ** sends the final protocol and closes the connection. 2061 */ 2062 2063 void 2064 smtpquit(m, mci, e) 2065 register MAILER *m; 2066 register MCI *mci; 2067 ENVELOPE *e; 2068 { 2069 bool oldSuprErrs = SuprErrs; 2070 int rcode; 2071 2072 CurHostName = mci->mci_host; /* XXX UGLY XXX */ 2073 if (CurHostName == NULL) 2074 CurHostName = MyHostName; 2075 2076 /* 2077 ** Suppress errors here -- we may be processing a different 2078 ** job when we do the quit connection, and we don't want the 2079 ** new job to be penalized for something that isn't it's 2080 ** problem. 2081 */ 2082 2083 SuprErrs = TRUE; 2084 2085 /* send the quit message if we haven't gotten I/O error */ 2086 if (mci->mci_state != MCIS_ERROR && 2087 mci->mci_state != MCIS_QUITING) 2088 { 2089 int origstate = mci->mci_state; 2090 2091 SmtpPhase = "client QUIT"; 2092 mci->mci_state = MCIS_QUITING; 2093 smtpmessage("QUIT", m, mci); 2094 (void) reply(m, mci, e, TimeOuts.to_quit, NULL, NULL); 2095 SuprErrs = oldSuprErrs; 2096 if (mci->mci_state == MCIS_CLOSED || 2097 origstate == MCIS_CLOSED) 2098 return; 2099 } 2100 2101 /* now actually close the connection and pick up the zombie */ 2102 rcode = endmailer(mci, e, NULL); 2103 if (rcode != EX_OK) 2104 { 2105 char *mailer = NULL; 2106 2107 if (mci->mci_mailer != NULL && 2108 mci->mci_mailer->m_name != NULL) 2109 mailer = mci->mci_mailer->m_name; 2110 2111 /* look for naughty mailers */ 2112 sm_syslog(LOG_ERR, e->e_id, 2113 "smtpquit: mailer%s%s exited with exit value %d", 2114 mailer == NULL ? "" : " ", 2115 mailer == NULL ? "" : mailer, 2116 rcode); 2117 } 2118 2119 SuprErrs = oldSuprErrs; 2120 } 2121 /* 2122 ** SMTPRSET -- send a RSET (reset) command 2123 ** 2124 ** Parameters: 2125 ** m -- a pointer to the mailer. 2126 ** mci -- the mailer connection information. 2127 ** e -- the current envelope. 2128 ** 2129 ** Returns: 2130 ** none. 2131 ** 2132 ** Side Effects: 2133 ** closes the connection if there is no reply to RSET. 2134 */ 2135 2136 void 2137 smtprset(m, mci, e) 2138 register MAILER *m; 2139 register MCI *mci; 2140 ENVELOPE *e; 2141 { 2142 int r; 2143 2144 CurHostName = mci->mci_host; /* XXX UGLY XXX */ 2145 if (CurHostName == NULL) 2146 CurHostName = MyHostName; 2147 2148 SmtpPhase = "client RSET"; 2149 smtpmessage("RSET", m, mci); 2150 r = reply(m, mci, e, TimeOuts.to_rset, NULL, NULL); 2151 if (r < 0) 2152 mci->mci_state = MCIS_ERROR; 2153 else 2154 { 2155 /* 2156 ** Any response is deemed to be acceptable. 2157 ** The standard does not state the proper action 2158 ** to take when a value other than 250 is received. 2159 ** 2160 ** However, if 421 is returned for the RSET, leave 2161 ** mci_state as MCIS_SSD (set in reply()). 2162 */ 2163 2164 if (mci->mci_state != MCIS_SSD) 2165 mci->mci_state = MCIS_OPEN; 2166 return; 2167 } 2168 smtpquit(m, mci, e); 2169 } 2170 /* 2171 ** SMTPPROBE -- check the connection state 2172 ** 2173 ** Parameters: 2174 ** mci -- the mailer connection information. 2175 ** 2176 ** Returns: 2177 ** none. 2178 ** 2179 ** Side Effects: 2180 ** closes the connection if there is no reply to RSET. 2181 */ 2182 2183 int 2184 smtpprobe(mci) 2185 register MCI *mci; 2186 { 2187 int r; 2188 MAILER *m = mci->mci_mailer; 2189 ENVELOPE *e; 2190 extern ENVELOPE BlankEnvelope; 2191 2192 CurHostName = mci->mci_host; /* XXX UGLY XXX */ 2193 if (CurHostName == NULL) 2194 CurHostName = MyHostName; 2195 2196 e = &BlankEnvelope; 2197 SmtpPhase = "client probe"; 2198 smtpmessage("RSET", m, mci); 2199 r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, NULL); 2200 if (r < 0 || REPLYTYPE(r) != 2) 2201 smtpquit(m, mci, e); 2202 return r; 2203 } 2204 /* 2205 ** REPLY -- read arpanet reply 2206 ** 2207 ** Parameters: 2208 ** m -- the mailer we are reading the reply from. 2209 ** mci -- the mailer connection info structure. 2210 ** e -- the current envelope. 2211 ** timeout -- the timeout for reads. 2212 ** pfunc -- processing function called on each line of response. 2213 ** If null, no special processing is done. 2214 ** 2215 ** Returns: 2216 ** reply code it reads. 2217 ** 2218 ** Side Effects: 2219 ** flushes the mail file. 2220 */ 2221 2222 int 2223 reply(m, mci, e, timeout, pfunc, enhstat) 2224 MAILER *m; 2225 MCI *mci; 2226 ENVELOPE *e; 2227 time_t timeout; 2228 void (*pfunc)(); 2229 char **enhstat; 2230 { 2231 register char *bufp; 2232 register int r; 2233 bool firstline = TRUE; 2234 char junkbuf[MAXLINE]; 2235 static char enhstatcode[ENHSCLEN]; 2236 int save_errno; 2237 2238 if (mci->mci_out != NULL) 2239 (void) fflush(mci->mci_out); 2240 2241 if (tTd(18, 1)) 2242 dprintf("reply\n"); 2243 2244 /* 2245 ** Read the input line, being careful not to hang. 2246 */ 2247 2248 bufp = SmtpReplyBuffer; 2249 for (;;) 2250 { 2251 register char *p; 2252 2253 /* actually do the read */ 2254 if (e->e_xfp != NULL) 2255 (void) fflush(e->e_xfp); /* for debugging */ 2256 2257 /* if we are in the process of closing just give the code */ 2258 if (mci->mci_state == MCIS_CLOSED) 2259 return SMTPCLOSING; 2260 2261 if (mci->mci_out != NULL) 2262 (void) fflush(mci->mci_out); 2263 2264 /* get the line from the other side */ 2265 p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); 2266 mci->mci_lastuse = curtime(); 2267 2268 if (p == NULL) 2269 { 2270 bool oldholderrs; 2271 extern char MsgBuf[]; 2272 2273 /* if the remote end closed early, fake an error */ 2274 if (errno == 0) 2275 # ifdef ECONNRESET 2276 errno = ECONNRESET; 2277 # else /* ECONNRESET */ 2278 errno = EPIPE; 2279 # endif /* ECONNRESET */ 2280 2281 mci->mci_errno = errno; 2282 oldholderrs = HoldErrs; 2283 HoldErrs = TRUE; 2284 usrerr("451 4.4.1 reply: read error from %s", 2285 CurHostName == NULL ? "NO_HOST" : CurHostName); 2286 2287 /* errors on QUIT should not be persistent */ 2288 if (strncmp(SmtpMsgBuffer, "QUIT", 4) != 0) 2289 mci_setstat(mci, EX_TEMPFAIL, "4.4.2", MsgBuf); 2290 2291 /* if debugging, pause so we can see state */ 2292 if (tTd(18, 100)) 2293 (void) pause(); 2294 mci->mci_state = MCIS_ERROR; 2295 save_errno = errno; 2296 smtpquit(m, mci, e); 2297 # if XDEBUG 2298 { 2299 char wbuf[MAXLINE]; 2300 int wbufleft = sizeof wbuf; 2301 2302 p = wbuf; 2303 if (e->e_to != NULL) 2304 { 2305 int plen; 2306 2307 snprintf(p, wbufleft, "%s... ", 2308 shortenstring(e->e_to, MAXSHORTSTR)); 2309 plen = strlen(p); 2310 p += plen; 2311 wbufleft -= plen; 2312 } 2313 snprintf(p, wbufleft, "reply(%.100s) during %s", 2314 CurHostName == NULL ? "NO_HOST" : CurHostName, 2315 SmtpPhase); 2316 checkfd012(wbuf); 2317 } 2318 # endif /* XDEBUG */ 2319 errno = save_errno; 2320 HoldErrs = oldholderrs; 2321 return -1; 2322 } 2323 fixcrlf(bufp, TRUE); 2324 2325 /* EHLO failure is not a real error */ 2326 if (e->e_xfp != NULL && (bufp[0] == '4' || 2327 (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0))) 2328 { 2329 /* serious error -- log the previous command */ 2330 if (SmtpNeedIntro) 2331 { 2332 /* inform user who we are chatting with */ 2333 fprintf(CurEnv->e_xfp, 2334 "... while talking to %s:\n", 2335 CurHostName == NULL ? "NO_HOST" : CurHostName); 2336 SmtpNeedIntro = FALSE; 2337 } 2338 if (SmtpMsgBuffer[0] != '\0') 2339 fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); 2340 SmtpMsgBuffer[0] = '\0'; 2341 2342 /* now log the message as from the other side */ 2343 fprintf(e->e_xfp, "<<< %s\n", bufp); 2344 } 2345 2346 /* display the input for verbose mode */ 2347 if (Verbose) 2348 nmessage("050 %s", bufp); 2349 2350 /* ignore improperly formatted input */ 2351 if (!ISSMTPREPLY(bufp)) 2352 continue; 2353 2354 if (bitset(MCIF_ENHSTAT, mci->mci_flags) && 2355 enhstat != NULL && 2356 extenhsc(bufp + 4, ' ', enhstatcode) > 0) 2357 *enhstat = enhstatcode; 2358 2359 /* process the line */ 2360 if (pfunc != NULL) 2361 (*pfunc)(bufp, firstline, m, mci, e); 2362 2363 firstline = FALSE; 2364 2365 /* decode the reply code */ 2366 r = atoi(bufp); 2367 2368 /* extra semantics: 0xx codes are "informational" */ 2369 if (r < 100) 2370 continue; 2371 2372 /* if no continuation lines, return this line */ 2373 if (bufp[3] != '-') 2374 break; 2375 2376 /* first line of real reply -- ignore rest */ 2377 bufp = junkbuf; 2378 } 2379 2380 /* 2381 ** Now look at SmtpReplyBuffer -- only care about the first 2382 ** line of the response from here on out. 2383 */ 2384 2385 /* save temporary failure messages for posterity */ 2386 if (SmtpReplyBuffer[0] == '4' && 2387 (bitnset(M_LMTP, m->m_flags) || SmtpError[0] == '\0')) 2388 snprintf(SmtpError, sizeof SmtpError, "%s", SmtpReplyBuffer); 2389 2390 /* reply code 421 is "Service Shutting Down" */ 2391 if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) 2392 { 2393 /* send the quit protocol */ 2394 mci->mci_state = MCIS_SSD; 2395 smtpquit(m, mci, e); 2396 } 2397 2398 return r; 2399 } 2400 /* 2401 ** SMTPMESSAGE -- send message to server 2402 ** 2403 ** Parameters: 2404 ** f -- format 2405 ** m -- the mailer to control formatting. 2406 ** a, b, c -- parameters 2407 ** 2408 ** Returns: 2409 ** none. 2410 ** 2411 ** Side Effects: 2412 ** writes message to mci->mci_out. 2413 */ 2414 2415 /*VARARGS1*/ 2416 void 2417 # ifdef __STDC__ 2418 smtpmessage(char *f, MAILER *m, MCI *mci, ...) 2419 # else /* __STDC__ */ 2420 smtpmessage(f, m, mci, va_alist) 2421 char *f; 2422 MAILER *m; 2423 MCI *mci; 2424 va_dcl 2425 # endif /* __STDC__ */ 2426 { 2427 VA_LOCAL_DECL 2428 2429 VA_START(mci); 2430 (void) vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap); 2431 VA_END; 2432 2433 if (tTd(18, 1) || Verbose) 2434 nmessage(">>> %s", SmtpMsgBuffer); 2435 if (TrafficLogFile != NULL) 2436 fprintf(TrafficLogFile, "%05d >>> %s\n", 2437 (int) getpid(), SmtpMsgBuffer); 2438 if (mci->mci_out != NULL) 2439 { 2440 fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, 2441 m == NULL ? "\r\n" : m->m_eol); 2442 } 2443 else if (tTd(18, 1)) 2444 { 2445 dprintf("smtpmessage: NULL mci_out\n"); 2446 } 2447 } 2448 2449 #endif /* SMTP */ 2450