Change decodeBase64() to accept a length for the encoded string and
not assume it is NUL-terminated.

In all cases, I found the length of the encoded string was already
available to the caller.  That let me remove a strlen() in
decodeBase64 and not add a strlen() to any call site.  One call site
even got simpler: in mhparse.c I was able to remove an apologetic
comment.

This change is part of an effort to move the nmh code base away
from assuming C strings and towards using a length for all data.
A comment in popsbr.c says this is a good idea.

And that effort is also deep background for fixing bug #66407, inc
long lines.  The fix there is complicated by how awkward it is for
the current code to deal with an arbitrary sequence of bytes from
the middle of a stream.


diff --git a/sbr/base64.h b/sbr/base64.h
index aea3bdde..64547c25 100644
--- a/sbr/base64.h
+++ b/sbr/base64.h
@@ -8,7 +8,7 @@
 int writeBase64aux(FILE *, FILE *, int);
 int writeBase64(const unsigned char *, size_t, unsigned char *);
 int writeBase64raw(const unsigned char *, size_t, unsigned char *);
-int decodeBase64(const char *, unsigned char **, size_t *, int);
+int decodeBase64(const char *, size_t, unsigned char **, size_t *, int);
 void hexify(const unsigned char *, size_t, char **);
 
 /* Includes trailing NUL. */
diff --git a/sbr/base64.c b/sbr/base64.c
index 5548a684..153b954f 100644
--- a/sbr/base64.c
+++ b/sbr/base64.c
@@ -248,21 +248,21 @@ static const unsigned char b642nib[0x80] = {
  *                skipped
  */
 int
-decodeBase64 (const char *encoded, unsigned char **decoded, size_t *len,
-	      int skip_crs)
+decodeBase64 (const char *encoded, size_t encoded_len,
+              unsigned char **decoded, size_t *len, int skip_crs)
 {
     const char *cp = encoded;
     int bitno, skip;
     uint32_t bits;
     /* Size the decoded string very conservatively. */
-    charstring_t decoded_c = charstring_create (strlen (encoded));
+    charstring_t decoded_c = charstring_create (encoded_len);
 
     bitno = 18;
     bits = 0L;
     skip = 0;
 
     bool self_delimiting = false;
-    for (; *cp; ++cp) {
+    for (; encoded_len > 0; --encoded_len, ++cp) {
         switch (*cp) {
             unsigned char value;
 
@@ -272,8 +272,9 @@ decodeBase64 (const char *encoded, unsigned char **decoded, size_t *len,
                 }
                 if (skip  ||  (((unsigned char) *cp) & 0x80)  ||
                     (value = b642nib[((unsigned char) *cp) & 0x7f]) > 0x3f) {
-                    inform("invalid base64 byte %#x: %.42s",
-                        *(unsigned char *)cp, cp);
+                    inform("invalid base64 byte %#x: %.*s",
+                           *(unsigned char *)cp, (int) min(encoded_len, 42),
+                           cp);
                     charstring_free (decoded_c);
                     *decoded = NULL;
 
@@ -317,8 +318,11 @@ test_end:
 
     if (! self_delimiting  &&  bitno != 18) {
         /* Show some context for the error. */
-        cp -= min(cp - encoded, 20);
-        inform("premature ending (bitno %d) near %s", bitno, cp);
+        int context = min(cp - encoded, 20);
+        cp -= context;
+        encoded_len += context;
+        inform("premature ending (bitno %d) near %.*s",
+               bitno, (int) encoded_len, cp);
         charstring_free (decoded_c);
         *decoded = NULL;
 
diff --git a/mts/smtp/smtp.c b/mts/smtp/smtp.c
index a9adfcc5..d5ed03b6 100644
--- a/mts/smtp/smtp.c
+++ b/mts/smtp/smtp.c
@@ -1055,7 +1055,7 @@ sm_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata,
 	    *outdata = NULL;
 	    *outdatalen = 0;
 	} else {
-	    rc = decodeBase64(line + 4, outdata, &len, 0);
+	    rc = decodeBase64(line + 4, len - 4, outdata, &len, 0);
 	    if (rc != OK) {
 		netsec_err(errstr, "Unable to decode base64 response");
 		return NOTOK;
diff --git a/sbr/netsec.c b/sbr/netsec.c
index bf0ff9cf..9e2f31d4 100644
--- a/sbr/netsec.c
+++ b/sbr/netsec.c
@@ -323,7 +323,7 @@ netsec_b64_snoop_decoder(netsec_context *nsc, const char *string, size_t len,
 	len -= offset;
     }
 
-    if (decodeBase64(string, &decoded, &decodedlen, 1) == OK) {
+    if (decodeBase64(string, len, &decoded, &decodedlen, 1) == OK) {
 	/*
 	 * Some mechanisms produce large binary tokens, which aren't really
 	 * readable.  So let's do a simple heuristic.  If the token is greater
diff --git a/uip/imaptest.c b/uip/imaptest.c
index 9e19aac6..254c1a9c 100644
--- a/uip/imaptest.c
+++ b/uip/imaptest.c
@@ -651,7 +651,7 @@ imap_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata,
 	    *outdata = NULL;
 	    *outdatalen = 0;
 	} else {
-	    rc = decodeBase64(line + 2, outdata, &len, 0);
+	    rc = decodeBase64(line + 2, len - 2, outdata, &len, 0);
 	    *outdatalen = len;
 	    if (rc != OK) {
 		netsec_err(errstr, "Unable to decode base64 response");
diff --git a/uip/mhparse.c b/uip/mhparse.c
index 7f3fac8d..052eceb6 100644
--- a/uip/mhparse.c
+++ b/uip/mhparse.c
@@ -1832,10 +1832,7 @@ openBase64 (CT ct, char **file)
         }
     }
 
-    /* decodeBase64() requires null-terminated input. */
-    *cp = '\0';
-
-    if (decodeBase64 (buffer, &decoded, &decoded_len,
+    if (decodeBase64 (buffer, cp - buffer, &decoded, &decoded_len,
 		      ct->c_type == CT_TEXT) != OK)
         goto clean_up;
 
diff --git a/uip/popsbr.c b/uip/popsbr.c
index 89fc4cac..79376a05 100644
--- a/uip/popsbr.c
+++ b/uip/popsbr.c
@@ -464,7 +464,7 @@ pop_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata,
 	    *outdata = NULL;
 	    *outdatalen = 0;
 	} else {
-	    rc = decodeBase64(line + 2, outdata, &len, 0);
+	    rc = decodeBase64(line + 2, len - 2, outdata, &len, 0);
 	    *outdatalen = len;
 	    if (rc != OK) {
 		netsec_err(errstr, "Unable to decode base64 response");

Reply via email to