os/ossrv/ssl/libcrypto/src/crypto/pkcs7/pk7_mime.c
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
sl@0
     1
/* pk7_mime.c */
sl@0
     2
/* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL
sl@0
     3
 * project.
sl@0
     4
 */
sl@0
     5
/* ====================================================================
sl@0
     6
 * Copyright (c) 1999-2005 The OpenSSL Project.  All rights reserved.
sl@0
     7
 *
sl@0
     8
 * Redistribution and use in source and binary forms, with or without
sl@0
     9
 * modification, are permitted provided that the following conditions
sl@0
    10
 * are met:
sl@0
    11
 *
sl@0
    12
 * 1. Redistributions of source code must retain the above copyright
sl@0
    13
 *    notice, this list of conditions and the following disclaimer. 
sl@0
    14
 *
sl@0
    15
 * 2. Redistributions in binary form must reproduce the above copyright
sl@0
    16
 *    notice, this list of conditions and the following disclaimer in
sl@0
    17
 *    the documentation and/or other materials provided with the
sl@0
    18
 *    distribution.
sl@0
    19
 *
sl@0
    20
 * 3. All advertising materials mentioning features or use of this
sl@0
    21
 *    software must display the following acknowledgment:
sl@0
    22
 *    "This product includes software developed by the OpenSSL Project
sl@0
    23
 *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
sl@0
    24
 *
sl@0
    25
 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
sl@0
    26
 *    endorse or promote products derived from this software without
sl@0
    27
 *    prior written permission. For written permission, please contact
sl@0
    28
 *    licensing@OpenSSL.org.
sl@0
    29
 *
sl@0
    30
 * 5. Products derived from this software may not be called "OpenSSL"
sl@0
    31
 *    nor may "OpenSSL" appear in their names without prior written
sl@0
    32
 *    permission of the OpenSSL Project.
sl@0
    33
 *
sl@0
    34
 * 6. Redistributions of any form whatsoever must retain the following
sl@0
    35
 *    acknowledgment:
sl@0
    36
 *    "This product includes software developed by the OpenSSL Project
sl@0
    37
 *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
sl@0
    38
 *
sl@0
    39
 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
sl@0
    40
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
sl@0
    41
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
sl@0
    42
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
sl@0
    43
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
sl@0
    44
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
sl@0
    45
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
sl@0
    46
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
sl@0
    47
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
sl@0
    48
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
sl@0
    49
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
sl@0
    50
 * OF THE POSSIBILITY OF SUCH DAMAGE.
sl@0
    51
 * ====================================================================
sl@0
    52
 *
sl@0
    53
 * This product includes cryptographic software written by Eric Young
sl@0
    54
 * (eay@cryptsoft.com).  This product includes software written by Tim
sl@0
    55
 * Hudson (tjh@cryptsoft.com).
sl@0
    56
 *
sl@0
    57
 */
sl@0
    58
/*
sl@0
    59
 © Portions copyright (c) 2006 Nokia Corporation.  All rights reserved.
sl@0
    60
 */
sl@0
    61
#include <stdio.h>
sl@0
    62
#include <ctype.h>
sl@0
    63
#include "cryptlib.h"
sl@0
    64
#include <openssl/rand.h>
sl@0
    65
#include <openssl/x509.h>
sl@0
    66
sl@0
    67
/* MIME and related routines */
sl@0
    68
sl@0
    69
/* MIME format structures
sl@0
    70
 * Note that all are translated to lower case apart from
sl@0
    71
 * parameter values. Quotes are stripped off
sl@0
    72
 */
sl@0
    73
sl@0
    74
typedef struct {
sl@0
    75
char *param_name;			/* Param name e.g. "micalg" */
sl@0
    76
char *param_value;			/* Param value e.g. "sha1" */
sl@0
    77
} MIME_PARAM;
sl@0
    78
sl@0
    79
DECLARE_STACK_OF(MIME_PARAM)
sl@0
    80
IMPLEMENT_STACK_OF(MIME_PARAM)
sl@0
    81
sl@0
    82
typedef struct {
sl@0
    83
char *name;				/* Name of line e.g. "content-type" */
sl@0
    84
char *value;				/* Value of line e.g. "text/plain" */
sl@0
    85
STACK_OF(MIME_PARAM) *params;		/* Zero or more parameters */
sl@0
    86
} MIME_HEADER;
sl@0
    87
sl@0
    88
DECLARE_STACK_OF(MIME_HEADER)
sl@0
    89
IMPLEMENT_STACK_OF(MIME_HEADER)
sl@0
    90
sl@0
    91
static int pkcs7_output_data(BIO *bio, BIO *data, PKCS7 *p7, int flags);
sl@0
    92
static int B64_write_PKCS7(BIO *bio, PKCS7 *p7);
sl@0
    93
static PKCS7 *B64_read_PKCS7(BIO *bio);
sl@0
    94
static char * strip_ends(char *name);
sl@0
    95
static char * strip_start(char *name);
sl@0
    96
static char * strip_end(char *name);
sl@0
    97
static MIME_HEADER *mime_hdr_new(char *name, char *value);
sl@0
    98
static int mime_hdr_addparam(MIME_HEADER *mhdr, char *name, char *value);
sl@0
    99
static STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio);
sl@0
   100
static int mime_hdr_cmp(const MIME_HEADER * const *a,
sl@0
   101
			const MIME_HEADER * const *b);
sl@0
   102
static int mime_param_cmp(const MIME_PARAM * const *a,
sl@0
   103
			const MIME_PARAM * const *b);
sl@0
   104
static void mime_param_free(MIME_PARAM *param);
sl@0
   105
static int mime_bound_check(char *line, int linelen, char *bound, int blen);
sl@0
   106
static int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret);
sl@0
   107
static int strip_eol(char *linebuf, int *plen);
sl@0
   108
static MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, char *name);
sl@0
   109
static MIME_PARAM *mime_param_find(MIME_HEADER *hdr, char *name);
sl@0
   110
static void mime_hdr_free(MIME_HEADER *hdr);
sl@0
   111
sl@0
   112
#define MAX_SMLEN 1024
sl@0
   113
#define mime_debug(x) /* x */
sl@0
   114
sl@0
   115
/* Base 64 read and write of PKCS#7 structure */
sl@0
   116
sl@0
   117
static int B64_write_PKCS7(BIO *bio, PKCS7 *p7)
sl@0
   118
{
sl@0
   119
	BIO *b64;
sl@0
   120
	if(!(b64 = BIO_new(BIO_f_base64()))) {
sl@0
   121
		PKCS7err(PKCS7_F_B64_WRITE_PKCS7,ERR_R_MALLOC_FAILURE);
sl@0
   122
		return 0;
sl@0
   123
	}
sl@0
   124
	bio = BIO_push(b64, bio);
sl@0
   125
	i2d_PKCS7_bio(bio, p7);
sl@0
   126
	(void)BIO_flush(bio);
sl@0
   127
	bio = BIO_pop(bio);
sl@0
   128
	BIO_free(b64);
sl@0
   129
	return 1;
sl@0
   130
}
sl@0
   131
sl@0
   132
static PKCS7 *B64_read_PKCS7(BIO *bio)
sl@0
   133
{
sl@0
   134
	BIO *b64;
sl@0
   135
	PKCS7 *p7;
sl@0
   136
	if(!(b64 = BIO_new(BIO_f_base64()))) {
sl@0
   137
		PKCS7err(PKCS7_F_B64_READ_PKCS7,ERR_R_MALLOC_FAILURE);
sl@0
   138
		return 0;
sl@0
   139
	}
sl@0
   140
	bio = BIO_push(b64, bio);
sl@0
   141
	if(!(p7 = d2i_PKCS7_bio(bio, NULL))) 
sl@0
   142
		PKCS7err(PKCS7_F_B64_READ_PKCS7,PKCS7_R_DECODE_ERROR);
sl@0
   143
	(void)BIO_flush(bio);
sl@0
   144
	bio = BIO_pop(bio);
sl@0
   145
	BIO_free(b64);
sl@0
   146
	return p7;
sl@0
   147
}
sl@0
   148
sl@0
   149
/* SMIME sender */
sl@0
   150
sl@0
   151
EXPORT_C int SMIME_write_PKCS7(BIO *bio, PKCS7 *p7, BIO *data, int flags)
sl@0
   152
{
sl@0
   153
	char bound[33], c;
sl@0
   154
	int i;
sl@0
   155
	char *mime_prefix, *mime_eol, *msg_type=NULL;
sl@0
   156
	if (flags & PKCS7_NOOLDMIMETYPE)
sl@0
   157
		mime_prefix = "application/pkcs7-";
sl@0
   158
	else
sl@0
   159
		mime_prefix = "application/x-pkcs7-";
sl@0
   160
sl@0
   161
	if (flags & PKCS7_CRLFEOL)
sl@0
   162
		mime_eol = "\r\n";
sl@0
   163
	else
sl@0
   164
		mime_eol = "\n";
sl@0
   165
	if((flags & PKCS7_DETACHED) && data) {
sl@0
   166
	/* We want multipart/signed */
sl@0
   167
		/* Generate a random boundary */
sl@0
   168
		RAND_pseudo_bytes((unsigned char *)bound, 32);
sl@0
   169
		for(i = 0; i < 32; i++) {
sl@0
   170
			c = bound[i] & 0xf;
sl@0
   171
			if(c < 10) c += '0';
sl@0
   172
			else c += 'A' - 10;
sl@0
   173
			bound[i] = c;
sl@0
   174
		}
sl@0
   175
		bound[32] = 0;
sl@0
   176
		BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol);
sl@0
   177
		BIO_printf(bio, "Content-Type: multipart/signed;");
sl@0
   178
		BIO_printf(bio, " protocol=\"%ssignature\";", mime_prefix);
sl@0
   179
		BIO_printf(bio, " micalg=sha1; boundary=\"----%s\"%s%s",
sl@0
   180
						bound, mime_eol, mime_eol);
sl@0
   181
		BIO_printf(bio, "This is an S/MIME signed message%s%s",
sl@0
   182
						mime_eol, mime_eol);
sl@0
   183
		/* Now write out the first part */
sl@0
   184
		BIO_printf(bio, "------%s%s", bound, mime_eol);
sl@0
   185
		pkcs7_output_data(bio, data, p7, flags);
sl@0
   186
		BIO_printf(bio, "%s------%s%s", mime_eol, bound, mime_eol);
sl@0
   187
sl@0
   188
		/* Headers for signature */
sl@0
   189
sl@0
   190
		BIO_printf(bio, "Content-Type: %ssignature;", mime_prefix); 
sl@0
   191
		BIO_printf(bio, " name=\"smime.p7s\"%s", mime_eol);
sl@0
   192
		BIO_printf(bio, "Content-Transfer-Encoding: base64%s",
sl@0
   193
								mime_eol);
sl@0
   194
		BIO_printf(bio, "Content-Disposition: attachment;");
sl@0
   195
		BIO_printf(bio, " filename=\"smime.p7s\"%s%s",
sl@0
   196
							mime_eol, mime_eol);
sl@0
   197
		B64_write_PKCS7(bio, p7);
sl@0
   198
		BIO_printf(bio,"%s------%s--%s%s", mime_eol, bound,
sl@0
   199
							mime_eol, mime_eol);
sl@0
   200
		return 1;
sl@0
   201
	}
