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



The following reply was made to PR bin/47567; it has been noted by GNATS.

From: Steffen "Daode" Nurpmeso <sdaoden%gmail.com@localhost>
To: gnats-bugs%NetBSD.org@localhost
Cc: gnats-admin%netbsd.org@localhost, netbsd-bugs%netbsd.org@localhost
Subject: Re: bin/47567: Mail(1)/mailx(1)/mail(1) - quoted-printable CTE
 excesses RFC limit
Date: Thu, 14 Feb 2013 19:15:43 +0100

 This is a multi-part message in MIME format.
 
 --=_01360865743=-C2i7u6QpIKuG+1sOp7X1OrfKIFPZ+M=_
 Content-Type: text/plain; charset=US-ASCII
 Content-Transfer-Encoding: 7bit
 Content-Disposition: inline
 
 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
 
 --=_01360865743=-C2i7u6QpIKuG+1sOp7X1OrfKIFPZ+M=_
 Content-Type: text/diff; charset=US-ASCII
 Content-Transfer-Encoding: quoted-printable
 Content-Disposition: attachment;
  filename="nbsd-mail.diff"
 
 --- 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.
   */
 =20
 +/*
 + * 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[] =3D {
 +              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 =3D (unsigned char)hex[0] - '0') >=3D __arraycount(atoi16) ||
 +          (i2 =3D (unsigned char)hex[1] - '0') >=3D __arraycount(atoi16))
 +              goto jerr;
 +      i1 =3D atoi16[i1];
 +      i2 =3D atoi16[i2];
 +      if ((i1 | i2) & 0xF0)
 +              goto jerr;
 +      r =3D i1;
 +      r <<=3D 4;
 +      r +=3D i2;
 +jleave:
 +      return r;
 +jerr:
 +      r =3D -1;
 +      goto jleave;
 +}
 +
 +/*
 + * Header specific "quoted-printable" decode!
 + * Differences with body QP decoding (see rfc 2047, sec 4.2):
 + * 1) '=3D' occurs _only_ when followed by two hex digits (FWS is not allo=
 wed).
 + * 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 inl=
 en)
 +{
 +      const char *p, *inend;
 +      char *outend;
 +      char *q;
 +
 +      outend =3D outbuf + outlen;
 +      inend =3D inbuf + inlen;
 +      q =3D outbuf;
 +      for (p =3D inbuf; p < inend; p++) {
 +              if (q >=3D outend)
 +                      return -1;
 +              if (*p =3D=3D '=3D') {
 +                      p++;
 +                      if (p + 1 < inend) {
 +                              int c =3D _qp_cfromhex(p++);
 +                              if (c < 0)
 +                                      return -1;
 +                              *q++ =3D (char)c;
 +                      }
 +                      else
 +                              return -1;
 +              }
 +              else if (*p =3D=3D '_')  /* header's may encode ' ' as '_' */
 +                      *q++ =3D ' ';
 +              else
 +                      *q++ =3D *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 =3D=3D '\n') {
 -                              if (p > beg && p[-1] =3D=3D '\r')
 +                              if (p > beg && p[-1] =3D=3D '\r') {
 +                                      if (l + 4 > limit)
 +                                              (void)fputs("=3D\n", fo);
                                        (void)fputs("=3D0A=3D", fo);
 +                              }
                                l =3D (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 !=3D '\n' && p + 1 < end) {
 -                                      int c;
 -                                      char buf[3];
 -
 -                                      buf[0] =3D *p++;
 -                                      buf[1] =3D *p;
 -                                      buf[2] =3D '\0';
 -                                      c =3D (int)strtol(buf, NULL, 16);
 -                                      (void)fputc(c, fo);
 +                                      int c =3D _qp_cfromhex(p++);
 +                                      if (c >=3D 0)
 +                                              (void)fputc(c, fo);
 +                                      else
 +                                              (void)fputs("[?]", fo);
                                }
                        }
                        else
 @@ -631,6 +705,24 @@ mime_fio_decoder(const char *ename)
  }
 =20
  /*
 + * 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 =3D -1;
 +
 +      if (encoding =3D=3D 'B' || encoding =3D=3D 'b') {
 +              if (outlen >=3D 3 * roundup(inlen, 4) / 4)
 +                      declen =3D mime_b64tobin(outbuf, inbuf, inlen);
 +      } else if (encoding =3D=3D 'Q' || encoding =3D=3D 'q')
 +              declen =3D 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
 =20
  void mime_fio_copy(FILE *, FILE *, void *);
 =20
 +ssize_t mime_rfc2047_decode(char, char *, size_t, const char *, size_t);
 +
  #include "mime.h"
 =20
  /* 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"
 =20
 -/*
 - * 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 inl=
 en)
 -{
 -      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) '=3D' occurs _only_ when followed by two hex digits (FWS is not allo=
 wed).
 - * 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 inl=
 en)
 -{
 -      const char *p, *inend;
 -      char *outend;
 -      char *q;
 -
 -      outend =3D outbuf + outlen;
 -      inend =3D inbuf + inlen;
 -      q =3D outbuf;
 -      for (p =3D inbuf; p < inend; p++) {
 -              if (q >=3D outend)
 -                      return -1;
 -              if (*p =3D=3D '=3D') {
 -                      p++;
 -                      if (p + 1 < inend) {
 -                              size_t c;
 -                              char *bufend;
 -                              char buf[3];
 -
 -                              buf[0] =3D *p++;
 -                              buf[1] =3D *p;
 -                              buf[2] =3D '\0';
 -                              c =3D strtol(buf, &bufend, 16);
 -                              if (bufend !=3D &buf[2])
 -                                      return -1;
 -                              *q++ =3D (char)c;
 -                      }
 -                      else
 -                              return -1;
 -              }
 -              else if (*p =3D=3D '_')  /* header's may encode ' ' as '_' */
 -                      *q++ =3D ' ';
 -              else
 -                      *q++ =3D *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 =3D to_cs ? decword : *obuf;
        dstlen =3D (to_cs ? sizeof(decword) : (size_t)(oend - *obuf)) - 1;
 =20
 -      if (enctype =3D=3D 'B' || enctype =3D=3D 'b')
 -              declen =3D mime_B64_decode(dstend, dstlen, encword, enclen);
 -      else if (enctype =3D=3D 'Q' || enctype =3D=3D 'q')
 -              declen =3D mime_QPh_decode(dstend, dstlen, encword, enclen);
 -      else
 -              return -1;
 -
 +      declen =3D mime_rfc2047_decode(enctype, dstend, dstlen, encword, 
enclen);
        if (declen =3D=3D -1)
                return -1;
 =20
 
 --=_01360865743=-C2i7u6QpIKuG+1sOp7X1OrfKIFPZ+M=_--
 


Home | Main Index | Thread Index | Old Index