]> diplodocus.org Git - nmh/commitdiff
Attempt to decode base64-encoded strings in -snoop traffic.
authorDavid Levine <levinedl@acm.org>
Sun, 14 Aug 2016 14:57:51 +0000 (10:57 -0400)
committerDavid Levine <levinedl@acm.org>
Sun, 14 Aug 2016 14:57:51 +0000 (10:57 -0400)
docs/pending-release-notes
h/prototypes.h
man/inc.man
man/msgchk.man
man/post.man
man/send.man
man/whom.man
mts/smtp/smtp.c
sbr/base64.c

index d56ae28d8913207b6700637cb94348b128910088..869befccef9878209ca05386ddcc070840ad492c 100644 (file)
@@ -45,6 +45,7 @@ NEW FEATURES
   binary when using the sendmail/pipe MTS.
 - Added support to send(1) to specify switches to post(1) based on address or
   domain name in From: header line in message draft.
+- post(8) -snoop now attempts to decode base64-encoded SMTP traffic.
 
 -----------------
 OBSOLETE FEATURES
index 45ee338dadaaa3076bcf6bd964ac8ed284eb07e6..884e0e0ab85d2826363aef9c52b5d6a0f57d3816 100644 (file)
@@ -482,6 +482,8 @@ int writeBase64raw (unsigned char *, size_t, unsigned char *);
  */
 int decodeBase64 (const char *, const char **, size_t *, int, unsigned char *);
 
+void hexify (const unsigned char *, size_t, char **);
+
 /*
  * credentials management
  */
index 61f6f8442ae4baa1d075c992f68975bf5e8079e1..46dda0e47be10ab2d7c3c3997bf4abac446407d3 100644 (file)
@@ -1,4 +1,4 @@
-.TH INC %manext1% "April 18, 2014" "%nmhversion%"
+.TH INC %manext1% "August 14, 2016" "%nmhversion%"
 .\"
 .\" %nmhwarning%
 .\"
@@ -263,7 +263,11 @@ will attempt to negotiate a security layer for session encryption.
 Encrypted traffic is labelled with `(encrypted)' and `(decrypted)'
 when viewing the POP transaction with the
 .B \-snoop
-switch.
+switch; see the
+.B post
+man page description of
+.B \-snoop
+for its other features.
 .SH FILES
 .PD 0
 .TP 20
index 0d5d6c9873e608cad37706f815f341548de0c606..b0c7bd48c454907a3d7f3d7ed7643d2195a944ec 100644 (file)
@@ -1,4 +1,4 @@
-.TH MSGCHK %manext1% "April 14, 2013" "%nmhversion%"
+.TH MSGCHK %manext1% "August 14, 2016" "%nmhversion%"
 .\"
 .\" %nmhwarning%
 .\"
@@ -119,7 +119,11 @@ a security layer for session encryption.  Encrypted traffic is labelled
 with `(encrypted)' and `(decrypted)' when viewing the POP transaction
 with the
 .B \-snoop
-switch.
+switch; see the
+.B post
+man page description of
+.B \-snoop
+for its other features.
 .SH FILES
 .fc ^ ~
 .nf
@@ -138,6 +142,7 @@ None
 .SH "SEE ALSO"
 .IR inc (1),
 .IR mh\-mail (5)
+.IR post (8)
 .SH DEFAULTS
 .nf
 .RB ` user "' defaults to the current user"
index fe13538178a4707f9f293fd1ab03a8c9d60e4ff9..1b1042b9e13e218da6c09d8476a8d81ed334b7ea 100644 (file)
@@ -1,4 +1,4 @@
-.TH POST %manext8% "July 8, 2014" "%nmhversion%"
+.TH POST %manext8% "August 14, 2016" "%nmhversion%"
 .\"
 .\" %nmhwarning%
 .\"
@@ -232,7 +232,11 @@ will attempt to negotiate a security layer for session encryption.
 Encrypted data is labelled with `(sasl-encrypted)' and `(sasl-decrypted)' when
 viewing the SMTP transaction with the
 .B \-snoop
-switch.  The
+switch.
+Base64-encoded data is wrapped with ` b64<>'.
+(Beware that the SMTP transaction may contain authentication information either
+in plaintext or easily decoded base64.)
+The
 .B \-saslmaxssf
 switch can be used to select the maximum value of the Security Strength Factor.
 This is an integer value and the exact meaning of this value depends on the
@@ -256,6 +260,9 @@ is labelled with `(tls-encrypted)' and
 `(tls-decrypted)' when viewing the SMTP transction with the
 .B \-snoop
 switch.
+Base64-encoded data is wrapped with `b64<>'.
+(Beware that the SMTP transaction may contain authentication information either
+in plaintext or easily decoded base64.)
 The
 .B \-notls
 switch will disable all attempts to negotiate TLS.