sl@0
   202
sl@0
   203
	/* Determine smime-type header */
sl@0
   204
sl@0
   205
	if (PKCS7_type_is_enveloped(p7))
sl@0
   206
		msg_type = "enveloped-data";
sl@0
   207
	else if (PKCS7_type_is_signed(p7))
sl@0
   208
		{
sl@0
   209
		/* If we have any signers it is signed-data othewise 
sl@0
   210
		 * certs-only.
sl@0
   211
		 */
sl@0
   212
		STACK_OF(PKCS7_SIGNER_INFO) *sinfos;
sl@0
   213
		sinfos = PKCS7_get_signer_info(p7);
sl@0
   214
		if (sk_PKCS7_SIGNER_INFO_num(sinfos) > 0)
sl@0
   215
			msg_type = "signed-data";
sl@0
   216
		else
sl@0
   217
			msg_type = "certs-only";
sl@0
   218
		}
sl@0
   219
	/* MIME headers */
sl@0
   220
	BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol);
sl@0
   221
	BIO_printf(bio, "Content-Disposition: attachment;");
sl@0
   222
	BIO_printf(bio, " filename=\"smime.p7m\"%s", mime_eol);
sl@0
   223
	BIO_printf(bio, "Content-Type: %smime;", mime_prefix);
sl@0
   224
	if (msg_type)
sl@0
   225
		BIO_printf(bio, " smime-type=%s;", msg_type);
sl@0
   226
	BIO_printf(bio, " name=\"smime.p7m\"%s", mime_eol);
sl@0
   227
	BIO_printf(bio, "Content-Transfer-Encoding: base64%s%s",
sl@0
   228
						mime_eol, mime_eol);
sl@0
   229
	B64_write_PKCS7(bio, p7);
sl@0
   230
	BIO_printf(bio, "%s", mime_eol);
sl@0
   231
	return 1;
sl@0
   232
}
sl@0
   233
sl@0
   234
/* Handle output of PKCS#7 data */
sl@0
   235
sl@0
   236
sl@0
   237
static int pkcs7_output_data(BIO *out, BIO *data, PKCS7 *p7, int flags)
sl@0
   238
	{
sl@0
   239
	BIO *tmpbio, *p7bio;
sl@0
   240
sl@0
   241
	if (!(flags & PKCS7_STREAM))
sl@0
   242
		{
sl@0
   243
		SMIME_crlf_copy(data, out, flags);
sl@0
   244
		return 1;
sl@0
   245
		}
sl@0
   246
sl@0
   247
	/* Partial sign operation */
sl@0
   248
sl@0
   249
	/* Initialize sign operation */
sl@0
   250
	p7bio = PKCS7_dataInit(p7, out);
sl@0
   251
sl@0
   252
	/* Copy data across, computing digests etc */
sl@0
   253
	SMIME_crlf_copy(data, p7bio, flags);
sl@0
   254
sl@0
   255
	/* Must be detached */
sl@0
   256
	PKCS7_set_detached(p7, 1);
sl@0
   257
sl@0
   258
	/* Finalize signatures */
sl@0
   259
	PKCS7_dataFinal(p7, p7bio);
sl@0
   260
sl@0
   261
	/* Now remove any digests prepended to the BIO */
sl@0
   262
sl@0
   263
	while (p7bio != out)
sl@0
   264
		{
sl@0
   265
		tmpbio = BIO_pop(p7bio);
sl@0
   266
		BIO_free(p7bio);
sl@0
   267
		p7bio = tmpbio;
sl@0
   268
		}
sl@0
   269
sl@0
   270
	return 1;
sl@0
   271
sl@0
   272
	}
sl@0
   273
sl@0
   274
/* SMIME reader: handle multipart/signed and opaque signing.
sl@0
   275
 * in multipart case the content is placed in a memory BIO
sl@0
   276
 * pointed to by "bcont". In opaque this is set to NULL
sl@0
   277
 */
sl@0
   278
sl@0
   279
