asn_mime.c revision 269686
160786Sps/* asn_mime.c */
260786Sps/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
360786Sps * project.
460786Sps */
560786Sps/* ====================================================================
660786Sps * Copyright (c) 1999-2008 The OpenSSL Project.  All rights reserved.
760786Sps *
860786Sps * Redistribution and use in source and binary forms, with or without
960786Sps * modification, are permitted provided that the following conditions
1060786Sps * are met:
1160786Sps *
1260786Sps * 1. Redistributions of source code must retain the above copyright
1360786Sps *    notice, this list of conditions and the following disclaimer.
1460786Sps *
1560786Sps * 2. Redistributions in binary form must reproduce the above copyright
1660786Sps *    notice, this list of conditions and the following disclaimer in
1760786Sps *    the documentation and/or other materials provided with the
1860786Sps *    distribution.
1960786Sps *
2060786Sps * 3. All advertising materials mentioning features or use of this
2160786Sps *    software must display the following acknowledgment:
2260786Sps *    "This product includes software developed by the OpenSSL Project
2360786Sps *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
2460786Sps *
2560786Sps * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
2660786Sps *    endorse or promote products derived from this software without
2760786Sps *    prior written permission. For written permission, please contact
2860786Sps *    licensing@OpenSSL.org.
2960786Sps *
3060786Sps * 5. Products derived from this software may not be called "OpenSSL"
3160786Sps *    nor may "OpenSSL" appear in their names without prior written
3260786Sps *    permission of the OpenSSL Project.
3360786Sps *
3460786Sps * 6. Redistributions of any form whatsoever must retain the following
3560786Sps *    acknowledgment:
3660786Sps *    "This product includes software developed by the OpenSSL Project
3760786Sps *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
3860786Sps *
3960786Sps * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
4060786Sps * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4160786Sps * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
4260786Sps * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
4360786Sps * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
4460786Sps * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
4560786Sps * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
4660786Sps * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4760786Sps * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
4860786Sps * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
4960786Sps * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
5060786Sps * OF THE POSSIBILITY OF SUCH DAMAGE.
5160786Sps * ====================================================================
5260786Sps *
5360786Sps */
5460786Sps
5560786Sps#include <stdio.h>
5660786Sps#include <ctype.h>
5760786Sps#include "cryptlib.h"
5860786Sps#include <openssl/rand.h>
5960786Sps#include <openssl/x509.h>
6060786Sps#include <openssl/asn1.h>
6160786Sps#include <openssl/asn1t.h>
6260786Sps#include "asn1_locl.h"
6360786Sps
6460786Sps/* Generalised MIME like utilities for streaming ASN1. Although many
6560786Sps * have a PKCS7/CMS like flavour others are more general purpose.
6660786Sps */
6760786Sps
6860786Sps/* MIME format structures
6960786Sps * Note that all are translated to lower case apart from
7060786Sps * parameter values. Quotes are stripped off
7160786Sps */
7260786Sps
7360786Spstypedef struct {
7460786Spschar *param_name;			/* Param name e.g. "micalg" */
7560786Spschar *param_value;			/* Param value e.g. "sha1" */
7660786Sps} MIME_PARAM;
7760786Sps
7860786SpsDECLARE_STACK_OF(MIME_PARAM)
7960786SpsIMPLEMENT_STACK_OF(MIME_PARAM)
8060786Sps
8160786Spstypedef struct {
8260786Spschar *name;				/* Name of line e.g. "content-type" */
8360786Spschar *value;				/* Value of line e.g. "text/plain" */
8460786SpsSTACK_OF(MIME_PARAM) *params;		/* Zero or more parameters */
8560786Sps} MIME_HEADER;
8660786Sps
8760786SpsDECLARE_STACK_OF(MIME_HEADER)
8860786SpsIMPLEMENT_STACK_OF(MIME_HEADER)
8960786Sps
9060786Spsstatic int asn1_output_data(BIO *out, BIO *data, ASN1_VALUE *val, int flags,
9160786Sps					const ASN1_ITEM *it);
9260786Spsstatic char * strip_ends(char *name);
9360786Spsstatic char * strip_start(char *name);
9460786Spsstatic char * strip_end(char *name);
9560786Spsstatic MIME_HEADER *mime_hdr_new(char *name, char *value);
9660786Spsstatic int mime_hdr_addparam(MIME_HEADER *mhdr, char *name, char *value);
9760786Spsstatic STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio);
9860786Spsstatic int mime_hdr_cmp(const MIME_HEADER * const *a,
9960786Sps			const MIME_HEADER * const *b);
10060786Spsstatic int mime_param_cmp(const MIME_PARAM * const *a,
10160786Sps			const MIME_PARAM * const *b);
10260786Spsstatic void mime_param_free(MIME_PARAM *param);
10360786Spsstatic int mime_bound_check(char *line, int linelen, char *bound, int blen);
10460786Spsstatic int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret);
10560786Spsstatic int strip_eol(char *linebuf, int *plen);
10660786Spsstatic MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, char *name);
10760786Spsstatic MIME_PARAM *mime_param_find(MIME_HEADER *hdr, char *name);
10860786Spsstatic void mime_hdr_free(MIME_HEADER *hdr);
10960786Sps
11060786Sps#define MAX_SMLEN 1024
11160786Sps#define mime_debug(x) /* x */
11260786Sps
11360786Sps/* Output an ASN1 structure in BER format streaming if necessary */
11460786Sps
11560786Spsint i2d_ASN1_bio_stream(BIO *out, ASN1_VALUE *val, BIO *in, int flags,
11660786Sps				const ASN1_ITEM *it)
11760786Sps	{
11860786Sps	/* If streaming create stream BIO and copy all content through it */
11960786Sps	if (flags & SMIME_STREAM)
12060786Sps		{
12160786Sps		BIO *bio, *tbio;
12260786Sps		bio = BIO_new_NDEF(out, val, it);
12360786Sps		if (!bio)
12460786Sps			{
12560786Sps			ASN1err(ASN1_F_I2D_ASN1_BIO_STREAM,ERR_R_MALLOC_FAILURE);
12660786Sps			return 0;
12760786Sps			}
12860786Sps		SMIME_crlf_copy(in, bio, flags);
12960786Sps		(void)BIO_flush(bio);
13060786Sps		/* Free up successive BIOs until we hit the old output BIO */
13160786Sps		do
13260786Sps			{
13360786Sps			tbio = BIO_pop(bio);
13460786Sps			BIO_free(bio);
13560786Sps			bio = tbio;
13660786Sps			} while (bio != out);
13760786Sps		}
13860786Sps	/* else just write out ASN1 structure which will have all content
13960786Sps	 * stored internally
14060786Sps	 */
14160786Sps	else
14260786Sps		ASN1_item_i2d_bio(it, out, val);
14360786Sps	return 1;
14460786Sps	}
14560786Sps
14660786Sps/* Base 64 read and write of ASN1 structure */
14760786Sps
14860786Spsstatic int B64_write_ASN1(BIO *out, ASN1_VALUE *val, BIO *in, int flags,
14960786Sps				const ASN1_ITEM *it)
15060786Sps	{
15160786Sps	BIO *b64;
15260786Sps	int r;
15360786Sps	b64 = BIO_new(BIO_f_base64());
15460786Sps	if(!b64)
15560786Sps		{
15660786Sps		ASN1err(ASN1_F_B64_WRITE_ASN1,ERR_R_MALLOC_FAILURE);
15760786Sps		return 0;
15860786Sps		}
15960786Sps	/* prepend the b64 BIO so all data is base64 encoded.
16060786Sps	 */
16160786Sps	out = BIO_push(b64, out);
16260786Sps	r = i2d_ASN1_bio_stream(out, val, in, flags, it);
16360786Sps	(void)BIO_flush(out);
16460786Sps	BIO_pop(out);
16560786Sps	BIO_free(b64);
16660786Sps	return r;
16760786Sps	}
16860786Sps
16960786Sps/* Streaming ASN1 PEM write */
17060786Sps
17160786Spsint PEM_write_bio_ASN1_stream(BIO *out, ASN1_VALUE *val, BIO *in, int flags,
17260786Sps				const char *hdr,
17360786Sps				const ASN1_ITEM *it)
17460786Sps	{
17560786Sps	int r;
17660786Sps	BIO_printf(out, "-----BEGIN %s-----\n", hdr);
17760786Sps	r = B64_write_ASN1(out, val, in, flags, it);
17860786Sps	BIO_printf(out, "-----END %s-----\n", hdr);
17960786Sps	return r;
18060786Sps	}
18160786Sps
18260786Spsstatic ASN1_VALUE *b64_read_asn1(BIO *bio, const ASN1_ITEM *it)
18360786Sps{
18460786Sps	BIO *b64;
18560786Sps	ASN1_VALUE *val;
18660786Sps	if(!(b64 = BIO_new(BIO_f_base64()))) {
18760786Sps		ASN1err(ASN1_F_B64_READ_ASN1,ERR_R_MALLOC_FAILURE);
18860786Sps		return 0;
18960786Sps	}
19060786Sps	bio = BIO_push(b64, bio);
19160786Sps	val = ASN1_item_d2i_bio(it, bio, NULL);
19260786Sps	if(!val)
19360786Sps		ASN1err(ASN1_F_B64_READ_ASN1,ASN1_R_DECODE_ERROR);
19460786Sps	(void)BIO_flush(bio);
19560786Sps	bio = BIO_pop(bio);
19660786Sps	BIO_free(b64);
19760786Sps	return val;
19860786Sps}
19960786Sps
20060786Sps/* Generate the MIME "micalg" parameter from RFC3851, RFC4490 */
20160786Sps
20260786Spsstatic int asn1_write_micalg(BIO *out, STACK_OF(X509_ALGOR) *mdalgs)
20360786Sps	{
20460786Sps	const EVP_MD *md;
20560786Sps	int i, have_unknown = 0, write_comma, ret = 0, md_nid;
20660786Sps	have_unknown = 0;
20760786Sps	write_comma = 0;
20860786Sps	for (i = 0; i < sk_X509_ALGOR_num(mdalgs); i++)
20960786Sps		{
21060786Sps		if (write_comma)
21160786Sps			BIO_write(out, ",", 1);
21260786Sps		write_comma = 1;
21360786Sps		md_nid = OBJ_obj2nid(sk_X509_ALGOR_value(mdalgs, i)->algorithm);
21460786Sps		md = EVP_get_digestbynid(md_nid);
21560786Sps		if (md && md->md_ctrl)
21660786Sps			{
21760786Sps			int rv;
21860786Sps			char *micstr;
21960786Sps			rv = md->md_ctrl(NULL, EVP_MD_CTRL_MICALG, 0, &micstr);
22060786Sps			if (rv > 0)
22160786Sps				{
22260786Sps				BIO_puts(out, micstr);
22360786Sps				OPENSSL_free(micstr);
22460786Sps				continue;
22560786Sps				}
22660786Sps			if (rv != -2)
22760786Sps				goto err;
22860786Sps			}
22960786Sps		switch(md_nid)
23060786Sps			{
23160786Sps			case NID_sha1:
23260786Sps			BIO_puts(out, "sha1");
23360786Sps			break;
23460786Sps
23560786Sps			case NID_md5:
23660786Sps			BIO_puts(out, "md5");
23760786Sps			break;
23860786Sps
23960786Sps			case NID_sha256:
24060786Sps			BIO_puts(out, "sha-256");
24160786Sps			break;
24260786Sps
24360786Sps			case NID_sha384:
24460786Sps			BIO_puts(out, "sha-384");
24560786Sps			break;
24660786Sps
24760786Sps			case NID_sha512:
24860786Sps			BIO_puts(out, "sha-512");
24960786Sps			break;
25060786Sps
25160786Sps			case NID_id_GostR3411_94:
25260786Sps			BIO_puts(out, "gostr3411-94");
25360786Sps				goto err;
25460786Sps			break;
25560786Sps
25660786Sps			default:
25760786Sps			if (have_unknown)
25860786Sps				write_comma = 0;
25960786Sps			else
26060786Sps				{
26160786Sps				BIO_puts(out, "unknown");
26260786Sps				have_unknown = 1;
26360786Sps				}
26460786Sps			break;
26560786Sps
26660786Sps			}
26760786Sps		}
26860786Sps
26960786Sps	ret = 1;
27060786Sps	err:
27160786Sps
27260786Sps	return ret;
27360786Sps
27460786Sps	}
27560786Sps
27660786Sps/* SMIME sender */
27760786Sps
27860786Spsint SMIME_write_ASN1(BIO *bio, ASN1_VALUE *val, BIO *data, int flags,
27960786Sps				int ctype_nid, int econt_nid,
28060786Sps				STACK_OF(X509_ALGOR) *mdalgs,
28160786Sps				const ASN1_ITEM *it)
28260786Sps{
28360786Sps	char bound[33], c;
28460786Sps	int i;
28560786Sps	const char *mime_prefix, *mime_eol, *cname = "smime.p7m";
28660786Sps	const char *msg_type=NULL;
28760786Sps	if (flags & SMIME_OLDMIME)
28860786Sps		mime_prefix = "application/x-pkcs7-";
28960786Sps	else
29060786Sps		mime_prefix = "application/pkcs7-";
29160786Sps
29260786Sps	if (flags & SMIME_CRLFEOL)
29360786Sps		mime_eol = "\r\n";
29460786Sps	else
29560786Sps		mime_eol = "\n";
29660786Sps	if((flags & SMIME_DETACHED) && data) {
29760786Sps	/* We want multipart/signed */
29860786Sps		/* Generate a random boundary */
29960786Sps		RAND_pseudo_bytes((unsigned char *)bound, 32);
30060786Sps		for(i = 0; i < 32; i++) {
30160786Sps			c = bound[i] & 0xf;
30260786Sps			if(c < 10) c += '0';
30360786Sps			else c += 'A' - 10;
30460786Sps			bound[i] = c;
30560786Sps		}
30660786Sps		bound[32] = 0;
30760786Sps		BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol);
30860786Sps		BIO_printf(bio, "Content-Type: multipart/signed;");
30960786Sps		BIO_printf(bio, " protocol=\"%ssignature\";", mime_prefix);
31060786Sps		BIO_puts(bio, " micalg=\"");
31160786Sps		asn1_write_micalg(bio, mdalgs);
31260786Sps		BIO_printf(bio, "\"; boundary=\"----%s\"%s%s",
31360786Sps						bound, mime_eol, mime_eol);
31460786Sps		BIO_printf(bio, "This is an S/MIME signed message%s%s",
31560786Sps						mime_eol, mime_eol);
31660786Sps		/* Now write out the first part */
31760786Sps		BIO_printf(bio, "------%s%s", bound, mime_eol);
31860786Sps		if (!asn1_output_data(bio, data, val, flags, it))
31960786Sps			return 0;
32060786Sps		BIO_printf(bio, "%s------%s%s", mime_eol, bound, mime_eol);
32160786Sps
32260786Sps		/* Headers for signature */
32360786Sps
32460786Sps		BIO_printf(bio, "Content-Type: %ssignature;", mime_prefix);
32560786Sps		BIO_printf(bio, " name=\"smime.p7s\"%s", mime_eol);
32660786Sps		BIO_printf(bio, "Content-Transfer-Encoding: base64%s",
32760786Sps								mime_eol);
32860786Sps		BIO_printf(bio, "Content-Disposition: attachment;");
32960786Sps		BIO_printf(bio, " filename=\"smime.p7s\"%s%s",
33060786Sps							mime_eol, mime_eol);
33160786Sps		B64_write_ASN1(bio, val, NULL, 0, it);
33260786Sps		BIO_printf(bio,"%s------%s--%s%s", mime_eol, bound,
33360786Sps							mime_eol, mime_eol);
33460786Sps		return 1;
33560786Sps	}
33660786Sps
33760786Sps	/* Determine smime-type header */
33860786Sps
33960786Sps	if (ctype_nid == NID_pkcs7_enveloped)
34060786Sps		msg_type = "enveloped-data";
34160786Sps	else if (ctype_nid == NID_pkcs7_signed)
34260786Sps		{
34360786Sps		if (econt_nid == NID_id_smime_ct_receipt)
34460786Sps			msg_type = "signed-receipt";
34560786Sps		else if (sk_X509_ALGOR_num(mdalgs) >= 0)
34660786Sps			msg_type = "signed-data";
34760786Sps		else
34860786Sps			msg_type = "certs-only";
34960786Sps		}
35060786Sps	else if (ctype_nid == NID_id_smime_ct_compressedData)
35160786Sps		{
35260786Sps		msg_type = "compressed-data";
35360786Sps		cname = "smime.p7z";
35460786Sps		}
35560786Sps	/* MIME headers */
35660786Sps	BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol);
35760786Sps	BIO_printf(bio, "Content-Disposition: attachment;");
35860786Sps	BIO_printf(bio, " filename=\"%s\"%s", cname, mime_eol);
35960786Sps	BIO_printf(bio, "Content-Type: %smime;", mime_prefix);
36060786Sps	if (msg_type)
36160786Sps		BIO_printf(bio, " smime-type=%s;", msg_type);
36260786Sps	BIO_printf(bio, " name=\"%s\"%s", cname, mime_eol);
36360786Sps	BIO_printf(bio, "Content-Transfer-Encoding: base64%s%s",
36460786Sps						mime_eol, mime_eol);
36560786Sps	if (!B64_write_ASN1(bio, val, data, flags, it))
36660786Sps		return 0;
36760786Sps	BIO_printf(bio, "%s", mime_eol);
36860786Sps	return 1;
36960786Sps}
37060786Sps
37160786Sps/* Handle output of ASN1 data */
37260786Sps
37360786Sps
37460786Spsstatic int asn1_output_data(BIO *out, BIO *data, ASN1_VALUE *val, int flags,
37560786Sps					const ASN1_ITEM *it)
37660786Sps	{
37760786Sps	BIO *tmpbio;
37860786Sps	const ASN1_AUX *aux = it->funcs;
37960786Sps	ASN1_STREAM_ARG sarg;
38060786Sps	int rv = 1;
38160786Sps
38260786Sps	/* If data is not deteched or resigning then the output BIO is
38360786Sps	 * already set up to finalise when it is written through.
38460786Sps	 */
38560786Sps	if (!(flags & SMIME_DETACHED) || (flags & PKCS7_REUSE_DIGEST))
38660786Sps		{
38760786Sps		SMIME_crlf_copy(data, out, flags);
38860786Sps		return 1;
38960786Sps		}
39060786Sps
39160786Sps	if (!aux || !aux->asn1_cb)
39260786Sps		{
39360786Sps		ASN1err(ASN1_F_ASN1_OUTPUT_DATA,
39460786Sps					ASN1_R_STREAMING_NOT_SUPPORTED);
39560786Sps		return 0;
39660786Sps		}
39760786Sps
39860786Sps	sarg.out = out;
39960786Sps	sarg.ndef_bio = NULL;
40060786Sps	sarg.boundary = NULL;
40160786Sps
40260786Sps	/* Let ASN1 code prepend any needed BIOs */
40360786Sps
40460786Sps	if (aux->asn1_cb(ASN1_OP_DETACHED_PRE, &val, it, &sarg) <= 0)
40560786Sps		return 0;
40660786Sps
40760786Sps	/* Copy data across, passing through filter BIOs for processing */
40860786Sps	SMIME_crlf_copy(data, sarg.ndef_bio, flags);
40960786Sps
41060786Sps	/* Finalize structure */
41160786Sps	if (aux->asn1_cb(ASN1_OP_DETACHED_POST, &val, it, &sarg) <= 0)
41260786Sps		rv = 0;
41360786Sps
41460786Sps	/* Now remove any digests prepended to the BIO */
41560786Sps
41660786Sps	while (sarg.ndef_bio != out)
41760786Sps		{
41860786Sps		tmpbio = BIO_pop(sarg.ndef_bio);
41960786Sps		BIO_free(sarg.ndef_bio);
42060786Sps		sarg.ndef_bio = tmpbio;
42160786Sps		}
42260786Sps
42360786Sps	return rv;
42460786Sps
42560786Sps	}
42660786Sps
42760786Sps/* SMIME reader: handle multipart/signed and opaque signing.
42860786Sps * in multipart case the content is placed in a memory BIO
42960786Sps * pointed to by "bcont". In opaque this is set to NULL
43060786Sps */
43160786Sps
43260786SpsASN1_VALUE *SMIME_read_ASN1(BIO *bio, BIO **bcont, const ASN1_ITEM *it)
43360786Sps{
43460786Sps	BIO *asnin;
43560786Sps	STACK_OF(MIME_HEADER) *headers = NULL;
43660786Sps	STACK_OF(BIO) *parts = NULL;
43760786Sps	MIME_HEADER *hdr;
43860786Sps	MIME_PARAM *prm;
43960786Sps	ASN1_VALUE *val;
44060786Sps	int ret;
44160786Sps
44260786Sps	if(bcont) *bcont = NULL;
44360786Sps
44460786Sps	if (!(headers = mime_parse_hdr(bio))) {
44560786Sps		ASN1err(ASN1_F_SMIME_READ_ASN1,ASN1_R_MIME_PARSE_ERROR);
44660786Sps		return NULL;
44760786Sps	}
44860786Sps
44960786Sps	if(!(hdr = mime_hdr_find(headers, "content-type")) || !hdr->value) {
45060786Sps		sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
45160786Sps		ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_CONTENT_TYPE);
45260786Sps		return NULL;
45360786Sps	}
45460786Sps
45560786Sps	/* Handle multipart/signed */
45660786Sps
45760786Sps	if(!strcmp(hdr->value, "multipart/signed")) {
45860786Sps		/* Split into two parts */
45960786Sps		prm = mime_param_find(hdr, "boundary");
46060786Sps		if(!prm || !prm->param_value) {
46160786Sps			sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
46260786Sps			ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_MULTIPART_BOUNDARY);
46360786Sps			return NULL;
46460786Sps		}
46560786Sps		ret = multi_split(bio, prm->param_value, &parts);
46660786Sps		sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
46760786Sps		if(!ret || (sk_BIO_num(parts) != 2) ) {
46860786Sps			ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_MULTIPART_BODY_FAILURE);
46960786Sps			sk_BIO_pop_free(parts, BIO_vfree);
47060786Sps			return NULL;
47160786Sps		}
47260786Sps
47360786Sps		/* Parse the signature piece */
47460786Sps		asnin = sk_BIO_value(parts, 1);
47560786Sps
47660786Sps		if (!(headers = mime_parse_hdr(asnin))) {
47760786Sps			ASN1err(ASN1_F_SMIME_READ_ASN1,ASN1_R_MIME_SIG_PARSE_ERROR);
47860786Sps			sk_BIO_pop_free(parts, BIO_vfree);
47960786Sps			return NULL;
48060786Sps		}
48160786Sps
48260786Sps		/* Get content type */
48360786Sps
48460786Sps		if(!(hdr = mime_hdr_find(headers, "content-type")) ||
48560786Sps								 !hdr->value) {
48660786Sps			sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
48760786Sps			ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_SIG_CONTENT_TYPE);
48860786Sps			return NULL;
48960786Sps		}
49060786Sps
49160786Sps		if(strcmp(hdr->value, "application/x-pkcs7-signature") &&
49260786Sps			strcmp(hdr->value, "application/pkcs7-signature")) {
49360786Sps			ASN1err(ASN1_F_SMIME_READ_ASN1,ASN1_R_SIG_INVALID_MIME_TYPE);
49460786Sps			ERR_add_error_data(2, "type: ", hdr->value);
49560786Sps			sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
49660786Sps			sk_BIO_pop_free(parts, BIO_vfree);
49760786Sps			return NULL;
49860786Sps		}
49960786Sps		sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
50060786Sps		/* Read in ASN1 */
50160786Sps		if(!(val = b64_read_asn1(asnin, it))) {
50260786Sps			ASN1err(ASN1_F_SMIME_READ_ASN1,ASN1_R_ASN1_SIG_PARSE_ERROR);
50360786Sps			sk_BIO_pop_free(parts, BIO_vfree);
50460786Sps			return NULL;
50560786Sps		}
50660786Sps
50760786Sps		if(bcont) {
50860786Sps			*bcont = sk_BIO_value(parts, 0);
50960786Sps			BIO_free(asnin);
51060786Sps			sk_BIO_free(parts);
51160786Sps		} else sk_BIO_pop_free(parts, BIO_vfree);
51260786Sps		return val;
51360786Sps	}
51460786Sps
51560786Sps	/* OK, if not multipart/signed try opaque signature */
51660786Sps
51760786Sps	if (strcmp (hdr->value, "application/x-pkcs7-mime") &&
51860786Sps	    strcmp (hdr->value, "application/pkcs7-mime")) {
51960786Sps		ASN1err(ASN1_F_SMIME_READ_ASN1,ASN1_R_INVALID_MIME_TYPE);
52060786Sps		ERR_add_error_data(2, "type: ", hdr->value);
52160786Sps		sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
52260786Sps		return NULL;
52360786Sps	}
52460786Sps
52560786Sps	sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
52660786Sps
52760786Sps	if(!(val = b64_read_asn1(bio, it))) {
52860786Sps		ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_ASN1_PARSE_ERROR);
52960786Sps		return NULL;
53060786Sps	}
53160786Sps	return val;
53260786Sps
53360786Sps}
53460786Sps
53560786Sps/* Copy text from one BIO to another making the output CRLF at EOL */
53660786Spsint SMIME_crlf_copy(BIO *in, BIO *out, int flags)
53760786Sps{
53860786Sps	BIO *bf;
53960786Sps	char eol;
54060786Sps	int len;
54160786Sps	char linebuf[MAX_SMLEN];
54260786Sps	/* Buffer output so we don't write one line at a time. This is
54360786Sps	 * useful when streaming as we don't end up with one OCTET STRING
54460786Sps	 * per line.
54560786Sps	 */
54660786Sps	bf = BIO_new(BIO_f_buffer());
54760786Sps	if (!bf)
54860786Sps		return 0;
54960786Sps	out = BIO_push(bf, out);
55060786Sps	if(flags & SMIME_BINARY)
55160786Sps		{
55260786Sps		while((len = BIO_read(in, linebuf, MAX_SMLEN)) > 0)
55360786Sps						BIO_write(out, linebuf, len);
55460786Sps		}
55560786Sps	else
55660786Sps		{
55760786Sps		if(flags & SMIME_TEXT)
55860786Sps			BIO_printf(out, "Content-Type: text/plain\r\n\r\n");
55960786Sps		while ((len = BIO_gets(in, linebuf, MAX_SMLEN)) > 0)
56060786Sps			{
56160786Sps			eol = strip_eol(linebuf, &len);
56260786Sps			if (len)
56360786Sps				BIO_write(out, linebuf, len);
56460786Sps			if(eol) BIO_write(out, "\r\n", 2);
56560786Sps			}
56660786Sps		}
56760786Sps	(void)BIO_flush(out);
56860786Sps	BIO_pop(out);
56960786Sps	BIO_free(bf);
57060786Sps	return 1;
57160786Sps}
57260786Sps
57360786Sps/* Strip off headers if they are text/plain */
57460786Spsint SMIME_text(BIO *in, BIO *out)
57560786Sps{
57660786Sps	char iobuf[4096];
57760786Sps	int len;
57860786Sps	STACK_OF(MIME_HEADER) *headers;
57960786Sps	MIME_HEADER *hdr;
58060786Sps
58160786Sps	if (!(headers = mime_parse_hdr(in))) {
58260786Sps		ASN1err(ASN1_F_SMIME_TEXT,ASN1_R_MIME_PARSE_ERROR);
58360786Sps		return 0;
58460786Sps	}
58560786Sps	if(!(hdr = mime_hdr_find(headers, "content-type")) || !hdr->value) {
58660786Sps		ASN1err(ASN1_F_SMIME_TEXT,ASN1_R_MIME_NO_CONTENT_TYPE);
58760786Sps		sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
58860786Sps		return 0;
58960786Sps	}
59060786Sps	if (strcmp (hdr->value, "text/plain")) {
59160786Sps		ASN1err(ASN1_F_SMIME_TEXT,ASN1_R_INVALID_MIME_TYPE);
59260786Sps		ERR_add_error_data(2, "type: ", hdr->value);
59360786Sps		sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
59460786Sps		return 0;
59560786Sps	}
59660786Sps	sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
59760786Sps	while ((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
59860786Sps						BIO_write(out, iobuf, len);
59960786Sps	if (len < 0)
60060786Sps		return 0;
60160786Sps	return 1;
60260786Sps}
60360786Sps
60460786Sps/* Split a multipart/XXX message body into component parts: result is
60560786Sps * canonical parts in a STACK of bios
60660786Sps */
60760786Sps
60860786Spsstatic int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret)
60960786Sps{
61060786Sps	char linebuf[MAX_SMLEN];
61160786Sps	int len, blen;
61260786Sps	int eol = 0, next_eol = 0;
61360786Sps	BIO *bpart = NULL;
61460786Sps	STACK_OF(BIO) *parts;
61560786Sps	char state, part, first;
61660786Sps
61760786Sps	blen = strlen(bound);
61860786Sps	part = 0;
61960786Sps	state = 0;
62060786Sps	first = 1;
62160786Sps	parts = sk_BIO_new_null();
62260786Sps	*ret = parts;
62360786Sps	while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) {
62460786Sps		state = mime_bound_check(linebuf, len, bound, blen);
62560786Sps		if(state == 1) {
62660786Sps			first = 1;
62760786Sps			part++;
62860786Sps		} else if(state == 2) {
62960786Sps			sk_BIO_push(parts, bpart);
63060786Sps			return 1;
63160786Sps		} else if(part) {
63260786Sps			/* Strip CR+LF from linebuf */
63360786Sps			next_eol = strip_eol(linebuf, &len);
63460786Sps			if(first) {
63560786Sps				first = 0;
63660786Sps				if(bpart) sk_BIO_push(parts, bpart);
63760786Sps				bpart = BIO_new(BIO_s_mem());
63860786Sps				BIO_set_mem_eof_return(bpart, 0);
63960786Sps			} else if (eol)
64060786Sps				BIO_write(bpart, "\r\n", 2);
64160786Sps			eol = next_eol;
64260786Sps			if (len)
64360786Sps				BIO_write(bpart, linebuf, len);
64460786Sps		}
64560786Sps	}
64660786Sps	return 0;
64760786Sps}
64860786Sps
64960786Sps/* This is the big one: parse MIME header lines up to message body */
65060786Sps
65160786Sps#define MIME_INVALID	0
65260786Sps#define MIME_START	1
65360786Sps#define MIME_TYPE	2
65460786Sps#define MIME_NAME	3
65560786Sps#define MIME_VALUE	4
65660786Sps#define MIME_QUOTE	5
65760786Sps#define MIME_COMMENT	6
65860786Sps
65960786Sps
66060786Spsstatic STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio)
66160786Sps{
66260786Sps	char *p, *q, c;
66360786Sps	char *ntmp;
66460786Sps	char linebuf[MAX_SMLEN];
66560786Sps	MIME_HEADER *mhdr = NULL;
66660786Sps	STACK_OF(MIME_HEADER) *headers;
66760786Sps	int len, state, save_state = 0;
66860786Sps
66960786Sps	headers = sk_MIME_HEADER_new(mime_hdr_cmp);
67060786Sps	if (!headers)
67160786Sps		return NULL;
67260786Sps	while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) {
67360786Sps	/* If whitespace at line start then continuation line */
67460786Sps	if(mhdr && isspace((unsigned char)linebuf[0])) state = MIME_NAME;
67560786Sps	else state = MIME_START;
67660786Sps	ntmp = NULL;
67760786Sps	/* Go through all characters */
67860786Sps	for(p = linebuf, q = linebuf; (c = *p) && (c!='\r') && (c!='\n'); p++) {
67960786Sps
68060786Sps	/* State machine to handle MIME headers
68160786Sps	 * if this looks horrible that's because it *is*
68260786Sps         */
68360786Sps
68460786Sps		switch(state) {
68560786Sps			case MIME_START:
68660786Sps			if(c == ':') {
68760786Sps				state = MIME_TYPE;
68860786Sps				*p = 0;
68960786Sps				ntmp = strip_ends(q);
69060786Sps				q = p + 1;
69160786Sps			}
69260786Sps			break;
69360786Sps
69460786Sps			case MIME_TYPE:
69560786Sps			if(c == ';') {
69660786Sps				mime_debug("Found End Value\n");
69760786Sps				*p = 0;
69860786Sps				mhdr = mime_hdr_new(ntmp, strip_ends(q));
69960786Sps				sk_MIME_HEADER_push(headers, mhdr);
70060786Sps				ntmp = NULL;
70160786Sps				q = p + 1;
70260786Sps				state = MIME_NAME;
70360786Sps			} else if(c == '(') {
70460786Sps				save_state = state;
70560786Sps				state = MIME_COMMENT;
70660786Sps			}
70760786Sps			break;
70860786Sps
70960786Sps			case MIME_COMMENT:
71060786Sps			if(c == ')') {
71160786Sps				state = save_state;
71260786Sps			}
71360786Sps			break;
71460786Sps
71560786Sps			case MIME_NAME:
71660786Sps			if(c == '=') {
71760786Sps				state = MIME_VALUE;
71860786Sps				*p = 0;
71960786Sps				ntmp = strip_ends(q);
72060786Sps				q = p + 1;
72160786Sps			}
72260786Sps			break ;
72360786Sps
72460786Sps			case MIME_VALUE:
72560786Sps			if(c == ';') {
72660786Sps				state = MIME_NAME;
72760786Sps				*p = 0;
72860786Sps				mime_hdr_addparam(mhdr, ntmp, strip_ends(q));
72960786Sps				ntmp = NULL;
73060786Sps				q = p + 1;
73160786Sps			} else if (c == '"') {
73260786Sps				mime_debug("Found Quote\n");
73360786Sps				state = MIME_QUOTE;
73460786Sps			} else if(c == '(') {
73560786Sps				save_state = state;
73660786Sps				state = MIME_COMMENT;
73760786Sps			}
73860786Sps			break;
73960786Sps
74060786Sps			case MIME_QUOTE:
74160786Sps			if(c == '"') {
74260786Sps				mime_debug("Found Match Quote\n");
74360786Sps				state = MIME_VALUE;
74460786Sps			}
74560786Sps			break;
74660786Sps		}
74760786Sps	}
74860786Sps
74960786Sps	if(state == MIME_TYPE) {
75060786Sps		mhdr = mime_hdr_new(ntmp, strip_ends(q));
75160786Sps		sk_MIME_HEADER_push(headers, mhdr);
75260786Sps	} else if(state == MIME_VALUE)
75360786Sps			 mime_hdr_addparam(mhdr, ntmp, strip_ends(q));
75460786Sps	if(p == linebuf) break;	/* Blank line means end of headers */
75560786Sps}
75660786Sps
75760786Spsreturn headers;
75860786Sps
75960786Sps}
76060786Sps
76160786Spsstatic char *strip_ends(char *name)
76260786Sps{
76360786Sps	return strip_end(strip_start(name));
76460786Sps}
76560786Sps
76660786Sps/* Strip a parameter of whitespace from start of param */
76760786Spsstatic char *strip_start(char *name)
76860786Sps{
76960786Sps	char *p, c;
77060786Sps	/* Look for first non white space or quote */
77160786Sps	for(p = name; (c = *p) ;p++) {
77260786Sps		if(c == '"') {
77360786Sps			/* Next char is start of string if non null */
77460786Sps			if(p[1]) return p + 1;
77560786Sps			/* Else null string */
77660786Sps			return NULL;
77760786Sps		}
77860786Sps		if(!isspace((unsigned char)c)) return p;
77960786Sps	}
78060786Sps	return NULL;
78160786Sps}
78260786Sps
78360786Sps/* As above but strip from end of string : maybe should handle brackets? */
78460786Spsstatic char *strip_end(char *name)
78560786Sps{
78660786Sps	char *p, c;
78760786Sps	if(!name) return NULL;
78860786Sps	/* Look for first non white space or quote */
78960786Sps	for(p = name + strlen(name) - 1; p >= name ;p--) {
79060786Sps		c = *p;
79160786Sps		if(c == '"') {
79260786Sps			if(p - 1 == name) return NULL;
79360786Sps			*p = 0;
79460786Sps			return name;
79560786Sps		}
79660786Sps		if(isspace((unsigned char)c)) *p = 0;
79760786Sps		else return name;
79860786Sps	}
79960786Sps	return NULL;
80060786Sps}
80160786Sps
80260786Spsstatic MIME_HEADER *mime_hdr_new(char *name, char *value)
80360786Sps{
80460786Sps	MIME_HEADER *mhdr;
80560786Sps	char *tmpname, *tmpval, *p;
80660786Sps	int c;
80760786Sps	if(name) {
80860786Sps		if(!(tmpname = BUF_strdup(name))) return NULL;
80960786Sps		for(p = tmpname ; *p; p++) {
81060786Sps			c = (unsigned char)*p;
81160786Sps			if(isupper(c)) {
81260786Sps				c = tolower(c);
81360786Sps				*p = c;
81460786Sps			}
81560786Sps		}
81660786Sps	} else tmpname = NULL;
81760786Sps	if(value) {
81860786Sps		if(!(tmpval = BUF_strdup(value))) return NULL;
81960786Sps		for(p = tmpval ; *p; p++) {
82060786Sps			c = (unsigned char)*p;
82160786Sps			if(isupper(c)) {
82260786Sps				c = tolower(c);
82360786Sps				*p = c;
82460786Sps			}
82560786Sps		}
82660786Sps	} else tmpval = NULL;
82760786Sps	mhdr = (MIME_HEADER *) OPENSSL_malloc(sizeof(MIME_HEADER));
82860786Sps	if(!mhdr) return NULL;
82960786Sps	mhdr->name = tmpname;
83060786Sps	mhdr->value = tmpval;
83160786Sps	if(!(mhdr->params = sk_MIME_PARAM_new(mime_param_cmp))) return NULL;
83260786Sps	return mhdr;
83360786Sps}
83460786Sps
83560786Spsstatic int mime_hdr_addparam(MIME_HEADER *mhdr, char *name, char *value)
83660786Sps{
83760786Sps	char *tmpname, *tmpval, *p;
83860786Sps	int c;
83960786Sps	MIME_PARAM *mparam;
84060786Sps	if(name) {
84160786Sps		tmpname = BUF_strdup(name);
84260786Sps		if(!tmpname) return 0;
84360786Sps		for(p = tmpname ; *p; p++) {
84460786Sps			c = (unsigned char)*p;
84560786Sps			if(isupper(c)) {
84660786Sps				c = tolower(c);
84760786Sps				*p = c;
84860786Sps			}
84960786Sps		}
85060786Sps	} else tmpname = NULL;
85160786Sps	if(value) {
85260786Sps		tmpval = BUF_strdup(value);
85360786Sps		if(!tmpval) return 0;
85460786Sps	} else tmpval = NULL;
85560786Sps	/* Parameter values are case sensitive so leave as is */
85660786Sps	mparam = (MIME_PARAM *) OPENSSL_malloc(sizeof(MIME_PARAM));
85760786Sps	if(!mparam) return 0;
85860786Sps	mparam->param_name = tmpname;
85960786Sps	mparam->param_value = tmpval;
86060786Sps	sk_MIME_PARAM_push(mhdr->params, mparam);
86160786Sps	return 1;
86260786Sps}
86360786Sps
86460786Spsstatic int mime_hdr_cmp(const MIME_HEADER * const *a,
86560786Sps			const MIME_HEADER * const *b)
86660786Sps{
86760786Sps	if (!(*a)->name || !(*b)->name)
86860786Sps		return !!(*a)->name - !!(*b)->name;
86960786Sps
87060786Sps	return(strcmp((*a)->name, (*b)->name));
87160786Sps}
87260786Sps
87360786Spsstatic int mime_param_cmp(const MIME_PARAM * const *a,
87460786Sps			const MIME_PARAM * const *b)
87560786Sps{
87660786Sps	if (!(*a)->param_name || !(*b)->param_name)
87760786Sps		return !!(*a)->param_name - !!(*b)->param_name;
87860786Sps	return(strcmp((*a)->param_name, (*b)->param_name));
87960786Sps}
88060786Sps
88160786Sps/* Find a header with a given name (if possible) */
88260786Sps
88360786Spsstatic MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, char *name)
88460786Sps{
88560786Sps	MIME_HEADER htmp;
88660786Sps	int idx;
88760786Sps	htmp.name = name;
88860786Sps	idx = sk_MIME_HEADER_find(hdrs, &htmp);
88960786Sps	if(idx < 0) return NULL;
89060786Sps	return sk_MIME_HEADER_value(hdrs, idx);
89160786Sps}
89260786Sps
89360786Spsstatic MIME_PARAM *mime_param_find(MIME_HEADER *hdr, char *name)
89460786Sps{
89560786Sps	MIME_PARAM param;
89660786Sps	int idx;
89760786Sps	param.param_name = name;
89860786Sps	idx = sk_MIME_PARAM_find(hdr->params, &param);
89960786Sps	if(idx < 0) return NULL;
90060786Sps	return sk_MIME_PARAM_value(hdr->params, idx);
90160786Sps}
90260786Sps
90360786Spsstatic void mime_hdr_free(MIME_HEADER *hdr)
90460786Sps{
90560786Sps	if(hdr->name) OPENSSL_free(hdr->name);
90660786Sps	if(hdr->value) OPENSSL_free(hdr->value);
90760786Sps	if(hdr->params) sk_MIME_PARAM_pop_free(hdr->params, mime_param_free);
90860786Sps	OPENSSL_free(hdr);
90960786Sps}
91060786Sps
91160786Spsstatic void mime_param_free(MIME_PARAM *param)
91260786Sps{
91360786Sps	if(param->param_name) OPENSSL_free(param->param_name);
91460786Sps	if(param->param_value) OPENSSL_free(param->param_value);
91560786Sps	OPENSSL_free(param);
91660786Sps}
91760786Sps
91860786Sps/* Check for a multipart boundary. Returns:
91960786Sps * 0 : no boundary
92060786Sps * 1 : part boundary
92160786Sps * 2 : final boundary
92260786Sps */
92360786Spsstatic int mime_bound_check(char *line, int linelen, char *bound, int blen)
92460786Sps{
92560786Sps	if(linelen == -1) linelen = strlen(line);
92660786Sps	if(blen == -1) blen = strlen(bound);
92760786Sps	/* Quickly eliminate if line length too short */
92860786Sps	if(blen + 2 > linelen) return 0;
92960786Sps	/* Check for part boundary */
93060786Sps	if(!strncmp(line, "--", 2) && !strncmp(line + 2, bound, blen)) {
93160786Sps		if(!strncmp(line + blen + 2, "--", 2)) return 2;
93260786Sps		else return 1;
93360786Sps	}
93460786Sps	return 0;
93560786Sps}
93660786Sps
93760786Spsstatic int strip_eol(char *linebuf, int *plen)
93860786Sps	{
93960786Sps	int len = *plen;
94060786Sps	char *p, c;
94160786Sps	int is_eol = 0;
94260786Sps	p = linebuf + len - 1;
94360786Sps	for (p = linebuf + len - 1; len > 0; len--, p--)
94460786Sps		{
94560786Sps		c = *p;
94660786Sps		if (c == '\n')
94760786Sps			is_eol = 1;
94860786Sps		else if (c != '\r')
94960786Sps			break;
95060786Sps		}
95160786Sps	*plen = len;
95260786Sps	return is_eol;
95360786Sps	}
95460786Sps