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