Home | History | Annotate | Download | only in asn1
      1 /* asn_mime.c */
      2 /* Written by Dr Stephen N Henson (steve (at) openssl.org) for the OpenSSL
      3  * project.
      4  */
      5 /* ====================================================================
      6  * Copyright (c) 1999-2008 The OpenSSL Project.  All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  *
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  *
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in
     17  *    the documentation and/or other materials provided with the
     18  *    distribution.
     19  *
     20  * 3. All advertising materials mentioning features or use of this
     21  *    software must display the following acknowledgment:
     22  *    "This product includes software developed by the OpenSSL Project
     23  *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
     24  *
     25  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
     26  *    endorse or promote products derived from this software without
     27  *    prior written permission. For written permission, please contact
     28  *    licensing (at) OpenSSL.org.
     29  *
     30  * 5. Products derived from this software may not be called "OpenSSL"
     31  *    nor may "OpenSSL" appear in their names without prior written
     32  *    permission of the OpenSSL Project.
     33  *
     34  * 6. Redistributions of any form whatsoever must retain the following
     35  *    acknowledgment:
     36  *    "This product includes software developed by the OpenSSL Project
     37  *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
     38  *
     39  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
     40  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     41  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     42  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
     43  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     44  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     45  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     46  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     47  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
     48  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     49  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
     50  * OF THE POSSIBILITY OF SUCH DAMAGE.
     51  * ====================================================================
     52  *
     53  */
     54 
     55 #include <stdio.h>
     56 #include <ctype.h>
     57 #include "cryptlib.h"
     58 #include <openssl/rand.h>
     59 #include <openssl/x509.h>
     60 #include <openssl/asn1.h>
     61 #include <openssl/asn1t.h>
     62 #include "asn1_locl.h"
     63 
     64 /* Generalised MIME like utilities for streaming ASN1. Although many
     65  * have a PKCS7/CMS like flavour others are more general purpose.
     66  */
     67 
     68 /* MIME format structures
     69  * Note that all are translated to lower case apart from
     70  * parameter values. Quotes are stripped off
     71  */
     72 
     73 typedef struct {
     74 char *param_name;			/* Param name e.g. "micalg" */
     75 char *param_value;			/* Param value e.g. "sha1" */
     76 } MIME_PARAM;
     77 
     78 DECLARE_STACK_OF(MIME_PARAM)
     79 IMPLEMENT_STACK_OF(MIME_PARAM)
     80 
     81 typedef struct {
     82 char *name;				/* Name of line e.g. "content-type" */
     83 char *value;				/* Value of line e.g. "text/plain" */
     84 STACK_OF(MIME_PARAM) *params;		/* Zero or more parameters */
     85 } MIME_HEADER;
     86 
     87 DECLARE_STACK_OF(MIME_HEADER)
     88 IMPLEMENT_STACK_OF(MIME_HEADER)
     89 
     90 static int asn1_output_data(BIO *out, BIO *data, ASN1_VALUE *val, int flags,
     91 					const ASN1_ITEM *it);
     92 static char * strip_ends(char *name);
     93 static char * strip_start(char *name);
     94 static char * strip_end(char *name);
     95 static MIME_HEADER *mime_hdr_new(char *name, char *value);
     96 static int mime_hdr_addparam(MIME_HEADER *mhdr, char *name, char *value);
     97 static STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio);
     98 static int mime_hdr_cmp(const MIME_HEADER * const *a,
     99 			const MIME_HEADER * const *b);
    100 static int mime_param_cmp(const MIME_PARAM * const *a,
    101 			const MIME_PARAM * const *b);
    102 static void mime_param_free(MIME_PARAM *param);
    103 static int mime_bound_check(char *line, int linelen, char *bound, int blen);
    104 static int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret);
    105 static int strip_eol(char *linebuf, int *plen);
    106 static MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, char *name);
    107 static MIME_PARAM *mime_param_find(MIME_HEADER *hdr, char *name);
    108 static void mime_hdr_free(MIME_HEADER *hdr);
    109 
    110 #define MAX_SMLEN 1024
    111 #define mime_debug(x) /* x */
    112 
    113 /* Output an ASN1 structure in BER format streaming if necessary */
    114 
    115 int i2d_ASN1_bio_stream(BIO *out, ASN1_VALUE *val, BIO *in, int flags,
    116 				const ASN1_ITEM *it)
    117 	{
    118 	/* If streaming create stream BIO and copy all content through it */
    119 	if (flags & SMIME_STREAM)
    120 		{
    121 		BIO *bio, *tbio;
    122 		bio = BIO_new_NDEF(out, val, it);
    123 		if (!bio)
    124 			{
    125 			ASN1err(ASN1_F_I2D_ASN1_BIO_STREAM,ERR_R_MALLOC_FAILURE);
    126 			return 0;
    127 			}
    128 		SMIME_crlf_copy(in, bio, flags);
    129 		(void)BIO_flush(bio);
    130 		/* Free up successive BIOs until we hit the old output BIO */
    131 		do
    132 			{
    133 			tbio = BIO_pop(bio);
    134 			BIO_free(bio);
    135 			bio = tbio;
    136 			} while (bio != out);
    137 		}
    138 	/* else just write out ASN1 structure which will have all content
    139 	 * stored internally
    140 	 */
    141 	else
    142 		ASN1_item_i2d_bio(it, out, val);
    143 	return 1;
    144 	}
    145 
    146 /* Base 64 read and write of ASN1 structure */
    147 
    148 static int B64_write_ASN1(BIO *out, ASN1_VALUE *val, BIO *in, int flags,
    149 				const ASN1_ITEM *it)
    150 	{
    151 	BIO *b64;
    152 	int r;
    153 	b64 = BIO_new(BIO_f_base64());
    154 	if(!b64)
    155 		{
    156 		ASN1err(ASN1_F_B64_WRITE_ASN1,ERR_R_MALLOC_FAILURE);
    157 		return 0;
    158 		}
    159 	/* prepend the b64 BIO so all data is base64 encoded.
    160 	 */
    161 	out = BIO_push(b64, out);
    162 	r = i2d_ASN1_bio_stream(out, val, in, flags, it);
    163 	(void)BIO_flush(out);
    164 	BIO_pop(out);
    165 	BIO_free(b64);
    166 	return r;
    167 	}
    168 
    169 /* Streaming ASN1 PEM write */
    170 
    171 int PEM_write_bio_ASN1_stream(BIO *out, ASN1_VALUE *val, BIO *in, int flags,
    172 				const char *hdr,
    173 				const ASN1_ITEM *it)
    174 	{
    175 	int r;
    176 	BIO_printf(out, "-----BEGIN %s-----\n", hdr);
    177 	r = B64_write_ASN1(out, val, in, flags, it);
    178 	BIO_printf(out, "-----END %s-----\n", hdr);
    179 	return r;
    180 	}
    181 
    182 static ASN1_VALUE *b64_read_asn1(BIO *bio, const ASN1_ITEM *it)
    183 {
    184 	BIO *b64;
    185 	ASN1_VALUE *val;
    186 	if(!(b64 = BIO_new(BIO_f_base64()))) {
    187 		ASN1err(ASN1_F_B64_READ_ASN1,ERR_R_MALLOC_FAILURE);
    188 		return 0;
    189 	}
    190 	bio = BIO_push(b64, bio);
    191 	val = ASN1_item_d2i_bio(it, bio, NULL);
    192 	if(!val)
    193 		ASN1err(ASN1_F_B64_READ_ASN1,ASN1_R_DECODE_ERROR);
    194 	(void)BIO_flush(bio);
    195 	bio = BIO_pop(bio);
    196 	BIO_free(b64);
    197 	return val;
    198 }
    199 
    200 /* Generate the MIME "micalg" parameter from RFC3851, RFC4490 */
    201 
    202 static int asn1_write_micalg(BIO *out, STACK_OF(X509_ALGOR) *mdalgs)
    203 	{
    204 	const EVP_MD *md;
    205 	int i, have_unknown = 0, write_comma, ret = 0, md_nid;
    206 	have_unknown = 0;
    207 	write_comma = 0;
    208 	for (i = 0; i < sk_X509_ALGOR_num(mdalgs); i++)
    209 		{
    210 		if (write_comma)
    211 			BIO_write(out, ",", 1);
    212 		write_comma = 1;
    213 		md_nid = OBJ_obj2nid(sk_X509_ALGOR_value(mdalgs, i)->algorithm);
    214 		md = EVP_get_digestbynid(md_nid);
    215 		if (md && md->md_ctrl)
    216 			{
    217 			int rv;
    218 			char *micstr;
    219 			rv = md->md_ctrl(NULL, EVP_MD_CTRL_MICALG, 0, &micstr);
    220 			if (rv > 0)
    221 				{
    222 				BIO_puts(out, micstr);
    223 				OPENSSL_free(micstr);
    224 				continue;
    225 				}
    226 			if (rv != -2)
    227 				goto err;
    228 			}
    229 		switch(md_nid)
    230 			{
    231 			case NID_sha1:
    232 			BIO_puts(out, "sha1");
    233 			break;
    234 
    235 			case NID_md5:
    236 			BIO_puts(out, "md5");
    237 			break;
    238 
    239 			case NID_sha256:
    240 			BIO_puts(out, "sha-256");
    241 			break;
    242 
    243 			case NID_sha384:
    244 			BIO_puts(out, "sha-384");
    245 			break;
    246 
    247 			case NID_sha512:
    248 			BIO_puts(out, "sha-512");
    249 			break;
    250 
    251 			case NID_id_GostR3411_94:
    252 			BIO_puts(out, "gostr3411-94");
    253 				goto err;
    254 			break;
    255 
    256 			default:
    257 			if (have_unknown)
    258 				write_comma = 0;
    259 			else
    260 				{
    261 				BIO_puts(out, "unknown");
    262 				have_unknown = 1;
    263 				}
    264 			break;
    265 
    266 			}
    267 		}
    268 
    269 	ret = 1;
    270 	err:
    271 
    272 	return ret;
    273 
    274 	}
    275 
    276 /* SMIME sender */
    277 
    278 int SMIME_write_ASN1(BIO *bio, ASN1_VALUE *val, BIO *data, int flags,
    279 				int ctype_nid, int econt_nid,
    280 				STACK_OF(X509_ALGOR) *mdalgs,
    281 				const ASN1_ITEM *it)
    282 {
    283 	char bound[33], c;
    284 	int i;
    285 	const char *mime_prefix, *mime_eol, *cname = "smime.p7m";
    286 	const char *msg_type=NULL;
    287 	if (flags & SMIME_OLDMIME)
    288 		mime_prefix = "application/x-pkcs7-";
    289 	else
    290 		mime_prefix = "application/pkcs7-";
    291 
    292 	if (flags & SMIME_CRLFEOL)
    293 		mime_eol = "\r\n";
    294 	else
    295 		mime_eol = "\n";
    296 	if((flags & SMIME_DETACHED) && data) {
    297 	/* We want multipart/signed */
    298 		/* Generate a random boundary */
    299 		RAND_pseudo_bytes((unsigned char *)bound, 32);
    300 		for(i = 0; i < 32; i++) {
    301 			c = bound[i] & 0xf;
    302 			if(c < 10) c += '0';
    303 			else c += 'A' - 10;
    304 			bound[i] = c;
    305 		}
    306 		bound[32] = 0;
    307 		BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol);
    308 		BIO_printf(bio, "Content-Type: multipart/signed;");
    309 		BIO_printf(bio, " protocol=\"%ssignature\";", mime_prefix);
    310 		BIO_puts(bio, " micalg=\"");
    311 		asn1_write_micalg(bio, mdalgs);
    312 		BIO_printf(bio, "\"; boundary=\"----%s\"%s%s",
    313 						bound, mime_eol, mime_eol);
    314 		BIO_printf(bio, "This is an S/MIME signed message%s%s",
    315 						mime_eol, mime_eol);
    316 		/* Now write out the first part */
    317 		BIO_printf(bio, "------%s%s", bound, mime_eol);
    318 		if (!asn1_output_data(bio, data, val, flags, it))
    319 			return 0;
    320 		BIO_printf(bio, "%s------%s%s", mime_eol, bound, mime_eol);
    321 
    322 		/* Headers for signature */
    323 
    324 		BIO_printf(bio, "Content-Type: %ssignature;", mime_prefix);
    325 		BIO_printf(bio, " name=\"smime.p7s\"%s", mime_eol);
    326 		BIO_printf(bio, "Content-Transfer-Encoding: base64%s",
    327 								mime_eol);
    328 		BIO_printf(bio, "Content-Disposition: attachment;");
    329 		BIO_printf(bio, " filename=\"smime.p7s\"%s%s",
    330 							mime_eol, mime_eol);
    331 		B64_write_ASN1(bio, val, NULL, 0, it);
    332 		BIO_printf(bio,"%s------%s--%s%s", mime_eol, bound,
    333 							mime_eol, mime_eol);
    334 		return 1;
    335 	}
    336 
    337 	/* Determine smime-type header */
    338 
    339 	if (ctype_nid == NID_pkcs7_enveloped)
    340 		msg_type = "enveloped-data";
    341 	else if (ctype_nid == NID_pkcs7_signed)
    342 		{
    343 		if (econt_nid == NID_id_smime_ct_receipt)
    344 			msg_type = "signed-receipt";
    345 		else if (sk_X509_ALGOR_num(mdalgs) >= 0)
    346 			msg_type = "signed-data";
    347 		else
    348 			msg_type = "certs-only";
    349 		}
    350 	else if (ctype_nid == NID_id_smime_ct_compressedData)
    351 		{
    352 		msg_type = "compressed-data";
    353 		cname = "smime.p7z";
    354 		}
    355 	/* MIME headers */
    356 	BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol);
    357 	BIO_printf(bio, "Content-Disposition: attachment;");
    358 	BIO_printf(bio, " filename=\"%s\"%s", cname, mime_eol);
    359 	BIO_printf(bio, "Content-Type: %smime;", mime_prefix);
    360 	if (msg_type)
    361 		BIO_printf(bio, " smime-type=%s;", msg_type);
    362 	BIO_printf(bio, " name=\"%s\"%s", cname, mime_eol);
    363 	BIO_printf(bio, "Content-Transfer-Encoding: base64%s%s",
    364 						mime_eol, mime_eol);
    365 	if (!B64_write_ASN1(bio, val, data, flags, it))
    366 		return 0;
    367 	BIO_printf(bio, "%s", mime_eol);
    368 	return 1;
    369 }
    370 
    371 /* Handle output of ASN1 data */
    372 
    373 
    374 static int asn1_output_data(BIO *out, BIO *data, ASN1_VALUE *val, int flags,
    375 					const ASN1_ITEM *it)
    376 	{
    377 	BIO *tmpbio;
    378 	const ASN1_AUX *aux = it->funcs;
    379 	ASN1_STREAM_ARG sarg;
    380 
    381 	if (!(flags & SMIME_DETACHED))
    382 		{
    383 		SMIME_crlf_copy(data, out, flags);
    384 		return 1;
    385 		}
    386 
    387 	if (!aux || !aux->asn1_cb)
    388 		{
    389 		ASN1err(ASN1_F_ASN1_OUTPUT_DATA,
    390 					ASN1_R_STREAMING_NOT_SUPPORTED);
    391 		return 0;
    392 		}
    393 
    394 	sarg.out = out;
    395 	sarg.ndef_bio = NULL;
    396 	sarg.boundary = NULL;
    397 
    398 	/* Let ASN1 code prepend any needed BIOs */
    399 
    400 	if (aux->asn1_cb(ASN1_OP_DETACHED_PRE, &val, it, &sarg) <= 0)
    401 		return 0;
    402 
    403 	/* Copy data across, passing through filter BIOs for processing */
    404 	SMIME_crlf_copy(data, sarg.ndef_bio, flags);
    405 
    406 	/* Finalize structure */
    407 	if (aux->asn1_cb(ASN1_OP_DETACHED_POST, &val, it, &sarg) <= 0)
    408 		return 0;
    409 
    410 	/* Now remove any digests prepended to the BIO */
    411 
    412 	while (sarg.ndef_bio != out)
    413 		{
    414 		tmpbio = BIO_pop(sarg.ndef_bio);
    415 		BIO_free(sarg.ndef_bio);
    416 		sarg.ndef_bio = tmpbio;
    417 		}
    418 
    419 	return 1;
    420 
    421 	}
    422 
    423 /* SMIME reader: handle multipart/signed and opaque signing.
    424  * in multipart case the content is placed in a memory BIO
    425  * pointed to by "bcont". In opaque this is set to NULL
    426  */
    427 
    428 ASN1_VALUE *SMIME_read_ASN1(BIO *bio, BIO **bcont, const ASN1_ITEM *it)
    429 {
    430 	BIO *asnin;
    431 	STACK_OF(MIME_HEADER) *headers = NULL;
    432 	STACK_OF(BIO) *parts = NULL;
    433 	MIME_HEADER *hdr;
    434 	MIME_PARAM *prm;
    435 	ASN1_VALUE *val;
    436 	int ret;
    437 
    438 	if(bcont) *bcont = NULL;
    439 
    440 	if (!(headers = mime_parse_hdr(bio))) {
    441 		ASN1err(ASN1_F_SMIME_READ_ASN1,ASN1_R_MIME_PARSE_ERROR);
    442 		return NULL;
    443 	}
    444 
    445 	if(!(hdr = mime_hdr_find(headers, "content-type")) || !hdr->value) {
    446 		sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
    447 		ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_CONTENT_TYPE);
    448 		return NULL;
    449 	}
    450 
    451 	/* Handle multipart/signed */
    452 
    453 	if(!strcmp(hdr->value, "multipart/signed")) {
    454 		/* Split into two parts */
    455 		prm = mime_param_find(hdr, "boundary");
    456 		if(!prm || !prm->param_value) {
    457 			sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
    458 			ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_MULTIPART_BOUNDARY);
    459 			return NULL;
    460 		}
    461 		ret = multi_split(bio, prm->param_value, &parts);
    462 		sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
    463 		if(!ret || (sk_BIO_num(parts) != 2) ) {
    464 			ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_MULTIPART_BODY_FAILURE);
    465 			sk_BIO_pop_free(parts, BIO_vfree);
    466 			return NULL;
    467 		}
    468 
    469 		/* Parse the signature piece */
    470 		asnin = sk_BIO_value(parts, 1);
    471 
    472 		if (!(headers = mime_parse_hdr(asnin))) {
    473 			ASN1err(ASN1_F_SMIME_READ_ASN1,ASN1_R_MIME_SIG_PARSE_ERROR);
    474 			sk_BIO_pop_free(parts, BIO_vfree);
    475 			return NULL;
    476 		}
    477 
    478 		/* Get content type */
    479 
    480 		if(!(hdr = mime_hdr_find(headers, "content-type")) ||
    481 								 !hdr->value) {
    482 			sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
    483 			ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_SIG_CONTENT_TYPE);
    484 			return NULL;
    485 		}
    486 
    487 		if(strcmp(hdr->value, "application/x-pkcs7-signature") &&
    488 			strcmp(hdr->value, "application/pkcs7-signature")) {
    489 			sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
    490 			ASN1err(ASN1_F_SMIME_READ_ASN1,ASN1_R_SIG_INVALID_MIME_TYPE);
    491 			ERR_add_error_data(2, "type: ", hdr->value);
    492 			sk_BIO_pop_free(parts, BIO_vfree);
    493 			return NULL;
    494 		}
    495 		sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
    496 		/* Read in ASN1 */
    497 		if(!(val = b64_read_asn1(asnin, it))) {
    498 			ASN1err(ASN1_F_SMIME_READ_ASN1,ASN1_R_ASN1_SIG_PARSE_ERROR);
    499 			sk_BIO_pop_free(parts, BIO_vfree);
    500 			return NULL;
    501 		}
    502 
    503 		if(bcont) {
    504 			*bcont = sk_BIO_value(parts, 0);
    505 			BIO_free(asnin);
    506 			sk_BIO_free(parts);
    507 		} else sk_BIO_pop_free(parts, BIO_vfree);
    508 		return val;
    509 	}
    510 
    511 	/* OK, if not multipart/signed try opaque signature */
    512 
    513 	if (strcmp (hdr->value, "application/x-pkcs7-mime") &&
    514 	    strcmp (hdr->value, "application/pkcs7-mime")) {
    515 		ASN1err(ASN1_F_SMIME_READ_ASN1,ASN1_R_INVALID_MIME_TYPE);
    516 		ERR_add_error_data(2, "type: ", hdr->value);
    517 		sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
    518 		return NULL;
    519 	}
    520 
    521 	sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
    522 
    523 	if(!(val = b64_read_asn1(bio, it))) {
    524 		ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_ASN1_PARSE_ERROR);
    525 		return NULL;
    526 	}
    527 	return val;
    528 
    529 }
    530 
    531 /* Copy text from one BIO to another making the output CRLF at EOL */
    532 int SMIME_crlf_copy(BIO *in, BIO *out, int flags)
    533 {
    534 	BIO *bf;
    535 	char eol;
    536 	int len;
    537 	char linebuf[MAX_SMLEN];
    538 	/* Buffer output so we don't write one line at a time. This is
    539 	 * useful when streaming as we don't end up with one OCTET STRING
    540 	 * per line.
    541 	 */
    542 	bf = BIO_new(BIO_f_buffer());
    543 	if (!bf)
    544 		return 0;
    545 	out = BIO_push(bf, out);
    546 	if(flags & SMIME_BINARY)
    547 		{
    548 		while((len = BIO_read(in, linebuf, MAX_SMLEN)) > 0)
    549 						BIO_write(out, linebuf, len);
    550 		}
    551 	else
    552 		{
    553 		if(flags & SMIME_TEXT)
    554 			BIO_printf(out, "Content-Type: text/plain\r\n\r\n");
    555 		while ((len = BIO_gets(in, linebuf, MAX_SMLEN)) > 0)
    556 			{
    557 			eol = strip_eol(linebuf, &len);
    558 			if (len)
    559 				BIO_write(out, linebuf, len);
    560 			if(eol) BIO_write(out, "\r\n", 2);
    561 			}
    562 		}
    563 	(void)BIO_flush(out);
    564 	BIO_pop(out);
    565 	BIO_free(bf);
    566 	return 1;
    567 }
    568 
    569 /* Strip off headers if they are text/plain */
    570 int SMIME_text(BIO *in, BIO *out)
    571 {
    572 	char iobuf[4096];
    573 	int len;
    574 	STACK_OF(MIME_HEADER) *headers;
    575 	MIME_HEADER *hdr;
    576 
    577 	if (!(headers = mime_parse_hdr(in))) {
    578 		ASN1err(ASN1_F_SMIME_TEXT,ASN1_R_MIME_PARSE_ERROR);
    579 		return 0;
    580 	}
    581 	if(!(hdr = mime_hdr_find(headers, "content-type")) || !hdr->value) {
    582 		ASN1err(ASN1_F_SMIME_TEXT,ASN1_R_MIME_NO_CONTENT_TYPE);
    583 		sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
    584 		return 0;
    585 	}
    586 	if (strcmp (hdr->value, "text/plain")) {
    587 		ASN1err(ASN1_F_SMIME_TEXT,ASN1_R_INVALID_MIME_TYPE);
    588 		ERR_add_error_data(2, "type: ", hdr->value);
    589 		sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
    590 		return 0;
    591 	}
    592 	sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
    593 	while ((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
    594 						BIO_write(out, iobuf, len);
    595 	if (len < 0)
    596 		return 0;
    597 	return 1;
    598 }
    599 
    600 /* Split a multipart/XXX message body into component parts: result is
    601  * canonical parts in a STACK of bios
    602  */
    603 
    604 static int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret)
    605 {
    606 	char linebuf[MAX_SMLEN];
    607 	int len, blen;
    608 	int eol = 0, next_eol = 0;
    609 	BIO *bpart = NULL;
    610 	STACK_OF(BIO) *parts;
    611 	char state, part, first;
    612 
    613 	blen = strlen(bound);
    614 	part = 0;
    615 	state = 0;
    616 	first = 1;
    617 	parts = sk_BIO_new_null();
    618 	*ret = parts;
    619 	while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) {
    620 		state = mime_bound_check(linebuf, len, bound, blen);
    621 		if(state == 1) {
    622 			first = 1;
    623 			part++;
    624 		} else if(state == 2) {
    625 			sk_BIO_push(parts, bpart);
    626 			return 1;
    627 		} else if(part) {
    628 			/* Strip CR+LF from linebuf */
    629 			next_eol = strip_eol(linebuf, &len);
    630 			if(first) {
    631 				first = 0;
    632 				if(bpart) sk_BIO_push(parts, bpart);
    633 				bpart = BIO_new(BIO_s_mem());
    634 				BIO_set_mem_eof_return(bpart, 0);
    635 			} else if (eol)
    636 				BIO_write(bpart, "\r\n", 2);
    637 			eol = next_eol;
    638 			if (len)
    639 				BIO_write(bpart, linebuf, len);
    640 		}
    641 	}
    642 	return 0;
    643 }
    644 
    645 /* This is the big one: parse MIME header lines up to message body */
    646 
    647 #define MIME_INVALID	0
    648 #define MIME_START	1
    649 #define MIME_TYPE	2
    650 #define MIME_NAME	3
    651 #define MIME_VALUE	4
    652 #define MIME_QUOTE	5
    653 #define MIME_COMMENT	6
    654 
    655 
    656 static STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio)
    657 {
    658 	char *p, *q, c;
    659 	char *ntmp;
    660 	char linebuf[MAX_SMLEN];
    661 	MIME_HEADER *mhdr = NULL;
    662 	STACK_OF(MIME_HEADER) *headers;
    663 	int len, state, save_state = 0;
    664 
    665 	headers = sk_MIME_HEADER_new(mime_hdr_cmp);
    666 	while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) {
    667 	/* If whitespace at line start then continuation line */
    668 	if(mhdr && isspace((unsigned char)linebuf[0])) state = MIME_NAME;
    669 	else state = MIME_START;
    670 	ntmp = NULL;
    671 	/* Go through all characters */
    672 	for(p = linebuf, q = linebuf; (c = *p) && (c!='\r') && (c!='\n'); p++) {
    673 
    674 	/* State machine to handle MIME headers
    675 	 * if this looks horrible that's because it *is*
    676          */
    677 
    678 		switch(state) {
    679 			case MIME_START:
    680 			if(c == ':') {
    681 				state = MIME_TYPE;
    682 				*p = 0;
    683 				ntmp = strip_ends(q);
    684 				q = p + 1;
    685 			}
    686 			break;
    687 
    688 			case MIME_TYPE:
    689 			if(c == ';') {
    690 				mime_debug("Found End Value\n");
    691 				*p = 0;
    692 				mhdr = mime_hdr_new(ntmp, strip_ends(q));
    693 				sk_MIME_HEADER_push(headers, mhdr);
    694 				ntmp = NULL;
    695 				q = p + 1;
    696 				state = MIME_NAME;
    697 			} else if(c == '(') {
    698 				save_state = state;
    699 				state = MIME_COMMENT;
    700 			}
    701 			break;
    702 
    703 			case MIME_COMMENT:
    704 			if(c == ')') {
    705 				state = save_state;
    706 			}
    707 			break;
    708 
    709 			case MIME_NAME:
    710 			if(c == '=') {
    711 				state = MIME_VALUE;
    712 				*p = 0;
    713 				ntmp = strip_ends(q);
    714 				q = p + 1;
    715 			}
    716 			break ;
    717 
    718 			case MIME_VALUE:
    719 			if(c == ';') {
    720 				state = MIME_NAME;
    721 				*p = 0;
    722 				mime_hdr_addparam(mhdr, ntmp, strip_ends(q));
    723 				ntmp = NULL;
    724 				q = p + 1;
    725 			} else if (c == '"') {
    726 				mime_debug("Found Quote\n");
    727 				state = MIME_QUOTE;
    728 			} else if(c == '(') {
    729 				save_state = state;
    730 				state = MIME_COMMENT;
    731 			}
    732 			break;
    733 
    734 			case MIME_QUOTE:
    735 			if(c == '"') {
    736 				mime_debug("Found Match Quote\n");
    737 				state = MIME_VALUE;
    738 			}
    739 			break;
    740 		}
    741 	}
    742 
    743 	if(state == MIME_TYPE) {
    744 		mhdr = mime_hdr_new(ntmp, strip_ends(q));
    745 		sk_MIME_HEADER_push(headers, mhdr);
    746 	} else if(state == MIME_VALUE)
    747 			 mime_hdr_addparam(mhdr, ntmp, strip_ends(q));
    748 	if(p == linebuf) break;	/* Blank line means end of headers */
    749 }
    750 
    751 return headers;
    752 
    753 }
    754 
    755 static char *strip_ends(char *name)
    756 {
    757 	return strip_end(strip_start(name));
    758 }
    759 
    760 /* Strip a parameter of whitespace from start of param */
    761 static char *strip_start(char *name)
    762 {
    763 	char *p, c;
    764 	/* Look for first non white space or quote */
    765 	for(p = name; (c = *p) ;p++) {
    766 		if(c == '"') {
    767 			/* Next char is start of string if non null */
    768 			if(p[1]) return p + 1;
    769 			/* Else null string */
    770 			return NULL;
    771 		}
    772 		if(!isspace((unsigned char)c)) return p;
    773 	}
    774 	return NULL;
    775 }
    776 
    777 /* As above but strip from end of string : maybe should handle brackets? */
    778 static char *strip_end(char *name)
    779 {
    780 	char *p, c;
    781 	if(!name) return NULL;
    782 	/* Look for first non white space or quote */
    783 	for(p = name + strlen(name) - 1; p >= name ;p--) {
    784 		c = *p;
    785 		if(c == '"') {
    786 			if(p - 1 == name) return NULL;
    787 			*p = 0;
    788 			return name;
    789 		}
    790 		if(isspace((unsigned char)c)) *p = 0;
    791 		else return name;
    792 	}
    793 	return NULL;
    794 }
    795 
    796 static MIME_HEADER *mime_hdr_new(char *name, char *value)
    797 {
    798 	MIME_HEADER *mhdr;
    799 	char *tmpname, *tmpval, *p;
    800 	int c;
    801 	if(name) {
    802 		if(!(tmpname = BUF_strdup(name))) return NULL;
    803 		for(p = tmpname ; *p; p++) {
    804 			c = *p;
    805 			if(isupper(c)) {
    806 				c = tolower(c);
    807 				*p = c;
    808 			}
    809 		}
    810 	} else tmpname = NULL;
    811 	if(value) {
    812 		if(!(tmpval = BUF_strdup(value))) return NULL;
    813 		for(p = tmpval ; *p; p++) {
    814 			c = *p;
    815 			if(isupper(c)) {
    816 				c = tolower(c);
    817 				*p = c;
    818 			}
    819 		}
    820 	} else tmpval = NULL;
    821 	mhdr = (MIME_HEADER *) OPENSSL_malloc(sizeof(MIME_HEADER));
    822 	if(!mhdr) return NULL;
    823 	mhdr->name = tmpname;
    824 	mhdr->value = tmpval;
    825 	if(!(mhdr->params = sk_MIME_PARAM_new(mime_param_cmp))) return NULL;
    826 	return mhdr;
    827 }
    828 
    829 static int mime_hdr_addparam(MIME_HEADER *mhdr, char *name, char *value)
    830 {
    831 	char *tmpname, *tmpval, *p;
    832 	int c;
    833 	MIME_PARAM *mparam;
    834 	if(name) {
    835 		tmpname = BUF_strdup(name);
    836 		if(!tmpname) return 0;
    837 		for(p = tmpname ; *p; p++) {
    838 			c = *p;
    839 			if(isupper(c)) {
    840 				c = tolower(c);
    841 				*p = c;
    842 			}
    843 		}
    844 	} else tmpname = NULL;
    845 	if(value) {
    846 		tmpval = BUF_strdup(value);
    847 		if(!tmpval) return 0;
    848 	} else tmpval = NULL;
    849 	/* Parameter values are case sensitive so leave as is */
    850 	mparam = (MIME_PARAM *) OPENSSL_malloc(sizeof(MIME_PARAM));
    851 	if(!mparam) return 0;
    852 	mparam->param_name = tmpname;
    853 	mparam->param_value = tmpval;
    854 	sk_MIME_PARAM_push(mhdr->params, mparam);
    855 	return 1;
    856 }
    857 
    858 static int mime_hdr_cmp(const MIME_HEADER * const *a,
    859 			const MIME_HEADER * const *b)
    860 {
    861 	return(strcmp((*a)->name, (*b)->name));
    862 }
    863 
    864 static int mime_param_cmp(const MIME_PARAM * const *a,
    865 			const MIME_PARAM * const *b)
    866 {
    867 	return(strcmp((*a)->param_name, (*b)->param_name));
    868 }
    869 
    870 /* Find a header with a given name (if possible) */
    871 
    872 static MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, char *name)
    873 {
    874 	MIME_HEADER htmp;
    875 	int idx;
    876 	htmp.name = name;
    877 	idx = sk_MIME_HEADER_find(hdrs, &htmp);
    878 	if(idx < 0) return NULL;
    879 	return sk_MIME_HEADER_value(hdrs, idx);
    880 }
    881 
    882 static MIME_PARAM *mime_param_find(MIME_HEADER *hdr, char *name)
    883 {
    884 	MIME_PARAM param;
    885 	int idx;
    886 	param.param_name = name;
    887 	idx = sk_MIME_PARAM_find(hdr->params, &param);
    888 	if(idx < 0) return NULL;
    889 	return sk_MIME_PARAM_value(hdr->params, idx);
    890 }
    891 
    892 static void mime_hdr_free(MIME_HEADER *hdr)
    893 {
    894 	if(hdr->name) OPENSSL_free(hdr->name);
    895 	if(hdr->value) OPENSSL_free(hdr->value);
    896 	if(hdr->params) sk_MIME_PARAM_pop_free(hdr->params, mime_param_free);
    897 	OPENSSL_free(hdr);
    898 }
    899 
    900 static void mime_param_free(MIME_PARAM *param)
    901 {
    902 	if(param->param_name) OPENSSL_free(param->param_name);
    903 	if(param->param_value) OPENSSL_free(param->param_value);
    904 	OPENSSL_free(param);
    905 }
    906 
    907 /* Check for a multipart boundary. Returns:
    908  * 0 : no boundary
    909  * 1 : part boundary
    910  * 2 : final boundary
    911  */
    912 static int mime_bound_check(char *line, int linelen, char *bound, int blen)
    913 {
    914 	if(linelen == -1) linelen = strlen(line);
    915 	if(blen == -1) blen = strlen(bound);
    916 	/* Quickly eliminate if line length too short */
    917 	if(blen + 2 > linelen) return 0;
    918 	/* Check for part boundary */
    919 	if(!strncmp(line, "--", 2) && !strncmp(line + 2, bound, blen)) {
    920 		if(!strncmp(line + blen + 2, "--", 2)) return 2;
    921 		else return 1;
    922 	}
    923 	return 0;
    924 }
    925 
    926 static int strip_eol(char *linebuf, int *plen)
    927 	{
    928 	int len = *plen;
    929 	char *p, c;
    930 	int is_eol = 0;
    931 	p = linebuf + len - 1;
    932 	for (p = linebuf + len - 1; len > 0; len--, p--)
    933 		{
    934 		c = *p;
    935 		if (c == '\n')
    936 			is_eol = 1;
    937 		else if (c != '\r')
    938 			break;
    939 		}
    940 	*plen = len;
    941 	return is_eol;
    942 	}
    943