EXPORT_C PKCS7 *SMIME_read_PKCS7(BIO *bio, BIO **bcont)
sl@0
   280
{
sl@0
   281
	BIO *p7in;
sl@0
   282
	STACK_OF(MIME_HEADER) *headers = NULL;
sl@0
   283
	STACK_OF(BIO) *parts = NULL;
sl@0
   284
	MIME_HEADER *hdr;
sl@0
   285
	MIME_PARAM *prm;
sl@0
   286
	PKCS7 *p7;
sl@0
   287
	int ret;
sl@0
   288
sl@0
   289
	if(bcont) *bcont = NULL;
sl@0
   290
sl@0
   291
	if (!(headers = mime_parse_hdr(bio))) {
sl@0
   292
		PKCS7err(PKCS7_F_SMIME_READ_PKCS7,PKCS7_R_MIME_PARSE_ERROR);
sl@0
   293
		return NULL;
sl@0
   294
	}
sl@0
   295
sl@0
   296
	if(!(hdr = mime_hdr_find(headers, "content-type")) || !hdr->value) {
sl@0
   297
		sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
sl@0
   298
		PKCS7err(PKCS7_F_SMIME_READ_PKCS7, PKCS7_R_NO_CONTENT_TYPE);
sl@0
   299
		return NULL;
sl@0
   300
	}
sl@0
   301
sl@0
   302
	/* Handle multipart/signed */
sl@0
   303
sl@0
   304
	if(!strcmp(hdr->value, "multipart/signed")) {
sl@0
   305
		/* Split into two parts */
sl@0
   306
		prm = mime_param_find(hdr, "boundary");
sl@0
   307
		if(!prm || !prm->param_value) {
sl@0
   308
			sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
sl@0
   309
			PKCS7err(PKCS7_F_SMIME_READ_PKCS7, PKCS7_R_NO_MULTIPART_BOUNDARY);
sl@0
   310
			return NULL;
sl@0
   311
		}
sl@0
   312
		ret = multi_split(bio, prm->param_value, &parts);
sl@0
   313
		sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
sl@0
   314
		if(!ret || (sk_BIO_num(parts) != 2) ) {
sl@0
   315
			PKCS7err(PKCS7_F_SMIME_READ_PKCS7, PKCS7_R_NO_MULTIPART_BODY_FAILURE);
sl@0
   316
			sk_BIO_pop_free(parts, BIO_vfree);
sl@0
   317
			return NULL;
sl@0
   318
		}
sl@0
   319
sl@0
   320
		/* Parse the signature piece */
sl@0
   321
		p7in = sk_BIO_value(parts, 1);
sl@0
   322
sl@0
   323
		if (!(headers = mime_parse_hdr(p7in))) {
sl@0
   324
			PKCS7err(PKCS7_F_SMIME_READ_PKCS7,PKCS7_R_MIME_SIG_PARSE_ERROR);
sl@0
   325
			sk_BIO_pop_free(parts, BIO_vfree);
sl@0
   326
			return NULL;
sl@0
   327
		}
sl@0
   328
sl@0
   329
		/* Get content type */
sl@0
   330
sl@0
   331
		if(!(hdr = mime_hdr_find(headers, "content-type")) ||
sl@0
   332
								 !hdr->value) {
sl@0
   333
			sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
sl@0
   334
			PKCS7err(PKCS7_F_SMIME_READ_PKCS7, PKCS7_R_NO_SIG_CONTENT_TYPE);
sl@0
   335
			return NULL;
sl@0
   336
		}
sl@0
   337
sl@0
   338
		if(strcmp(hdr->value, "application/x-pkcs7-signature") &&
sl@0
   339
			strcmp(hdr->value, "application/pkcs7-signature")) {
sl@0
   340
			sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
sl@0
   341
			PKCS7err(PKCS7_F_SMIME_READ_PKCS7,PKCS7_R_SIG_INVALID_MIME_TYPE);
sl@0
   342
			ERR_add_error_data(2, "type: ", hdr->value);
sl@0
   343
			sk_BIO_pop_free(parts, BIO_vfree);
sl@0
   344
			return NULL;
sl@0
   345
		}
sl@0
   346
		sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
sl@0
   347
		/* Read in PKCS#7 */
sl@0
   348
		if(!(p7 = B64_read_PKCS7(p7in))) {
sl@0
   349
			PKCS7err(PKCS7_F_SMIME_READ_PKCS7,PKCS7_R_PKCS7_SIG_PARSE_ERROR);
sl@0
   350
			sk_BIO_pop_free(parts, BIO_vfree);
sl@0
   351
			return NULL;
sl@0
   352
		}
sl@0
   353
sl@0
   354
		if(bcont) {
sl@0
   355
			*bcont = sk_BIO_value(parts, 0);
sl@0
   356
			BIO_free(p7in);
sl@0
   357
			sk_BIO_free(parts);
sl@0
   358
		} else sk_BIO_pop_free(parts, BIO_vfree);
sl@0
   359
		return p7;
sl@0
   360
	}
sl@0
   361
		
sl@0
   362
	/* OK, if not multipart/signed try opaque signature */
sl@0
   363
sl@0
   364
	if (strcmp (hdr->value, "application/x-pkcs7-mime") &&
sl@0
   365
	    strcmp (hdr->value, "application/pkcs7-mime")) {
sl@0
   366
		PKCS7err(PKCS7_F_SMIME_READ_PKCS7,PKCS7_R_INVALID_MIME_TYPE);
sl@0
   367
		ERR_add_error_data(2, "type: ", hdr->value);
sl@0
   368
		sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
sl@0
   369
		return NULL;
sl@0
   370
	}
sl@0
   371
sl@0
   372
	sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
sl@0
   373
	
sl@0
   374
	if(!(p7 = B64_read_PKCS7(bio))) {
sl@0
   375
		PKCS7err(PKCS7_F_SMIME_READ_PKCS7, PKCS7_R_PKCS7_PARSE_ERROR);
sl@0
   376
		return NULL;
sl@0
   377
	}
sl@0
   378
	return p7;
sl@0
   379
sl@0
   380
}
sl@0
   381
