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