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;
 

Reply via email to