sl@0
   382
/* Copy text from one BIO to another making the output CRLF at EOL */
sl@0
   383
EXPORT_C int SMIME_crlf_copy(BIO *in, BIO *out, int flags)
sl@0
   384
{
sl@0
   385
	char eol;
sl@0
   386
	int len;
sl@0
   387
	char linebuf[MAX_SMLEN];
sl@0
   388
	if(flags & PKCS7_BINARY) {
sl@0
   389
		while((len = BIO_read(in, linebuf, MAX_SMLEN)) > 0)
sl@0
   390
						BIO_write(out, linebuf, len);
sl@0
   391
		return 1;
sl@0
   392
	}
sl@0
   393
	if(flags & PKCS7_TEXT)
sl@0
   394
		BIO_printf(out, "Content-Type: text/plain\r\n\r\n");
sl@0
   395
	while ((len = BIO_gets(in, linebuf, MAX_SMLEN)) > 0) {
sl@0
   396
		eol = strip_eol(linebuf, &len);
sl@0
   397
		if (len)
sl@0
   398
			BIO_write(out, linebuf, len);
sl@0
   399
		if(eol) BIO_write(out, "\r\n", 2);
sl@0
   400
	}
sl@0
   401
	return 1;
sl@0
   402
}
sl@0
   403
sl@0
   404
/* Strip off headers if they are text/plain */
sl@0
   405
EXPORT_C int SMIME_text(BIO *in, BIO *out)
sl@0
   406
{
sl@0
   407
#ifndef SYMBIAN	
sl@0
   408
	char iobuf[4096];
sl@0
   409
#else
sl@0
   410
  char iobuf[512];
sl@0
   411
#endif	
sl@0
   412
	int len;
sl@0
   413
	STACK_OF(MIME_HEADER) *headers;
sl@0
   414
	MIME_HEADER *hdr;
sl@0
   415
sl@0
   416
	if (!(headers = mime_parse_hdr(in))) {
sl@0
   417
		PKCS7err(PKCS7_F_SMIME_TEXT,PKCS7_R_MIME_PARSE_ERROR);
sl@0
   418
		return 0;
sl@0
   419
	}
sl@0
   420
	if(!(hdr = mime_hdr_find(headers, "content-type")) || !hdr->value) {
sl@0
   421
		PKCS7err(PKCS7_F_SMIME_TEXT,PKCS7_R_MIME_NO_CONTENT_TYPE);
sl@0
   422
		sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
sl@0
   423
		return 0;
sl@0
   424
	}
sl@0
   425
	if (strcmp (hdr->value, "text/plain")) {
sl@0
   426
		PKCS7err(PKCS7_F_SMIME_TEXT,PKCS7_R_INVALID_MIME_TYPE);
sl@0
   427
		ERR_add_error_data(2, "type: ", hdr->value);
sl@0
   428
		sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
sl@0
   429
		return 0;
sl@0
   430
	}
sl@0
   431
	sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
sl@0
   432
	while ((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
sl@0
   433
						BIO_write(out, iobuf, len);
sl@0
   434
	return 1;
sl@0
   435
}
sl@0
   436
sl@0
   437
/* Split a multipart/XXX message body into component parts: result is
sl@0
   438
 * canonical parts in a STACK of bios
sl@0
   439
 */
sl@0
   440
sl@0
   441
static int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret)
sl@0
   442
{
sl@0
   443
	char linebuf[MAX_SMLEN];
sl@0
   444
	int len, blen;
sl@0
   445
	int eol = 0, next_eol = 0;
sl@0
   446
	BIO *bpart = NULL;
sl@0
   447
	STACK_OF(BIO) *parts;
sl@0
   448
	char state, part, first;
sl@0
   449
sl@0
   450
	blen = strlen(bound);
sl@0
   451
	part = 0;
sl@0
   452
	state = 0;
sl@0
   453
	first = 1;
sl@0
   454
	parts = sk_BIO_new_null();
sl@0
   455
	*ret = parts;
sl@0
   456
	while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) {
sl@0
   457
		state = mime_bound_check(linebuf, len, bound, blen);
sl@0
   458
		if(state == 1) {
sl@0
   459
			first = 1;
sl@0
   460
			part++;
sl@0
   461
		} else if(state == 2) {
sl@0
   462
			sk_BIO_push(parts, bpart);
sl@0
   463
			return 1;
sl@0
   464
		} else if(part) {
sl@0
   465
			/* Strip CR+LF from linebuf */
sl@0
   466
			next_eol = strip_eol(linebuf, &len);
sl@0
   467
			if(first) {
sl@0
   468
				first = 0;
sl@0
   469
				if(bpart) sk_BIO_push(parts, bpart);
sl@0
   470
				bpart = BIO_new(BIO_s_mem());
sl@0
   471
				BIO_set_mem_eof_return(bpart, 0);
sl@0
   472
			} else if (eol)
sl@0
   473
				BIO_write(bpart, "\r\n", 2);
sl@0
   474
			eol = next_eol;
sl@0
   475
			if (len)
sl@0
   476
				BIO_write(bpart, linebuf, len);
sl@0
   477
		}
sl@0
   478
	}
sl@0
   479
	return 0;
sl@0
   480
}
sl@0
   481
