NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: bin/47567: Mail(1)/mailx(1)/mail(1) - quoted-printable CTE excesses RFC limit



sdaoden%gmail.com@localhost wrote:
 |>Number:         47567
 |>Category:       bin
 |>Synopsis:       Mail(1)/mailx(1)/mail(1) - quoted-printable CTE excesses \
 |  RFC limit
 |>Confidential:   no
 |>Severity:       non-critical
 |>Priority:       low
 |>Responsible:    bin-bug-people
 |>State:          open
 |>Class:          sw-bug
 |>Submitter-Id:   net
 |>Arrival-Date:   Thu Feb 14 18:00:00 +0000 2013
 |>Originator:     Steffen Nurpmeso
 |>Release:        6.99.16
 |>Organization:
 |>Environment:
 |NetBSD nhead 6.99.16 NetBSD 6.99.16 (GENERIC) #0: Mon Feb 11 21:12:26 UTC \
 |2013  
builds%b6.netbsd.org@localhost:/home/builds/ab/HEAD/amd64/201302111840Z-obj/home\
 |/builds/ab/HEAD/src/sys/arch/amd64/compile/GENERIC amd64
 |
 |>Description:
 |mail(1) may excess the 76 character line limit as REQUIREd by RFC 2045.
 |
 |>How-To-Repeat:
 |cat <<_EOT | awk 'BEGIN{ORS="\r\n"}{print}' | env mime-encode-message= mail \
 |-s test ./test.mbox
 |Ich bin eine DOS-Datei mit sehr langen Zeilen und auch sonst sehr doof.
 |Ich bin eine DOS-Datei mit sehr langen Zeilen und auch sonst sehr doof..
 |Ich bin eine DOS-Datei mit sehr langen Zeilen und auch sonst sehr doof...
 |Ich bin eine DOS-Datei mit sehr langen Zeilen und auch sonst sehr doof....
 |_EOT
 |>Fix:
 |I'll reply with a patch that addresses the problem.
 |
 |It'll also encapsulate all the content-transfer-encoding stuff in \
 |mime_codecs.c and replace calls of strtol(3) with a handcrafted version \
 |that allows simple error checking by testing the return value.
 |
 |The latter allowed to easily add special code to handle illegal QP \
 |sequences.

The patch:

