This is an update of the patch made by Antti Tapio for 0.9.8a - ticket 
#1261

Index: apps/smime.c
===================================================================
RCS file: /home/john/cvsroot/openssl/apps/smime.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.2.1
diff -u -p -r1.1.1.1 -r1.1.1.1.2.1
--- apps/smime.c	14 Oct 2011 11:17:40 -0000	1.1.1.1
+++ apps/smime.c	20 Oct 2011 07:16:06 -0000	1.1.1.1.2.1
@@ -78,7 +78,7 @@ static int smime_cb(int ok, X509_STORE_C
 #define SMIME_ENCRYPT	(1 | SMIME_OP)
 #define SMIME_DECRYPT	(2 | SMIME_IP)
 #define SMIME_SIGN	(3 | SMIME_OP | SMIME_SIGNERS)
-#define SMIME_VERIFY	(4 | SMIME_IP)
+#define SMIME_VERIFY	(4 | SMIME_IP | SMIME_OP)
 #define SMIME_PK7OUT	(5 | SMIME_IP | SMIME_OP)
 #define SMIME_RESIGN	(6 | SMIME_IP | SMIME_OP | SMIME_SIGNERS)
 
@@ -365,6 +365,23 @@ int MAIN(int argc, char **argv)
 				goto argerr;
 			contfile = *++args;
 			}
+		else if (!strcmp(*args, "-transenc") || !strcmp (*args, "-transferencoding"))
+			{
+			if (args[1])
+				{
+				if (!strcmp(args[1], "binary"))
+					flags |= SMIME_TRANSFER_ENCODING_BINARY;
+				else if (!strcmp(args[1], "base64"))
+					;
+				else {
+					BIO_printf(bio_err, "Supported transfer encodings are base64 and binary\n");
+					badarg = 1;
+				}
+				args++;
+				}
+			else
+				badarg = 1;
+			}
 		else if (args_verify(&args, NULL, &badarg, bio_err, &vpm))
 			continue;
 		else if ((cipher = EVP_get_cipherbyname(*args + 1)) == NULL)
@@ -488,6 +505,7 @@ int MAIN(int argc, char **argv)
 		BIO_printf(bio_err,  "-rand file%cfile%c...\n", LIST_SEPARATOR_CHAR, LIST_SEPARATOR_CHAR);
 		BIO_printf(bio_err,  "               load the file (or the files in the directory) into\n");
 		BIO_printf(bio_err,  "               the random number generator\n");
+		BIO_printf(bio_err,  "-transenc enc  transfer encoding to use (base64 or binary)\n");
 		BIO_printf (bio_err, "cert.pem       recipient certificate(s) for encryption\n");
 		goto end;
 		}
