Module Name: src Committed By: christos Date: Thu Feb 14 18:23:46 UTC 2013
Modified Files: src/usr.bin/mail: mime_codecs.c mime_codecs.h mime_header.c Log Message: PR/47657: Steffen "Daode" Nurpmeso: quoted printable CTE exceeds RFC limit. - Encapsulated all the content-transfer-encoding stuff in mime_codecs.c - Replaced calls of strtol(3) with a handcrafted version that allows simple error checking by testing the return value. This allows to easily add special code to handle illegal QP sequences. To generate a diff of this commit: cvs rdiff -u -r1.10 -r1.11 src/usr.bin/mail/mime_codecs.c cvs rdiff -u -r1.4 -r1.5 src/usr.bin/mail/mime_codecs.h cvs rdiff -u -r1.8 -r1.9 src/usr.bin/mail/mime_header.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/usr.bin/mail/mime_codecs.c diff -u src/usr.bin/mail/mime_codecs.c:1.10 src/usr.bin/mail/mime_codecs.c:1.11 --- src/usr.bin/mail/mime_codecs.c:1.10 Sat Nov 24 16:40:02 2012 +++ src/usr.bin/mail/mime_codecs.c Thu Feb 14 13:23:45 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: mime_codecs.c,v 1.10 2012/11/24 21:40:02 christos Exp $ */ +/* $NetBSD: mime_codecs.c,v 1.11 2013/02/14 18:23:45 christos Exp $ */ /*- * Copyright (c) 2006 The NetBSD Foundation, Inc. @@ -52,7 +52,7 @@ #include <sys/cdefs.h> #ifndef __lint__ -__RCSID("$NetBSD: mime_codecs.c,v 1.10 2012/11/24 21:40:02 christos Exp $"); +__RCSID("$NetBSD: mime_codecs.c,v 1.11 2013/02/14 18:23:45 christos Exp $"); #endif /* not __lint__ */ #include <assert.h> @@ -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. Index: src/usr.bin/mail/mime_codecs.h diff -u src/usr.bin/mail/mime_codecs.h:1.4 src/usr.bin/mail/mime_codecs.h:1.5 --- src/usr.bin/mail/mime_codecs.h:1.4 Mon Apr 28 16:24:14 2008 +++ src/usr.bin/mail/mime_codecs.h Thu Feb 14 13:23:45 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: mime_codecs.h,v 1.4 2008/04/28 20:24:14 martin Exp $ */ +/* $NetBSD: mime_codecs.h,v 1.5 2013/02/14 18:23:45 christos Exp $ */ /*- * Copyright (c) 2006 The NetBSD Foundation, Inc. @@ -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. */ Index: src/usr.bin/mail/mime_header.c diff -u src/usr.bin/mail/mime_header.c:1.8 src/usr.bin/mail/mime_header.c:1.9 --- src/usr.bin/mail/mime_header.c:1.8 Fri Apr 10 09:08:25 2009 +++ src/usr.bin/mail/mime_header.c Thu Feb 14 13:23:45 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: mime_header.c,v 1.8 2009/04/10 13:08:25 christos Exp $ */ +/* $NetBSD: mime_header.c,v 1.9 2013/02/14 18:23:45 christos Exp $ */ /*- * Copyright (c) 2006 The NetBSD Foundation, Inc. @@ -39,7 +39,7 @@ #include <sys/cdefs.h> #ifndef __lint__ -__RCSID("$NetBSD: mime_header.c,v 1.8 2009/04/10 13:08:25 christos Exp $"); +__RCSID("$NetBSD: mime_header.c,v 1.9 2013/02/14 18:23:45 christos Exp $"); #endif /* not __lint__ */ #include <assert.h> @@ -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;