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