Index: crypto/asn1/asn1.h
===================================================================
RCS file: /home/john/cvsroot/openssl/crypto/asn1/asn1.h,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.2.1
diff -u -p -r1.1.1.1 -r1.1.1.1.2.1
--- crypto/asn1/asn1.h	14 Oct 2011 11:17:40 -0000	1.1.1.1
+++ crypto/asn1/asn1.h	15 Oct 2011 09:36:51 -0000	1.1.1.1.2.1
@@ -161,6 +161,7 @@ extern "C" {
 #define SMIME_OLDMIME		0x400
 #define SMIME_CRLFEOL		0x800
 #define SMIME_STREAM		0x1000
+#define SMIME_TRANSFER_ENCODING_BINARY	0x2000
 
 struct X509_algor_st;
 DECLARE_STACK_OF(X509_ALGOR)
@@ -1222,6 +1223,8 @@ void ERR_load_ASN1_strings(void);
 #define ASN1_F_ASN1_VERIFY				 137
 #define ASN1_F_B64_READ_ASN1				 209
 #define ASN1_F_B64_WRITE_ASN1				 210
+#define ASN1_F_BINARY_READ_ASN1				 219
+#define ASN1_F_BINARY_WRITE_ASN1			 220
 #define ASN1_F_BIO_NEW_NDEF				 208
 #define ASN1_F_BITSTR_CB				 180
 #define ASN1_F_BN_TO_ASN1_ENUMERATED			 138
@@ -1335,6 +1338,7 @@ void ERR_load_ASN1_strings(void);
 #define ASN1_R_INVALID_OBJECT_ENCODING			 216
 #define ASN1_R_INVALID_SEPARATOR			 131
 #define ASN1_R_INVALID_TIME_FORMAT			 132
+#define ASN1_R_INVALID_TRANSFER_ENCODING		 217
 #define ASN1_R_INVALID_UNIVERSALSTRING_LENGTH		 133
 #define ASN1_R_INVALID_UTF8STRING			 134
 #define ASN1_R_IV_TOO_LARGE				 135
Index: crypto/asn1/asn1_err.c
===================================================================
RCS file: /home/john/cvsroot/openssl/crypto/asn1/asn1_err.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.2.1
diff -u -p -r1.1.1.1 -r1.1.1.1.2.1
--- crypto/asn1/asn1_err.c	14 Oct 2011 11:17:40 -0000	1.1.1.1
+++ crypto/asn1/asn1_err.c	15 Oct 2011 09:36:51 -0000	1.1.1.1.2.1
@@ -135,6 +135,8 @@ static ERR_STRING_DATA ASN1_str_functs[]
 {ERR_FUNC(ASN1_F_ASN1_VERIFY),	"ASN1_verify"},
 {ERR_FUNC(ASN1_F_B64_READ_ASN1),	"B64_READ_ASN1"},
 {ERR_FUNC(ASN1_F_B64_WRITE_ASN1),	"B64_WRITE_ASN1"},
+{ERR_FUNC(ASN1_F_BINARY_READ_ASN1),	"BINARY_READ_ASN1"},
+{ERR_FUNC(ASN1_F_BINARY_WRITE_ASN1),	"BINARY_WRITE_ASN1"},
 {ERR_FUNC(ASN1_F_BIO_NEW_NDEF),	"BIO_new_NDEF"},
 {ERR_FUNC(ASN1_F_BITSTR_CB),	"BITSTR_CB"},
 {ERR_FUNC(ASN1_F_BN_TO_ASN1_ENUMERATED),	"BN_to_ASN1_ENUMERATED"},
Index: crypto/asn1/asn_mime.c
===================================================================
RCS file: /home/john/cvsroot/openssl/crypto/asn1/asn_mime.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.2.2
diff -u -p -r1.1.1.1 -r1.1.1.1.2.2
--- crypto/asn1/asn_mime.c	14 Oct 2011 11:17:40 -0000	1.1.1.1
+++ crypto/asn1/asn_mime.c	20 Oct 2011 07:16:07 -0000	1.1.1.1.2.2
@@ -100,7 +100,6 @@ static int mime_hdr_cmp(const MIME_HEADE
 static int mime_param_cmp(const MIME_PARAM * const *a,
 			const MIME_PARAM * const *b);
 static void mime_param_free(MIME_PARAM *param);
-static int mime_bound_check(char *line, int linelen, char *bound, int blen);
 static int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret);
 static int strip_eol(char *linebuf, int *plen);
 static MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, char *name);
@@ -143,7 +142,7 @@ int i2d_ASN1_bio_stream(BIO *out, ASN1_V
 	return 1;
 	}
 
-/* Base 64 read and write of ASN1 structure */
+/* Base 64 and binary read and write of ASN1 structure */
 
 static int B64_write_ASN1(BIO *out, ASN1_VALUE *val, BIO *in, int flags,
 				const ASN1_ITEM *it)
@@ -166,6 +165,15 @@ static int B64_write_ASN1(BIO *out, ASN1
 	return r;
 	}
 
