X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/b3ebdfc56e7a0199a60d80f54872e86969d96933..303e8387acecca26329e939f228f78ca805b7a15:/mts/smtp/smtp.c?ds=inline diff --git a/mts/smtp/smtp.c b/mts/smtp/smtp.c index a29ca695..79c1e79b 100644 --- a/mts/smtp/smtp.c +++ b/mts/smtp/smtp.c @@ -11,6 +11,7 @@ #include #include #include +#include #ifdef CYRUS_SASL #include @@ -76,9 +77,7 @@ #define SM_DOT 600 /* see above */ #define SM_QUIT 30 #define SM_CLOS 10 -#ifdef CYRUS_SASL #define SM_AUTH 45 -#endif /* CYRUS_SASL */ static int sm_addrs = 0; static int sm_alarmed = 0; @@ -155,7 +154,7 @@ static char *EHLOkeys[MAXEHLO + 1]; * static prototypes */ static int smtp_init (char *, char *, char *, int, int, int, int, int, - char *, char *, int); + char *, char *, const char *, int); static int sendmail_init (char *, char *, int, int, int, int, int, char *, char *); @@ -176,6 +175,7 @@ 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 *, const char *, int); #ifdef CYRUS_SASL /* @@ -187,11 +187,13 @@ 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, int tls) + int debug, int sasl, int saslssf, char *saslmech, char *user, + const char *oauth_svc, int tls) { if (sm_mts == MTS_SMTP) return smtp_init (client, server, port, watch, verbose, - debug, sasl, saslssf, saslmech, user, tls); + debug, sasl, saslssf, saslmech, user, + oauth_svc, tls); else return sendmail_init (client, server, watch, verbose, debug, sasl, saslssf, saslmech, user); @@ -200,12 +202,11 @@ sm_init (char *client, char *server, char *port, int watch, int verbose, static int smtp_init (char *client, char *server, char *port, int watch, int verbose, int debug, - int sasl, int saslssf, char *saslmech, char *user, int tls) + int sasl, int saslssf, char *saslmech, char *user, + const char *oauth_svc, int tls) { int result, sd1, sd2; -#ifdef CYRUS_SASL - char *server_mechs; -#else /* CYRUS_SASL */ +#ifndef CYRUS_SASL NMH_UNUSED (sasl); NMH_UNUSED (saslssf); NMH_UNUSED (saslmech); @@ -365,6 +366,7 @@ smtp_init (char *client, char *server, char *port, int watch, int verbose, */ if (sasl) { + char *server_mechs; if (! (server_mechs = EHLOset("AUTH"))) { sm_end(NOTOK); return sm_ierror("SMTP server does not support SASL"); @@ -377,7 +379,10 @@ smtp_init (char *client, char *server, char *port, int watch, int verbose, saslmech, server_mechs); } - if (sm_auth_sasl(user, saslssf, saslmech ? saslmech : server_mechs, + /* Don't call sm_auth_sasl() for XAUTH2 with -sasl. Instead, call + sm_auth_xoauth2() below. */ + if (oauth_svc == NULL && + sm_auth_sasl(user, saslssf, saslmech ? saslmech : server_mechs, server) != RP_OK) { sm_end(NOTOK); return NOTOK; @@ -385,6 +390,19 @@ smtp_init (char *client, char *server, char *port, int watch, int verbose, } #endif /* CYRUS_SASL */ + 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(user, oauth_svc, debug) != RP_OK) { + sm_end(NOTOK); + return NOTOK; + } + } + send_options: ; if (watch && EHLOset ("XVRB")) smtalk (SM_HELO, "VERB on"); @@ -1135,6 +1153,53 @@ sm_get_pass(sasl_conn_t *conn, void *context, int id, } #endif /* CYRUS_SASL */ +/* https://developers.google.com/gmail/xoauth2_protocol */ +static int +sm_auth_xoauth2(const char *user, const char *oauth_svc, int snoop) +{ + 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; + } + + /* + * Status is 334 and sm_reply.text contains base64-encoded JSON. As far as + * epg can tell, no matter the error, the JSON is always the same: + * {"status":"400","schemes":"Bearer","scope":"https://mail.google.com/"} + * I tried these errors: + * - garbage token + * - expired token + * - wrong scope + * - wrong username + */ + /* Then we're supposed to send an empty response ("\r\n"). */ + smtalk(SM_AUTH, ""); + /* + * And now we always get this, again, no matter the error: + * 535-5.7.8 Username and Password not accepted. Learn more at + * 535 5.7.8 http://support.google.com/mail/bin/answer.py?answer=14257 + */ + return RP_BHST; +} + static int sm_ierror (char *fmt, ...) {