1183234Ssimon/* asn_mime.c */ 2296465Sdelphij/* 3296465Sdelphij * Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL 4183234Ssimon * project. 5183234Ssimon */ 6183234Ssimon/* ==================================================================== 7183234Ssimon * Copyright (c) 1999-2008 The OpenSSL Project. All rights reserved. 8183234Ssimon * 9183234Ssimon * Redistribution and use in source and binary forms, with or without 10183234Ssimon * modification, are permitted provided that the following conditions 11183234Ssimon * are met: 12183234Ssimon * 13183234Ssimon * 1. Redistributions of source code must retain the above copyright 14296465Sdelphij * notice, this list of conditions and the following disclaimer. 15183234Ssimon * 16183234Ssimon * 2. Redistributions in binary form must reproduce the above copyright 17183234Ssimon * notice, this list of conditions and the following disclaimer in 18183234Ssimon * the documentation and/or other materials provided with the 19183234Ssimon * distribution. 20183234Ssimon * 21183234Ssimon * 3. All advertising materials mentioning features or use of this 22183234Ssimon * software must display the following acknowledgment: 23183234Ssimon * "This product includes software developed by the OpenSSL Project 24183234Ssimon * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" 25183234Ssimon * 26183234Ssimon * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 27183234Ssimon * endorse or promote products derived from this software without 28183234Ssimon * prior written permission. For written permission, please contact 29183234Ssimon * licensing@OpenSSL.org. 30183234Ssimon * 31183234Ssimon * 5. Products derived from this software may not be called "OpenSSL" 32183234Ssimon * nor may "OpenSSL" appear in their names without prior written 33183234Ssimon * permission of the OpenSSL Project. 34183234Ssimon * 35183234Ssimon * 6. Redistributions of any form whatsoever must retain the following 36183234Ssimon * acknowledgment: 37183234Ssimon * "This product includes software developed by the OpenSSL Project 38183234Ssimon * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" 39183234Ssimon * 40183234Ssimon * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 41183234Ssimon * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 42183234Ssimon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 43183234Ssimon * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 44183234Ssimon * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 45183234Ssimon * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 46183234Ssimon * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 47183234Ssimon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 48183234Ssimon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 49183234Ssimon * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 50183234Ssimon * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 51183234Ssimon * OF THE POSSIBILITY OF SUCH DAMAGE. 52183234Ssimon * ==================================================================== 53183234Ssimon * 54183234Ssimon */ 55183234Ssimon 56183234Ssimon#include <stdio.h> 57183234Ssimon#include <ctype.h> 58183234Ssimon#include "cryptlib.h" 59183234Ssimon#include <openssl/rand.h> 60183234Ssimon#include <openssl/x509.h> 61183234Ssimon#include <openssl/asn1.h> 62183234Ssimon#include <openssl/asn1t.h> 63183234Ssimon 64296465Sdelphij/* 65296465Sdelphij * Generalised MIME like utilities for streaming ASN1. Although many have a 66296465Sdelphij * PKCS7/CMS like flavour others are more general purpose. 67183234Ssimon */ 68183234Ssimon 69296465Sdelphij/* 70296465Sdelphij * MIME format structures Note that all are translated to lower case apart 71296465Sdelphij * from parameter values. Quotes are stripped off 72183234Ssimon */ 73183234Ssimon 74183234Ssimontypedef struct { 75296465Sdelphij char *param_name; /* Param name e.g. "micalg" */ 76296465Sdelphij char *param_value; /* Param value e.g. "sha1" */ 77183234Ssimon} MIME_PARAM; 78183234Ssimon 79183234SsimonDECLARE_STACK_OF(MIME_PARAM) 80183234SsimonIMPLEMENT_STACK_OF(MIME_PARAM) 81183234Ssimon 82183234Ssimontypedef struct { 83296465Sdelphij char *name; /* Name of line e.g. "content-type" */ 84296465Sdelphij char *value; /* Value of line e.g. "text/plain" */ 85296465Sdelphij STACK_OF(MIME_PARAM) *params; /* Zero or more parameters */ 86183234Ssimon} MIME_HEADER; 87183234Ssimon 88183234SsimonDECLARE_STACK_OF(MIME_HEADER) 89183234SsimonIMPLEMENT_STACK_OF(MIME_HEADER) 90183234Ssimon 91296465Sdelphijstatic char *strip_ends(char *name); 92296465Sdelphijstatic char *strip_start(char *name); 93296465Sdelphijstatic char *strip_end(char *name); 94183234Ssimonstatic MIME_HEADER *mime_hdr_new(char *name, char *value); 95183234Ssimonstatic int mime_hdr_addparam(MIME_HEADER *mhdr, char *name, char *value); 96183234Ssimonstatic STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio); 97296465Sdelphijstatic int mime_hdr_cmp(const MIME_HEADER *const *a, 98296465Sdelphij const MIME_HEADER *const *b); 99296465Sdelphijstatic int mime_param_cmp(const MIME_PARAM *const *a, 100296465Sdelphij const MIME_PARAM *const *b); 101183234Ssimonstatic void mime_param_free(MIME_PARAM *param); 102183234Ssimonstatic int mime_bound_check(char *line, int linelen, char *bound, int blen); 103183234Ssimonstatic int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret); 104183234Ssimonstatic int strip_eol(char *linebuf, int *plen); 105183234Ssimonstatic MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, char *name); 106183234Ssimonstatic MIME_PARAM *mime_param_find(MIME_HEADER *hdr, char *name); 107183234Ssimonstatic void mime_hdr_free(MIME_HEADER *hdr); 108183234Ssimon 109183234Ssimon#define MAX_SMLEN 1024 110296465Sdelphij#define mime_debug(x) /* x */ 111183234Ssimon 112183234Ssimon/* Base 64 read and write of ASN1 structure */ 113183234Ssimon 114183234Ssimonstatic int B64_write_ASN1(BIO *out, ASN1_VALUE *val, BIO *in, int flags, 115296465Sdelphij const ASN1_ITEM *it) 116296465Sdelphij{ 117296465Sdelphij BIO *b64; 118296465Sdelphij int r; 119296465Sdelphij b64 = BIO_new(BIO_f_base64()); 120296465Sdelphij if (!b64) { 121296465Sdelphij ASN1err(ASN1_F_B64_WRITE_ASN1, ERR_R_MALLOC_FAILURE); 122296465Sdelphij return 0; 123296465Sdelphij } 124296465Sdelphij /* 125296465Sdelphij * prepend the b64 BIO so all data is base64 encoded. 126296465Sdelphij */ 127296465Sdelphij out = BIO_push(b64, out); 128296465Sdelphij r = ASN1_item_i2d_bio(it, out, val); 129296465Sdelphij (void)BIO_flush(out); 130296465Sdelphij BIO_pop(out); 131296465Sdelphij BIO_free(b64); 132296465Sdelphij return r; 133296465Sdelphij} 134183234Ssimon 135183234Ssimonstatic ASN1_VALUE *b64_read_asn1(BIO *bio, const ASN1_ITEM *it) 136183234Ssimon{ 137296465Sdelphij BIO *b64; 138296465Sdelphij ASN1_VALUE *val; 139296465Sdelphij if (!(b64 = BIO_new(BIO_f_base64()))) { 140296465Sdelphij ASN1err(ASN1_F_B64_READ_ASN1, ERR_R_MALLOC_FAILURE); 141296465Sdelphij return 0; 142296465Sdelphij } 143296465Sdelphij bio = BIO_push(b64, bio); 144296465Sdelphij val = ASN1_item_d2i_bio(it, bio, NULL); 145296465Sdelphij if (!val) 146296465Sdelphij ASN1err(ASN1_F_B64_READ_ASN1, ASN1_R_DECODE_ERROR); 147296465Sdelphij (void)BIO_flush(bio); 148296465Sdelphij bio = BIO_pop(bio); 149296465Sdelphij BIO_free(b64); 150296465Sdelphij return val; 151183234Ssimon} 152183234Ssimon 153183234Ssimon/* Generate the MIME "micalg" parameter from RFC3851, RFC4490 */ 154183234Ssimon 155183234Ssimonstatic int asn1_write_micalg(BIO *out, STACK_OF(X509_ALGOR) *mdalgs) 156296465Sdelphij{ 157296465Sdelphij int i, have_unknown = 0, write_comma, md_nid; 158296465Sdelphij have_unknown = 0; 159296465Sdelphij write_comma = 0; 160296465Sdelphij for (i = 0; i < sk_X509_ALGOR_num(mdalgs); i++) { 161296465Sdelphij if (write_comma) 162296465Sdelphij BIO_write(out, ",", 1); 163296465Sdelphij write_comma = 1; 164296465Sdelphij md_nid = OBJ_obj2nid(sk_X509_ALGOR_value(mdalgs, i)->algorithm); 165296465Sdelphij switch (md_nid) { 166296465Sdelphij case NID_sha1: 167296465Sdelphij BIO_puts(out, "sha1"); 168296465Sdelphij break; 169183234Ssimon 170296465Sdelphij case NID_md5: 171296465Sdelphij BIO_puts(out, "md5"); 172296465Sdelphij break; 173183234Ssimon 174296465Sdelphij case NID_sha256: 175296465Sdelphij BIO_puts(out, "sha-256"); 176296465Sdelphij break; 177183234Ssimon 178296465Sdelphij case NID_sha384: 179296465Sdelphij BIO_puts(out, "sha-384"); 180296465Sdelphij break; 181183234Ssimon 182296465Sdelphij case NID_sha512: 183296465Sdelphij BIO_puts(out, "sha-512"); 184296465Sdelphij break; 185183234Ssimon 186296465Sdelphij default: 187296465Sdelphij if (have_unknown) 188296465Sdelphij write_comma = 0; 189296465Sdelphij else { 190296465Sdelphij BIO_puts(out, "unknown"); 191296465Sdelphij have_unknown = 1; 192296465Sdelphij } 193296465Sdelphij break; 194183234Ssimon 195296465Sdelphij } 196296465Sdelphij } 197183234Ssimon 198296465Sdelphij return 1; 199183234Ssimon 200296465Sdelphij} 201183234Ssimon 202183234Ssimon/* SMIME sender */ 203183234Ssimon 204183234Ssimonint int_smime_write_ASN1(BIO *bio, ASN1_VALUE *val, BIO *data, int flags, 205296465Sdelphij int ctype_nid, int econt_nid, 206296465Sdelphij STACK_OF(X509_ALGOR) *mdalgs, 207296465Sdelphij asn1_output_data_fn * data_fn, const ASN1_ITEM *it) 208183234Ssimon{ 209296465Sdelphij char bound[33], c; 210296465Sdelphij int i; 211296465Sdelphij const char *mime_prefix, *mime_eol, *cname = "smime.p7m"; 212296465Sdelphij const char *msg_type = NULL; 213296465Sdelphij if (flags & SMIME_OLDMIME) 214296465Sdelphij mime_prefix = "application/x-pkcs7-"; 215296465Sdelphij else 216296465Sdelphij mime_prefix = "application/pkcs7-"; 217183234Ssimon 218296465Sdelphij if (flags & SMIME_CRLFEOL) 219296465Sdelphij mime_eol = "\r\n"; 220296465Sdelphij else 221296465Sdelphij mime_eol = "\n"; 222296465Sdelphij if ((flags & SMIME_DETACHED) && data) { 223296465Sdelphij /* We want multipart/signed */ 224296465Sdelphij /* Generate a random boundary */ 225296465Sdelphij RAND_pseudo_bytes((unsigned char *)bound, 32); 226296465Sdelphij for (i = 0; i < 32; i++) { 227296465Sdelphij c = bound[i] & 0xf; 228296465Sdelphij if (c < 10) 229296465Sdelphij c += '0'; 230296465Sdelphij else 231296465Sdelphij c += 'A' - 10; 232296465Sdelphij bound[i] = c; 233296465Sdelphij } 234296465Sdelphij bound[32] = 0; 235296465Sdelphij BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol); 236296465Sdelphij BIO_printf(bio, "Content-Type: multipart/signed;"); 237296465Sdelphij BIO_printf(bio, " protocol=\"%ssignature\";", mime_prefix); 238296465Sdelphij BIO_puts(bio, " micalg=\""); 239296465Sdelphij asn1_write_micalg(bio, mdalgs); 240296465Sdelphij BIO_printf(bio, "\"; boundary=\"----%s\"%s%s", 241296465Sdelphij bound, mime_eol, mime_eol); 242296465Sdelphij BIO_printf(bio, "This is an S/MIME signed message%s%s", 243296465Sdelphij mime_eol, mime_eol); 244296465Sdelphij /* Now write out the first part */ 245296465Sdelphij BIO_printf(bio, "------%s%s", bound, mime_eol); 246296465Sdelphij if (!data_fn(bio, data, val, flags, it)) 247296465Sdelphij return 0; 248296465Sdelphij BIO_printf(bio, "%s------%s%s", mime_eol, bound, mime_eol); 249183234Ssimon 250296465Sdelphij /* Headers for signature */ 251183234Ssimon 252296465Sdelphij BIO_printf(bio, "Content-Type: %ssignature;", mime_prefix); 253296465Sdelphij BIO_printf(bio, " name=\"smime.p7s\"%s", mime_eol); 254296465Sdelphij BIO_printf(bio, "Content-Transfer-Encoding: base64%s", mime_eol); 255296465Sdelphij BIO_printf(bio, "Content-Disposition: attachment;"); 256296465Sdelphij BIO_printf(bio, " filename=\"smime.p7s\"%s%s", mime_eol, mime_eol); 257296465Sdelphij B64_write_ASN1(bio, val, NULL, 0, it); 258296465Sdelphij BIO_printf(bio, "%s------%s--%s%s", mime_eol, bound, 259296465Sdelphij mime_eol, mime_eol); 260296465Sdelphij return 1; 261296465Sdelphij } 262183234Ssimon 263296465Sdelphij /* Determine smime-type header */ 264183234Ssimon 265296465Sdelphij if (ctype_nid == NID_pkcs7_enveloped) 266296465Sdelphij msg_type = "enveloped-data"; 267296465Sdelphij else if (ctype_nid == NID_pkcs7_signed) { 268296465Sdelphij if (econt_nid == NID_id_smime_ct_receipt) 269296465Sdelphij msg_type = "signed-receipt"; 270296465Sdelphij else if (sk_X509_ALGOR_num(mdalgs) >= 0) 271296465Sdelphij msg_type = "signed-data"; 272296465Sdelphij else 273296465Sdelphij msg_type = "certs-only"; 274296465Sdelphij } else if (ctype_nid == NID_id_smime_ct_compressedData) { 275296465Sdelphij msg_type = "compressed-data"; 276296465Sdelphij cname = "smime.p7z"; 277296465Sdelphij } 278296465Sdelphij /* MIME headers */ 279296465Sdelphij BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol); 280296465Sdelphij BIO_printf(bio, "Content-Disposition: attachment;"); 281296465Sdelphij BIO_printf(bio, " filename=\"%s\"%s", cname, mime_eol); 282296465Sdelphij BIO_printf(bio, "Content-Type: %smime;", mime_prefix); 283296465Sdelphij if (msg_type) 284296465Sdelphij BIO_printf(bio, " smime-type=%s;", msg_type); 285296465Sdelphij BIO_printf(bio, " name=\"%s\"%s", cname, mime_eol); 286296465Sdelphij BIO_printf(bio, "Content-Transfer-Encoding: base64%s%s", 287296465Sdelphij mime_eol, mime_eol); 288296465Sdelphij if (!B64_write_ASN1(bio, val, data, flags, it)) 289296465Sdelphij return 0; 290296465Sdelphij BIO_printf(bio, "%s", mime_eol); 291296465Sdelphij return 1; 292183234Ssimon} 293183234Ssimon 294183234Ssimon#if 0 295183234Ssimon 296183234Ssimon/* Handle output of ASN1 data */ 297183234Ssimon 298183234Ssimonstatic int asn1_output_data(BIO *out, BIO *data, ASN1_VALUE *val, int flags, 299296465Sdelphij const ASN1_ITEM *it) 300296465Sdelphij{ 301296465Sdelphij BIO *tmpbio; 302296465Sdelphij const ASN1_AUX *aux = it->funcs; 303296465Sdelphij ASN1_STREAM_ARG sarg; 304183234Ssimon 305296465Sdelphij if (!(flags & SMIME_DETACHED)) { 306296465Sdelphij SMIME_crlf_copy(data, out, flags); 307296465Sdelphij return 1; 308296465Sdelphij } 309183234Ssimon 310296465Sdelphij if (!aux || !aux->asn1_cb) { 311296465Sdelphij ASN1err(ASN1_F_ASN1_OUTPUT_DATA, ASN1_R_STREAMING_NOT_SUPPORTED); 312296465Sdelphij return 0; 313296465Sdelphij } 314183234Ssimon 315296465Sdelphij sarg.out = out; 316296465Sdelphij sarg.ndef_bio = NULL; 317296465Sdelphij sarg.boundary = NULL; 318183234Ssimon 319296465Sdelphij /* Let ASN1 code prepend any needed BIOs */ 320183234Ssimon 321296465Sdelphij if (aux->asn1_cb(ASN1_OP_DETACHED_PRE, &val, it, &sarg) <= 0) 322296465Sdelphij return 0; 323183234Ssimon 324296465Sdelphij /* Copy data across, passing through filter BIOs for processing */ 325296465Sdelphij SMIME_crlf_copy(data, sarg.ndef_bio, flags); 326183234Ssimon 327296465Sdelphij /* Finalize structure */ 328296465Sdelphij if (aux->asn1_cb(ASN1_OP_DETACHED_POST, &val, it, &sarg) <= 0) 329296465Sdelphij return 0; 330183234Ssimon 331296465Sdelphij /* Now remove any digests prepended to the BIO */ 332183234Ssimon 333296465Sdelphij while (sarg.ndef_bio != out) { 334296465Sdelphij tmpbio = BIO_pop(sarg.ndef_bio); 335296465Sdelphij BIO_free(sarg.ndef_bio); 336296465Sdelphij sarg.ndef_bio = tmpbio; 337296465Sdelphij } 338183234Ssimon 339296465Sdelphij return 1; 340183234Ssimon 341296465Sdelphij} 342183234Ssimon 343183234Ssimon#endif 344183234Ssimon 345296465Sdelphij/* 346296465Sdelphij * SMIME reader: handle multipart/signed and opaque signing. in multipart 347296465Sdelphij * case the content is placed in a memory BIO pointed to by "bcont". In 348296465Sdelphij * opaque this is set to NULL 349183234Ssimon */ 350183234Ssimon 351183234SsimonASN1_VALUE *SMIME_read_ASN1(BIO *bio, BIO **bcont, const ASN1_ITEM *it) 352183234Ssimon{ 353296465Sdelphij BIO *asnin; 354296465Sdelphij STACK_OF(MIME_HEADER) *headers = NULL; 355296465Sdelphij STACK_OF(BIO) *parts = NULL; 356296465Sdelphij MIME_HEADER *hdr; 357296465Sdelphij MIME_PARAM *prm; 358296465Sdelphij ASN1_VALUE *val; 359296465Sdelphij int ret; 360183234Ssimon 361296465Sdelphij if (bcont) 362296465Sdelphij *bcont = NULL; 363183234Ssimon 364296465Sdelphij if (!(headers = mime_parse_hdr(bio))) { 365296465Sdelphij ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_MIME_PARSE_ERROR); 366296465Sdelphij return NULL; 367296465Sdelphij } 368183234Ssimon 369296465Sdelphij if (!(hdr = mime_hdr_find(headers, "content-type")) || !hdr->value) { 370296465Sdelphij sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 371296465Sdelphij ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_CONTENT_TYPE); 372296465Sdelphij return NULL; 373296465Sdelphij } 374183234Ssimon 375296465Sdelphij /* Handle multipart/signed */ 376183234Ssimon 377296465Sdelphij if (!strcmp(hdr->value, "multipart/signed")) { 378296465Sdelphij /* Split into two parts */ 379296465Sdelphij prm = mime_param_find(hdr, "boundary"); 380296465Sdelphij if (!prm || !prm->param_value) { 381296465Sdelphij sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 382296465Sdelphij ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_MULTIPART_BOUNDARY); 383296465Sdelphij return NULL; 384296465Sdelphij } 385296465Sdelphij ret = multi_split(bio, prm->param_value, &parts); 386296465Sdelphij sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 387296465Sdelphij if (!ret || (sk_BIO_num(parts) != 2)) { 388296465Sdelphij ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_MULTIPART_BODY_FAILURE); 389296465Sdelphij sk_BIO_pop_free(parts, BIO_vfree); 390296465Sdelphij return NULL; 391296465Sdelphij } 392183234Ssimon 393296465Sdelphij /* Parse the signature piece */ 394296465Sdelphij asnin = sk_BIO_value(parts, 1); 395183234Ssimon 396296465Sdelphij if (!(headers = mime_parse_hdr(asnin))) { 397296465Sdelphij ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_MIME_SIG_PARSE_ERROR); 398296465Sdelphij sk_BIO_pop_free(parts, BIO_vfree); 399296465Sdelphij return NULL; 400296465Sdelphij } 401183234Ssimon 402296465Sdelphij /* Get content type */ 403183234Ssimon 404296465Sdelphij if (!(hdr = mime_hdr_find(headers, "content-type")) || !hdr->value) { 405296465Sdelphij sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 406296465Sdelphij ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_SIG_CONTENT_TYPE); 407296465Sdelphij return NULL; 408296465Sdelphij } 409183234Ssimon 410296465Sdelphij if (strcmp(hdr->value, "application/x-pkcs7-signature") && 411296465Sdelphij strcmp(hdr->value, "application/pkcs7-signature")) { 412296465Sdelphij ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_SIG_INVALID_MIME_TYPE); 413296465Sdelphij ERR_add_error_data(2, "type: ", hdr->value); 414296465Sdelphij sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 415296465Sdelphij sk_BIO_pop_free(parts, BIO_vfree); 416296465Sdelphij return NULL; 417296465Sdelphij } 418296465Sdelphij sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 419296465Sdelphij /* Read in ASN1 */ 420296465Sdelphij if (!(val = b64_read_asn1(asnin, it))) { 421296465Sdelphij ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_ASN1_SIG_PARSE_ERROR); 422296465Sdelphij sk_BIO_pop_free(parts, BIO_vfree); 423296465Sdelphij return NULL; 424296465Sdelphij } 425183234Ssimon 426296465Sdelphij if (bcont) { 427296465Sdelphij *bcont = sk_BIO_value(parts, 0); 428296465Sdelphij BIO_free(asnin); 429296465Sdelphij sk_BIO_free(parts); 430296465Sdelphij } else 431296465Sdelphij sk_BIO_pop_free(parts, BIO_vfree); 432296465Sdelphij return val; 433296465Sdelphij } 434183234Ssimon 435296465Sdelphij /* OK, if not multipart/signed try opaque signature */ 436183234Ssimon 437296465Sdelphij if (strcmp(hdr->value, "application/x-pkcs7-mime") && 438296465Sdelphij strcmp(hdr->value, "application/pkcs7-mime")) { 439296465Sdelphij ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_INVALID_MIME_TYPE); 440296465Sdelphij ERR_add_error_data(2, "type: ", hdr->value); 441296465Sdelphij sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 442296465Sdelphij return NULL; 443296465Sdelphij } 444183234Ssimon 445296465Sdelphij sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 446296465Sdelphij 447296465Sdelphij if (!(val = b64_read_asn1(bio, it))) { 448296465Sdelphij ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_ASN1_PARSE_ERROR); 449296465Sdelphij return NULL; 450296465Sdelphij } 451296465Sdelphij return val; 452296465Sdelphij 453183234Ssimon} 454183234Ssimon 455183234Ssimon/* Copy text from one BIO to another making the output CRLF at EOL */ 456183234Ssimonint SMIME_crlf_copy(BIO *in, BIO *out, int flags) 457183234Ssimon{ 458296465Sdelphij BIO *bf; 459296465Sdelphij char eol; 460296465Sdelphij int len; 461296465Sdelphij char linebuf[MAX_SMLEN]; 462296465Sdelphij /* 463296465Sdelphij * Buffer output so we don't write one line at a time. This is useful 464296465Sdelphij * when streaming as we don't end up with one OCTET STRING per line. 465296465Sdelphij */ 466296465Sdelphij bf = BIO_new(BIO_f_buffer()); 467296465Sdelphij if (!bf) 468296465Sdelphij return 0; 469296465Sdelphij out = BIO_push(bf, out); 470296465Sdelphij if (flags & SMIME_BINARY) { 471296465Sdelphij while ((len = BIO_read(in, linebuf, MAX_SMLEN)) > 0) 472296465Sdelphij BIO_write(out, linebuf, len); 473296465Sdelphij } else { 474296465Sdelphij if (flags & SMIME_TEXT) 475296465Sdelphij BIO_printf(out, "Content-Type: text/plain\r\n\r\n"); 476296465Sdelphij while ((len = BIO_gets(in, linebuf, MAX_SMLEN)) > 0) { 477296465Sdelphij eol = strip_eol(linebuf, &len); 478296465Sdelphij if (len) 479296465Sdelphij BIO_write(out, linebuf, len); 480296465Sdelphij if (eol) 481296465Sdelphij BIO_write(out, "\r\n", 2); 482296465Sdelphij } 483296465Sdelphij } 484296465Sdelphij (void)BIO_flush(out); 485296465Sdelphij BIO_pop(out); 486296465Sdelphij BIO_free(bf); 487296465Sdelphij return 1; 488183234Ssimon} 489183234Ssimon 490183234Ssimon/* Strip off headers if they are text/plain */ 491183234Ssimonint SMIME_text(BIO *in, BIO *out) 492183234Ssimon{ 493296465Sdelphij char iobuf[4096]; 494296465Sdelphij int len; 495296465Sdelphij STACK_OF(MIME_HEADER) *headers; 496296465Sdelphij MIME_HEADER *hdr; 497183234Ssimon 498296465Sdelphij if (!(headers = mime_parse_hdr(in))) { 499296465Sdelphij ASN1err(ASN1_F_SMIME_TEXT, ASN1_R_MIME_PARSE_ERROR); 500296465Sdelphij return 0; 501296465Sdelphij } 502296465Sdelphij if (!(hdr = mime_hdr_find(headers, "content-type")) || !hdr->value) { 503296465Sdelphij ASN1err(ASN1_F_SMIME_TEXT, ASN1_R_MIME_NO_CONTENT_TYPE); 504296465Sdelphij sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 505296465Sdelphij return 0; 506296465Sdelphij } 507296465Sdelphij if (strcmp(hdr->value, "text/plain")) { 508296465Sdelphij ASN1err(ASN1_F_SMIME_TEXT, ASN1_R_INVALID_MIME_TYPE); 509296465Sdelphij ERR_add_error_data(2, "type: ", hdr->value); 510296465Sdelphij sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 511296465Sdelphij return 0; 512296465Sdelphij } 513296465Sdelphij sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 514296465Sdelphij while ((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0) 515296465Sdelphij BIO_write(out, iobuf, len); 516296465Sdelphij if (len < 0) 517296465Sdelphij return 0; 518296465Sdelphij return 1; 519183234Ssimon} 520183234Ssimon 521296465Sdelphij/* 522296465Sdelphij * Split a multipart/XXX message body into component parts: result is 523183234Ssimon * canonical parts in a STACK of bios 524183234Ssimon */ 525183234Ssimon 526183234Ssimonstatic int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret) 527183234Ssimon{ 528296465Sdelphij char linebuf[MAX_SMLEN]; 529296465Sdelphij int len, blen; 530296465Sdelphij int eol = 0, next_eol = 0; 531296465Sdelphij BIO *bpart = NULL; 532296465Sdelphij STACK_OF(BIO) *parts; 533296465Sdelphij char state, part, first; 534183234Ssimon 535296465Sdelphij blen = strlen(bound); 536296465Sdelphij part = 0; 537296465Sdelphij state = 0; 538296465Sdelphij first = 1; 539296465Sdelphij parts = sk_BIO_new_null(); 540296465Sdelphij *ret = parts; 541296465Sdelphij while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) { 542296465Sdelphij state = mime_bound_check(linebuf, len, bound, blen); 543296465Sdelphij if (state == 1) { 544296465Sdelphij first = 1; 545296465Sdelphij part++; 546296465Sdelphij } else if (state == 2) { 547296465Sdelphij sk_BIO_push(parts, bpart); 548296465Sdelphij return 1; 549296465Sdelphij } else if (part) { 550296465Sdelphij /* Strip CR+LF from linebuf */ 551296465Sdelphij next_eol = strip_eol(linebuf, &len); 552296465Sdelphij if (first) { 553296465Sdelphij first = 0; 554296465Sdelphij if (bpart) 555296465Sdelphij sk_BIO_push(parts, bpart); 556296465Sdelphij bpart = BIO_new(BIO_s_mem()); 557296465Sdelphij BIO_set_mem_eof_return(bpart, 0); 558296465Sdelphij } else if (eol) 559296465Sdelphij BIO_write(bpart, "\r\n", 2); 560296465Sdelphij eol = next_eol; 561296465Sdelphij if (len) 562296465Sdelphij BIO_write(bpart, linebuf, len); 563296465Sdelphij } 564296465Sdelphij } 565296465Sdelphij return 0; 566183234Ssimon} 567183234Ssimon 568183234Ssimon/* This is the big one: parse MIME header lines up to message body */ 569183234Ssimon 570296465Sdelphij#define MIME_INVALID 0 571296465Sdelphij#define MIME_START 1 572296465Sdelphij#define MIME_TYPE 2 573296465Sdelphij#define MIME_NAME 3 574296465Sdelphij#define MIME_VALUE 4 575296465Sdelphij#define MIME_QUOTE 5 576296465Sdelphij#define MIME_COMMENT 6 577183234Ssimon 578183234Ssimonstatic STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio) 579183234Ssimon{ 580296465Sdelphij char *p, *q, c; 581296465Sdelphij char *ntmp; 582296465Sdelphij char linebuf[MAX_SMLEN]; 583296465Sdelphij MIME_HEADER *mhdr = NULL; 584296465Sdelphij STACK_OF(MIME_HEADER) *headers; 585296465Sdelphij int len, state, save_state = 0; 586183234Ssimon 587296465Sdelphij headers = sk_MIME_HEADER_new(mime_hdr_cmp); 588296465Sdelphij if (!headers) 589296465Sdelphij return NULL; 590296465Sdelphij while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) { 591296465Sdelphij /* If whitespace at line start then continuation line */ 592296465Sdelphij if (mhdr && isspace((unsigned char)linebuf[0])) 593296465Sdelphij state = MIME_NAME; 594296465Sdelphij else 595296465Sdelphij state = MIME_START; 596296465Sdelphij ntmp = NULL; 597296465Sdelphij /* Go through all characters */ 598296465Sdelphij for (p = linebuf, q = linebuf; (c = *p) && (c != '\r') && (c != '\n'); 599296465Sdelphij p++) { 600183234Ssimon 601296465Sdelphij /* 602296465Sdelphij * State machine to handle MIME headers if this looks horrible 603296465Sdelphij * that's because it *is* 604296465Sdelphij */ 605183234Ssimon 606296465Sdelphij switch (state) { 607296465Sdelphij case MIME_START: 608296465Sdelphij if (c == ':') { 609296465Sdelphij state = MIME_TYPE; 610296465Sdelphij *p = 0; 611296465Sdelphij ntmp = strip_ends(q); 612296465Sdelphij q = p + 1; 613296465Sdelphij } 614296465Sdelphij break; 615183234Ssimon 616296465Sdelphij case MIME_TYPE: 617296465Sdelphij if (c == ';') { 618296465Sdelphij mime_debug("Found End Value\n"); 619296465Sdelphij *p = 0; 620296465Sdelphij mhdr = mime_hdr_new(ntmp, strip_ends(q)); 621296465Sdelphij sk_MIME_HEADER_push(headers, mhdr); 622296465Sdelphij ntmp = NULL; 623296465Sdelphij q = p + 1; 624296465Sdelphij state = MIME_NAME; 625296465Sdelphij } else if (c == '(') { 626296465Sdelphij save_state = state; 627296465Sdelphij state = MIME_COMMENT; 628296465Sdelphij } 629296465Sdelphij break; 630183234Ssimon 631296465Sdelphij case MIME_COMMENT: 632296465Sdelphij if (c == ')') { 633296465Sdelphij state = save_state; 634296465Sdelphij } 635296465Sdelphij break; 636183234Ssimon 637296465Sdelphij case MIME_NAME: 638296465Sdelphij if (c == '=') { 639296465Sdelphij state = MIME_VALUE; 640296465Sdelphij *p = 0; 641296465Sdelphij ntmp = strip_ends(q); 642296465Sdelphij q = p + 1; 643296465Sdelphij } 644296465Sdelphij break; 645183234Ssimon 646296465Sdelphij case MIME_VALUE: 647296465Sdelphij if (c == ';') { 648296465Sdelphij state = MIME_NAME; 649296465Sdelphij *p = 0; 650296465Sdelphij mime_hdr_addparam(mhdr, ntmp, strip_ends(q)); 651296465Sdelphij ntmp = NULL; 652296465Sdelphij q = p + 1; 653296465Sdelphij } else if (c == '"') { 654296465Sdelphij mime_debug("Found Quote\n"); 655296465Sdelphij state = MIME_QUOTE; 656296465Sdelphij } else if (c == '(') { 657296465Sdelphij save_state = state; 658296465Sdelphij state = MIME_COMMENT; 659296465Sdelphij } 660296465Sdelphij break; 661183234Ssimon 662296465Sdelphij case MIME_QUOTE: 663296465Sdelphij if (c == '"') { 664296465Sdelphij mime_debug("Found Match Quote\n"); 665296465Sdelphij state = MIME_VALUE; 666296465Sdelphij } 667296465Sdelphij break; 668296465Sdelphij } 669296465Sdelphij } 670183234Ssimon 671296465Sdelphij if (state == MIME_TYPE) { 672296465Sdelphij mhdr = mime_hdr_new(ntmp, strip_ends(q)); 673296465Sdelphij sk_MIME_HEADER_push(headers, mhdr); 674296465Sdelphij } else if (state == MIME_VALUE) 675296465Sdelphij mime_hdr_addparam(mhdr, ntmp, strip_ends(q)); 676296465Sdelphij if (p == linebuf) 677296465Sdelphij break; /* Blank line means end of headers */ 678296465Sdelphij } 679183234Ssimon 680296465Sdelphij return headers; 681183234Ssimon 682183234Ssimon} 683183234Ssimon 684183234Ssimonstatic char *strip_ends(char *name) 685183234Ssimon{ 686296465Sdelphij return strip_end(strip_start(name)); 687183234Ssimon} 688183234Ssimon 689183234Ssimon/* Strip a parameter of whitespace from start of param */ 690183234Ssimonstatic char *strip_start(char *name) 691183234Ssimon{ 692296465Sdelphij char *p, c; 693296465Sdelphij /* Look for first non white space or quote */ 694296465Sdelphij for (p = name; (c = *p); p++) { 695296465Sdelphij if (c == '"') { 696296465Sdelphij /* Next char is start of string if non null */ 697296465Sdelphij if (p[1]) 698296465Sdelphij return p + 1; 699296465Sdelphij /* Else null string */ 700296465Sdelphij return NULL; 701296465Sdelphij } 702296465Sdelphij if (!isspace((unsigned char)c)) 703296465Sdelphij return p; 704296465Sdelphij } 705296465Sdelphij return NULL; 706183234Ssimon} 707183234Ssimon 708183234Ssimon/* As above but strip from end of string : maybe should handle brackets? */ 709183234Ssimonstatic char *strip_end(char *name) 710183234Ssimon{ 711296465Sdelphij char *p, c; 712296465Sdelphij if (!name) 713296465Sdelphij return NULL; 714296465Sdelphij /* Look for first non white space or quote */ 715296465Sdelphij for (p = name + strlen(name) - 1; p >= name; p--) { 716296465Sdelphij c = *p; 717296465Sdelphij if (c == '"') { 718296465Sdelphij if (p - 1 == name) 719296465Sdelphij return NULL; 720296465Sdelphij *p = 0; 721296465Sdelphij return name; 722296465Sdelphij } 723296465Sdelphij if (isspace((unsigned char)c)) 724296465Sdelphij *p = 0; 725296465Sdelphij else 726296465Sdelphij return name; 727296465Sdelphij } 728296465Sdelphij return NULL; 729183234Ssimon} 730183234Ssimon 731183234Ssimonstatic MIME_HEADER *mime_hdr_new(char *name, char *value) 732183234Ssimon{ 733296465Sdelphij MIME_HEADER *mhdr; 734296465Sdelphij char *tmpname, *tmpval, *p; 735296465Sdelphij int c; 736296465Sdelphij if (name) { 737296465Sdelphij if (!(tmpname = BUF_strdup(name))) 738296465Sdelphij return NULL; 739296465Sdelphij for (p = tmpname; *p; p++) { 740296465Sdelphij c = *p; 741296465Sdelphij if (isupper(c)) { 742296465Sdelphij c = tolower(c); 743296465Sdelphij *p = c; 744296465Sdelphij } 745296465Sdelphij } 746296465Sdelphij } else 747296465Sdelphij tmpname = NULL; 748296465Sdelphij if (value) { 749296465Sdelphij if (!(tmpval = BUF_strdup(value))) 750296465Sdelphij return NULL; 751296465Sdelphij for (p = tmpval; *p; p++) { 752296465Sdelphij c = *p; 753296465Sdelphij if (isupper(c)) { 754296465Sdelphij c = tolower(c); 755296465Sdelphij *p = c; 756296465Sdelphij } 757296465Sdelphij } 758296465Sdelphij } else 759296465Sdelphij tmpval = NULL; 760296465Sdelphij mhdr = (MIME_HEADER *)OPENSSL_malloc(sizeof(MIME_HEADER)); 761296465Sdelphij if (!mhdr) 762296465Sdelphij return NULL; 763296465Sdelphij mhdr->name = tmpname; 764296465Sdelphij mhdr->value = tmpval; 765296465Sdelphij if (!(mhdr->params = sk_MIME_PARAM_new(mime_param_cmp))) 766296465Sdelphij return NULL; 767296465Sdelphij return mhdr; 768183234Ssimon} 769296465Sdelphij 770183234Ssimonstatic int mime_hdr_addparam(MIME_HEADER *mhdr, char *name, char *value) 771183234Ssimon{ 772296465Sdelphij char *tmpname, *tmpval, *p; 773296465Sdelphij int c; 774296465Sdelphij MIME_PARAM *mparam; 775296465Sdelphij if (name) { 776296465Sdelphij tmpname = BUF_strdup(name); 777296465Sdelphij if (!tmpname) 778296465Sdelphij return 0; 779296465Sdelphij for (p = tmpname; *p; p++) { 780296465Sdelphij c = *p; 781296465Sdelphij if (isupper(c)) { 782296465Sdelphij c = tolower(c); 783296465Sdelphij *p = c; 784296465Sdelphij } 785296465Sdelphij } 786296465Sdelphij } else 787296465Sdelphij tmpname = NULL; 788296465Sdelphij if (value) { 789296465Sdelphij tmpval = BUF_strdup(value); 790296465Sdelphij if (!tmpval) 791296465Sdelphij return 0; 792296465Sdelphij } else 793296465Sdelphij tmpval = NULL; 794296465Sdelphij /* Parameter values are case sensitive so leave as is */ 795296465Sdelphij mparam = (MIME_PARAM *)OPENSSL_malloc(sizeof(MIME_PARAM)); 796296465Sdelphij if (!mparam) 797296465Sdelphij return 0; 798296465Sdelphij mparam->param_name = tmpname; 799296465Sdelphij mparam->param_value = tmpval; 800296465Sdelphij sk_MIME_PARAM_push(mhdr->params, mparam); 801296465Sdelphij return 1; 802183234Ssimon} 803183234Ssimon 804296465Sdelphijstatic int mime_hdr_cmp(const MIME_HEADER *const *a, 805296465Sdelphij const MIME_HEADER *const *b) 806183234Ssimon{ 807296465Sdelphij if (!(*a)->name || !(*b)->name) 808296465Sdelphij return ! !(*a)->name - ! !(*b)->name; 809237998Sjkim 810296465Sdelphij return (strcmp((*a)->name, (*b)->name)); 811183234Ssimon} 812183234Ssimon 813296465Sdelphijstatic int mime_param_cmp(const MIME_PARAM *const *a, 814296465Sdelphij const MIME_PARAM *const *b) 815183234Ssimon{ 816296465Sdelphij if (!(*a)->param_name || !(*b)->param_name) 817296465Sdelphij return ! !(*a)->param_name - ! !(*b)->param_name; 818296465Sdelphij return (strcmp((*a)->param_name, (*b)->param_name)); 819183234Ssimon} 820183234Ssimon 821183234Ssimon/* Find a header with a given name (if possible) */ 822183234Ssimon 823183234Ssimonstatic MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, char *name) 824183234Ssimon{ 825296465Sdelphij MIME_HEADER htmp; 826296465Sdelphij int idx; 827296465Sdelphij htmp.name = name; 828296465Sdelphij idx = sk_MIME_HEADER_find(hdrs, &htmp); 829296465Sdelphij if (idx < 0) 830296465Sdelphij return NULL; 831296465Sdelphij return sk_MIME_HEADER_value(hdrs, idx); 832183234Ssimon} 833183234Ssimon 834183234Ssimonstatic MIME_PARAM *mime_param_find(MIME_HEADER *hdr, char *name) 835183234Ssimon{ 836296465Sdelphij MIME_PARAM param; 837296465Sdelphij int idx; 838296465Sdelphij param.param_name = name; 839296465Sdelphij idx = sk_MIME_PARAM_find(hdr->params, ¶m); 840296465Sdelphij if (idx < 0) 841296465Sdelphij return NULL; 842296465Sdelphij return sk_MIME_PARAM_value(hdr->params, idx); 843183234Ssimon} 844183234Ssimon 845183234Ssimonstatic void mime_hdr_free(MIME_HEADER *hdr) 846183234Ssimon{ 847296465Sdelphij if (hdr->name) 848296465Sdelphij OPENSSL_free(hdr->name); 849296465Sdelphij if (hdr->value) 850296465Sdelphij OPENSSL_free(hdr->value); 851296465Sdelphij if (hdr->params) 852296465Sdelphij sk_MIME_PARAM_pop_free(hdr->params, mime_param_free); 853296465Sdelphij OPENSSL_free(hdr); 854183234Ssimon} 855183234Ssimon 856183234Ssimonstatic void mime_param_free(MIME_PARAM *param) 857183234Ssimon{ 858296465Sdelphij if (param->param_name) 859296465Sdelphij OPENSSL_free(param->param_name); 860296465Sdelphij if (param->param_value) 861296465Sdelphij OPENSSL_free(param->param_value); 862296465Sdelphij OPENSSL_free(param); 863183234Ssimon} 864183234Ssimon 865296465Sdelphij/*- 866296465Sdelphij * Check for a multipart boundary. Returns: 867183234Ssimon * 0 : no boundary 868183234Ssimon * 1 : part boundary 869183234Ssimon * 2 : final boundary 870183234Ssimon */ 871183234Ssimonstatic int mime_bound_check(char *line, int linelen, char *bound, int blen) 872183234Ssimon{ 873296465Sdelphij if (linelen == -1) 874296465Sdelphij linelen = strlen(line); 875296465Sdelphij if (blen == -1) 876296465Sdelphij blen = strlen(bound); 877296465Sdelphij /* Quickly eliminate if line length too short */ 878296465Sdelphij if (blen + 2 > linelen) 879296465Sdelphij return 0; 880296465Sdelphij /* Check for part boundary */ 881296465Sdelphij if (!strncmp(line, "--", 2) && !strncmp(line + 2, bound, blen)) { 882296465Sdelphij if (!strncmp(line + blen + 2, "--", 2)) 883296465Sdelphij return 2; 884296465Sdelphij else 885296465Sdelphij return 1; 886296465Sdelphij } 887296465Sdelphij return 0; 888183234Ssimon} 889183234Ssimon 890183234Ssimonstatic int strip_eol(char *linebuf, int *plen) 891296465Sdelphij{ 892296465Sdelphij int len = *plen; 893296465Sdelphij char *p, c; 894296465Sdelphij int is_eol = 0; 895296465Sdelphij p = linebuf + len - 1; 896296465Sdelphij for (p = linebuf + len - 1; len > 0; len--, p--) { 897296465Sdelphij c = *p; 898296465Sdelphij if (c == '\n') 899296465Sdelphij is_eol = 1; 900296465Sdelphij else if (c != '\r') 901296465Sdelphij break; 902296465Sdelphij } 903296465Sdelphij *plen = len; 904296465Sdelphij return is_eol; 905296465Sdelphij} 906