]> 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 3516f7760f790025d5146c296da611f923954411..79c1e79b4d0da2b5850cab8899c1a93be27edf37 100644 (file)
@@ -11,6 +11,7 @@
 #include <h/mts.h>
 #include <h/signals.h>
 #include <h/utils.h>
+#include <h/oauth.h>
 
 #ifdef CYRUS_SASL
 #include <sasl/sasl.h>
@@ -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);
+    }
+}