sl@0
   482
/* This is the big one: parse MIME header lines up to message body */
sl@0
   483
sl@0
   484
#define MIME_INVALID	0
sl@0
   485
#define MIME_START	1
sl@0
   486
#define MIME_TYPE	2
sl@0
   487
#define MIME_NAME	3
sl@0
   488
#define MIME_VALUE	4
sl@0
   489
#define MIME_QUOTE	5
sl@0
   490
#define MIME_COMMENT	6
sl@0
   491
sl@0
   492
sl@0
   493
static STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio)
sl@0
   494
{
sl@0
   495
	char *p, *q, c;
sl@0
   496
	char *ntmp;
sl@0
   497
	char linebuf[MAX_SMLEN];
sl@0
   498
	MIME_HEADER *mhdr = NULL;
sl@0
   499
	STACK_OF(MIME_HEADER) *headers;
sl@0
   500
	int len, state, save_state = 0;
sl@0
   501
sl@0
   502
	headers = sk_MIME_HEADER_new(mime_hdr_cmp);
sl@0
   503
	while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) {
sl@0
   504
	/* If whitespace at line start then continuation line */
sl@0
   505
	if(mhdr && isspace((unsigned char)linebuf[0])) state = MIME_NAME;
sl@0
   506
	else state = MIME_START;
sl@0
   507
	ntmp = NULL;
sl@0
   508
	/* Go through all characters */
sl@0
   509
	for(p = linebuf, q = linebuf; (c = *p) && (c!='\r') && (c!='\n'); p++) {
sl@0
   510
sl@0
   511
	/* State machine to handle MIME headers
sl@0
   512
	 * if this looks horrible that's because it *is*
sl@0
   513
         */
sl@0
   514
sl@0
   515
		switch(state) {
sl@0
   516
			case MIME_START:
sl@0
   517
			if(c == ':') {
sl@0
   518
				state = MIME_TYPE;
sl@0
   519
				*p = 0;
sl@0
   520
				ntmp = strip_ends(q);
sl@0
   521
				q = p + 1;
sl@0
   522
			}
sl@0
   523
			break;
sl@0
   524
sl@0
   525
			case MIME_TYPE:
sl@0
   526
			if(c == ';') {
sl@0
   527
				mime_debug("Found End Value\n");
sl@0
   528
				*p = 0;
sl@0
   529
				mhdr = mime_hdr_new(ntmp, strip_ends(q));
sl@0
   530
				sk_MIME_HEADER_push(headers, mhdr);
sl@0
   531
				ntmp = NULL;
sl@0
   532
				q = p + 1;
sl@0
   533
				state = MIME_NAME;
sl@0
   534
			} else if(c == '(') {
sl@0
   535
				save_state = state;
sl@0
   536
				state = MIME_COMMENT;
sl@0
   537
			}
sl@0
   538
			break;
sl@0
   539
sl@0
   540
			case MIME_COMMENT:
sl@0
   541
			if(c == ')') {
sl@0
   542
				state = save_state;
sl@0
   543
			}
sl@0
   544
			break;
sl@0
   545
sl@0
   546
			case MIME_NAME:
sl@0
   547
			if(c == '=') {
sl@0
   548
				state = MIME_VALUE;
sl@0
   549
				*p = 0;
sl@0
   550
				ntmp = strip_ends(q);
sl@0
   551
				q = p + 1;
sl@0
   552
			}
sl@0
   553
			break ;
sl@0
   554
sl@0
   555
			case MIME_VALUE:
sl@0
   556
			if(c == ';') {
sl@0
   557
				state = MIME_NAME;
sl@0
   558
				*p = 0;
sl@0
   559
				mime_hdr_addparam(mhdr, ntmp, strip_ends(q));
sl@0
   560
				ntmp = NULL;
sl@0
   561
				q = p + 1;
sl@0
   562
			} else if (c == '"') {
sl@0
   563
				mime_debug("Found Quote\n");
sl@0
   564
				state = MIME_QUOTE;
sl@0
   565
			} else if(c == '(') {
sl@0
   566
				save_state = state;
sl@0
   567
				state = MIME_COMMENT;
sl@0
   568
			}
sl@0
   569
			break;
sl@0
   570
sl@0
   571
			case MIME_QUOTE:
sl@0
   572
			if(c == '"') {
sl@0
   573
				mime_debug("Found Match Quote\n");
sl@0
   574
				state = MIME_VALUE;
sl@0
   575
			}
sl@0
   576
			break;
sl@0
   577
		}
sl@0
   578
	}
sl@0
   579
sl@0
   580
	if(state == MIME_TYPE) {
sl@0
   581
		mhdr = mime_hdr_new(ntmp, strip_ends(q));
sl@0
   582
		sk_MIME_HEADER_push(headers, mhdr);
sl@0
   583
	} else if(state == MIME_VALUE)
sl@0
   584
			 mime_hdr_addparam(mhdr, ntmp, strip_ends(q));
sl@0
   585
	if(p == linebuf) break;	/* Blank line means end of headers */
sl@0
   586
}
sl@0
   587
