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 /* 493 * Buffer output so we don't write one line at a time. This is useful 494 * when streaming as we don't end up with one OCTET STRING per line. 495 */ 496 bf = BIO_new(BIO_f_buffer()); 497 if (bf == NULL) 498 return 0; 499 out = BIO_push(bf, out); 500 if (flags & SMIME_BINARY) { 501 while ((len = BIO_read(in, linebuf, MAX_SMLEN)) > 0) 502 BIO_write(out, linebuf, len); 503 } else { 504 int eolcnt = 0; 505 if (flags & SMIME_TEXT) 506 BIO_printf(out, "Content-Type: text/plain\r\n\r\n"); 507 while ((len = BIO_gets(in, linebuf, MAX_SMLEN)) > 0) { 508 eol = strip_eol(linebuf, &len, flags); 509 if (len) { 510 /* Not EOF: write out all CRLF */ 511 if (flags & SMIME_ASCIICRLF) { 512 int i; 513 for (i = 0; i < eolcnt; i++) 514 BIO_write(out, "\r\n", 2); 515 eolcnt = 0; 516 } 517 BIO_write(out, linebuf, len); 518 if (eol) 519 BIO_write(out, "\r\n", 2); 520 } else if (flags & SMIME_ASCIICRLF) 521 eolcnt++; 522 else if (eol) 523 BIO_write(out, "\r\n", 2); 524 } 525 } 526 (void)BIO_flush(out); 527 BIO_pop(out); 528 BIO_free(bf); 529 return 1; 530 } 531 532 /* Strip off headers if they are text/plain */ 533 int SMIME_text(BIO *in, BIO *out) 534 { 535 char iobuf[4096]; 536 int len; 537 STACK_OF(MIME_HEADER) *headers; 538 MIME_HEADER *hdr; 539 540 if ((headers = mime_parse_hdr(in)) == NULL) { 541 ASN1err(ASN1_F_SMIME_TEXT, ASN1_R_MIME_PARSE_ERROR); 542 return 0; 543 } 544 if ((hdr = mime_hdr_find(headers, "content-type")) == NULL 545 || hdr->value == NULL) { 546 ASN1err(ASN1_F_SMIME_TEXT, ASN1_R_MIME_NO_CONTENT_TYPE); 547 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 548 return 0; 549 } 550 if (strcmp(hdr->value, "text/plain")) { 551 ASN1err(ASN1_F_SMIME_TEXT, ASN1_R_INVALID_MIME_TYPE); 552 ERR_add_error_data(2, "type: ", hdr->value); 553 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 554 return 0; 555 } 556 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 557 while ((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0) 558 BIO_write(out, iobuf, len); 559 if (len < 0) 560 return 0; 561 return 1; 562 } 563 564 /* 565 * Split a multipart/XXX message body into component parts: result is 566 * canonical parts in a STACK of bios 567 */ 568 569 static int multi_split(BIO *bio, const char *bound, STACK_OF(BIO) **ret) 570 { 571 char linebuf[MAX_SMLEN]; 572 int len, blen; 573 int eol = 0, next_eol = 0; 574 BIO *bpart = NULL; 575 STACK_OF(BIO) *parts; 576 char state, part, first; 577 578 blen = strlen(bound); 579 part = 0; 580 state = 0; 581 first = 1; 582 parts = sk_BIO_new_null(); 583 *ret = parts; 584 if (*ret == NULL) 585 return 0; 586 while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) { 587 state = mime_bound_check(linebuf, len, bound, blen); 588 if (state == 1) { 589 first = 1; 590 part++; 591 } else if (state == 2) { 592 if (!sk_BIO_push(parts, bpart)) { 593 BIO_free(bpart); 594 return 0; 595 } 596 return 1; 597 } else if (part) { 598 /* Strip CR+LF from linebuf */ 599 next_eol = strip_eol(linebuf, &len, 0); 600 if (first) { 601 first = 0; 602 if (bpart) 603 if (!sk_BIO_push(parts, bpart)) { 604 BIO_free(bpart); 605 return 0; 606 } 607 bpart = BIO_new(BIO_s_mem()); 608 if (bpart == NULL) 609 return 0; 610 BIO_set_mem_eof_return(bpart, 0); 611 } else if (eol) 612 BIO_write(bpart, "\r\n", 2); 613 eol = next_eol; 614 if (len) 615 BIO_write(bpart, linebuf, len); 616 } 617 } 618 BIO_free(bpart); 619 return 0; 620 } 621 622 /* This is the big one: parse MIME header lines up to message body */ 623 624 #define MIME_INVALID 0 625 #define MIME_START 1 626 #define MIME_TYPE 2 627 #define MIME_NAME 3 628 #define MIME_VALUE 4 629 #define MIME_QUOTE 5 630 #define MIME_COMMENT 6 631 632 static STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio) 633 { 634 char *p, *q, c; 635 char *ntmp; 636 char linebuf[MAX_SMLEN]; 637 MIME_HEADER *mhdr = NULL, *new_hdr = NULL; 638 STACK_OF(MIME_HEADER) *headers; 639 int len, state, save_state = 0; 640 641 headers = sk_MIME_HEADER_new(mime_hdr_cmp); 642 if (headers == NULL) 643 return NULL; 644 while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) { 645 /* If whitespace at line start then continuation line */ 646 if (mhdr && ossl_isspace(linebuf[0])) 647 state = MIME_NAME; 648 else 649 state = MIME_START; 650 ntmp = NULL; 651 /* Go through all characters */ 652 for (p = linebuf, q = linebuf; (c = *p) && (c != '\r') && (c != '\n'); 653 p++) { 654 655 /* 656 * State machine to handle MIME headers if this looks horrible 657 * that's because it *is* 658 */ 659 660 switch (state) { 661 case MIME_START: 662 if (c == ':') { 663 state = MIME_TYPE; 664 *p = 0; 665 ntmp = strip_ends(q); 666 q = p + 1; 667 } 668 break; 669 670 case MIME_TYPE: 671 if (c == ';') { 672 mime_debug("Found End Value\n"); 673 *p = 0; 674 new_hdr = mime_hdr_new(ntmp, strip_ends(q)); 675 if (new_hdr == NULL) 676 goto err; 677 if (!sk_MIME_HEADER_push(headers, new_hdr)) 678 goto err; 679 mhdr = new_hdr; 680 new_hdr = NULL; 681 ntmp = NULL; 682 q = p + 1; 683 state = MIME_NAME; 684 } else if (c == '(') { 685 save_state = state; 686 state = MIME_COMMENT; 687 } 688 break; 689 690 case MIME_COMMENT: 691 if (c == ')') { 692 state = save_state; 693 } 694 break; 695 696 case MIME_NAME: 697 if (c == '=') { 698 state = MIME_VALUE; 699 *p = 0; 700 ntmp = strip_ends(q); 701 q = p + 1; 702 } 703 break; 704 705 case MIME_VALUE: 706 if (c == ';') { 707 state = MIME_NAME; 708 *p = 0; 709 mime_hdr_addparam(mhdr, ntmp, strip_ends(q)); 710 ntmp = NULL; 711 q = p + 1; 712 } else if (c == '"') { 713 mime_debug("Found Quote\n"); 714 state = MIME_QUOTE; 715 } else if (c == '(') { 716 save_state = state; 717 state = MIME_COMMENT; 718 } 719 break; 720 721 case MIME_QUOTE: 722 if (c == '"') { 723 mime_debug("Found Match Quote\n"); 724 state = MIME_VALUE; 725 } 726 break; 727 } 728 } 729 730 if (state == MIME_TYPE) { 731 new_hdr = mime_hdr_new(ntmp, strip_ends(q)); 732 if (new_hdr == NULL) 733 goto err; 734 if (!sk_MIME_HEADER_push(headers, new_hdr)) 735 goto err; 736 mhdr = new_hdr; 737 new_hdr = NULL; 738 } else if (state == MIME_VALUE) 739 mime_hdr_addparam(mhdr, ntmp, strip_ends(q)); 740 if (p == linebuf) 741 break; /* Blank line means end of headers */ 742 } 743 744 return headers; 745 746 err: 747 mime_hdr_free(new_hdr); 748 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 749 return NULL; 750 } 751 752 static 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 */ 758 static 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 (!ossl_isspace(c)) 771 return p; 772 } 773 return NULL; 774 } 775 776 /* As above but strip from end of string : maybe should handle brackets? */ 777 static 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 (ossl_isspace(c)) 792 *p = 0; 793 else 794 return name; 795 } 796 return NULL; 797 } 798 799 static MIME_HEADER *mime_hdr_new(const char *name, const char *value) 800 { 801 MIME_HEADER *mhdr = NULL; 802 char *tmpname = NULL, *tmpval = NULL, *p; 803 804 if (name) { 805 if ((tmpname = OPENSSL_strdup(name)) == NULL) 806 return NULL; 807 for (p = tmpname; *p; p++) 808 *p = ossl_tolower(*p); 809 } 810 if (value) { 811 if ((tmpval = OPENSSL_strdup(value)) == NULL) 812 goto err; 813 for (p = tmpval; *p; p++) 814 *p = ossl_tolower(*p); 815 } 816 mhdr = OPENSSL_malloc(sizeof(*mhdr)); 817 if (mhdr == NULL) 818 goto err; 819 mhdr->name = tmpname; 820 mhdr->value = tmpval; 821 if ((mhdr->params = sk_MIME_PARAM_new(mime_param_cmp)) == NULL) 822 goto err; 823 return mhdr; 824 825 err: 826 OPENSSL_free(tmpname); 827 OPENSSL_free(tmpval); 828 OPENSSL_free(mhdr); 829 return NULL; 830 } 831 832 static int mime_hdr_addparam(MIME_HEADER *mhdr, const char *name, const char *value) 833 { 834 char *tmpname = NULL, *tmpval = NULL, *p; 835 MIME_PARAM *mparam = NULL; 836 837 if (name) { 838 tmpname = OPENSSL_strdup(name); 839 if (!tmpname) 840 goto err; 841 for (p = tmpname; *p; p++) 842 *p = ossl_tolower(*p); 843 } 844 if (value) { 845 tmpval = OPENSSL_strdup(value); 846 if (!tmpval) 847 goto err; 848 } 849 /* Parameter values are case sensitive so leave as is */ 850 mparam = OPENSSL_malloc(sizeof(*mparam)); 851 if (mparam == NULL) 852 goto err; 853 mparam->param_name = tmpname; 854 mparam->param_value = tmpval; 855 if (!sk_MIME_PARAM_push(mhdr->params, mparam)) 856 goto err; 857 return 1; 858 err: 859 OPENSSL_free(tmpname); 860 OPENSSL_free(tmpval); 861 OPENSSL_free(mparam); 862 return 0; 863 } 864 865 static int mime_hdr_cmp(const MIME_HEADER *const *a, 866 const MIME_HEADER *const *b) 867 { 868 if (!(*a)->name || !(*b)->name) 869 return ! !(*a)->name - ! !(*b)->name; 870 871 return strcmp((*a)->name, (*b)->name); 872 } 873 874 static int mime_param_cmp(const MIME_PARAM *const *a, 875 const MIME_PARAM *const *b) 876 { 877 if (!(*a)->param_name || !(*b)->param_name) 878 return ! !(*a)->param_name - ! !(*b)->param_name; 879 return strcmp((*a)->param_name, (*b)->param_name); 880 } 881 882 /* Find a header with a given name (if possible) */ 883 884 static MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, const char *name) 885 { 886 MIME_HEADER htmp; 887 int idx; 888 889 htmp.name = (char *)name; 890 htmp.value = NULL; 891 htmp.params = NULL; 892 893 idx = sk_MIME_HEADER_find(hdrs, &htmp); 894 return sk_MIME_HEADER_value(hdrs, idx); 895 } 896 897 static MIME_PARAM *mime_param_find(MIME_HEADER *hdr, const char *name) 898 { 899 MIME_PARAM param; 900 int idx; 901 902 param.param_name = (char *)name; 903 param.param_value = NULL; 904 idx = sk_MIME_PARAM_find(hdr->params, ¶m); 905 return sk_MIME_PARAM_value(hdr->params, idx); 906 } 907 908 static void mime_hdr_free(MIME_HEADER *hdr) 909 { 910 if (hdr == NULL) 911 return; 912 OPENSSL_free(hdr->name); 913 OPENSSL_free(hdr->value); 914 if (hdr->params) 915 sk_MIME_PARAM_pop_free(hdr->params, mime_param_free); 916 OPENSSL_free(hdr); 917 } 918 919 static void mime_param_free(MIME_PARAM *param) 920 { 921 OPENSSL_free(param->param_name); 922 OPENSSL_free(param->param_value); 923 OPENSSL_free(param); 924 } 925 926 /*- 927 * Check for a multipart boundary. Returns: 928 * 0 : no boundary 929 * 1 : part boundary 930 * 2 : final boundary 931 */ 932 static int mime_bound_check(char *line, int linelen, const char *bound, int blen) 933 { 934 if (linelen == -1) 935 linelen = strlen(line); 936 if (blen == -1) 937 blen = strlen(bound); 938 /* Quickly eliminate if line length too short */ 939 if (blen + 2 > linelen) 940 return 0; 941 /* Check for part boundary */ 942 if ((strncmp(line, "--", 2) == 0) 943 && strncmp(line + 2, bound, blen) == 0) { 944 if (strncmp(line + blen + 2, "--", 2) == 0) 945 return 2; 946 else 947 return 1; 948 } 949 return 0; 950 } 951 952 static int strip_eol(char *linebuf, int *plen, int flags) 953 { 954 int len = *plen; 955 char *p, c; 956 int is_eol = 0; 957 958 for (p = linebuf + len - 1; len > 0; len--, p--) { 959 c = *p; 960 if (c == '\n') { 961 is_eol = 1; 962 } else if (is_eol && flags & SMIME_ASCIICRLF && c == 32) { 963 /* Strip trailing space on a line; 32 == ASCII for ' ' */ 964 continue; 965 } else if (c != '\r') { 966 break; 967 } 968 } 969 *plen = len; 970 return is_eol; 971 } 972