]>
diplodocus.org Git - nmh/blob - mts/smtp/smtp.c
1b92db9f056376a054e5aa11dea7bf09c7eb57a8
1 /* smtp.c -- nmh SMTP interface
3 * This code is Copyright (c) 2002, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
11 #include <h/signals.h>
15 #include <sys/socket.h>
16 #include "../../sbr/base64.h"
19 * This module implements an interface to SendMail very similar
20 * to the MMDF mm_(3) routines. The sm_() routines herein talk
21 * SMTP to a sendmail process, mapping SMTP reply codes into
25 #define NBITS ((sizeof (int)) * 8)
27 /* Timeout in seconds for SMTP commands.
28 * Lore has it they must be distinct. */
29 #define SM_OPEN 300 /* Changed to 5 minutes to comply with a SHOULD in RFC 1123 */
32 #define SM_MAIL 301 /* changed to 5 minutes and a second (for uniqueness), see above */
33 #define SM_RCPT 302 /* see above */
34 #define SM_DATA 120 /* see above */
35 #define SM_DOT 600 /* see above */
38 static int sm_addrs
= 0;
39 static int sm_child
= NOTOK
;
40 static int sm_debug
= 0;
41 static bool sm_nl
= true;
42 static int sm_verbose
= 0;
43 static netsec_context
*nsc
= NULL
;
45 static char *sm_noreply
= "No reply text given";
46 static char *sm_moreply
= "; ";
47 static struct smtp sm_reply
;
52 static char *EHLOkeys
[MAXEHLO
+ 1];
57 static int smtp_init (char *, char *, char *, int, int, int, int, const char *,
58 const char *, const char *, int);
59 static int sendmail_init (char *, int, int, int, int, const char *,
62 static int rclient (char *, char *, char **);
63 static int sm_ierror (const char *fmt
, ...) CHECK_PRINTF(1, 2);
64 static int sm_nerror (char *);
65 static int smtalk (int time
, char *fmt
, ...) CHECK_PRINTF(2, 3);
66 static int sm_wstream (char *, int);
67 static int smhear (void);
68 static char *EHLOset (char *) PURE
;
69 static int sm_sasl_callback(enum sasl_message_type
, unsigned const char *,
70 unsigned int, unsigned char **, unsigned int *,
74 sm_init (char *client
, char *server
, char *port
, int watch
, int verbose
,
75 int debug
, int sasl
, const char *saslmech
, const char *user
,
76 const char *oauth_svc
, int tls
)
78 if (sm_mts
== MTS_SMTP
)
79 return smtp_init (client
, server
, port
, watch
, verbose
,
80 debug
, sasl
, saslmech
, user
, oauth_svc
, tls
);
82 return sendmail_init (client
, watch
, verbose
, debug
, sasl
,
87 smtp_init (char *client
, char *server
, char *port
, int watch
, int verbose
,
88 int debug
, int sasl
, const char *saslmech
, const char *user
,
89 const char *oauth_svc
, int tls
)
92 char *errstr
, *chosen_server
;
103 if (client
== NULL
|| *client
== '\0') {
107 client
= LocalName(1); /* no clientname -> LocalName */
111 * Last-ditch check just in case client still isn't set to anything
113 if (client
== NULL
|| *client
== '\0')
114 client
= "localhost";
120 netsec_set_userid(nsc
, user
);
122 if ((sd1
= rclient (server
, port
, &chosen_server
)) == NOTOK
)
125 SIGNAL (SIGPIPE
, SIG_IGN
);
127 netsec_set_fd(nsc
, sd1
, sd1
);
129 netsec_set_hostname(nsc
, chosen_server
);
132 netsec_set_snoop(nsc
, 1);
135 if (netsec_set_sasl_params(nsc
, "smtp", saslmech
, sm_sasl_callback
,
137 return sm_nerror(errstr
);
141 if (netsec_set_oauth_service(nsc
, oauth_svc
) != OK
)
142 return sm_ierror("OAuth2 not supported");
145 if (tls
& S_TLSENABLEMASK
) {
146 if (netsec_set_tls(nsc
, 1, tls
& S_NOVERIFY
, &errstr
) != OK
)
147 return sm_nerror(errstr
);
151 * If tls == S_INITTLS, that means that the user requested
152 * "initial" TLS, which happens right after the connection has
153 * opened. Do that negotiation now
156 if (tls
& S_INITTLS
) {
157 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
159 return sm_nerror(errstr
);
163 netsec_set_timeout(nsc
, SM_OPEN
);
176 * Give EHLO or HELO command
180 result
= smtalk (SM_HELO
, "EHLO %s", client
);
183 if (result
>= 500 && result
<= 599)
184 result
= smtalk (SM_HELO
, "HELO %s", client
);
192 * If the user requested TLS support, then try to do the STARTTLS command
193 * as part of the initial dialog. Assuming this works, we then need to
194 * restart the EHLO dialog after TLS negotiation is complete.
197 if (tls
& S_STARTTLS
) {
198 if (! EHLOset("STARTTLS")) {
200 return sm_ierror("SMTP server does not support TLS");
203 result
= smtalk(SM_HELO
, "STARTTLS");
211 * Okay, the other side should be waiting for us to start TLS
212 * negotiation. Oblige them.
215 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
217 return sm_nerror(errstr
);
221 result
= smtalk (SM_HELO
, "EHLO %s", client
);
231 * If the user asked for SASL, then check to see if the SMTP server
232 * supports it. Otherwise, error out (because the SMTP server
233 * might have been spoofed; we don't want to just silently not
239 if (! (server_mechs
= EHLOset("AUTH"))) {
241 return sm_ierror("SMTP server does not support SASL");
244 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
246 return sm_nerror(errstr
);
251 if (watch
&& EHLOset ("XVRB"))
252 smtalk (SM_HELO
, "VERB on");
258 sendmail_init (char *client
, int watch
, int verbose
, int debug
, int sasl
,
259 const char *saslmech
, const char *user
)
261 unsigned int i
, result
, vecp
;
263 char *vec
[15], *errstr
;
268 sm_verbose
= verbose
;
273 if (client
== NULL
|| *client
== '\0') {
277 client
= LocalName(1); /* no clientname -> LocalName */
280 * Last-ditch check just in case client still isn't set to anything
282 if (client
== NULL
|| *client
== '\0')
283 client
= "localhost";
289 netsec_set_userid(nsc
, user
);
291 netsec_set_hostname(nsc
, client
);
294 netsec_set_snoop(nsc
, 1);
297 if (netsec_set_sasl_params(nsc
, "smtp", saslmech
, sm_sasl_callback
,
299 return sm_nerror(errstr
);
302 if (pipe (pdi
) == NOTOK
)
303 return sm_ierror ("no pipes");
304 if (pipe (pdo
) == NOTOK
) {
307 return sm_ierror ("no pipes");
310 for (i
= 0; (sm_child
= fork ()) == NOTOK
&& i
< 5; i
++)
319 return sm_ierror ("unable to fork");
322 if (pdo
[0] != fileno (stdin
))
323 dup2 (pdo
[0], fileno (stdin
));
324 if (pdi
[1] != fileno (stdout
))
325 dup2 (pdi
[1], fileno (stdout
));
326 if (pdi
[1] != fileno (stderr
))
327 dup2 (pdi
[1], fileno (stderr
));
328 for (i
= fileno (stderr
) + 1; i
< NBITS
; i
++)
332 vec
[vecp
++] = r1bindex (sendmail
, '/');
334 vec
[vecp
++] = watch
? "-odi" : "-odb";
335 vec
[vecp
++] = "-oem";
341 execvp (sendmail
, vec
);
342 fprintf (stderr
, "unable to exec ");
344 _exit (-1); /* NOTREACHED */
347 SIGNAL (SIGPIPE
, SIG_IGN
);
352 netsec_set_fd(nsc
, pdi
[0], pdo
[1]);
353 netsec_set_timeout(nsc
, SM_OPEN
);
365 result
= smtalk (SM_HELO
, "EHLO %s", client
);
368 if (500 <= result
&& result
<= 599)
369 result
= smtalk (SM_HELO
, "HELO %s", client
);
381 * If the user asked for SASL, then check to see if the SMTP server
382 * supports it. Otherwise, error out (because the SMTP server
383 * might have been spoofed; we don't want to just silently not
389 if (! (server_mechs
= EHLOset("AUTH"))) {
391 return sm_ierror("SMTP server does not support SASL");
393 if (netsec_negotiate_sasl(nsc
, server_mechs
, &errstr
) != OK
) {
395 return sm_nerror(errstr
);
400 smtalk (SM_HELO
, "VERB on");
407 rclient (char *server
, char *service
, char **chosen_server
)
410 char response
[BUFSIZ
];
415 *chosen_server
= server
;
417 if ((sd
= client (server
, service
, response
, sizeof(response
),
421 sm_ierror ("%s", response
);
426 sm_winit (char *from
, int smtputf8
, int eightbit
)
428 const char *mail_parameters
= "";
431 /* Just for information, if an attempt is made to send to an 8-bit
432 address without specifying SMTPUTF8, Gmail responds with
433 555 5.5.2 Syntax error.
434 Gmail doesn't require the 8BITMIME, but RFC 6531 Sec. 1.2 does. */
435 if (EHLOset ("8BITMIME") && EHLOset ("SMTPUTF8")) {
436 mail_parameters
= " BODY=8BITMIME SMTPUTF8";
438 inform("SMTP server does not support %s, not sending.\n"
439 "Rebuild message with 7-bit headers, WITHOUT -headerencoding utf-8.",
440 EHLOset ("SMTPUTF8") ? "8BITMIME" : "SMTPUTF8");
444 } else if (eightbit
) {
445 /* Comply with RFC 6152, for messages that have any 8-bit characters
447 if (EHLOset ("8BITMIME")) {
448 mail_parameters
= " BODY=8BITMIME";
450 inform("SMTP server does not support 8BITMIME, not sending.\n"
451 "Suggest encoding message for 7-bit transport by setting your\n"
452 "locale to C, and/or specifying *b64 in mhbuild directives.");
458 switch (smtalk (SM_MAIL
, "MAIL FROM:<%s>%s", from
, mail_parameters
)) {
475 sm_wadr (char *mbox
, char *host
, char *path
)
477 switch (smtalk (SM_RCPT
, host
&& *host
? "RCPT TO:<%s%s@%s>"
479 FENDNULL(path
), mbox
, host
)) {
510 switch (smtalk (SM_DATA
, "DATA")) {
532 sm_wtxt (char *buffer
, int len
)
536 result
= sm_wstream (buffer
, len
);
538 return (result
== NOTOK
? RP_BHST
: RP_OK
);
545 if (sm_wstream ((char *) NULL
, 0) == NOTOK
)
548 switch (smtalk (SM_DOT
+ 3 * sm_addrs
, ".")) {
571 if (sm_mts
== MTS_SENDMAIL_SMTP
) {
587 smtalk (SM_QUIT
, "QUIT");
591 sm_note
.code
= sm_reply
.code
;
592 sm_note
.length
= sm_reply
.length
;
593 memcpy (sm_note
.text
, sm_reply
.text
, sm_reply
.length
+ 1);
596 if (smtalk (SM_RSET
, "RSET") == 250 && type
== DONE
)
598 if (sm_mts
== MTS_SMTP
)
599 smtalk (SM_QUIT
, "QUIT");
601 /* The SIGPIPE block replaces old calls to discard ().
602 We're not sure what the discard () calls were for,
603 maybe to prevent deadlock on old systems. In any
604 case, blocking SIGPIPE should be harmless.
605 Because the file handles are closed below, leave it
609 sigaddset (&set
, SIGPIPE
);
610 sigprocmask (SIG_BLOCK
, &set
, &oset
);
612 kill (sm_child
, SIGKILL
);
616 sm_reply
.code
= sm_note
.code
;
617 sm_reply
.length
= sm_note
.length
;
618 memcpy (sm_reply
.text
, sm_note
.text
, sm_note
.length
+ 1);
624 netsec_shutdown(nsc
);
628 if (sm_mts
== MTS_SMTP
) {
630 } else if (sm_child
!= NOTOK
) {
631 status
= pidwait (sm_child
, OK
);
637 return (status
? RP_BHST
: RP_OK
);
642 sm_ierror (const char *fmt
, ...)
647 vsnprintf (sm_reply
.text
, sizeof(sm_reply
.text
), fmt
, ap
);
650 sm_reply
.length
= strlen (sm_reply
.text
);
651 sm_reply
.code
= NOTOK
;
657 * Like sm_ierror, but assume it's an allocated error string we need to free.
661 sm_nerror (char *str
)
663 strncpy(sm_reply
.text
, str
, sizeof(sm_reply
.text
));
664 sm_reply
.text
[sizeof(sm_reply
.text
) - 1] = '\0';
665 sm_reply
.length
= strlen(sm_reply
.text
);
666 sm_reply
.code
= NOTOK
;
673 smtalk (int time
, char *fmt
, ...)
680 result
= netsec_vprintf (nsc
, &errstr
, fmt
, ap
);
684 return sm_nerror(errstr
);
686 if (netsec_printf (nsc
, &errstr
, "\r\n") != OK
)
687 return sm_nerror(errstr
);
689 if (netsec_flush (nsc
, &errstr
) != OK
)
690 return sm_nerror(errstr
);
692 netsec_set_timeout(nsc
, time
);
699 sm_wstream (char *buffer
, int len
)
702 static char lc
= '\0';
706 sm_ierror("No socket opened");
710 if (buffer
== NULL
&& len
== 0) {
713 rc
= netsec_write(nsc
, "\r\n", 2, &errstr
);
720 for (bp
= buffer
; bp
&& len
> 0; bp
++, len
--) {
724 if (netsec_write(nsc
, "\r", 1, &errstr
) != OK
) {
732 if (netsec_write(nsc
, ".", 1, &errstr
) != OK
) {
741 if (netsec_write(nsc
, bp
, 1, &errstr
) != OK
) {
762 char **ehlo
= EHLOkeys
, *buffer
;
765 static int at_least_once
= 0;
770 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
785 sm_reply
.text
[0] = 0;
787 rc
= sizeof(sm_reply
.text
) - 1;
789 for (more
= false; (buffer
= netsec_readline(nsc
, &buflen
,
790 &errstr
)) != NULL
; ) {
793 && has_prefix(buffer
, "250")
794 && (buffer
[3] == '-' || doingEHLO
== 2)
796 if (doingEHLO
== 2) {
797 if ((*ehlo
= malloc ((size_t) (strlen (buffer
+ 4) + 1)))) {
798 strcpy (*ehlo
++, buffer
+ 4);
800 if (ehlo
>= EHLOkeys
+ MAXEHLO
)
810 bp
= (unsigned char *) buffer
;
812 for (; buflen
> 0 && (!isascii (*bp
) || !isdigit (*bp
)); bp
++, buflen
--)
816 code
= atoi ((char *) bp
);
817 bp
+= 3, buflen
-= 3;
818 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
820 if (buflen
> 0 && *bp
== '-') {
823 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
828 if (code
!= sm_reply
.code
|| cont
)
832 sm_reply
.code
= code
;
835 bp
= (unsigned char *) sm_noreply
;
836 buflen
= strlen (sm_noreply
);
840 if ((i
= min (buflen
, rc
)) > 0) {
844 i
= strlen(sm_moreply
);
845 if (more
&& (int) rc
> i
+ 1) {
846 memcpy (rp
, sm_moreply
, i
); /* safe because of check in if() */
853 if (sm_reply
.code
< 100) {
861 sm_reply
.length
= rp
- sm_reply
.text
;
862 sm_reply
.text
[sm_reply
.length
] = 0;
863 return sm_reply
.code
;
875 /* The additional space is to avoid warning from gcc -Wformat-truncation. */
876 static char buffer
[BUFSIZ
+ 19];
878 switch (sm_reply
.code
!= NOTOK
? code
: NOTOK
) {
898 snprintf (buffer
, sizeof(buffer
), "[%s] %s", text
, sm_reply
.text
);
918 snprintf (buffer
, sizeof(buffer
), "[%s] %3d %s",
919 text
, sm_reply
.code
, sm_reply
.text
);
931 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
933 if (has_prefix(ep
, s
)) {
934 for (ep
+= len
; *ep
== ' '; ep
++)
944 * Our SASL callback; we are either given SASL tokens to generate network
945 * protocols messages for, or we decode incoming protocol messages and
946 * convert them to binary SASL tokens to pass up into the SASL library.
950 sm_sasl_callback(enum sasl_message_type mtype
, unsigned const char *indata
,
951 unsigned int indatalen
, unsigned char **outdata
,
952 unsigned int *outdatalen
, char **errstr
)
959 case NETSEC_SASL_START
:
961 * Generate an AUTH message; if we were given an input token
962 * then generate a an AUTH message that includes the initial
966 mech
= netsec_get_sasl_mechanism(nsc
);
971 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
972 writeBase64raw(indata
, indatalen
, (unsigned char *) b64data
);
974 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
,
976 snoopoffset
= 6 + strlen(mech
);
977 rc
= netsec_printf(nsc
, errstr
, "AUTH %s %s\r\n", mech
, b64data
);
979 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
981 rc
= netsec_printf(nsc
, errstr
, "AUTH %s\r\n", mech
);
987 if (netsec_flush(nsc
, errstr
) != OK
)
992 case NETSEC_SASL_READ
:
994 * Read in a line that should contain a 334 response code, followed
995 * by base64 response data.
998 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, &snoopoffset
);
1000 line
= netsec_readline(nsc
, &len
, errstr
);
1001 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1007 netsec_err(errstr
, "Invalid format for SASL response");
1011 if (!has_prefix(line
, "334 ")) {
1012 netsec_err(errstr
, "Improper SASL protocol response: %s", line
);
1020 rc
= decodeBase64(line
+ 4, outdata
, &len
, 0, NULL
);
1022 netsec_err(errstr
, "Unable to decode base64 response");
1029 case NETSEC_SASL_WRITE
:
1031 * The output encoding is pretty simple, so this is easy.
1033 if (indatalen
== 0) {
1034 rc
= netsec_printf(nsc
, errstr
, "\r\n");
1036 unsigned char *b64data
;
1037 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
1038 writeBase64raw(indata
, indatalen
, b64data
);
1039 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, NULL
);
1040 rc
= netsec_printf(nsc
, errstr
, "%s\r\n", b64data
);
1041 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1048 if (netsec_flush(nsc
, errstr
) != OK
)
1052 case NETSEC_SASL_FINISH
:
1054 * Finish the protocol; we're looking for a 235 message.
1056 line
= netsec_readline(nsc
, &len
, errstr
);
1060 if (!has_prefix(line
, "235 ")) {
1062 netsec_err(errstr
, "Authentication failed: %s", line
+ 4);
1064 netsec_err(errstr
, "Authentication failed: %s", line
);
1069 case NETSEC_SASL_CANCEL
:
1071 * Cancel the SASL exchange; this is done by sending a single "*".
1073 rc
= netsec_printf(nsc
, errstr
, "*\r\n");
1075 rc
= netsec_flush(nsc
, errstr
);