]>
diplodocus.org Git - nmh/blob - mts/smtp/smtp.c
2f32877cf87e5d6782755a5b7565e2c4a279466a
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 int 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
[i
], 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
) {
756 int i
, code
, cont
, more
;
761 char **ehlo
= EHLOkeys
, *buffer
;
764 static int at_least_once
= 0;
769 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
784 sm_reply
.text
[0] = 0;
786 rc
= sizeof(sm_reply
.text
) - 1;
788 for (more
= FALSE
; (buffer
= netsec_readline(nsc
, &buflen
,
789 &errstr
)) != NULL
; ) {
792 && has_prefix(buffer
, "250")
793 && (buffer
[3] == '-' || doingEHLO
== 2)
795 if (doingEHLO
== 2) {
796 if ((*ehlo
= malloc ((size_t) (strlen (buffer
+ 4) + 1)))) {
797 strcpy (*ehlo
++, buffer
+ 4);
799 if (ehlo
>= EHLOkeys
+ MAXEHLO
)
809 bp
= (unsigned char *) buffer
;
811 for (; buflen
> 0 && (!isascii (*bp
) || !isdigit (*bp
)); bp
++, buflen
--)
815 code
= atoi ((char *) bp
);
816 bp
+= 3, buflen
-= 3;
817 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
819 if (buflen
> 0 && *bp
== '-') {
822 for (; buflen
> 0 && isspace (*bp
); bp
++, buflen
--)
827 if (code
!= sm_reply
.code
|| cont
)
831 sm_reply
.code
= code
;
834 bp
= (unsigned char *) sm_noreply
;
835 buflen
= strlen (sm_noreply
);
839 if ((i
= min (buflen
, rc
)) > 0) {
843 i
= strlen(sm_moreply
);
844 if (more
&& (int) rc
> i
+ 1) {
845 memcpy (rp
, sm_moreply
, i
); /* safe because of check in if() */
852 if (sm_reply
.code
< 100) {
860 sm_reply
.length
= rp
- sm_reply
.text
;
861 sm_reply
.text
[sm_reply
.length
] = 0;
862 return sm_reply
.code
;
874 /* The additional space is to avoid warning from gcc -Wformat-truncation. */
875 static char buffer
[BUFSIZ
+ 19];
877 switch (sm_reply
.code
!= NOTOK
? code
: NOTOK
) {
897 snprintf (buffer
, sizeof(buffer
), "[%s] %s", text
, sm_reply
.text
);
917 snprintf (buffer
, sizeof(buffer
), "[%s] %3d %s",
918 text
, sm_reply
.code
, sm_reply
.text
);
930 for (ehlo
= EHLOkeys
; *ehlo
; ehlo
++) {
932 if (has_prefix(ep
, s
)) {
933 for (ep
+= len
; *ep
== ' '; ep
++)
943 * Our SASL callback; we are either given SASL tokens to generate network
944 * protocols messages for, or we decode incoming protocol messages and
945 * convert them to binary SASL tokens to pass up into the SASL library.
949 sm_sasl_callback(enum sasl_message_type mtype
, unsigned const char *indata
,
950 unsigned int indatalen
, unsigned char **outdata
,
951 unsigned int *outdatalen
, char **errstr
)
958 case NETSEC_SASL_START
:
960 * Generate an AUTH message; if we were given an input token
961 * then generate a an AUTH message that includes the initial
965 mech
= netsec_get_sasl_mechanism(nsc
);
970 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
971 writeBase64raw(indata
, indatalen
, (unsigned char *) b64data
);
973 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
,
975 snoopoffset
= 6 + strlen(mech
);
976 rc
= netsec_printf(nsc
, errstr
, "AUTH %s %s\r\n", mech
, b64data
);
978 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
980 rc
= netsec_printf(nsc
, errstr
, "AUTH %s\r\n", mech
);
986 if (netsec_flush(nsc
, errstr
) != OK
)
991 case NETSEC_SASL_READ
:
993 * Read in a line that should contain a 334 response code, followed
994 * by base64 response data.
997 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, &snoopoffset
);
999 line
= netsec_readline(nsc
, &len
, errstr
);
1000 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1006 netsec_err(errstr
, "Invalid format for SASL response");
1010 if (!has_prefix(line
, "334 ")) {
1011 netsec_err(errstr
, "Improper SASL protocol response: %s", line
);
1019 rc
= decodeBase64(line
+ 4, outdata
, &len
, 0, NULL
);
1021 netsec_err(errstr
, "Unable to decode base64 response");
1028 case NETSEC_SASL_WRITE
:
1030 * The output encoding is pretty simple, so this is easy.
1032 if (indatalen
== 0) {
1033 rc
= netsec_printf(nsc
, errstr
, "\r\n");
1035 unsigned char *b64data
;
1036 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
1037 writeBase64raw(indata
, indatalen
, b64data
);
1038 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, NULL
);
1039 rc
= netsec_printf(nsc
, errstr
, "%s\r\n", b64data
);
1040 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
1047 if (netsec_flush(nsc
, errstr
) != OK
)
1051 case NETSEC_SASL_FINISH
:
1053 * Finish the protocol; we're looking for a 235 message.
1055 line
= netsec_readline(nsc
, &len
, errstr
);
1059 if (!has_prefix(line
, "235 ")) {
1061 netsec_err(errstr
, "Authentication failed: %s", line
+ 4);
1063 netsec_err(errstr
, "Authentication failed: %s", line
);
1068 case NETSEC_SASL_CANCEL
:
1070 * Cancel the SASL exchange; this is done by sending a single "*".
1072 rc
= netsec_printf(nsc
, errstr
, "*\r\n");
1074 rc
= netsec_flush(nsc
, errstr
);