X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/2e04b8ea0e57e411665aa6a8814ff65db0d4cd2b..303e8387acecca26329e939f228f78ca805b7a15:/mts/smtp/smtp.c diff --git a/mts/smtp/smtp.c b/mts/smtp/smtp.c index 3516f776..79c1e79b 100644 --- a/mts/smtp/smtp.c +++ b/mts/smtp/smtp.c @@ -11,6 +11,7 @@ #include #include #include +#include #ifdef CYRUS_SASL #include @@ -88,6 +89,8 @@ static int sm_verbose = 0; static FILE *sm_rfp = NULL; static FILE *sm_wfp = NULL; +static int next_line_encoded = 0; + #ifdef CYRUS_SASL /* * Some globals needed by SASL @@ -166,12 +169,13 @@ static int sm_rrecord (char *, int *); static int sm_rerror (int); static void alrmser (int); static char *EHLOset (char *); +static char *prepare_for_display (const char *, int *); static int sm_fwrite(char *, int); static int sm_fputs(char *); static int sm_fputc(int); static void sm_fflush(void); static int sm_fgets(char *, int, FILE *); -static int sm_auth_xoauth2(const char *); +static int sm_auth_xoauth2(const char *, const char *, int); #ifdef CYRUS_SASL /* @@ -184,12 +188,12 @@ static int sm_auth_sasl(char *, int, char *, char *); int sm_init (char *client, char *server, char *port, int watch, int verbose, int debug, int sasl, int saslssf, char *saslmech, char *user, - const char *xoauth_client_res, int tls) + const char *oauth_svc, int tls) { if (sm_mts == MTS_SMTP) return smtp_init (client, server, port, watch, verbose, debug, sasl, saslssf, saslmech, user, - xoauth_client_res, tls); + oauth_svc, tls); else return sendmail_init (client, server, watch, verbose, debug, sasl, saslssf, saslmech, user); @@ -199,7 +203,7 @@ static int smtp_init (char *client, char *server, char *port, int watch, int verbose, int debug, int sasl, int saslssf, char *saslmech, char *user, - const char *xoauth_client_res, int tls) + const char *oauth_svc, int tls) { int result, sd1, sd2; #ifndef CYRUS_SASL @@ -377,7 +381,7 @@ smtp_init (char *client, char *server, char *port, int watch, int verbose, /* Don't call sm_auth_sasl() for XAUTH2 with -sasl. Instead, call sm_auth_xoauth2() below. */ - if (xoauth_client_res == NULL && + if (oauth_svc == NULL && sm_auth_sasl(user, saslssf, saslmech ? saslmech : server_mechs, server) != RP_OK) { sm_end(NOTOK); @@ -386,14 +390,14 @@ smtp_init (char *client, char *server, char *port, int watch, int verbose, } #endif /* CYRUS_SASL */ - if (xoauth_client_res != NULL) { + if (oauth_svc != NULL) { char *server_mechs; if ((server_mechs = EHLOset("AUTH")) == NULL || stringdex("XOAUTH2", server_mechs) == -1) { sm_end(NOTOK); return sm_ierror("SMTP server does not support SASL XOAUTH2"); } - if (sm_auth_xoauth2(xoauth_client_res) != RP_OK) { + if (sm_auth_xoauth2(user, oauth_svc, debug) != RP_OK) { sm_end(NOTOK); return NOTOK; } @@ -1151,9 +1155,26 @@ sm_get_pass(sasl_conn_t *conn, void *context, int id, /* https://developers.google.com/gmail/xoauth2_protocol */ static int -sm_auth_xoauth2(const char *client_res) +sm_auth_xoauth2(const char *user, const char *oauth_svc, int snoop) { - int status = smtalk(SM_AUTH, "AUTH XOAUTH2 %s", client_res); + const char *xoauth_client_res; + int status; + +#ifdef OAUTH_SUPPORT + xoauth_client_res = mh_oauth_do_xoauth(user, oauth_svc, + snoop ? stderr : NULL); + + if (xoauth_client_res == NULL) { + return sm_ierror("Internal error: mh_oauth_do_xoauth() returned NULL"); + } +#else + NMH_UNUSED(user); + NMH_UNUSED(snoop); + adios(NULL, "sendfrom built without OAUTH_SUPPORT, " + "so oauth_svc %s is not supported", oauth_svc); +#endif /* OAUTH_SUPPORT */ + + status = smtalk(SM_AUTH, "AUTH XOAUTH2 %s", xoauth_client_res); if (status == 235) { /* It worked! */ return RP_OK; @@ -1216,11 +1237,15 @@ smtalk (int time, char *fmt, ...) } if (sm_debug) { + char *decoded_buffer = + prepare_for_display (buffer, &next_line_encoded); + if (sasl_ssf) printf("(sasl-encrypted) "); if (tls_active) printf("(tls-encrypted) "); - printf ("=> %s\n", buffer); + printf ("=> %s\n", decoded_buffer); + free (decoded_buffer); fflush (stdout); } @@ -1551,11 +1576,15 @@ again: ; for (more = FALSE; sm_rrecord ((char *) (bp = (unsigned char *) buffer), &bc) != NOTOK ; ) { if (sm_debug) { + char *decoded_buffer = + prepare_for_display (buffer, &next_line_encoded); + if (sasl_ssf > 0) printf("(sasl-decrypted) "); if (tls_active) printf("(tls-decrypted) "); - printf ("<= %s\n", buffer); + printf ("<= %s\n", decoded_buffer); + free (decoded_buffer); fflush (stdout); } @@ -1897,3 +1926,69 @@ EHLOset (char *s) return 0; } + + +/* + * Detects, using heuristics, if an SMTP server or client response string + * contains a base64-encoded portion. If it does, decodes it and replaces + * any non-printable characters with a hex representation. Caller is + * responsible for free'ing return value. If the decode fails, a copy of + * the input string is returned. + */ +static +char * +prepare_for_display (const char *string, int *next_line_encoded) { + const char *start = NULL; + const char *decoded; + size_t decoded_len; + int prefix_len = -1; + + if (strncmp (string, "AUTH ", 5) == 0) { + /* AUTH line: the mechanism isn't encoded. If there's an initial + response, it must be base64 encoded.. */ + char *mechanism = strchr (string + 5, ' '); + + if (mechanism != NULL) { + prefix_len = (int) (mechanism - string + 1); + } /* else no space following the mechanism, so no initial response */ + *next_line_encoded = 0; + } else if (strncmp (string, "334 ", 4) == 0) { + /* 334 is the server's request for user or password. */ + prefix_len = 4; + /* The next (client response) line must be base64 encoded. */ + *next_line_encoded = 1; + } else if (*next_line_encoded) { + /* "next" line now refers to this line, which is a base64-encoded + client response. */ + prefix_len = 0; + *next_line_encoded = 0; + } else { + *next_line_encoded = 0; + } + + /* Don't attempt to decoded unencoded initial response ('=') or cancel + response ('*'). */ + if (prefix_len > -1 && + string[prefix_len] != '=' && string[prefix_len] != '*') { + start = string + prefix_len; + } + + if (start && decodeBase64 (start, &decoded, &decoded_len, 1, NULL) == OK) { + char *hexified; + char *prefix = mh_xmalloc(prefix_len + 1); + char *display_string; + + /* prefix is the beginning portion, which isn't base64 encoded. */ + snprintf (prefix, prefix_len + 1, "%*s", prefix_len, string); + hexify ((const unsigned char *) decoded, decoded_len, &hexified); + /* Wrap the decoded portion in "b64<>". */ + display_string = concat (prefix, "b64<", hexified, ">", NULL); + free (hexified); + free (prefix); + free ((char *) decoded); + + return display_string; + } else { + return getcpy (string); + } +}