+static int BINARY_write_ASN1(BIO *out, ASN1_VALUE *val, BIO *in, int flags,
+				const ASN1_ITEM *it)
+	{
+	int r;
+	r = i2d_ASN1_bio_stream(out, val, in, flags, it);
+	(void)BIO_flush(out);
+	return r;
+	}
+
 /* Streaming ASN1 PEM write */
 
 int PEM_write_bio_ASN1_stream(BIO *out, ASN1_VALUE *val, BIO *in, int flags,
@@ -197,6 +205,16 @@ static ASN1_VALUE *b64_read_asn1(BIO *bi
 	return val;
 }
 
+static ASN1_VALUE *binary_read_asn1(BIO *bio, const ASN1_ITEM *it)
+{
+	ASN1_VALUE *val;
+	val = ASN1_item_d2i_bio(it, bio, NULL);
+	if(!val)
+		ASN1err(ASN1_F_BINARY_READ_ASN1,ASN1_R_DECODE_ERROR);
+	(void)BIO_flush(bio);
+	return val;
+}
+
 /* Generate the MIME "micalg" parameter from RFC3851, RFC4490 */
 
 static int asn1_write_micalg(BIO *out, STACK_OF(X509_ALGOR) *mdalgs)
@@ -323,12 +341,20 @@ int SMIME_write_ASN1(BIO *bio, ASN1_VALU
 
 		BIO_printf(bio, "Content-Type: %ssignature;", mime_prefix); 
 		BIO_printf(bio, " name=\"smime.p7s\"%s", mime_eol);
-		BIO_printf(bio, "Content-Transfer-Encoding: base64%s",
+		BIO_printf(bio, "Content-Transfer-Encoding: %s%s",
+								(flags&SMIME_TRANSFER_ENCODING_BINARY) ? "binary" : "base64",
 								mime_eol);
 		BIO_printf(bio, "Content-Disposition: attachment;");
 		BIO_printf(bio, " filename=\"smime.p7s\"%s%s",
 							mime_eol, mime_eol);
-		B64_write_ASN1(bio, val, NULL, 0, it);
+
+		if (flags & SMIME_TRANSFER_ENCODING_BINARY) {
+			BINARY_write_ASN1(bio, val, NULL, 0, it);
+			BIO_printf(bio, "%s", mime_eol);
+		}
+		else
+			B64_write_ASN1(bio, val, NULL, 0, it);
+
 		BIO_printf(bio,"%s------%s--%s%s", mime_eol, bound,
 							mime_eol, mime_eol);
 		return 1;
@@ -360,10 +386,18 @@ int SMIME_write_ASN1(BIO *bio, ASN1_VALU
 	if (msg_type)
 		BIO_printf(bio, " smime-type=%s;", msg_type);
 	BIO_printf(bio, " name=\"%s\"%s", cname, mime_eol);
-	BIO_printf(bio, "Content-Transfer-Encoding: base64%s%s",
+	BIO_printf(bio, "Content-Transfer-Encoding: %s%s%s",
+						(flags&SMIME_TRANSFER_ENCODING_BINARY) ? "binary" : "base64",
 						mime_eol, mime_eol);
-	if (!B64_write_ASN1(bio, val, data, flags, it))
-		return 0;
+	if (flags & SMIME_TRANSFER_ENCODING_BINARY) {
+		if (!BINARY_write_ASN1(bio, val, data, flags, it))
+			return 0;
+	}
+	else {
+		if (!B64_write_ASN1(bio, val, data, flags, it))
+			return 0;
+	}
+		
 	BIO_printf(bio, "%s", mime_eol);
 	return 1;
 }
@@ -434,6 +468,7 @@ ASN1_VALUE *SMIME_read_ASN1(BIO *bio, BI
 	MIME_PARAM *prm;
 	ASN1_VALUE *val;
 	int ret;
+	int base64;
 
 	if(bcont) *bcont = NULL;
 
@@ -492,9 +527,29 @@ ASN1_VALUE *SMIME_read_ASN1(BIO *bio, BI
 			sk_BIO_pop_free(parts, BIO_vfree);
 			return NULL;
 		}
+
+		/* Base64 or binary?  Default to binary.  This is
+		   inconsistent with old code, but needed for AS2 */
+
+		base64 = 0;
+		if ((hdr = mime_hdr_find(headers, "content-transfer-encoding"))) {
+			if (hdr->value && strcmp (hdr->value, "base64") == 0) 
+				base64 = 1;
+			else if (!hdr->value || strcmp (hdr->value, "binary") != 0) {
+				sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
+				ASN1err(ASN1_F_SMIME_READ_ASN1,ASN1_R_INVALID_TRANSFER_ENCODING);
+				sk_BIO_pop_free (parts, BIO_vfree);
+				return NULL;
+			}
+		}
+
 		sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
 		/* Read in ASN1 */
-		if(!(val = b64_read_asn1(asnin, it))) {
+
+		if(!(val = base64 ?
+			b64_read_asn1(asnin, it) :
+			binary_read_asn1(asnin, it)))
+		{
 			ASN1err(ASN1_F_SMIME_READ_ASN1,ASN1_R_ASN1_SIG_PARSE_ERROR);
 			sk_BIO_pop_free(parts, BIO_vfree);
 			return NULL;
@@ -518,9 +573,25 @@ ASN1_VALUE *SMIME_read_ASN1(BIO *bio, BI
 		return NULL;
 	}
 
+	/* Base64 or binary?  Default to binary.  This is
+	   inconsistent with old code, but needed for AS2 */
+
+	base64 = 0;
+	if ((hdr = mime_hdr_find(headers, "content-transfer-encoding"))) {
+		if (hdr->value && strcmp (hdr->value, "base64") == 0) 
+			base64 = 1;
+		else if (!hdr->value || strcmp (hdr->value, "binary") != 0) {
+			sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
+			ASN1err(ASN1_F_SMIME_READ_ASN1,ASN1_R_INVALID_TRANSFER_ENCODING);
+			return NULL;
+		}
+	}
+
 	sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
 	
-	if(!(val = b64_read_asn1(bio, it))) {
+	if(!(val = base64 ? b64_read_asn1(bio, it) : 
+			binary_read_asn1(bio, it)))
+	{
 		ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_ASN1_PARSE_ERROR);
 		return NULL;
 	}
@@ -597,48 +668,163 @@ int SMIME_text(BIO *in, BIO *out)
 	return 1;
 }
 
-/* Split a multipart/XXX message body into component parts: result is
- * canonical parts in a STACK of bios
+/*
+ * Returns >0 if the given string starts with a linebreak.
+ * The return code is the index of the first character
+ * after the line break.
+ */
+static int starts_with_linebreak(const char *s) {
+	const char *p;
+	p = s;
+	if(*p == '\x0d') {
+		++p;
+		if(*p == '\x0a')
+			++p;
+	} else if(*p == '\x0a') {
+		++p;
+	}
+	return (p - s);
+}
+
+/*
+ * Finds a boundary in a buffer and returns the length of the boundary (this can
+ * vary depending on the kind of linebreaks), the starting index of the boundary
+ * and a flag telling if it is an ending boundary.
+ * Parameters:
+ *   buf - buffer to search in
+ *   buflen - length of buf
+ *   bound - the boundary string to match
+ *   blen - length of bound
+ *   boundstart - returns the index of the start of the boundary
+ *   ending - returns 1 if the found boundary is an ending boundary,
+ *     i.e. if it ends with a "--"
+ *
+ * Returns:
+ *   0 - No boundary found 
+ *   >0 - Boundary found and the return value is the length of boundary,
+ *        which can be skipped to proceed to the next MIME part.
  */
+static int find_boundary_start(const char *buf, int buflen, const char *bound, int blen, int *boundstart, int *ending) {
+	int e = -1;
+	int i, e2;
+	int is;
+	const char *p;
 
-static int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret)
-{
-	char linebuf[MAX_SMLEN];
-	int len, blen;
-	int eol = 0, next_eol = 0;
+	if(buflen == -1) return 0;
+	if(blen == -1) blen = strlen(bound);
+
+
+	/* Quickly eliminate if line length too short */
+	if((blen + 3) > buflen) 
+		return 0;
+
+	*ending = 0;
+	is = buflen - (blen + 3);
+	for(i = 0; i <= is; i++) {
+		*boundstart = i;
+		p = buf + i;
+		e = i;
+		if((e = starts_with_linebreak(p)) > 0) {
+			if(!strncmp(p + e, "--", 2)) {
+				e += 2;
+				if((e + blen) < buflen && !strncmp(p + e, bound, blen))
+					e += blen;
+				else
+					e = 0;
+			} else
+				e = 0;
+		}
+
+		if(e > 0) {
+			/* Look what is behind */
+			if((e + 2) < buflen && !strncmp(p + e, "--", 2)) {
+				e += 2;
+				*ending = 1;	
+			} 
+
+			/* Strip away the trailing linebreaks, for test issues */
+			if(e > 0 && (e + 2) < buflen && (e2 = starts_with_linebreak(p + e)) > 0) {
+				e += e2;
+			}
+			break;
+		}
+	}
+
+	return e;
+}
+
+/* Split a multipart/XXX message body into component parts: result is
+ * parts in a STACK of bios. Is considered to be able to read also
+ * binary parts.
+ * Tries to split after the following rules (EBNF):
+ * { LineBreak }, LineBreak, Boundary, LineBreak, Data,
+ * ( LineBreak, Boundary, [ "--" ] | [ LineBreak | EOF ] ) |
+ * EOF
+ * LineBreak := ( "\x0d", "\x0a" ) | "\x0a" | "\x0d"
+ */
+static int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret) {
+	int len, len2, boundlen;
+	char rbuf[MAX_SMLEN];
+	char *buf = rbuf;
 	BIO *bpart = NULL;
 	STACK_OF(BIO) *parts;
-	char state, part, first;
-
-	blen = strlen(bound);
-	part = 0;
-	state = 0;
-	first = 1;
+	int boundary_found, boundary_start, boundary_ending, maxboundlen;
+	
+	boundlen = strlen(bound);
+	maxboundlen = 2 + 2 + boundlen + 2 + 2;
 	parts = sk_BIO_new_null();
 	*ret = parts;
-	while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) {
-		state = mime_bound_check(linebuf, len, bound, blen);
-		if(state == 1) {
-			first = 1;
-			part++;
-		} else if(state == 2) {
-			sk_BIO_push(parts, bpart);
-			return 1;
-		} else if(part) {
-			/* Strip CR+LF from linebuf */
-			next_eol = strip_eol(linebuf, &len);
-			if(first) {
-				first = 0;
-				if(bpart) sk_BIO_push(parts, bpart);
+	len = 0;
+
+	while((len = (len > 0) ? len : BIO_read(bio, (buf = rbuf), MAX_SMLEN)) > 0) {
+		/* If there are not enough bytes in the buffer, to compare with the
+		 * boundary, fill the buffer up
+		 */
+		if(len <= maxboundlen) {
+			memmove(rbuf, buf, len); buf = rbuf;
+			len2 = BIO_read(bio, buf + len, MAX_SMLEN - len);
+			if(len2 >= (maxboundlen - len))
+				len += len2;
+			else {
+				/* Stream ended here, write the characters away and return */
+				BIO_write(bpart, buf, (len + (len2 > 0) ? len2 : 0));
+				if(bpart) 
+					sk_BIO_push(parts, bpart);
+				break;
+			}
+		}
+
+		if((boundary_found = find_boundary_start(buf, len, bound, boundlen, &boundary_start, &boundary_ending)) > 0) {
+			/* If there is something before the boundary, write it to the part */
+			if(boundary_start > 0 && bpart)
+				BIO_write(bpart, buf, boundary_start);
+			buf += boundary_start; len -= boundary_start;
+			memmove(rbuf, buf, len); buf = rbuf;
+			len2 = BIO_read(bio, buf + len, MAX_SMLEN - len);
+			len += (len2) ? len2 : 0;
+
+			boundary_found = find_boundary_start(buf, len, bound, boundlen, &boundary_start, &boundary_ending);
+			/* If boundary is not the last one, create a new part */
+			if(!boundary_ending) {
+				if(bpart) 
+					sk_BIO_push(parts, bpart);
 				bpart = BIO_new(BIO_s_mem());
 				BIO_set_mem_eof_return(bpart, 0);
-			} else if (eol)
-				BIO_write(bpart, "\r\n", 2);
-			eol = next_eol;
-			if (len)
-				BIO_write(bpart, linebuf, len);
+			} else {
+				/* End is reached */
+				if(bpart) 
+					sk_BIO_push(parts, bpart);
+				return 1;
+			}
+			buf += (boundary_start + boundary_found); len -= (boundary_start + boundary_found);
+		} else {
+			if(bpart)
+				BIO_write(bpart, buf, len - maxboundlen);
+			buf += (len - maxboundlen); len = maxboundlen;
+			/* ELSE: Just forget it, no MIME part started */
 		}
 	}
+
 	return 0;
 }
 
@@ -904,25 +1090,6 @@ static void mime_param_free(MIME_PARAM *
 	OPENSSL_free(param);
 }
 
-/* Check for a multipart boundary. Returns:
- * 0 : no boundary
- * 1 : part boundary
- * 2 : final boundary
- */
-static int mime_bound_check(char *line, int linelen, char *bound, int blen)
-{
-	if(linelen == -1) linelen = strlen(line);
-	if(blen == -1) blen = strlen(bound);
-	/* Quickly eliminate if line length too short */
-	if(blen + 2 > linelen) return 0;
-	/* Check for part boundary */
-	if(!strncmp(line, "--", 2) && !strncmp(line + 2, bound, blen)) {
-		if(!strncmp(line + blen + 2, "--", 2)) return 2;
-		else return 1;
-	}
-	return 0;
-}
-
 static int strip_eol(char *linebuf, int *plen)
 	{
 	int len = *plen;

Reply via email to