sl@0
   588
return headers;
sl@0
   589
sl@0
   590
}
sl@0
   591
sl@0
   592
static char *strip_ends(char *name)
sl@0
   593
{
sl@0
   594
	return strip_end(strip_start(name));
sl@0
   595
}
sl@0
   596
sl@0
   597
/* Strip a parameter of whitespace from start of param */
sl@0
   598
static char *strip_start(char *name)
sl@0
   599
{
sl@0
   600
	char *p, c;
sl@0
   601
	/* Look for first non white space or quote */
sl@0
   602
	for(p = name; (c = *p) ;p++) {
sl@0
   603
		if(c == '"') {
sl@0
   604
			/* Next char is start of string if non null */
sl@0
   605
			if(p[1]) return p + 1;
sl@0
   606
			/* Else null string */
sl@0
   607
			return NULL;
sl@0
   608
		}
sl@0
   609
		if(!isspace((unsigned char)c)) return p;
sl@0
   610
	}
sl@0
   611
	return NULL;
sl@0
   612
}
sl@0
   613
sl@0
   614
/* As above but strip from end of string : maybe should handle brackets? */
sl@0
   615
static char *strip_end(char *name)
sl@0
   616
{
sl@0
   617
	char *p, c;
sl@0
   618
	if(!name) return NULL;
sl@0
   619
	/* Look for first non white space or quote */
sl@0
   620
	for(p = name + strlen(name) - 1; p >= name ;p--) {
sl@0
   621
		c = *p;
sl@0
   622
		if(c == '"') {
sl@0
   623
			if(p - 1 == name) return NULL;
sl@0
   624
			*p = 0;
sl@0
   625
			return name;
sl@0
   626
		}
sl@0
   627
		if(isspace((unsigned char)c)) *p = 0;	
sl@0
   628
		else return name;
sl@0
   629
	}
sl@0
   630
	return NULL;
sl@0
   631
}
sl@0
   632
sl@0
   633
static MIME_HEADER *mime_hdr_new(char *name, char *value)
sl@0
   634
{
sl@0
   635
	MIME_HEADER *mhdr;
sl@0
   636
	char *tmpname, *tmpval, *p;
sl@0
   637
	int c;
sl@0
   638
	if(name) {
sl@0
   639
		if(!(tmpname = BUF_strdup(name))) return NULL;
sl@0
   640
		for(p = tmpname ; *p; p++) {
sl@0
   641
			c = *p;
sl@0
   642
			if(isupper(c)) {
sl@0
   643
				c = tolower(c);
sl@0
   644
				*p = c;
sl@0
   645
			}
sl@0
   646
		}
sl@0
   647
	} else tmpname = NULL;
sl@0
   648
	if(value) {
sl@0
   649
		if(!(tmpval = BUF_strdup(value))) return NULL;
sl@0
   650
		for(p = tmpval ; *p; p++) {
sl@0
   651
			c = *p;
sl@0
   652
			if(isupper(c)) {
sl@0
   653
				c = tolower(c);
sl@0
   654
				*p = c;
sl@0
   655
			}
sl@0
   656
		}
sl@0
   657
	} else tmpval = NULL;
sl@0
   658
	mhdr = (MIME_HEADER *) OPENSSL_malloc(sizeof(MIME_HEADER));
sl@0
   659
	if(!mhdr) return NULL;
sl@0
   660
	mhdr->name = tmpname;
sl@0
   661
	mhdr->value = tmpval;
sl@0
   662
	if(!(mhdr->params = sk_MIME_PARAM_new(mime_param_cmp))) return NULL;
sl@0
   663
	return mhdr;
sl@0
   664
}
sl@0
   665
		
sl@0
   666