index f568d991144d2b5dfbaeef92d9f455d3f69f9fb4..87c2cfd2630fa2a47adbd09f41a3f89cdaf04396 100644 (file)
@@ -1,7 +1,7 @@
 .\"
 .\" %nmhwarning%
 .\"
-.TH SEND %manext1% "July 8, 2016" "%nmhversion%"
+.TH SEND %manext1% "August 14, 2016" "%nmhversion%"
 .SH NAME
 send \- send a message
 .SH SYNOPSIS
@@ -418,7 +418,12 @@ will attempt to negotiate a security layer for session encryption.
 Encrypted data is labelled with `(encrypted)' and `(decrypted)' when
 viewing the SMTP transaction with the
 .B \-snoop
-switch.  The
+switch; see the
+.B post
+man page description of
+.B \-snoop
+for its other features.
+The
 .B \-saslmaxssf
 switch can be used to select the maximum value of the Security Strength Factor.
 This is an integer value and the exact meaning of this value depends on the
@@ -441,7 +446,11 @@ taken place, before any SMTP commands are sent or received.  Encrypted data
 is labelled with `(tls-encrypted)' and
 `(tls-decrypted)' when viewing the SMTP transction with the
 .B \-snoop
-switch.
+switch; see the
+.B post
+man page description of
+.B \-snoop
+for its other features.
 The
 .B \-notls
 switch will disable all attempts to negotiate TLS.
index da7aee2e2dd4ad95d068c603041dba764c905719..cbad25f5718608530c983fb42c2bdc62bc72a671 100644 (file)
@@ -1,4 +1,4 @@
-.TH WHOM %manext1% "April 14, 2013" "%nmhversion%"
+.TH WHOM %manext1% "August 14, 2016" "%nmhversion%"
 .\"
 .\" %nmhwarning%
 .\"
@@ -102,7 +102,11 @@ will attempt to negotiate a security layer for session encryption.
 Encrypted data is labelled with `(encrypted)' and `(decrypted)' when
 viewing the SMTP transaction with the
 .B \-snoop
-switch.  The
+switch; see the
+.B post
+man page description of
+.B \-snoop
+for its other features.  The
 .B \-saslmaxssf
 switch can be used to select the maximum value of the Security Strength Factor.
 This is an integer value and the exact meaning of this value depends on the
@@ -118,7 +122,11 @@ switches will require and disable the negotiation of TLS support when connecting
 SMTP MTA.  Encrypted data is labelled with `(tls-encrypted)' and
 `(tls-decrypted)' when viewing the SMTP transction with the
 .B \-snoop
-switch.
+switch; see the
+.B post
+man page description of
+.B \-snoop
+for its other features.
 .PP
 The files specified by the profile entry \*(lqAliasfile:\*(rq and any
 additional alias files given by the
index 8b89dec3e7fb00bf8845eda7b58f78bce43ab4a2..92b91095f63561e4378841b6b4d420e65ac6cac8 100644 (file)
@@ -90,6 +90,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
@@ -168,6 +170,7 @@ 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);
@@ -1169,11 +1172,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);
     }
 
@@ -1504,11 +1511,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);
        }
 
@@ -1850,3 +1861,75 @@ 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 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;
+        *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;
+    }
+
+    if (prefix_len > -1) {
+        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);
+    }
+}
index 80ca86e08726bb4f8774e7929fb80524bf3ab73a..9047eb5f0a855fbaa26234998c7fe5b38ad82568 100644 (file)
@@ -324,3 +324,35 @@ test_end:
 
     return OK;
 }
+
+
+/*
+ * Prepare an unsigned char array for display by replacing non-printable
+ * ASCII bytes with their hex representation.  Assumes ASCII input.  output
+ * is allocated by the function and must be freed by the caller.
+ */
+void
+hexify (const unsigned char *input, size_t len, char **output) {
+    /* Start with a charstring capacity that's arbitrarily larger than len. */
+    const charstring_t tmp = charstring_create (2 * len);
+    const unsigned char *cp = input;
+    size_t i;
+
+    for (i = 0; i < len; ++i, ++cp) {
+        if (isprint(*cp)) {
+            charstring_push_back (tmp, (const char) *cp);
+        } else {
+            char s[16];
+            const int num = snprintf(s, sizeof s, "[0x%02x]", *cp);
+
+            if (num <= 0  ||  (unsigned int) num >= sizeof s) {
+                advise (NULL, "hexify failed to write nonprintable character, needed %d bytes", num + 1);
+            } else {
+                charstring_append_cstring (tmp, s);
+            }
+        }
+    }
+
+    *output = charstring_buffer_copy (tmp);
+    charstring_free (tmp);
+}