2 * Copyright (c) 1998-2006 Sendmail, Inc. and its suppliers.
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.
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.
16 SM_RCSID("@(#)$Id: usersmtp.c,v 8.470 2007/10/17 21:35:30 ca Exp $")
21 static void esmtp_check
__P((char *, bool, MAILER
*, MCI
*, ENVELOPE
*));
22 static void helo_options
__P((char *, bool, MAILER
*, MCI
*, ENVELOPE
*));
23 static int smtprcptstat
__P((ADDRESS
*, MAILER
*, MCI
*, ENVELOPE
*));
26 extern void *sm_sasl_malloc
__P((unsigned long));
27 extern void sm_sasl_free
__P((void *));
31 ** USERSMTP -- run SMTP protocol from the user end.
33 ** This protocol is described in RFC821.
36 #define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */
37 #define SMTPCLOSING 421 /* "Service Shutting Down" */
39 #define ENHSCN(e, d) ((e) == NULL ? (d) : (e))
41 #define ENHSCN_RPOOL(e, d, rpool) \
42 ((e) == NULL ? (d) : sm_rpool_strdup_x(rpool, e))
44 static char SmtpMsgBuffer
[MAXLINE
]; /* buffer for commands */
45 static char SmtpReplyBuffer
[MAXLINE
]; /* buffer for replies */
46 static bool SmtpNeedIntro
; /* need "while talking" in transcript */
48 ** SMTPINIT -- initialize SMTP.
50 ** Opens the connection and sends the initial protocol.
53 ** m -- mailer to create connection to.
54 ** mci -- the mailer connection info.
56 ** onlyhelo -- send only helo command?
62 ** creates connection and sends initial protocol.
66 smtpinit(m
, mci
, e
, onlyhelo
)
81 sm_dprintf("smtpinit ");
82 mci_dump(sm_debug_file(), mci
, false);
86 ** Open the connection to the mailer.
90 SmtpMsgBuffer
[0] = '\0';
91 CurHostName
= mci
->mci_host
; /* XXX UGLY XXX */
92 if (CurHostName
== NULL
)
93 CurHostName
= MyHostName
;
95 state
= mci
->mci_state
;
101 /* need to clear old information */
113 /* shouldn't happen */
118 syserr("451 4.4.0 smtpinit: state CLOSED (was %d)", state
);
127 mci
->mci_state
= MCIS_OPENING
;
131 ** Get the greeting message.
132 ** This should appear spontaneously. Give it five minutes to
136 SmtpPhase
= mci
->mci_phase
= "client greeting";
137 sm_setproctitle(true, e
, "%s %s: %s",
138 qid_printname(e
), CurHostName
, mci
->mci_phase
);
139 r
= reply(m
, mci
, e
, TimeOuts
.to_initial
, esmtp_check
, NULL
,
143 if (REPLYTYPE(r
) == 4)
145 if (REPLYTYPE(r
) != 2)
149 ** Send the HELO command.
150 ** My mother taught me to always introduce myself.
154 if (bitnset(M_ESMTP
, m
->m_flags
) || bitnset(M_LMTP
, m
->m_flags
))
155 mci
->mci_flags
|= MCIF_ESMTP
;
156 hn
= mci
->mci_heloname
? mci
->mci_heloname
: MyHostName
;
159 #if _FFR_IGNORE_EXT_ON_HELO
160 mci
->mci_flags
&= ~MCIF_HELO
;
161 #endif /* _FFR_IGNORE_EXT_ON_HELO */
162 if (bitnset(M_LMTP
, m
->m_flags
))
164 smtpmessage("LHLO %s", m
, mci
, hn
);
165 SmtpPhase
= mci
->mci_phase
= "client LHLO";
167 else if (bitset(MCIF_ESMTP
, mci
->mci_flags
) &&
168 !bitnset(M_FSMTP
, m
->m_flags
))
170 smtpmessage("EHLO %s", m
, mci
, hn
);
171 SmtpPhase
= mci
->mci_phase
= "client EHLO";
175 smtpmessage("HELO %s", m
, mci
, hn
);
176 SmtpPhase
= mci
->mci_phase
= "client HELO";
177 #if _FFR_IGNORE_EXT_ON_HELO
178 mci
->mci_flags
|= MCIF_HELO
;
179 #endif /* _FFR_IGNORE_EXT_ON_HELO */
181 sm_setproctitle(true, e
, "%s %s: %s", qid_printname(e
),
182 CurHostName
, mci
->mci_phase
);
184 bitnset(M_LMTP
, m
->m_flags
) ? TimeOuts
.to_lhlo
186 helo_options
, NULL
, XS_DEFAULT
);
189 else if (REPLYTYPE(r
) == 5)
191 if (bitset(MCIF_ESMTP
, mci
->mci_flags
) &&
192 !bitnset(M_LMTP
, m
->m_flags
))
194 /* try old SMTP instead */
195 mci
->mci_flags
&= ~MCIF_ESMTP
;
200 else if (REPLYTYPE(r
) != 2)
204 ** Check to see if we actually ended up talking to ourself.
205 ** This means we didn't know about an alias or MX, or we managed
206 ** to connect to an echo server.
209 p
= strchr(&SmtpReplyBuffer
[4], ' ');
212 if (!bitnset(M_NOLOOPCHECK
, m
->m_flags
) &&
213 !bitnset(M_LMTP
, m
->m_flags
) &&
214 sm_strcasecmp(&SmtpReplyBuffer
[4], MyHostName
) == 0)
216 syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)",
218 mci_setstat(mci
, EX_CONFIG
, "5.3.5",
219 "553 5.3.5 system config error");
226 ** If this is expected to be another sendmail, send some internal
228 ** If we're running as MSP, "propagate" -v flag if possible.
231 if ((UseMSP
&& Verbose
&& bitset(MCIF_VERB
, mci
->mci_flags
))
232 # if !_FFR_DEPRECATE_MAILER_FLAG_I
233 || bitnset(M_INTERNAL
, m
->m_flags
)
234 # endif /* !_FFR_DEPRECATE_MAILER_FLAG_I */
237 /* tell it to be verbose */
238 smtpmessage("VERB", m
, mci
);
239 r
= reply(m
, mci
, e
, TimeOuts
.to_miscshort
, NULL
, &enhsc
,
245 if (mci
->mci_state
!= MCIS_CLOSED
)
247 mci
->mci_state
= MCIS_OPEN
;
251 /* got a 421 error code during startup */
254 mci_setstat(mci
, EX_TEMPFAIL
, ENHSCN(enhsc
, "4.4.2"), NULL
);
255 if (mci
->mci_state
!= MCIS_CLOSED
)
260 /* XXX should use code from other end iff ENHANCEDSTATUSCODES */
261 mci_setstat(mci
, EX_TEMPFAIL
, ENHSCN(enhsc
, "4.5.0"),
263 if (mci
->mci_state
!= MCIS_CLOSED
)
268 mci_setstat(mci
, EX_UNAVAILABLE
, "5.5.0", SmtpReplyBuffer
);
273 ** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
276 ** line -- the response line.
277 ** firstline -- set if this is the first line of the reply.
279 ** mci -- the mailer connection info.
280 ** e -- the envelope.
287 esmtp_check(line
, firstline
, m
, mci
, e
)
294 if (strstr(line
, "ESMTP") != NULL
)
295 mci
->mci_flags
|= MCIF_ESMTP
;
298 ** Dirty hack below. Quoting the author:
299 ** This was a response to people who wanted SMTP transmission to be
300 ** just-send-8 by default. Essentially, you could put this tag into
301 ** your greeting message to behave as though the F=8 flag was set on
305 if (strstr(line
, "8BIT-OK") != NULL
)
306 mci
->mci_flags
|= MCIF_8BITOK
;
310 /* specify prototype so compiler can check calls */
311 static char *str_union
__P((char *, char *, SM_RPOOL_T
*));
314 ** STR_UNION -- create the union of two lists
317 ** s1, s2 -- lists of items (separated by single blanks).
318 ** rpool -- resource pool from which result is allocated.
321 ** the union of both lists.
325 str_union(s1
, s2
, rpool
)
329 char *hr
, *h1
, *h
, *res
;
332 if (s1
== NULL
|| *s1
== '\0')
334 if (s2
== NULL
|| *s2
== '\0')
339 res
= (char *) sm_rpool_malloc(rpool
, rl
+ 2);
346 (void) sm_strlcpy(res
, s1
, rl
);
351 /* walk through s2 */
352 while (h
!= NULL
&& *h1
!= '\0')
354 /* is there something after the current word? */
355 if ((h
= strchr(h1
, ' ')) != NULL
)
359 /* does the current word appear in s1 ? */
360 if (iteminlist(h1
, s1
, " ") == NULL
)
362 /* add space as delimiter */
368 /* advance pointer in result list */
374 /* there are more items */
384 ** HELO_OPTIONS -- process the options on a HELO line.
387 ** line -- the response line.
388 ** firstline -- set if this is the first line of the reply.
390 ** mci -- the mailer connection info.
391 ** e -- the envelope (unused).
398 helo_options(line
, firstline
, m
, mci
, e
)
406 #if _FFR_IGNORE_EXT_ON_HELO
407 static bool logged
= false;
408 #endif /* _FFR_IGNORE_EXT_ON_HELO */
413 mci
->mci_saslcap
= NULL
;
415 #if _FFR_IGNORE_EXT_ON_HELO
417 #endif /* _FFR_IGNORE_EXT_ON_HELO */
420 #if _FFR_IGNORE_EXT_ON_HELO
421 else if (bitset(MCIF_HELO
, mci
->mci_flags
))
423 if (LogLevel
> 8 && !logged
)
425 sm_syslog(LOG_WARNING
, NOQID
,
426 "server=%s [%s] returned extensions despite HELO command",
427 macvalue(macid("{server_name}"), e
),
428 macvalue(macid("{server_addr}"), e
));
433 #endif /* _FFR_IGNORE_EXT_ON_HELO */
435 if (strlen(line
) < 5)
438 p
= strpbrk(line
, " =");
441 if (sm_strcasecmp(line
, "size") == 0)
443 mci
->mci_flags
|= MCIF_SIZE
;
445 mci
->mci_maxsize
= atol(p
);
447 else if (sm_strcasecmp(line
, "8bitmime") == 0)
449 mci
->mci_flags
|= MCIF_8BITMIME
;
450 mci
->mci_flags
&= ~MCIF_7BIT
;
452 else if (sm_strcasecmp(line
, "expn") == 0)
453 mci
->mci_flags
|= MCIF_EXPN
;
454 else if (sm_strcasecmp(line
, "dsn") == 0)
455 mci
->mci_flags
|= MCIF_DSN
;
456 else if (sm_strcasecmp(line
, "enhancedstatuscodes") == 0)
457 mci
->mci_flags
|= MCIF_ENHSTAT
;
458 else if (sm_strcasecmp(line
, "pipelining") == 0)
459 mci
->mci_flags
|= MCIF_PIPELINED
;
460 else if (sm_strcasecmp(line
, "verb") == 0)
461 mci
->mci_flags
|= MCIF_VERB
;
463 else if (sm_strcasecmp(line
, "starttls") == 0)
464 mci
->mci_flags
|= MCIF_TLS
;
465 #endif /* STARTTLS */
466 else if (sm_strcasecmp(line
, "deliverby") == 0)
468 mci
->mci_flags
|= MCIF_DLVR_BY
;
470 mci
->mci_min_by
= atol(p
);
473 else if (sm_strcasecmp(line
, "auth") == 0)
475 if (p
!= NULL
&& *p
!= '\0')
477 if (mci
->mci_saslcap
!= NULL
)
480 ** Create the union with previous auth
481 ** offerings because we recognize "auth "
482 ** and "auth=" (old format).
485 mci
->mci_saslcap
= str_union(mci
->mci_saslcap
,
487 mci
->mci_flags
|= MCIF_AUTH
;
494 mci
->mci_saslcap
= (char *)
495 sm_rpool_malloc(mci
->mci_rpool
, l
);
496 if (mci
->mci_saslcap
!= NULL
)
498 (void) sm_strlcpy(mci
->mci_saslcap
, p
,
500 mci
->mci_flags
|= MCIF_AUTH
;
509 static int getsimple
__P((void *, int, const char **, unsigned *));
510 static int getsecret
__P((sasl_conn_t
*, void *, int, sasl_secret_t
**));
511 static int saslgetrealm
__P((void *, int, const char **, const char **));
512 static int readauth
__P((char *, bool, SASL_AI_T
*m
, SM_RPOOL_T
*));
513 static int getauth
__P((MCI
*, ENVELOPE
*, SASL_AI_T
*));
514 static char *removemech
__P((char *, char *, SM_RPOOL_T
*));
515 static int attemptauth
__P((MAILER
*, MCI
*, ENVELOPE
*, SASL_AI_T
*));
517 static sasl_callback_t callbacks
[] =
519 { SASL_CB_GETREALM
, &saslgetrealm
, NULL
},
520 #define CB_GETREALM_IDX 0
521 { SASL_CB_PASS
, &getsecret
, NULL
},
522 #define CB_PASS_IDX 1
523 { SASL_CB_USER
, &getsimple
, NULL
},
524 #define CB_USER_IDX 2
525 { SASL_CB_AUTHNAME
, &getsimple
, NULL
},
526 #define CB_AUTHNAME_IDX 3
527 { SASL_CB_VERIFYFILE
, &safesaslfile
, NULL
},
528 #define CB_SAFESASL_IDX 4
529 { SASL_CB_LIST_END
, NULL
, NULL
}
533 ** INIT_SASL_CLIENT -- initialize client side of Cyrus-SASL
539 ** SASL_OK -- if successful.
540 ** SASL error code -- otherwise.
543 ** checks/sets sasl_clt_init.
546 ** Callbacks are ignored if sasl_client_init() has
547 ** been called before (by a library such as libnss_ldap)
550 static bool sasl_clt_init
= false;
559 result
= sasl_client_init(callbacks
);
561 /* should we retry later again or just remember that it failed? */
562 if (result
== SASL_OK
)
563 sasl_clt_init
= true;
567 ** STOP_SASL_CLIENT -- shutdown client side of Cyrus-SASL
576 ** checks/sets sasl_clt_init.
584 sasl_clt_init
= false;
588 ** GETSASLDATA -- process the challenges from the SASL protocol
590 ** This gets the relevant sasl response data out of the reply
594 ** line -- the response line.
595 ** firstline -- set if this is the first line of the reply.
597 ** mci -- the mailer connection info.
598 ** e -- the envelope (unused).
604 static void getsasldata
__P((char *, bool, MAILER
*, MCI
*, ENVELOPE
*));
607 getsasldata(line
, firstline
, m
, mci
, e
)
618 # endif /* SASL < 20000 */
620 /* if not a continue we don't care about it */
624 !isascii(line
[1]) || !isdigit(line
[1]) ||
625 !isascii(line
[2]) || !isdigit(line
[2]))
627 SM_FREE_CLR(mci
->mci_sasl_string
);
631 /* forget about "334 " */
635 /* XXX put this into a macro/function? It's duplicated below */
636 if (mci
->mci_sasl_string
!= NULL
)
638 if (mci
->mci_sasl_string_len
<= len
)
640 sm_free(mci
->mci_sasl_string
); /* XXX */
641 mci
->mci_sasl_string
= xalloc(len
+ 1);
645 mci
->mci_sasl_string
= xalloc(len
+ 1);
647 result
= sasl_decode64(line
, len
, mci
->mci_sasl_string
, len
+ 1,
648 (unsigned int *) &mci
->mci_sasl_string_len
);
649 if (result
!= SASL_OK
)
651 mci
->mci_sasl_string_len
= 0;
652 *mci
->mci_sasl_string
= '\0';
654 # else /* SASL >= 20000 */
655 out
= (char *) sm_rpool_malloc_x(mci
->mci_rpool
, len
+ 1);
656 result
= sasl_decode64(line
, len
, out
, (unsigned int *) &len
);
657 if (result
!= SASL_OK
)
664 ** mci_sasl_string is "shared" with Cyrus-SASL library; hence
665 ** it can't be in an rpool unless we use the same memory
666 ** management mechanism (with same rpool!) for Cyrus SASL.
669 if (mci
->mci_sasl_string
!= NULL
)
671 if (mci
->mci_sasl_string_len
<= len
)
673 sm_free(mci
->mci_sasl_string
); /* XXX */
674 mci
->mci_sasl_string
= xalloc(len
+ 1);
678 mci
->mci_sasl_string
= xalloc(len
+ 1);
680 memcpy(mci
->mci_sasl_string
, out
, len
);
681 mci
->mci_sasl_string
[len
] = '\0';
682 mci
->mci_sasl_string_len
= len
;
683 # endif /* SASL >= 20000 */
687 ** READAUTH -- read auth values from a file
690 ** filename -- name of file to read.
691 ** safe -- if set, this is a safe read.
692 ** sai -- where to store auth_info.
693 ** rpool -- resource pool for sai.
696 ** EX_OK -- data succesfully read.
697 ** EX_UNAVAILABLE -- no valid filename.
698 ** EX_TEMPFAIL -- temporary failure.
701 static char *sasl_info_name
[] =
710 readauth(filename
, safe
, sai
, rpool
)
723 if (filename
== NULL
|| filename
[0] == '\0')
724 return EX_UNAVAILABLE
;
726 #if !_FFR_ALLOW_SASLINFO
728 ** make sure we don't use a program that is not
729 ** accesible to the user who specified a different authinfo file.
730 ** However, currently we don't pass this info (authinfo file
731 ** specified by user) around, so we just turn off program access.
734 if (filename
[0] == '|')
739 char *argv
[MAXPV
+ 1];
742 for (p
= strtok(&filename
[1], " \t"); p
!= NULL
;
743 p
= strtok(NULL
, " \t"))
750 pid
= prog_open(argv
, &fd
, CurEnv
);
754 f
= sm_io_open(SmFtStdiofd
, SM_TIME_DEFAULT
,
755 (void *) &fd
, SM_IO_RDONLY
, NULL
);
758 #endif /* !_FFR_ALLOW_SASLINFO */
761 sff
= SFF_REGONLY
|SFF_SAFEDIRPATH
|SFF_NOWLINK
762 |SFF_NOGWFILES
|SFF_NOWWFILES
|SFF_NOWRFILES
;
763 # if _FFR_GROUPREADABLEAUTHINFOFILE
764 if (!bitnset(DBS_GROUPREADABLEAUTHINFOFILE
, DontBlameSendmail
))
765 # endif /* _FFR_GROUPREADABLEAUTHINFOFILE */
766 sff
|= SFF_NOGRFILES
;
767 if (DontLockReadFiles
)
770 #if _FFR_ALLOW_SASLINFO
772 ** XXX: make sure we don't read or open files that are not
773 ** accesible to the user who specified a different authinfo
778 #else /* _FFR_ALLOW_SASLINFO */
780 sff
|= SFF_OPENASROOT
;
781 #endif /* _FFR_ALLOW_SASLINFO */
783 f
= safefopen(filename
, O_RDONLY
, 0, sff
);
788 sm_syslog(LOG_ERR
, NOQID
,
789 "AUTH=client, error: can't open %s: %s",
790 filename
, sm_errstring(errno
));
795 while (lc
<= SASL_MECHLIST
&&
796 sm_io_fgets(f
, SM_TIME_DEFAULT
, buf
, sizeof(buf
)) != NULL
)
800 (*sai
)[lc
] = sm_rpool_strdup_x(rpool
, buf
);
801 if ((s
= strchr((*sai
)[lc
], '\n')) != NULL
)
807 (void) sm_io_close(f
, SM_TIME_DEFAULT
);
810 if (lc
< SASL_PASSWORD
)
813 sm_syslog(LOG_ERR
, NOQID
,
814 "AUTH=client, error: can't read %s from %s",
815 sasl_info_name
[lc
+ 1], filename
);
822 ** GETAUTH -- get authinfo from ruleset call
824 ** {server_name}, {server_addr} must be set
827 ** mci -- the mailer connection structure.
828 ** e -- the envelope (including the sender to specify).
829 ** sai -- pointer to authinfo (result).
832 ** EX_OK -- ruleset was succesfully called, data may not
833 ** be available, sai must be checked.
834 ** EX_UNAVAILABLE -- ruleset unavailable (or failed).
835 ** EX_TEMPFAIL -- temporary failure (from ruleset).
838 ** Fills in sai if successful.
847 int i
, r
, l
, got
, ret
;
849 char pvpbuf
[PSBUFSIZE
];
851 r
= rscap("authinfo", macvalue(macid("{server_name}"), e
),
852 macvalue(macid("{server_addr}"), e
), e
,
853 &pvp
, pvpbuf
, sizeof(pvpbuf
));
856 return EX_UNAVAILABLE
;
858 /* other than expected return value: ok (i.e., no auth) */
859 if (pvp
== NULL
|| pvp
[0] == NULL
|| (pvp
[0][0] & 0377) != CANONNET
)
861 if (pvp
[1] != NULL
&& sm_strncasecmp(pvp
[1], "temp", 4) == 0)
865 ** parse the data, put it into sai
866 ** format: "TDstring" (including the '"' !)
867 ** where T is a tag: 'U', ...
868 ** D is a delimiter: ':' or '='
871 ret
= EX_OK
; /* default return value */
874 while (i
< SASL_ENTRIES
)
876 if (pvp
[i
+ 1] == NULL
)
878 if (pvp
[i
+ 1][0] != '"')
880 switch (pvp
[i
+ 1][1])
905 l
= strlen(pvp
[i
+ 1]);
908 if (l
<= 3 || pvp
[i
+ 1][l
- 1] != '"')
911 /* remove closing quote */
912 pvp
[i
+ 1][l
- 1] = '\0';
914 /* remove "TD and " */
916 (*sai
)[r
] = (char *) sm_rpool_malloc(mci
->mci_rpool
, l
+ 1);
917 if ((*sai
)[r
] == NULL
)
919 if (pvp
[i
+ 1][2] == ':')
921 /* ':text' (just copy) */
922 (void) sm_strlcpy((*sai
)[r
], pvp
[i
+ 1] + 3, l
+ 1);
925 else if (pvp
[i
+ 1][2] == '=')
929 /* '=base64' (decode) */
931 ret
= sasl_decode64(pvp
[i
+ 1] + 3,
932 (unsigned int) l
, (*sai
)[r
],
933 (unsigned int) l
+ 1, &len
);
934 # else /* SASL >= 20000 */
935 ret
= sasl_decode64(pvp
[i
+ 1] + 3,
936 (unsigned int) l
, (*sai
)[r
], &len
);
937 # endif /* SASL >= 20000 */
945 sm_syslog(LOG_DEBUG
, NOQID
, "getauth %s=%s",
946 sasl_info_name
[r
], (*sai
)[r
]);
950 /* did we get the expected data? */
951 /* XXX: EXTERNAL mechanism only requires (and only uses) SASL_USER */
952 if (!(bitset(SASL_USER_BIT
|SASL_AUTHID_BIT
, got
) &&
953 bitset(SASL_PASSWORD_BIT
, got
)))
956 /* no authid? copy uid */
957 if (!bitset(SASL_AUTHID_BIT
, got
))
959 l
= strlen((*sai
)[SASL_USER
]) + 1;
960 (*sai
)[SASL_AUTHID
] = (char *) sm_rpool_malloc(mci
->mci_rpool
,
962 if ((*sai
)[SASL_AUTHID
] == NULL
)
964 (void) sm_strlcpy((*sai
)[SASL_AUTHID
], (*sai
)[SASL_USER
], l
);
967 /* no uid? copy authid */
968 if (!bitset(SASL_USER_BIT
, got
))
970 l
= strlen((*sai
)[SASL_AUTHID
]) + 1;
971 (*sai
)[SASL_USER
] = (char *) sm_rpool_malloc(mci
->mci_rpool
,
973 if ((*sai
)[SASL_USER
] == NULL
)
975 (void) sm_strlcpy((*sai
)[SASL_USER
], (*sai
)[SASL_AUTHID
], l
);
983 sm_syslog(LOG_WARNING
, NOQID
,
984 "AUTH=client, relay=%.64s [%.16s], authinfo %sfailed",
985 macvalue(macid("{server_name}"), e
),
986 macvalue(macid("{server_addr}"), e
),
987 ret
== EX_TEMPFAIL
? "temp" : "");
988 for (i
= 0; i
<= SASL_MECHLIST
; i
++)
989 (*sai
)[i
] = NULL
; /* just clear; rpool */
995 ** GETSIMPLE -- callback to get userid or authid
1000 ** result -- (pointer to) result
1001 ** len -- (pointer to) length of result
1004 ** OK/failure values
1008 getsimple(context
, id
, result
, len
)
1011 const char **result
;
1016 if (result
== NULL
|| context
== NULL
)
1017 return SASL_BADPARAM
;
1018 sai
= (SASL_AI_T
*) context
;
1023 *result
= (*sai
)[SASL_USER
];
1025 sm_syslog(LOG_DEBUG
, NOQID
, "AUTH username '%s'",
1028 *len
= *result
!= NULL
? strlen(*result
) : 0;
1031 case SASL_CB_AUTHNAME
:
1032 *result
= (*sai
)[SASL_AUTHID
];
1034 sm_syslog(LOG_DEBUG
, NOQID
, "AUTH authid '%s'",
1037 *len
= *result
!= NULL
? strlen(*result
) : 0;
1040 case SASL_CB_LANGUAGE
:
1047 return SASL_BADPARAM
;
1052 ** GETSECRET -- callback to get password
1055 ** conn -- connection information
1058 ** psecret -- (pointer to) result
1061 ** OK/failure values
1065 getsecret(conn
, context
, id
, psecret
)
1067 SM_UNUSED(void *context
);
1069 sasl_secret_t
**psecret
;
1075 if (conn
== NULL
|| psecret
== NULL
|| id
!= SASL_CB_PASS
)
1076 return SASL_BADPARAM
;
1078 mci
= (MCI
*) context
;
1079 authpass
= mci
->mci_sai
[SASL_PASSWORD
];
1080 len
= strlen(authpass
);
1083 ** use an rpool because we are responsible for free()ing the secret,
1084 ** but we can't free() it until after the auth completes
1087 *psecret
= (sasl_secret_t
*) sm_rpool_malloc(mci
->mci_rpool
,
1088 sizeof(sasl_secret_t
) +
1090 if (*psecret
== NULL
)
1092 (void) sm_strlcpy((char *) (*psecret
)->data
, authpass
, len
+ 1);
1093 (*psecret
)->len
= (unsigned long) len
;
1096 # else /* SASL >= 20000 */
1098 ** GETSIMPLE -- callback to get userid or authid
1103 ** result -- (pointer to) result
1104 ** len -- (pointer to) length of result
1107 ** OK/failure values
1111 getsimple(context
, id
, result
, len
)
1114 const char **result
;
1120 # endif /* SASL > 10509 */
1123 char *authid
= NULL
;
1125 if (result
== NULL
|| context
== NULL
)
1126 return SASL_BADPARAM
;
1127 sai
= (SASL_AI_T
*) context
;
1130 ** Unfortunately it is not clear whether this routine should
1131 ** return a copy of a string or just a pointer to a string.
1132 ** The Cyrus-SASL plugins treat these return values differently, e.g.,
1133 ** plugins/cram.c free()s authid, plugings/digestmd5.c does not.
1134 ** The best solution to this problem is to fix Cyrus-SASL, but it
1135 ** seems there is nobody who creates patches... Hello CMU!?
1136 ** The second best solution is to have flags that tell this routine
1137 ** whether to return an malloc()ed copy.
1138 ** The next best solution is to always return an malloc()ed copy,
1139 ** and suffer from some memory leak, which is ugly for persistent
1141 ** For now we go with the last solution...
1142 ** We can't use rpools (which would avoid this particular problem)
1143 ** as explained in sasl.c.
1149 l
= strlen((*sai
)[SASL_USER
]) + 1;
1150 s
= sm_sasl_malloc(l
);
1158 (void) sm_strlcpy(s
, (*sai
)[SASL_USER
], l
);
1161 sm_syslog(LOG_DEBUG
, NOQID
, "AUTH username '%s'",
1164 *len
= *result
!= NULL
? strlen(*result
) : 0;
1167 case SASL_CB_AUTHNAME
:
1168 h
= (*sai
)[SASL_AUTHID
];
1170 /* XXX maybe other mechanisms too?! */
1171 addrealm
= (*sai
)[SASL_MECH
] != NULL
&&
1172 sm_strcasecmp((*sai
)[SASL_MECH
], "CRAM-MD5") == 0;
1175 ** Add realm to authentication id unless authid contains
1176 ** '@' (i.e., a realm) or the default realm is empty.
1179 if (addrealm
&& h
!= NULL
&& strchr(h
, '@') == NULL
)
1181 /* has this been done before? */
1182 if ((*sai
)[SASL_ID_REALM
] == NULL
)
1186 realm
= (*sai
)[SASL_DEFREALM
];
1188 /* do not add an empty realm */
1192 (*sai
)[SASL_ID_REALM
] = NULL
;
1196 l
= strlen(h
) + strlen(realm
) + 2;
1198 /* should use rpool, but from where? */
1199 authid
= sm_sasl_malloc(l
);
1202 (void) sm_snprintf(authid
, l
,
1205 (*sai
)[SASL_ID_REALM
] = authid
;
1210 (*sai
)[SASL_ID_REALM
] = NULL
;
1215 authid
= (*sai
)[SASL_ID_REALM
];
1218 # endif /* SASL > 10509 */
1220 l
= strlen(authid
) + 1;
1221 s
= sm_sasl_malloc(l
);
1229 (void) sm_strlcpy(s
, authid
, l
);
1232 sm_syslog(LOG_DEBUG
, NOQID
, "AUTH authid '%s'",
1235 *len
= authid
? strlen(authid
) : 0;
1238 case SASL_CB_LANGUAGE
:
1245 return SASL_BADPARAM
;
1250 ** GETSECRET -- callback to get password
1253 ** conn -- connection information
1256 ** psecret -- (pointer to) result
1259 ** OK/failure values
1263 getsecret(conn
, context
, id
, psecret
)
1265 SM_UNUSED(void *context
);
1267 sasl_secret_t
**psecret
;
1273 if (conn
== NULL
|| psecret
== NULL
|| id
!= SASL_CB_PASS
)
1274 return SASL_BADPARAM
;
1276 sai
= (SASL_AI_T
*) context
;
1277 authpass
= (*sai
)[SASL_PASSWORD
];
1278 len
= strlen(authpass
);
1279 *psecret
= (sasl_secret_t
*) sm_sasl_malloc(sizeof(sasl_secret_t
) +
1281 if (*psecret
== NULL
)
1283 (void) sm_strlcpy((*psecret
)->data
, authpass
, len
+ 1);
1284 (*psecret
)->len
= (unsigned long) len
;
1287 # endif /* SASL >= 20000 */
1290 ** SAFESASLFILE -- callback for sasl: is file safe?
1293 ** context -- pointer to context between invocations (unused)
1294 ** file -- name of file to check
1295 ** type -- type of file to check
1298 ** SASL_OK -- file can be used
1299 ** SASL_CONTINUE -- don't use file
1300 ** SASL_FAIL -- failure (not used here)
1306 safesaslfile(context
, file
, type
)
1307 #else /* SASL > 10515 */
1308 safesaslfile(context
, file
)
1309 #endif /* SASL > 10515 */
1313 # else /* SASL >= 20000 */
1315 # endif /* SASL >= 20000 */
1318 sasl_verify_type_t type
;
1319 # else /* SASL >= 20000 */
1321 # endif /* SASL >= 20000 */
1322 #endif /* SASL > 10515 */
1328 #endif /* SASL <= 10515 */
1331 if (file
== NULL
|| *file
== '\0')
1333 sff
= SFF_SAFEDIRPATH
|SFF_NOWLINK
|SFF_NOWWFILES
|SFF_ROOTOK
;
1335 if ((p
= strrchr(file
, '/')) == NULL
)
1340 /* everything beside libs and .conf files must not be readable */
1342 if ((len
<= 3 || strncmp(p
, "lib", 3) != 0) &&
1343 (len
<= 5 || strncmp(p
+ len
- 5, ".conf", 5) != 0))
1345 if (!bitnset(DBS_GROUPREADABLESASLDBFILE
, DontBlameSendmail
))
1346 sff
|= SFF_NORFILES
;
1347 if (!bitnset(DBS_GROUPWRITABLESASLDBFILE
, DontBlameSendmail
))
1348 sff
|= SFF_NOGWFILES
;
1350 #else /* SASL <= 10515 */
1351 /* files containing passwords should be not readable */
1352 if (type
== SASL_VRFY_PASSWD
)
1354 if (bitnset(DBS_GROUPREADABLESASLDBFILE
, DontBlameSendmail
))
1355 sff
|= SFF_NOWRFILES
;
1357 sff
|= SFF_NORFILES
;
1358 if (!bitnset(DBS_GROUPWRITABLESASLDBFILE
, DontBlameSendmail
))
1359 sff
|= SFF_NOGWFILES
;
1361 #endif /* SASL <= 10515 */
1364 if ((r
= safefile(p
, RunAsUid
, RunAsGid
, RunAsUserName
, sff
,
1365 S_IRUSR
, NULL
)) == 0)
1367 if (LogLevel
> (r
!= ENOENT
? 8 : 10))
1368 sm_syslog(LOG_WARNING
, NOQID
, "error: safesasl(%s) failed: %s",
1369 p
, sm_errstring(r
));
1370 return SASL_CONTINUE
;
1374 ** SASLGETREALM -- return the realm for SASL
1376 ** return the realm for the client
1379 ** context -- context shared between invocations
1380 ** availrealms -- list of available realms
1381 ** {realm, realm, ...}
1382 ** result -- pointer to result
1389 saslgetrealm(context
, id
, availrealms
, result
)
1392 const char **availrealms
;
1393 const char **result
;
1398 sai
= (SASL_AI_T
*) context
;
1401 r
= (*sai
)[SASL_DEFREALM
];
1404 sm_syslog(LOG_INFO
, NOQID
,
1405 "AUTH=client, realm=%s, available realms=%s",
1406 r
== NULL
? "<No Realm>" : r
,
1407 (availrealms
== NULL
|| *availrealms
== NULL
)
1408 ? "<No Realms>" : *availrealms
);
1410 /* check whether context is in list */
1411 if (availrealms
!= NULL
&& *availrealms
!= NULL
)
1413 if (iteminlist(context
, (char *)(*availrealms
+ 1), " ,}") ==
1417 sm_syslog(LOG_ERR
, NOQID
,
1418 "AUTH=client, realm=%s not in list=%s",
1427 ** ITEMINLIST -- does item appear in list?
1429 ** Check whether item appears in list (which must be separated by a
1430 ** character in delim) as a "word", i.e. it must appear at the begin
1431 ** of the list or after a space, and it must end with a space or the
1435 ** item -- item to search.
1436 ** list -- list of items.
1437 ** delim -- list of delimiters.
1440 ** pointer to occurrence (NULL if not found).
1444 iteminlist(item
, list
, delim
)
1452 if (list
== NULL
|| *list
== '\0')
1454 if (item
== NULL
|| *item
== '\0')
1458 while (s
!= NULL
&& *s
!= '\0')
1460 if (sm_strncasecmp(s
, item
, len
) == 0 &&
1461 (s
[len
] == '\0' || strchr(delim
, s
[len
]) != NULL
))
1463 s
= strpbrk(s
, delim
);
1471 ** REMOVEMECH -- remove item [rem] from list [list]
1474 ** rem -- item to remove
1475 ** list -- list of items
1476 ** rpool -- resource pool from which result is allocated.
1479 ** pointer to new list (NULL in case of error).
1483 removemech(rem
, list
, rpool
)
1494 if (rem
== NULL
|| *rem
== '\0')
1496 /* take out what? */
1500 /* find the item in the list */
1501 if ((needle
= iteminlist(rem
, list
, " ")) == NULL
)
1503 /* not in there: return original */
1507 /* length of string without rem */
1508 len
= strlen(list
) - strlen(rem
);
1511 ret
= (char *) sm_rpool_malloc_x(rpool
, 1);
1515 ret
= (char *) sm_rpool_malloc_x(rpool
, len
);
1516 memset(ret
, '\0', len
);
1518 /* copy from start to removed item */
1519 memcpy(ret
, list
, needle
- list
);
1521 /* length of rest of string past removed item */
1522 len
= strlen(needle
) - strlen(rem
) - 1;
1525 /* not last item -- copy into string */
1526 memcpy(ret
+ (needle
- list
),
1527 list
+ (needle
- list
) + strlen(rem
) + 1,
1531 ret
[(needle
- list
) - 1] = '\0';
1535 ** ATTEMPTAUTH -- try to AUTHenticate using one mechanism
1539 ** mci -- the mailer connection structure.
1540 ** e -- the envelope (including the sender to specify).
1541 ** sai - sasl authinfo
1544 ** EX_OK -- authentication was successful.
1545 ** EX_NOPERM -- authentication failed.
1546 ** EX_IOERR -- authentication dialogue failed (I/O problem?).
1547 ** EX_TEMPFAIL -- temporary failure.
1552 attemptauth(m
, mci
, e
, sai
)
1558 int saslresult
, smtpresult
;
1561 const char *auth_id
;
1563 # else /* SASL >= 20000 */
1564 sasl_external_properties_t ssf
;
1566 # endif /* SASL >= 20000 */
1567 unsigned int outlen
;
1568 sasl_interact_t
*client_interact
= NULL
;
1570 sasl_security_properties_t ssp
;
1571 char in64
[MAXOUTLEN
];
1572 #if NETINET || (NETINET6 && SASL >= 20000)
1573 extern SOCKADDR CurHostAddr
;
1574 #endif /* NETINET || (NETINET6 && SASL >= 20000) */
1576 /* no mechanism selected (yet) */
1577 (*sai
)[SASL_MECH
] = NULL
;
1579 /* dispose old connection */
1580 if (mci
->mci_conn
!= NULL
)
1581 sasl_dispose(&(mci
->mci_conn
));
1583 /* make a new client sasl connection */
1586 ** We provide the callbacks again because global callbacks in
1587 ** sasl_client_init() are ignored if SASL has been initialized
1588 ** before, for example, by a library such as libnss-ldap.
1591 saslresult
= sasl_client_new(bitnset(M_LMTP
, m
->m_flags
) ? "lmtp"
1593 CurHostName
, NULL
, NULL
, callbacks
, 0,
1595 # else /* SASL >= 20000 */
1596 saslresult
= sasl_client_new(bitnset(M_LMTP
, m
->m_flags
) ? "lmtp"
1598 CurHostName
, NULL
, 0, &mci
->mci_conn
);
1599 # endif /* SASL >= 20000 */
1600 if (saslresult
!= SASL_OK
)
1603 /* set properties */
1604 (void) memset(&ssp
, '\0', sizeof(ssp
));
1606 /* XXX should these be options settable via .cf ? */
1608 ssp
.max_ssf
= MaxSLBits
;
1609 ssp
.maxbufsize
= MAXOUTLEN
;
1611 ssp
.security_flags
= SASL_SEC_NOPLAINTEXT
;
1614 saslresult
= sasl_setprop(mci
->mci_conn
, SASL_SEC_PROPS
, &ssp
);
1615 if (saslresult
!= SASL_OK
)
1619 /* external security strength factor, authentication id */
1623 out
= macvalue(macid("{cert_subject}"), e
);
1624 if (out
!= NULL
&& *out
!= '\0')
1626 out
= macvalue(macid("{cipher_bits}"), e
);
1627 if (out
!= NULL
&& *out
!= '\0')
1629 # endif /* STARTTLS */
1630 saslresult
= sasl_setprop(mci
->mci_conn
, SASL_SSF_EXTERNAL
, &ssf
);
1631 if (saslresult
!= SASL_OK
)
1633 saslresult
= sasl_setprop(mci
->mci_conn
, SASL_AUTH_EXTERNAL
, auth_id
);
1634 if (saslresult
!= SASL_OK
)
1637 # if NETINET || NETINET6
1638 /* set local/remote ipv4 addresses */
1639 if (mci
->mci_out
!= NULL
&& (
1641 CurHostAddr
.sa
.sa_family
== AF_INET6
||
1642 # endif /* NETINET6 */
1643 CurHostAddr
.sa
.sa_family
== AF_INET
))
1645 SOCKADDR_LEN_T addrsize
;
1647 char localip
[60], remoteip
[60];
1649 switch (CurHostAddr
.sa
.sa_family
)
1652 addrsize
= sizeof(struct sockaddr_in
);
1656 addrsize
= sizeof(struct sockaddr_in6
);
1658 # endif /* NETINET6 */
1662 if (iptostring(&CurHostAddr
, addrsize
,
1663 remoteip
, sizeof(remoteip
)))
1665 if (sasl_setprop(mci
->mci_conn
, SASL_IPREMOTEPORT
,
1666 remoteip
) != SASL_OK
)
1669 addrsize
= sizeof(saddr_l
);
1670 if (getsockname(sm_io_getinfo(mci
->mci_out
, SM_IO_WHAT_FD
,
1672 (struct sockaddr
*) &saddr_l
, &addrsize
) == 0)
1674 if (iptostring(&saddr_l
, addrsize
,
1675 localip
, sizeof(localip
)))
1677 if (sasl_setprop(mci
->mci_conn
,
1679 localip
) != SASL_OK
)
1684 # endif /* NETINET || NETINET6 */
1686 /* start client side of sasl */
1687 saslresult
= sasl_client_start(mci
->mci_conn
, mci
->mci_saslcap
,
1690 (const char **) &mechusing
);
1691 # else /* SASL >= 20000 */
1692 /* external security strength factor, authentication id */
1696 out
= macvalue(macid("{cert_subject}"), e
);
1697 if (out
!= NULL
&& *out
!= '\0')
1699 out
= macvalue(macid("{cipher_bits}"), e
);
1700 if (out
!= NULL
&& *out
!= '\0')
1701 ssf
.ssf
= atoi(out
);
1702 # endif /* STARTTLS */
1703 saslresult
= sasl_setprop(mci
->mci_conn
, SASL_SSF_EXTERNAL
, &ssf
);
1704 if (saslresult
!= SASL_OK
)
1708 /* set local/remote ipv4 addresses */
1709 if (mci
->mci_out
!= NULL
&& CurHostAddr
.sa
.sa_family
== AF_INET
)
1711 SOCKADDR_LEN_T addrsize
;
1712 struct sockaddr_in saddr_l
;
1714 if (sasl_setprop(mci
->mci_conn
, SASL_IP_REMOTE
,
1715 (struct sockaddr_in
*) &CurHostAddr
)
1718 addrsize
= sizeof(struct sockaddr_in
);
1719 if (getsockname(sm_io_getinfo(mci
->mci_out
, SM_IO_WHAT_FD
,
1721 (struct sockaddr
*) &saddr_l
, &addrsize
) == 0)
1723 if (sasl_setprop(mci
->mci_conn
, SASL_IP_LOCAL
,
1724 &saddr_l
) != SASL_OK
)
1728 # endif /* NETINET */
1730 /* start client side of sasl */
1731 saslresult
= sasl_client_start(mci
->mci_conn
, mci
->mci_saslcap
,
1732 NULL
, &client_interact
,
1734 (const char **) &mechusing
);
1735 # endif /* SASL >= 20000 */
1737 if (saslresult
!= SASL_OK
&& saslresult
!= SASL_CONTINUE
)
1739 if (saslresult
== SASL_NOMECH
&& LogLevel
> 8)
1741 sm_syslog(LOG_NOTICE
, e
->e_id
,
1742 "AUTH=client, available mechanisms do not fulfill requirements");
1747 /* just point current mechanism to the data in the sasl library */
1748 (*sai
)[SASL_MECH
] = mechusing
;
1750 /* send the info across the wire */
1752 /* login and digest-md5 up to 1.5.28 set out="" */
1754 (sm_strcasecmp(mechusing
, "LOGIN") == 0 ||
1755 sm_strcasecmp(mechusing
, "DIGEST-MD5") == 0))
1758 /* no initial response */
1759 smtpmessage("AUTH %s", m
, mci
, mechusing
);
1761 else if (outlen
== 0)
1764 ** zero-length initial response, per RFC 2554 4.:
1765 ** "Unlike a zero-length client answer to a 334 reply, a zero-
1766 ** length initial response is sent as a single equals sign"
1769 smtpmessage("AUTH %s =", m
, mci
, mechusing
);
1773 saslresult
= sasl_encode64(out
, outlen
, in64
, MAXOUTLEN
, NULL
);
1774 if (saslresult
!= SASL_OK
) /* internal error */
1777 sm_syslog(LOG_ERR
, e
->e_id
,
1778 "encode64 for AUTH failed");
1781 smtpmessage("AUTH %s %s", m
, mci
, mechusing
, in64
);
1784 sm_sasl_free(out
); /* XXX only if no rpool is used */
1785 # endif /* SASL < 20000 */
1788 smtpresult
= reply(m
, mci
, e
, TimeOuts
.to_auth
, getsasldata
, NULL
,
1793 /* check return code from server */
1794 if (smtpresult
== 235)
1796 macdefine(&mci
->mci_macro
, A_TEMP
, macid("{auth_type}"),
1800 if (smtpresult
== -1)
1802 if (REPLYTYPE(smtpresult
) == 5)
1803 return EX_NOPERM
; /* ugly, but ... */
1804 if (REPLYTYPE(smtpresult
) != 3)
1806 /* should we fail deliberately, see RFC 2554 4. ? */
1807 /* smtpmessage("*", m, mci); */
1811 saslresult
= sasl_client_step(mci
->mci_conn
,
1812 mci
->mci_sasl_string
,
1813 mci
->mci_sasl_string_len
,
1817 if (saslresult
!= SASL_OK
&& saslresult
!= SASL_CONTINUE
)
1820 sm_dprintf("AUTH FAIL=%s (%d)\n",
1821 sasl_errstring(saslresult
, NULL
, NULL
),
1824 /* fail deliberately, see RFC 2554 4. */
1825 smtpmessage("*", m
, mci
);
1828 ** but we should only fail for this authentication
1829 ** mechanism; how to do that?
1832 smtpresult
= reply(m
, mci
, e
, TimeOuts
.to_auth
,
1833 getsasldata
, NULL
, XS_AUTH
);
1839 saslresult
= sasl_encode64(out
, outlen
, in64
,
1841 if (saslresult
!= SASL_OK
)
1843 /* give an error reply to the other side! */
1844 smtpmessage("*", m
, mci
);
1851 sm_sasl_free(out
); /* XXX only if no rpool is used */
1852 # endif /* SASL < 20000 */
1853 smtpmessage("%s", m
, mci
, in64
);
1854 smtpresult
= reply(m
, mci
, e
, TimeOuts
.to_auth
,
1855 getsasldata
, NULL
, XS_AUTH
);
1860 ** SMTPAUTH -- try to AUTHenticate
1862 ** This will try mechanisms in the order the sasl library decided until:
1863 ** - there are no more mechanisms
1864 ** - a mechanism succeeds
1865 ** - the sasl library fails initializing
1869 ** mci -- the mailer connection info.
1870 ** e -- the envelope.
1873 ** EX_OK -- authentication was successful
1874 ** EX_UNAVAILABLE -- authentication not possible, e.g.,
1875 ** no data available.
1876 ** EX_NOPERM -- authentication failed.
1877 ** EX_TEMPFAIL -- temporary failure.
1879 ** Notice: AuthInfo is used for all connections, hence we must
1880 ** return EX_TEMPFAIL only if we really want to retry, i.e.,
1881 ** iff getauth() tempfailed or getauth() was used and
1882 ** authentication tempfailed.
1895 mci
->mci_sasl_auth
= false;
1896 for (i
= 0; i
< SASL_MECH
; i
++)
1897 mci
->mci_sai
[i
] = NULL
;
1899 result
= getauth(mci
, e
, &(mci
->mci_sai
));
1900 if (result
== EX_TEMPFAIL
)
1904 /* no data available: don't try to authenticate */
1905 if (result
== EX_OK
&& mci
->mci_sai
[SASL_AUTHID
] == NULL
)
1907 if (result
!= EX_OK
)
1909 if (SASLInfo
== NULL
)
1910 return EX_UNAVAILABLE
;
1912 /* read authinfo from file */
1913 result
= readauth(SASLInfo
, true, &(mci
->mci_sai
),
1915 if (result
!= EX_OK
)
1917 usedgetauth
= false;
1920 /* check whether sufficient data is available */
1921 if (mci
->mci_sai
[SASL_PASSWORD
] == NULL
||
1922 *(mci
->mci_sai
)[SASL_PASSWORD
] == '\0')
1923 return EX_UNAVAILABLE
;
1924 if ((mci
->mci_sai
[SASL_AUTHID
] == NULL
||
1925 *(mci
->mci_sai
)[SASL_AUTHID
] == '\0') &&
1926 (mci
->mci_sai
[SASL_USER
] == NULL
||
1927 *(mci
->mci_sai
)[SASL_USER
] == '\0'))
1928 return EX_UNAVAILABLE
;
1930 /* set the context for the callback function to sai */
1932 callbacks
[CB_PASS_IDX
].context
= (void *) mci
;
1933 # else /* SASL >= 20000 */
1934 callbacks
[CB_PASS_IDX
].context
= (void *) &mci
->mci_sai
;
1935 # endif /* SASL >= 20000 */
1936 callbacks
[CB_USER_IDX
].context
= (void *) &mci
->mci_sai
;
1937 callbacks
[CB_AUTHNAME_IDX
].context
= (void *) &mci
->mci_sai
;
1938 callbacks
[CB_GETREALM_IDX
].context
= (void *) &mci
->mci_sai
;
1940 callbacks
[CB_SAFESASL_IDX
].context
= (void *) &mci
->mci_sai
;
1943 /* set default value for realm */
1944 if ((mci
->mci_sai
)[SASL_DEFREALM
] == NULL
)
1945 (mci
->mci_sai
)[SASL_DEFREALM
] = sm_rpool_strdup_x(e
->e_rpool
,
1946 macvalue('j', CurEnv
));
1948 /* set default value for list of mechanism to use */
1949 if ((mci
->mci_sai
)[SASL_MECHLIST
] == NULL
||
1950 *(mci
->mci_sai
)[SASL_MECHLIST
] == '\0')
1951 (mci
->mci_sai
)[SASL_MECHLIST
] = AuthMechanisms
;
1953 /* create list of mechanisms to try */
1954 mci
->mci_saslcap
= intersect((mci
->mci_sai
)[SASL_MECHLIST
],
1955 mci
->mci_saslcap
, mci
->mci_rpool
);
1957 /* initialize sasl client library */
1958 result
= init_sasl_client();
1959 if (result
!= SASL_OK
)
1960 return usedgetauth
? EX_TEMPFAIL
: EX_UNAVAILABLE
;
1963 result
= attemptauth(m
, mci
, e
, &(mci
->mci_sai
));
1964 if (result
== EX_OK
)
1965 mci
->mci_sasl_auth
= true;
1966 else if (result
== EX_TEMPFAIL
|| result
== EX_NOPERM
)
1968 mci
->mci_saslcap
= removemech((mci
->mci_sai
)[SASL_MECH
],
1971 if (mci
->mci_saslcap
== NULL
||
1972 *(mci
->mci_saslcap
) == '\0')
1973 return usedgetauth
? result
1978 } while (result
!= EX_OK
);
1984 ** SMTPMAILFROM -- send MAIL command
1988 ** mci -- the mailer connection structure.
1989 ** e -- the envelope (including the sender to specify).
1993 smtpmailfrom(m
, mci
, e
)
2002 char buf
[MAXNAME
+ 1];
2003 char optbuf
[MAXLINE
];
2006 sm_dprintf("smtpmailfrom: CurHost=%s\n", CurHostName
);
2010 ** Check if connection is gone, if so
2011 ** it's a tempfail and we use mci_errno
2015 if (mci
->mci_state
== MCIS_CLOSED
)
2017 errno
= mci
->mci_errno
;
2021 /* set up appropriate options to include */
2022 if (bitset(MCIF_SIZE
, mci
->mci_flags
) && e
->e_msgsize
> 0)
2024 (void) sm_snprintf(optbuf
, sizeof(optbuf
), " SIZE=%ld",
2026 bufp
= &optbuf
[strlen(optbuf
)];
2034 bodytype
= e
->e_bodytype
;
2035 if (bitset(MCIF_8BITMIME
, mci
->mci_flags
))
2037 if (bodytype
== NULL
&&
2038 bitset(MM_MIME8BIT
, MimeMode
) &&
2039 bitset(EF_HAS8BIT
, e
->e_flags
) &&
2040 !bitset(EF_DONT_MIME
, e
->e_flags
) &&
2041 !bitnset(M_8BITS
, m
->m_flags
))
2042 bodytype
= "8BITMIME";
2043 if (bodytype
!= NULL
&&
2044 SPACELEFT(optbuf
, bufp
) > strlen(bodytype
) + 7)
2046 (void) sm_snprintf(bufp
, SPACELEFT(optbuf
, bufp
),
2047 " BODY=%s", bodytype
);
2048 bufp
+= strlen(bufp
);
2051 else if (bitnset(M_8BITS
, m
->m_flags
) ||
2052 !bitset(EF_HAS8BIT
, e
->e_flags
) ||
2053 bitset(MCIF_8BITOK
, mci
->mci_flags
))
2056 /* just pass it through */
2059 else if (bitset(MM_CVTMIME
, MimeMode
) &&
2060 !bitset(EF_DONT_MIME
, e
->e_flags
) &&
2061 (!bitset(MM_PASS8BIT
, MimeMode
) ||
2062 bitset(EF_IS_MIME
, e
->e_flags
)))
2064 /* must convert from 8bit MIME format to 7bit encoded */
2065 mci
->mci_flags
|= MCIF_CVT8TO7
;
2067 #endif /* MIME8TO7 */
2068 else if (!bitset(MM_PASS8BIT
, MimeMode
))
2070 /* cannot just send a 8-bit version */
2071 extern char MsgBuf
[];
2073 usrerrenh("5.6.3", "%s does not support 8BITMIME", CurHostName
);
2074 mci_setstat(mci
, EX_NOTSTICKY
, "5.6.3", MsgBuf
);
2078 if (bitset(MCIF_DSN
, mci
->mci_flags
))
2080 if (e
->e_envid
!= NULL
&&
2081 SPACELEFT(optbuf
, bufp
) > strlen(e
->e_envid
) + 7)
2083 (void) sm_snprintf(bufp
, SPACELEFT(optbuf
, bufp
),
2084 " ENVID=%s", e
->e_envid
);
2085 bufp
+= strlen(bufp
);
2088 /* RET= parameter */
2089 if (bitset(EF_RET_PARAM
, e
->e_flags
) &&
2090 SPACELEFT(optbuf
, bufp
) > 9)
2092 (void) sm_snprintf(bufp
, SPACELEFT(optbuf
, bufp
),
2094 bitset(EF_NO_BODY_RETN
, e
->e_flags
) ?
2096 bufp
+= strlen(bufp
);
2100 if (bitset(MCIF_AUTH
, mci
->mci_flags
) && e
->e_auth_param
!= NULL
&&
2101 SPACELEFT(optbuf
, bufp
) > strlen(e
->e_auth_param
) + 7
2103 && (!bitset(SASL_AUTH_AUTH
, SASLOpts
) || mci
->mci_sasl_auth
)
2107 (void) sm_snprintf(bufp
, SPACELEFT(optbuf
, bufp
),
2108 " AUTH=%s", e
->e_auth_param
);
2109 bufp
+= strlen(bufp
);
2113 ** 17 is the max length required, we could use log() to compute
2114 ** the exact length (and check IS_DLVR_TRACE())
2117 if (bitset(MCIF_DLVR_BY
, mci
->mci_flags
) &&
2118 IS_DLVR_BY(e
) && SPACELEFT(optbuf
, bufp
) > 17)
2123 ** Avoid problems with delays (for R) since the check
2124 ** in deliver() whether min-deliver-time is sufficient.
2125 ** Alternatively we could pass the computed time to this
2129 dby
= e
->e_deliver_by
- (curtime() - e
->e_ctime
);
2130 if (dby
<= 0 && IS_DLVR_RETURN(e
))
2131 dby
= mci
->mci_min_by
<= 0 ? 1 : mci
->mci_min_by
;
2132 (void) sm_snprintf(bufp
, SPACELEFT(optbuf
, bufp
),
2135 IS_DLVR_RETURN(e
) ? 'R' : 'N',
2136 IS_DLVR_TRACE(e
) ? "T" : "");
2137 bufp
+= strlen(bufp
);
2141 ** Send the MAIL command.
2142 ** Designates the sender.
2145 mci
->mci_state
= MCIS_MAIL
;
2147 if (bitset(EF_RESPONSE
, e
->e_flags
) &&
2148 !bitnset(M_NO_NULL_FROM
, m
->m_flags
))
2151 expand("\201g", buf
, sizeof(buf
), e
);
2154 /* strip off <angle brackets> (put back on below) */
2155 bufp
= &buf
[strlen(buf
) - 1];
2162 if (bitnset(M_LOCALMAILER
, e
->e_from
.q_mailer
->m_flags
) ||
2163 !bitnset(M_FROMPATH
, m
->m_flags
))
2165 smtpmessage("MAIL From:<%s>%s", m
, mci
, bufp
, optbuf
);
2169 smtpmessage("MAIL From:<@%s%c%s>%s", m
, mci
, MyHostName
,
2170 *bufp
== '@' ? ',' : ':', bufp
, optbuf
);
2172 SmtpPhase
= mci
->mci_phase
= "client MAIL";
2173 sm_setproctitle(true, e
, "%s %s: %s", qid_printname(e
),
2174 CurHostName
, mci
->mci_phase
);
2175 r
= reply(m
, mci
, e
, TimeOuts
.to_mail
, NULL
, &enhsc
, XS_DEFAULT
);
2178 /* communications failure */
2179 mci_setstat(mci
, EX_TEMPFAIL
, "4.4.2", NULL
);
2182 else if (r
== SMTPCLOSING
)
2184 /* service shutting down: handled by reply() */
2187 else if (REPLYTYPE(r
) == 4)
2189 mci_setstat(mci
, EX_NOTSTICKY
, ENHSCN(enhsc
, smtptodsn(r
)),
2193 else if (REPLYTYPE(r
) == 2)
2199 /* syntax error in arguments */
2200 mci_setstat(mci
, EX_NOTSTICKY
, ENHSCN(enhsc
, "5.5.2"),
2206 /* mailbox name not allowed */
2207 mci_setstat(mci
, EX_NOTSTICKY
, ENHSCN(enhsc
, "5.1.3"),
2213 /* exceeded storage allocation */
2214 mci_setstat(mci
, EX_NOTSTICKY
, ENHSCN(enhsc
, "5.3.4"),
2216 if (bitset(MCIF_SIZE
, mci
->mci_flags
))
2217 e
->e_flags
|= EF_NO_BODY_RETN
;
2218 return EX_UNAVAILABLE
;
2220 else if (REPLYTYPE(r
) == 5)
2223 mci_setstat(mci
, EX_NOTSTICKY
, ENHSCN(enhsc
, "5.0.0"),
2225 return EX_UNAVAILABLE
;
2230 sm_syslog(LOG_CRIT
, e
->e_id
,
2231 "%.100s: SMTP MAIL protocol error: %s",
2233 shortenstring(SmtpReplyBuffer
, 403));
2236 /* protocol error -- close up */
2237 mci_setstat(mci
, EX_PROTOCOL
, ENHSCN(enhsc
, "5.5.1"),
2239 smtpquit(m
, mci
, e
);
2243 ** SMTPRCPT -- designate recipient.
2246 ** to -- address of recipient.
2247 ** m -- the mailer we are sending to.
2248 ** mci -- the connection info for this transaction.
2249 ** e -- the envelope for this transaction.
2252 ** exit status corresponding to recipient status.
2255 ** Sends the mail via SMTP.
2259 smtprcpt(to
, m
, mci
, e
, ctladdr
, xstart
)
2268 char optbuf
[MAXLINE
];
2272 ** If there is status waiting from the other end, read it.
2273 ** This should normally happen because of SMTP pipelining.
2276 while (mci
->mci_nextaddr
!= NULL
&&
2277 sm_io_getinfo(mci
->mci_in
, SM_IO_IS_READABLE
, NULL
) > 0)
2281 r
= smtprcptstat(mci
->mci_nextaddr
, m
, mci
, e
);
2284 markfailure(e
, mci
->mci_nextaddr
, mci
, r
, false);
2285 giveresponse(r
, mci
->mci_nextaddr
->q_status
, m
, mci
,
2286 ctladdr
, xstart
, e
, to
);
2288 mci
->mci_nextaddr
= mci
->mci_nextaddr
->q_pchain
;
2290 #endif /* PIPELINING */
2293 ** Check if connection is gone, if so
2294 ** it's a tempfail and we use mci_errno
2298 if (mci
->mci_state
== MCIS_CLOSED
)
2300 errno
= mci
->mci_errno
;
2308 ** Warning: in the following it is assumed that the free space
2309 ** in bufp is sizeof(optbuf)
2312 if (bitset(MCIF_DSN
, mci
->mci_flags
))
2314 if (IS_DLVR_NOTIFY(e
) &&
2315 !bitset(MCIF_DLVR_BY
, mci
->mci_flags
))
2317 /* RFC 2852: 4.1.4.2 */
2318 if (!bitset(QHASNOTIFY
, to
->q_flags
))
2319 to
->q_flags
|= QPINGONFAILURE
|QPINGONDELAY
|QHASNOTIFY
;
2320 else if (bitset(QPINGONSUCCESS
, to
->q_flags
) ||
2321 bitset(QPINGONFAILURE
, to
->q_flags
) ||
2322 bitset(QPINGONDELAY
, to
->q_flags
))
2323 to
->q_flags
|= QPINGONDELAY
;
2326 /* NOTIFY= parameter */
2327 if (bitset(QHASNOTIFY
, to
->q_flags
) &&
2328 bitset(QPRIMARY
, to
->q_flags
) &&
2329 !bitnset(M_LOCALMAILER
, m
->m_flags
))
2331 bool firstone
= true;
2333 (void) sm_strlcat(bufp
, " NOTIFY=", sizeof(optbuf
));
2334 if (bitset(QPINGONSUCCESS
, to
->q_flags
))
2336 (void) sm_strlcat(bufp
, "SUCCESS", sizeof(optbuf
));
2339 if (bitset(QPINGONFAILURE
, to
->q_flags
))
2342 (void) sm_strlcat(bufp
, ",",
2344 (void) sm_strlcat(bufp
, "FAILURE", sizeof(optbuf
));
2347 if (bitset(QPINGONDELAY
, to
->q_flags
))
2350 (void) sm_strlcat(bufp
, ",",
2352 (void) sm_strlcat(bufp
, "DELAY", sizeof(optbuf
));
2356 (void) sm_strlcat(bufp
, "NEVER", sizeof(optbuf
));
2357 bufp
+= strlen(bufp
);
2360 /* ORCPT= parameter */
2361 if (to
->q_orcpt
!= NULL
&&
2362 SPACELEFT(optbuf
, bufp
) > strlen(to
->q_orcpt
) + 7)
2364 (void) sm_snprintf(bufp
, SPACELEFT(optbuf
, bufp
),
2365 " ORCPT=%s", to
->q_orcpt
);
2366 bufp
+= strlen(bufp
);
2370 smtpmessage("RCPT To:<%s>%s", m
, mci
, to
->q_user
, optbuf
);
2371 mci
->mci_state
= MCIS_RCPT
;
2373 SmtpPhase
= mci
->mci_phase
= "client RCPT";
2374 sm_setproctitle(true, e
, "%s %s: %s", qid_printname(e
),
2375 CurHostName
, mci
->mci_phase
);
2379 ** If running SMTP pipelining, we will pick up status later
2382 if (bitset(MCIF_PIPELINED
, mci
->mci_flags
))
2384 #endif /* PIPELINING */
2386 return smtprcptstat(to
, m
, mci
, e
);
2389 ** SMTPRCPTSTAT -- get recipient status
2391 ** This is only called during SMTP pipelining
2394 ** to -- address of recipient.
2395 ** m -- mailer being sent to.
2396 ** mci -- the mailer connection information.
2397 ** e -- the envelope for this message.
2400 ** EX_* -- protocol status
2404 smtprcptstat(to
, m
, mci
, e
)
2408 register ENVELOPE
*e
;
2415 ** Check if connection is gone, if so
2416 ** it's a tempfail and we use mci_errno
2420 if (mci
->mci_state
== MCIS_CLOSED
)
2422 errno
= mci
->mci_errno
;
2427 r
= reply(m
, mci
, e
, TimeOuts
.to_rcpt
, NULL
, &enhsc
, XS_DEFAULT
);
2429 to
->q_rstatus
= sm_rpool_strdup_x(e
->e_rpool
, SmtpReplyBuffer
);
2430 to
->q_status
= ENHSCN_RPOOL(enhsc
, smtptodsn(r
), e
->e_rpool
);
2431 if (!bitnset(M_LMTP
, m
->m_flags
))
2432 to
->q_statmta
= mci
->mci_host
;
2433 if (r
< 0 || REPLYTYPE(r
) == 4)
2435 mci
->mci_retryrcpt
= true;
2439 else if (REPLYTYPE(r
) == 2)
2443 if ((t
= mci
->mci_tolist
) != NULL
)
2448 for (p
= to
->q_paddr
; *p
!= '\0'; *t
++ = *p
++)
2451 mci
->mci_tolist
= t
;
2455 #endif /* PIPELINING */
2460 to
->q_status
= ENHSCN_RPOOL(enhsc
, "5.1.1", e
->e_rpool
);
2465 to
->q_status
= ENHSCN_RPOOL(enhsc
, "5.1.6", e
->e_rpool
);
2470 to
->q_status
= ENHSCN_RPOOL(enhsc
, "5.1.3", e
->e_rpool
);
2473 else if (REPLYTYPE(r
) == 5)
2475 return EX_UNAVAILABLE
;
2480 sm_syslog(LOG_CRIT
, e
->e_id
,
2481 "%.100s: SMTP RCPT protocol error: %s",
2483 shortenstring(SmtpReplyBuffer
, 403));
2486 mci_setstat(mci
, EX_PROTOCOL
, ENHSCN(enhsc
, "5.5.1"),
2491 ** SMTPDATA -- send the data and clean up the transaction.
2494 ** m -- mailer being sent to.
2495 ** mci -- the mailer connection information.
2496 ** e -- the envelope for this message.
2499 ** exit status corresponding to DATA command.
2503 smtpdata(m
, mci
, e
, ctladdr
, xstart
)
2506 register ENVELOPE
*e
;
2517 ** Check if connection is gone, if so
2518 ** it's a tempfail and we use mci_errno
2522 if (mci
->mci_state
== MCIS_CLOSED
)
2524 errno
= mci
->mci_errno
;
2532 ** First send the command and check that it is ok.
2533 ** Then send the data (if there are valid recipients).
2534 ** Follow it up with a dot to terminate.
2535 ** Finally get the results of the transaction.
2538 /* send the command and check ok to proceed */
2539 smtpmessage("DATA", m
, mci
);
2542 if (mci
->mci_nextaddr
!= NULL
)
2544 char *oldto
= e
->e_to
;
2546 /* pick up any pending RCPT responses for SMTP pipelining */
2547 while (mci
->mci_nextaddr
!= NULL
)
2551 e
->e_to
= mci
->mci_nextaddr
->q_paddr
;
2552 r
= smtprcptstat(mci
->mci_nextaddr
, m
, mci
, e
);
2555 markfailure(e
, mci
->mci_nextaddr
, mci
, r
,
2557 giveresponse(r
, mci
->mci_nextaddr
->q_status
, m
,
2558 mci
, ctladdr
, xstart
, e
,
2560 if (r
== EX_TEMPFAIL
)
2561 mci
->mci_nextaddr
->q_state
= QS_RETRY
;
2563 mci
->mci_nextaddr
= mci
->mci_nextaddr
->q_pchain
;
2568 ** Connection might be closed in response to a RCPT command,
2569 ** i.e., the server responded with 421. In that case (at
2570 ** least) one RCPT has a temporary failure, hence we don't
2571 ** need to check mci_okrcpts (as it is done below) to figure
2572 ** out which error to return.
2575 if (mci
->mci_state
== MCIS_CLOSED
)
2577 errno
= mci
->mci_errno
;
2581 #endif /* PIPELINING */
2583 /* now proceed with DATA phase */
2584 SmtpPhase
= mci
->mci_phase
= "client DATA 354";
2585 mci
->mci_state
= MCIS_DATA
;
2586 sm_setproctitle(true, e
, "%s %s: %s",
2587 qid_printname(e
), CurHostName
, mci
->mci_phase
);
2588 r
= reply(m
, mci
, e
, TimeOuts
.to_datainit
, NULL
, &enhsc
, XS_DEFAULT
);
2589 if (r
< 0 || REPLYTYPE(r
) == 4)
2592 smtpquit(m
, mci
, e
);
2593 errno
= mci
->mci_errno
;
2596 else if (REPLYTYPE(r
) == 5)
2598 smtprset(m
, mci
, e
);
2600 if (mci
->mci_okrcpts
<= 0)
2601 return mci
->mci_retryrcpt
? EX_TEMPFAIL
2603 #endif /* PIPELINING */
2604 return EX_UNAVAILABLE
;
2606 else if (REPLYTYPE(r
) != 3)
2610 sm_syslog(LOG_CRIT
, e
->e_id
,
2611 "%.100s: SMTP DATA-1 protocol error: %s",
2613 shortenstring(SmtpReplyBuffer
, 403));
2615 smtprset(m
, mci
, e
);
2616 mci_setstat(mci
, EX_PROTOCOL
, ENHSCN(enhsc
, "5.5.1"),
2619 if (mci
->mci_okrcpts
<= 0)
2620 return mci
->mci_retryrcpt
? EX_TEMPFAIL
2622 #endif /* PIPELINING */
2627 if (mci
->mci_okrcpts
> 0)
2629 #endif /* PIPELINING */
2632 ** Set timeout around data writes. Make it at least large
2633 ** enough for DNS timeouts on all recipients plus some fudge
2634 ** factor. The main thing is that it should not be infinite.
2639 /* simulate a DATA timeout */
2643 timeout
= DATA_PROGRESS_TIMEOUT
* 1000;
2644 sm_io_setinfo(mci
->mci_out
, SM_IO_WHAT_TIMEOUT
, &timeout
);
2648 ** Output the actual message.
2651 if (!(*e
->e_puthdr
)(mci
, e
->e_header
, e
, M87F_OUTER
))
2656 /* simulate a DATA timeout */
2660 if (!(*e
->e_putbody
)(mci
, e
, NULL
))
2664 ** Cleanup after sending message.
2670 #endif /* PIPELINING */
2672 #if _FFR_CATCH_BROKEN_MTAS
2673 if (sm_io_getinfo(mci
->mci_in
, SM_IO_IS_READABLE
, NULL
) > 0)
2675 /* terminate the message */
2676 (void) sm_io_fprintf(mci
->mci_out
, SM_TIME_DEFAULT
, ".%s",
2678 if (TrafficLogFile
!= NULL
)
2679 (void) sm_io_fprintf(TrafficLogFile
, SM_TIME_DEFAULT
,
2680 "%05d >>> .\n", (int) CurrentPid
);
2684 sm_syslog(LOG_CRIT
, e
->e_id
,
2685 "%.100s: SMTP DATA-1 protocol error: remote server returned response before final dot",
2687 mci
->mci_errno
= EIO
;
2688 mci
->mci_state
= MCIS_ERROR
;
2689 mci_setstat(mci
, EX_PROTOCOL
, "5.5.0", NULL
);
2690 smtpquit(m
, mci
, e
);
2693 #endif /* _FFR_CATCH_BROKEN_MTAS */
2695 if (sm_io_error(mci
->mci_out
))
2697 /* error during processing -- don't send the dot */
2698 mci
->mci_errno
= EIO
;
2699 mci
->mci_state
= MCIS_ERROR
;
2700 mci_setstat(mci
, EX_IOERR
, "4.4.2", NULL
);
2701 smtpquit(m
, mci
, e
);
2705 /* terminate the message */
2706 if (sm_io_fprintf(mci
->mci_out
, SM_TIME_DEFAULT
, "%s.%s",
2707 bitset(MCIF_INLONGLINE
, mci
->mci_flags
) ? m
->m_eol
: "",
2708 m
->m_eol
) == SM_IO_EOF
)
2710 if (TrafficLogFile
!= NULL
)
2711 (void) sm_io_fprintf(TrafficLogFile
, SM_TIME_DEFAULT
,
2712 "%05d >>> .\n", (int) CurrentPid
);
2716 /* check for the results of the transaction */
2717 SmtpPhase
= mci
->mci_phase
= "client DATA status";
2718 sm_setproctitle(true, e
, "%s %s: %s", qid_printname(e
),
2719 CurHostName
, mci
->mci_phase
);
2720 if (bitnset(M_LMTP
, m
->m_flags
))
2722 r
= reply(m
, mci
, e
, TimeOuts
.to_datafinal
, NULL
, &enhsc
, XS_DEFAULT
);
2725 if (mci
->mci_state
== MCIS_DATA
)
2726 mci
->mci_state
= MCIS_OPEN
;
2727 xstat
= EX_NOTSTICKY
;
2729 rstat
= EX_TEMPFAIL
;
2730 else if (REPLYTYPE(r
) == 4)
2731 rstat
= xstat
= EX_TEMPFAIL
;
2732 else if (REPLYTYPE(r
) == 2)
2733 rstat
= xstat
= EX_OK
;
2734 else if (REPLYCLASS(r
) != 5)
2735 rstat
= xstat
= EX_PROTOCOL
;
2736 else if (REPLYTYPE(r
) == 5)
2737 rstat
= EX_UNAVAILABLE
;
2739 rstat
= EX_PROTOCOL
;
2740 mci_setstat(mci
, xstat
, ENHSCN(enhsc
, smtptodsn(r
)),
2742 if (bitset(MCIF_ENHSTAT
, mci
->mci_flags
) &&
2743 (r
= isenhsc(SmtpReplyBuffer
+ 4, ' ')) > 0)
2747 e
->e_statmsg
= sm_rpool_strdup_x(e
->e_rpool
, &SmtpReplyBuffer
[r
]);
2748 SmtpPhase
= mci
->mci_phase
= "idle";
2749 sm_setproctitle(true, e
, "%s: %s", CurHostName
, mci
->mci_phase
);
2750 if (rstat
!= EX_PROTOCOL
)
2754 sm_syslog(LOG_CRIT
, e
->e_id
,
2755 "%.100s: SMTP DATA-2 protocol error: %s",
2757 shortenstring(SmtpReplyBuffer
, 403));
2762 mci
->mci_errno
= errno
;
2763 mci
->mci_state
= MCIS_ERROR
;
2764 mci_setstat(mci
, EX_TEMPFAIL
, "4.4.2", NULL
);
2767 ** If putbody() couldn't finish due to a timeout,
2768 ** rewind it here in the timeout handler. See
2769 ** comments at the end of putbody() for reasoning.
2772 if (e
->e_dfp
!= NULL
)
2773 (void) bfrewind(e
->e_dfp
);
2775 errno
= mci
->mci_errno
;
2776 syserr("451 4.4.1 timeout writing message to %s", CurHostName
);
2777 smtpquit(m
, mci
, e
);
2782 ** SMTPGETSTAT -- get status code from DATA in LMTP
2785 ** m -- the mailer to which we are sending the message.
2786 ** mci -- the mailer connection structure.
2787 ** e -- the current envelope.
2790 ** The exit status corresponding to the reply code.
2794 smtpgetstat(m
, mci
, e
)
2806 /* check for the results of the transaction */
2807 r
= reply(m
, mci
, e
, TimeOuts
.to_datafinal
, NULL
, &enhsc
, XS_DEFAULT
);
2810 xstat
= EX_NOTSTICKY
;
2811 if (REPLYTYPE(r
) == 4)
2812 status
= EX_TEMPFAIL
;
2813 else if (REPLYTYPE(r
) == 2)
2814 status
= xstat
= EX_OK
;
2815 else if (REPLYCLASS(r
) != 5)
2816 status
= xstat
= EX_PROTOCOL
;
2817 else if (REPLYTYPE(r
) == 5)
2818 status
= EX_UNAVAILABLE
;
2820 status
= EX_PROTOCOL
;
2821 if (bitset(MCIF_ENHSTAT
, mci
->mci_flags
) &&
2822 (off
= isenhsc(SmtpReplyBuffer
+ 4, ' ')) > 0)
2826 e
->e_statmsg
= sm_rpool_strdup_x(e
->e_rpool
, &SmtpReplyBuffer
[off
]);
2827 mci_setstat(mci
, xstat
, ENHSCN(enhsc
, smtptodsn(r
)), SmtpReplyBuffer
);
2828 if (LogLevel
> 1 && status
== EX_PROTOCOL
)
2830 sm_syslog(LOG_CRIT
, e
->e_id
,
2831 "%.100s: SMTP DATA-3 protocol error: %s",
2833 shortenstring(SmtpReplyBuffer
, 403));
2838 ** SMTPQUIT -- close the SMTP connection.
2841 ** m -- a pointer to the mailer.
2842 ** mci -- the mailer connection information.
2843 ** e -- the current envelope.
2849 ** sends the final protocol and closes the connection.
2858 bool oldSuprErrs
= SuprErrs
;
2862 if (mci
->mci_state
== MCIS_CLOSED
)
2864 mci_close(mci
, "smtpquit:1");
2868 oldcurhost
= CurHostName
;
2869 CurHostName
= mci
->mci_host
; /* XXX UGLY XXX */
2870 if (CurHostName
== NULL
)
2871 CurHostName
= MyHostName
;
2874 mci
->mci_okrcpts
= 0;
2875 #endif /* PIPELINING */
2878 ** Suppress errors here -- we may be processing a different
2879 ** job when we do the quit connection, and we don't want the
2880 ** new job to be penalized for something that isn't it's
2886 /* send the quit message if we haven't gotten I/O error */
2887 if (mci
->mci_state
!= MCIS_ERROR
&&
2888 mci
->mci_state
!= MCIS_QUITING
)
2890 SmtpPhase
= "client QUIT";
2891 mci
->mci_state
= MCIS_QUITING
;
2892 smtpmessage("QUIT", m
, mci
);
2893 (void) reply(m
, mci
, e
, TimeOuts
.to_quit
, NULL
, NULL
,
2895 SuprErrs
= oldSuprErrs
;
2896 if (mci
->mci_state
== MCIS_CLOSED
)
2900 /* now actually close the connection and pick up the zombie */
2901 rcode
= endmailer(mci
, e
, NULL
);
2904 char *mailer
= NULL
;
2906 if (mci
->mci_mailer
!= NULL
&&
2907 mci
->mci_mailer
->m_name
!= NULL
)
2908 mailer
= mci
->mci_mailer
->m_name
;
2910 /* look for naughty mailers */
2911 sm_syslog(LOG_ERR
, e
->e_id
,
2912 "smtpquit: mailer%s%s exited with exit value %d",
2913 mailer
== NULL
? "" : " ",
2914 mailer
== NULL
? "" : mailer
,
2918 SuprErrs
= oldSuprErrs
;
2921 CurHostName
= oldcurhost
;
2925 ** SMTPRSET -- send a RSET (reset) command
2928 ** m -- a pointer to the mailer.
2929 ** mci -- the mailer connection information.
2930 ** e -- the current envelope.
2936 ** closes the connection if there is no reply to RSET.
2947 CurHostName
= mci
->mci_host
; /* XXX UGLY XXX */
2948 if (CurHostName
== NULL
)
2949 CurHostName
= MyHostName
;
2952 mci
->mci_okrcpts
= 0;
2953 #endif /* PIPELINING */
2956 ** Check if connection is gone, if so
2957 ** it's a tempfail and we use mci_errno
2961 if (mci
->mci_state
== MCIS_CLOSED
)
2963 errno
= mci
->mci_errno
;
2967 SmtpPhase
= "client RSET";
2968 smtpmessage("RSET", m
, mci
);
2969 r
= reply(m
, mci
, e
, TimeOuts
.to_rset
, NULL
, NULL
, XS_DEFAULT
);
2974 ** Any response is deemed to be acceptable.
2975 ** The standard does not state the proper action
2976 ** to take when a value other than 250 is received.
2978 ** However, if 421 is returned for the RSET, leave
2979 ** mci_state alone (MCIS_SSD can be set in reply()
2980 ** and MCIS_CLOSED can be set in smtpquit() if
2981 ** reply() gets a 421 and calls smtpquit()).
2984 if (mci
->mci_state
!= MCIS_SSD
&& mci
->mci_state
!= MCIS_CLOSED
)
2985 mci
->mci_state
= MCIS_OPEN
;
2986 else if (mci
->mci_exitstat
== EX_OK
)
2987 mci_setstat(mci
, EX_TEMPFAIL
, "4.5.0", NULL
);
2990 ** SMTPPROBE -- check the connection state
2993 ** mci -- the mailer connection information.
2999 ** closes the connection if there is no reply to RSET.
3007 MAILER
*m
= mci
->mci_mailer
;
3009 extern ENVELOPE BlankEnvelope
;
3011 CurHostName
= mci
->mci_host
; /* XXX UGLY XXX */
3012 if (CurHostName
== NULL
)
3013 CurHostName
= MyHostName
;
3016 SmtpPhase
= "client probe";
3017 smtpmessage("RSET", m
, mci
);
3018 r
= reply(m
, mci
, e
, TimeOuts
.to_miscshort
, NULL
, NULL
, XS_DEFAULT
);
3019 if (REPLYTYPE(r
) != 2)
3020 smtpquit(m
, mci
, e
);
3024 ** REPLY -- read arpanet reply
3027 ** m -- the mailer we are reading the reply from.
3028 ** mci -- the mailer connection info structure.
3029 ** e -- the current envelope.
3030 ** timeout -- the timeout for reads.
3031 ** pfunc -- processing function called on each line of response.
3032 ** If null, no special processing is done.
3033 ** enhstat -- optional, returns enhanced error code string (if set)
3034 ** rtype -- type of SmtpMsgBuffer: does it contains secret data?
3037 ** reply code it reads.
3040 ** flushes the mail file.
3044 reply(m
, mci
, e
, timeout
, pfunc
, enhstat
, rtype
)
3049 void (*pfunc
) __P((char *, bool, MAILER
*, MCI
*, ENVELOPE
*));
3053 register char *bufp
;
3055 bool firstline
= true;
3056 char junkbuf
[MAXLINE
];
3057 static char enhstatcode
[ENHSCLEN
];
3061 ** Flush the output before reading response.
3063 ** For SMTP pipelining, it would be better if we didn't do
3064 ** this if there was already data waiting to be read. But
3065 ** to do it properly means pushing it to the I/O library,
3066 ** since it really needs to be done below the buffer layer.
3069 if (mci
->mci_out
!= NULL
)
3070 (void) sm_io_flush(mci
->mci_out
, SM_TIME_DEFAULT
);
3073 sm_dprintf("reply\n");
3076 ** Read the input line, being careful not to hang.
3079 bufp
= SmtpReplyBuffer
;
3084 /* actually do the read */
3085 if (e
->e_xfp
!= NULL
) /* for debugging */
3086 (void) sm_io_flush(e
->e_xfp
, SM_TIME_DEFAULT
);
3088 /* if we are in the process of closing just give the code */
3089 if (mci
->mci_state
== MCIS_CLOSED
)
3092 /* don't try to read from a non-existent fd */
3093 if (mci
->mci_in
== NULL
)
3095 if (mci
->mci_errno
== 0)
3096 mci
->mci_errno
= EBADF
;
3098 /* errors on QUIT should be ignored */
3099 if (strncmp(SmtpMsgBuffer
, "QUIT", 4) == 0)
3101 errno
= mci
->mci_errno
;
3102 mci_close(mci
, "reply:1");
3105 mci
->mci_state
= MCIS_ERROR
;
3106 smtpquit(m
, mci
, e
);
3107 errno
= mci
->mci_errno
;
3111 if (mci
->mci_out
!= NULL
)
3112 (void) sm_io_flush(mci
->mci_out
, SM_TIME_DEFAULT
);
3114 /* get the line from the other side */
3115 p
= sfgets(bufp
, MAXLINE
, mci
->mci_in
, timeout
, SmtpPhase
);
3117 mci
->mci_lastuse
= curtime();
3122 extern char MsgBuf
[];
3124 /* errors on QUIT should be ignored */
3125 if (strncmp(SmtpMsgBuffer
, "QUIT", 4) == 0)
3127 mci_close(mci
, "reply:2");
3131 /* if the remote end closed early, fake an error */
3135 (void) sm_snprintf(SmtpReplyBuffer
,
3136 sizeof(SmtpReplyBuffer
),
3137 "421 4.4.1 Connection reset by %s",
3141 #else /* ECONNRESET */
3143 #endif /* ECONNRESET */
3146 mci
->mci_errno
= errno
;
3147 oldholderrs
= HoldErrs
;
3149 usrerr("451 4.4.1 reply: read error from %s",
3151 mci_setstat(mci
, EX_TEMPFAIL
, "4.4.2", MsgBuf
);
3153 /* if debugging, pause so we can see state */
3156 mci
->mci_state
= MCIS_ERROR
;
3157 smtpquit(m
, mci
, e
);
3163 if (e
->e_to
!= NULL
)
3165 (void) sm_snprintf(p
,
3168 shortenstring(e
->e_to
, MAXSHORTSTR
));
3171 (void) sm_snprintf(p
, SPACELEFT(wbuf
, p
),
3172 "reply(%.100s) during %s",
3173 CURHOSTNAME
, SmtpPhase
);
3177 HoldErrs
= oldholderrs
;
3181 fixcrlf(bufp
, true);
3183 /* EHLO failure is not a real error */
3184 if (e
->e_xfp
!= NULL
&& (bufp
[0] == '4' ||
3185 (bufp
[0] == '5' && strncmp(SmtpMsgBuffer
, "EHLO", 4) != 0)))
3187 /* serious error -- log the previous command */
3190 /* inform user who we are chatting with */
3191 (void) sm_io_fprintf(CurEnv
->e_xfp
,
3193 "... while talking to %s:\n",
3195 SmtpNeedIntro
= false;
3197 if (SmtpMsgBuffer
[0] != '\0')
3199 (void) sm_io_fprintf(e
->e_xfp
,
3202 (rtype
== XS_STARTTLS
)
3203 ? "STARTTLS dialogue"
3204 : ((rtype
== XS_AUTH
)
3207 SmtpMsgBuffer
[0] = '\0';
3210 /* now log the message as from the other side */
3211 (void) sm_io_fprintf(e
->e_xfp
, SM_TIME_DEFAULT
,
3215 /* display the input for verbose mode */
3217 nmessage("050 %s", bufp
);
3219 /* ignore improperly formatted input */
3220 if (!ISSMTPREPLY(bufp
))
3223 if (bitset(MCIF_ENHSTAT
, mci
->mci_flags
) &&
3225 extenhsc(bufp
+ 4, ' ', enhstatcode
) > 0)
3226 *enhstat
= enhstatcode
;
3228 /* process the line */
3230 (*pfunc
)(bufp
, firstline
, m
, mci
, e
);
3234 /* decode the reply code */
3237 /* extra semantics: 0xx codes are "informational" */
3241 /* if no continuation lines, return this line */
3245 /* first line of real reply -- ignore rest */
3250 ** Now look at SmtpReplyBuffer -- only care about the first
3251 ** line of the response from here on out.
3254 /* save temporary failure messages for posterity */
3255 if (SmtpReplyBuffer
[0] == '4')
3256 (void) sm_strlcpy(SmtpError
, SmtpReplyBuffer
, sizeof(SmtpError
));
3258 /* reply code 421 is "Service Shutting Down" */
3259 if (r
== SMTPCLOSING
&& mci
->mci_state
!= MCIS_SSD
&&
3260 mci
->mci_state
!= MCIS_QUITING
)
3262 /* send the quit protocol */
3263 mci
->mci_state
= MCIS_SSD
;
3264 smtpquit(m
, mci
, e
);
3270 ** SMTPMESSAGE -- send message to server
3274 ** m -- the mailer to control formatting.
3275 ** a, b, c -- parameters
3281 ** writes message to mci->mci_out.
3287 smtpmessage(char *f
, MAILER
*m
, MCI
*mci
, ...)
3288 #else /* __STDC__ */
3289 smtpmessage(f
, m
, mci
, va_alist
)
3294 #endif /* __STDC__ */
3298 SM_VA_START(ap
, mci
);
3299 (void) sm_vsnprintf(SmtpMsgBuffer
, sizeof(SmtpMsgBuffer
), f
, ap
);
3302 if (tTd(18, 1) || Verbose
)
3303 nmessage(">>> %s", SmtpMsgBuffer
);
3304 if (TrafficLogFile
!= NULL
)
3305 (void) sm_io_fprintf(TrafficLogFile
, SM_TIME_DEFAULT
,
3306 "%05d >>> %s\n", (int) CurrentPid
,
3308 if (mci
->mci_out
!= NULL
)
3310 (void) sm_io_fprintf(mci
->mci_out
, SM_TIME_DEFAULT
, "%s%s",
3311 SmtpMsgBuffer
, m
== NULL
? "\r\n"
3314 else if (tTd(18, 1))
3316 sm_dprintf("smtpmessage: NULL mci_out\n");