--steffen
--- mime_codecs.c.orig  2013-02-12 19:25:00.000000000 +0100
+++ mime_codecs.c       2013-02-13 04:08:50.000000000 +0100
@@ -389,10 +389,84 @@ mime_fB64_decode(FILE *fi, FILE *fo, voi
 /************************************************************************
  * Core quoted-printable routines.
  *
- * Note: the header QP routines are slightly different and burried
- * inside mime_header.c
+ * Defined in sec 6.7 of RFC 2045.
  */
 
+/*
+ * strtol(3), but inline and with easy error indication.
+ */
+static inline int
+_qp_cfromhex(char const *hex)
+{
+       /* Be robust, allow lowercase hexadecimal letters, too */
+       static unsigned char const atoi16[] = {
+               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x30-0x37 */
+               0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x38-0x3F */
+               0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, /* 0x40-0x47 */
+               0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x48-0x4f */
+               0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x50-0x57 */
+               0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x58-0x5f */
+               0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF  /* 0x60-0x67 */
+       };
+       unsigned char i1, i2;
+       int r;
+
+       if ((i1 = (unsigned char)hex[0] - '0') >= __arraycount(atoi16) ||
+           (i2 = (unsigned char)hex[1] - '0') >= __arraycount(atoi16))
+               goto jerr;
+       i1 = atoi16[i1];
+       i2 = atoi16[i2];
+       if ((i1 | i2) & 0xF0)
+               goto jerr;
+       r = i1;
+       r <<= 4;
+       r += i2;
+jleave:
+       return r;
+jerr:
+       r = -1;
+       goto jleave;
+}
+
+/*
+ * Header specific "quoted-printable" decode!
+ * Differences with body QP decoding (see rfc 2047, sec 4.2):
+ * 1) '=' occurs _only_ when followed by two hex digits (FWS is not allowed).
+ * 2) Spaces can be encoded as '_' in headers for readability.
+ */
+static ssize_t
+mime_QPh_decode(char *outbuf, size_t outlen, const char *inbuf, size_t inlen)
+{
+       const char *p, *inend;
+       char *outend;
+       char *q;
+
+       outend = outbuf + outlen;
+       inend = inbuf + inlen;
+       q = outbuf;
+       for (p = inbuf; p < inend; p++) {
+               if (q >= outend)
+                       return -1;
+               if (*p == '=') {
+                       p++;
+                       if (p + 1 < inend) {
+                               int c = _qp_cfromhex(p++);
+                               if (c < 0)
+                                       return -1;
+                               *q++ = (char)c;
+                       }
+                       else
+                               return -1;
+               }
+               else if (*p == '_')  /* header's may encode ' ' as '_' */
+                       *q++ = ' ';
+               else
+                       *q++ = *p;
+       }
+       return q - outbuf;
+}
+
+
 static int
 mustquote(unsigned char *p, unsigned char *end, size_t l)
 {
@@ -479,8 +553,11 @@ fput_quoted_line(FILE *fo, char *line, s
                }
                else {
                        if (*p == '\n') {
-                               if (p > beg && p[-1] == '\r')
+                               if (p > beg && p[-1] == '\r') {
+                                       if (l + 4 > limit)
+                                               (void)fputs("=\n", fo);
                                        (void)fputs("=0A=", fo);
+                               }
                                l = (size_t)-1;
                        }
                        else if (l + 2 > limit) {
@@ -541,14 +618,11 @@ mime_fQP_decode(FILE *fi, FILE *fo, void
                                while (p < end && is_WSP(*p))
                                        p++;
                                if (*p != '\n' && p + 1 < end) {
-                                       int c;
-                                       char buf[3];
-
-                                       buf[0] = *p++;
-                                       buf[1] = *p;
-                                       buf[2] = '\0';
-                                       c = (int)strtol(buf, NULL, 16);
-                                       (void)fputc(c, fo);
+                                       int c = _qp_cfromhex(p++);
+                                       if (c >= 0)
+                                               (void)fputc(c, fo);
+                                       else
+                                               (void)fputs("[?]", fo);
                                }
                        }
                        else
@@ -631,6 +705,24 @@ mime_fio_decoder(const char *ename)
 }
 
 /*
+ * Decode a RFC 2047 extended message header *encoded-word*.
+ * *encoding* is the corresponding character of the *encoded-word*.
+ */
+PUBLIC ssize_t
+mime_rfc2047_decode(char encoding, char *outbuf, size_t outlen,
+       const char *inbuf, size_t inlen)
+{
+       ssize_t declen = -1;
+
+       if (encoding == 'B' || encoding == 'b') {
+               if (outlen >= 3 * roundup(inlen, 4) / 4)
+                       declen = mime_b64tobin(outbuf, inbuf, inlen);
+       } else if (encoding == 'Q' || encoding == 'q')
+               declen = mime_QPh_decode(outbuf, outlen, inbuf, inlen);
+       return declen;
+}
+
+/*
  * This is for use in complete.c and mime.c to get the list of
  * encoding names without exposing the transfer_encoding_tbl[].  The
  * first name is returned if called with a pointer to a NULL pointer.
--- mime_codecs.h.orig  2013-02-12 19:25:05.000000000 +0100
+++ mime_codecs.h       2013-02-12 19:58:45.000000000 +0100
@@ -50,6 +50,8 @@ mime_codec_t mime_fio_decoder(const char
 
 void mime_fio_copy(FILE *, FILE *, void *);
 
+ssize_t mime_rfc2047_decode(char, char *, size_t, const char *, size_t);
+
 #include "mime.h"
 
 /* This is also declared in mime.h for export to complete.c. */
--- mime_header.c.orig  2013-02-12 19:25:16.000000000 +0100
+++ mime_header.c       2013-02-12 19:58:26.000000000 +0100
@@ -53,68 +53,6 @@ __RCSID("$NetBSD: mime_header.c,v 1.8 20
 #include "mime_header.h"
 #include "mime_codecs.h"
 
-/*
- * Our interface to mime_b64tobin()
- *
- * XXX - This should move to mime_codecs.c.
- */
-static ssize_t
-mime_B64_decode(char *outbuf, size_t outlen, const char *inbuf, size_t inlen)
-{
-       if (outlen < 3 * roundup(inlen, 4) / 4)
-               return -1;
-
-       return mime_b64tobin(outbuf, inbuf, inlen);
-}
-
-
-/*
- * Header specific "quoted-printable" decode!
- * Differences with body QP decoding (see rfc 2047, sec 4.2):
- * 1) '=' occurs _only_ when followed by two hex digits (FWS is not allowed).
- * 2) Spaces can be encoded as '_' in headers for readability.
- *
- * XXX - This should move to mime_codecs.c.
- */
-static ssize_t
-mime_QPh_decode(char *outbuf, size_t outlen, const char *inbuf, size_t inlen)
-{
-       const char *p, *inend;
-       char *outend;
-       char *q;
-
-       outend = outbuf + outlen;
-       inend = inbuf + inlen;
-       q = outbuf;
-       for (p = inbuf; p < inend; p++) {
-               if (q >= outend)
-                       return -1;
-               if (*p == '=') {
-                       p++;
-                       if (p + 1 < inend) {
-                               size_t c;
-                               char *bufend;
-                               char buf[3];
-
-                               buf[0] = *p++;
-                               buf[1] = *p;
-                               buf[2] = '\0';
-                               c = strtol(buf, &bufend, 16);
-                               if (bufend != &buf[2])
-                                       return -1;
-                               *q++ = (char)c;
-                       }
-                       else
-                               return -1;
-               }
-               else if (*p == '_')  /* header's may encode ' ' as '_' */
-                       *q++ = ' ';
-               else
-                       *q++ = *p;
-       }
-       return q - outbuf;
-}
-
 static const char *
 grab_charset(char *from_cs, size_t from_cs_len, const char *p)
 {
@@ -190,13 +128,7 @@ decode_word(const char **ibuf, char **ob
        dstend = to_cs ? decword : *obuf;
        dstlen = (to_cs ? sizeof(decword) : (size_t)(oend - *obuf)) - 1;
 
-       if (enctype == 'B' || enctype == 'b')
-               declen = mime_B64_decode(dstend, dstlen, encword, enclen);
-       else if (enctype == 'Q' || enctype == 'q')
-               declen = mime_QPh_decode(dstend, dstlen, encword, enclen);
-       else
-               return -1;
-
+       declen = mime_rfc2047_decode(enctype, dstend, dstlen, encword, enclen);
        if (declen == -1)
                return -1;
 


Home | Main Index | Thread Index | Old Index