]> diplodocus.org Git - nmh/blobdiff - mts/smtp/smtp.c
Added NMH_ADDL_CPPFLAGS macro in new m4/cppflags.m4, to remove
[nmh] / mts / smtp / smtp.c
index 92b91095f63561e4378841b6b4d420e65ac6cac8..79c1e79b4d0da2b5850cab8899c1a93be27edf37 100644 (file)
@@ -11,6 +11,7 @@
 #include <h/mts.h>
 #include <h/signals.h>
 #include <h/utils.h>
 #include <h/mts.h>
 #include <h/signals.h>
 #include <h/utils.h>
+#include <h/oauth.h>
 
 #ifdef CYRUS_SASL
 #include <sasl/sasl.h>
 
 #ifdef CYRUS_SASL
 #include <sasl/sasl.h>
@@ -76,9 +77,7 @@
 #define        SM_DOT  600     /* see above */
 #define        SM_QUIT  30
 #define        SM_CLOS  10
 #define        SM_DOT  600     /* see above */
 #define        SM_QUIT  30
 #define        SM_CLOS  10
-#ifdef CYRUS_SASL
 #define        SM_AUTH  45
 #define        SM_AUTH  45
-#endif /* CYRUS_SASL */
 
 static int sm_addrs = 0;
 static int sm_alarmed = 0;
 
 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,
  * 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 *);
 
 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_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
 /*
 
 #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
 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,
 {
     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);
     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,
 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;
 {
     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);
     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) {
      */
 
     if (sasl) {
+        char *server_mechs;
        if (! (server_mechs = EHLOset("AUTH"))) {
            sm_end(NOTOK);
            return sm_ierror("SMTP server does not support SASL");
        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);
        }
 
                             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;
                         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 */
 
     }
 #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");
 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 */
 
 }
 #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, ...)
 {
 static int
 sm_ierror (char *fmt, ...)
 {
@@ -1878,23 +1943,14 @@ prepare_for_display (const char *string, int *next_line_encoded) {
     size_t decoded_len;
     int prefix_len = -1;
 
     size_t decoded_len;
     int prefix_len = -1;
 
-    if (strncmp (string, "AUTH XOAUTH2 ", 13) == 0) {
-        /* Entire XOAUTH2 line. */
-        prefix_len = 13;
-        *next_line_encoded = 0;
-    } else if (strncmp (string, "AUTH LOGIN ", 11) == 0) {
-        /* AUTH LOGIN followed by login name.
-           For AUTH LOGIN not followed by the name, the response to the 334
-           server request will be handled by the code below. */
-        prefix_len = 11;
-        *next_line_encoded = 0;
-    } else if (strncmp (string, "AUTH PLAIN ", 11) == 0) {
-        /* AUTH PLAIN followed by authorization/authentication string, e.g.,
-           the display output will be:
-           AUTH PLAIN b64<test@example.com[0x00]test@example.com[0x00]my_password>
-           For AUTH PLAIN not followed by the string, the response to the 334
-           will be handled by the code below. */
-        prefix_len = 11;
+    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. */
         *next_line_encoded = 0;
     } else if (strncmp (string, "334 ", 4) == 0) {
         /* 334 is the server's request for user or password. */
@@ -1910,7 +1966,10 @@ prepare_for_display (const char *string, int *next_line_encoded) {
         *next_line_encoded = 0;
     }
 
         *next_line_encoded = 0;
     }
 
-    if (prefix_len > -1) {
+    /* 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;
     }
 
         start = string + prefix_len;
     }