static int mime_hdr_addparam(MIME_HEADER *mhdr, char *name, char *value)
sl@0
   667
{
sl@0
   668
	char *tmpname, *tmpval, *p;
sl@0
   669
	int c;
sl@0
   670
	MIME_PARAM *mparam;
sl@0
   671
	if(name) {
sl@0
   672
		tmpname = BUF_strdup(name);
sl@0
   673
		if(!tmpname) return 0;
sl@0
   674
		for(p = tmpname ; *p; p++) {
sl@0
   675
			c = *p;
sl@0
   676
			if(isupper(c)) {
sl@0
   677
				c = tolower(c);
sl@0
   678
				*p = c;
sl@0
   679
			}
sl@0
   680
		}
sl@0
   681
	} else tmpname = NULL;
sl@0
   682
	if(value) {
sl@0
   683
		tmpval = BUF_strdup(value);
sl@0
   684
		if(!tmpval) return 0;
sl@0
   685
	} else tmpval = NULL;
sl@0
   686
	/* Parameter values are case sensitive so leave as is */
sl@0
   687
	mparam = (MIME_PARAM *) OPENSSL_malloc(sizeof(MIME_PARAM));
sl@0
   688
	if(!mparam) return 0;
sl@0
   689
	mparam->param_name = tmpname;
sl@0
   690
	mparam->param_value = tmpval;
sl@0
   691
	sk_MIME_PARAM_push(mhdr->params, mparam);
sl@0
   692
	return 1;
sl@0
   693
}
sl@0
   694
sl@0
   695
static int mime_hdr_cmp(const MIME_HEADER * const *a,
sl@0
   696
			const MIME_HEADER * const *b)
sl@0
   697
{
sl@0
   698
	return(strcmp((*a)->name, (*b)->name));
sl@0
   699
}
sl@0
   700
sl@0
   701
static int mime_param_cmp(const MIME_PARAM * const *a,
sl@0
   702
			const MIME_PARAM * const *b)
sl@0
   703
{
sl@0
   704
	return(strcmp((*a)->param_name, (*b)->param_name));
sl@0
   705
}
sl@0
   706
sl@0
   707
/* Find a header with a given name (if possible) */
sl@0
   708
sl@0
   709
static MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, char *name)
sl@0
   710
{
sl@0
   711
	MIME_HEADER htmp;
sl@0
   712
	int idx;
sl@0
   713
	htmp.name = name;
sl@0
   714
	idx = sk_MIME_HEADER_find(hdrs, &htmp);
sl@0
   715
	if(idx < 0) return NULL;
sl@0
   716
	return sk_MIME_HEADER_value(hdrs, idx);
sl@0
   717
}
sl@0
   718
sl@0
   719
static MIME_PARAM *mime_param_find(MIME_HEADER *hdr, char *name)
sl@0
   720
{
sl@0
   721
	MIME_PARAM param;
sl@0
   722
	int idx;
sl@0
   723
	param.param_name = name;
sl@0
   724
	idx = sk_MIME_PARAM_find(hdr->params, &param);
sl@0
   725
	if(idx < 0) return NULL;
sl@0
   726
	return sk_MIME_PARAM_value(hdr->params, idx);
sl@0
   727
}
sl@0
   728
sl@0
   729
static void mime_hdr_free(MIME_HEADER *hdr)
sl@0
   730
{
sl@0
   731
	if(hdr->name) OPENSSL_free(hdr->name);
sl@0
   732
	if(hdr->value) OPENSSL_free(hdr->value);
sl@0
   733
	if(hdr->params) sk_MIME_PARAM_pop_free(hdr->params, mime_param_free);
sl@0
   734
	OPENSSL_free(hdr);
sl@0
   735
}
sl@0
   736
sl@0
   737
static void mime_param_free(MIME_PARAM *param)
sl@0
   738
{
sl@0
   739
	if(param->param_name) OPENSSL_free(param->param_name);
sl@0
   740
	if(param->param_value) OPENSSL_free(param->param_value);
sl@0
   741
	OPENSSL_free(param);
sl@0
   742
}
sl@0
   743
sl@0
   744
/* Check for a multipart boundary. Returns:
sl@0
   745
 * 0 : no boundary
sl@0
   746
 * 1 : part boundary
sl@0
   747
 * 2 : final boundary
sl@0
   748
 */
sl@0
   749
static int mime_bound_check(char *line, int linelen, char *bound, int blen)
sl@0
   750
{
sl@0
   751
	if(linelen == -1) linelen = strlen(line);
sl@0
   752
	if(blen == -1) blen = strlen(bound);
sl@0
   753
	/* Quickly eliminate if line length too short */
sl@0
   754
	if(blen + 2 > linelen) return 0;
sl@0
   755
	/* Check for part boundary */
sl@0
   756
	if(!strncmp(line, "--", 2) && !strncmp(line + 2, bound, blen)) {
sl@0
   757
		if(!strncmp(line + blen + 2, "--", 2)) return 2;
sl@0
   758
		else return 1;
sl@0
   759
	}
sl@0
   760
	return 0;
sl@0
   761
}
sl@0
   762
sl@0
   763
static int strip_eol(char *linebuf, int *plen)
sl@0
   764
	{
sl@0
   765
	int len = *plen;
sl@0
   766
	char *p, c;
sl@0
   767
	int is_eol = 0;
sl@0
   768
	p = linebuf + len - 1;
sl@0
   769
	for (p = linebuf + len - 1; len > 0; len--, p--)
sl@0
   770
		{
sl@0
   771
		c = *p;
sl@0
   772
		if (c == '\n')
sl@0
   773
			is_eol = 1;
sl@0
   774
		else if (c != '\r')
sl@0
   775
			break;
sl@0
   776
		}
sl@0
   777
	*plen = len;
sl@0
   778
	return is_eol;
sl@0
   779
	}