1/* 2 * Copyright 2008-2022 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 32struct mime_param_st { 33 char *param_name; /* Param name e.g. "micalg" */ 34 char *param_value; /* Param value e.g. "sha1" */ 35}; 36 37struct 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 43static int asn1_output_data(BIO *out, BIO *data, ASN1_VALUE *val, int flags, 44 const ASN1_ITEM *it); 45static char *strip_ends(char *name); 46static char *strip_start(char *name); 47static char *strip_end(char *name); 48static MIME_HEADER *mime_hdr_new(const char *name, const char *value); 49static int mime_hdr_addparam(MIME_HEADER *mhdr, const char *name, const char *value); 50static STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio); 51static int mime_hdr_cmp(const MIME_HEADER *const *a, 52 const MIME_HEADER *const *b); 53static int mime_param_cmp(const MIME_PARAM *const *a, 54 const MIME_PARAM *const *b); 55static void mime_param_free(MIME_PARAM *param); 56static int mime_bound_check(char *line, int linelen, const char *bound, int blen); 57static int multi_split(BIO *bio, int flags, const char *bound, STACK_OF(BIO) **ret); 58static int strip_eol(char *linebuf, int *plen, int flags); 59static MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, const char *name); 60static MIME_PARAM *mime_param_find(MIME_HEADER *hdr, const char *name); 61static 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() */ 69int 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 105static 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 128int 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 138static 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 160static 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 238int 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 330int 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() */ 341static 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 397ASN1_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 506ASN1_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 */ 512int 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 * Buffer output so we don't write one line at a time. This is useful 521 * when streaming as we don't end up with one OCTET STRING per line. 522 */ 523 bf = BIO_new(BIO_f_buffer()); 524 if (bf == NULL) { 525 ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); 526 return 0; 527 } 528 out = BIO_push(bf, out); 529 if (flags & SMIME_BINARY) { 530 while ((len = BIO_read(in, linebuf, MAX_SMLEN)) > 0) 531 BIO_write(out, linebuf, len); 532 } else { 533 int eolcnt = 0; 534 if (flags & SMIME_TEXT) 535 BIO_printf(out, "Content-Type: text/plain\r\n\r\n"); 536 while ((len = BIO_gets(in, linebuf, MAX_SMLEN)) > 0) { 537 eol = strip_eol(linebuf, &len, flags); 538 if (len > 0) { 539 /* Not EOF: write out all CRLF */ 540 if (flags & SMIME_ASCIICRLF) { 541 int i; 542 for (i = 0; i < eolcnt; i++) 543 BIO_write(out, "\r\n", 2); 544 eolcnt = 0; 545 } 546 BIO_write(out, linebuf, len); 547 if (eol) 548 BIO_write(out, "\r\n", 2); 549 } else if (flags & SMIME_ASCIICRLF) { 550 eolcnt++; 551 } else if (eol) { 552 BIO_write(out, "\r\n", 2); 553 } 554 } 555 } 556 ret = BIO_flush(out); 557 BIO_pop(out); 558 BIO_free(bf); 559 if (ret <= 0) 560 return 0; 561 562 return 1; 563} 564 565/* Strip off headers if they are text/plain */ 566int SMIME_text(BIO *in, BIO *out) 567{ 568 char iobuf[4096]; 569 int len; 570 STACK_OF(MIME_HEADER) *headers; 571 MIME_HEADER *hdr; 572 573 if ((headers = mime_parse_hdr(in)) == NULL) { 574 ERR_raise(ERR_LIB_ASN1, ASN1_R_MIME_PARSE_ERROR); 575 return 0; 576 } 577 if ((hdr = mime_hdr_find(headers, "content-type")) == NULL 578 || hdr->value == NULL) { 579 ERR_raise(ERR_LIB_ASN1, ASN1_R_MIME_NO_CONTENT_TYPE); 580 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 581 return 0; 582 } 583 if (strcmp(hdr->value, "text/plain")) { 584 ERR_raise_data(ERR_LIB_ASN1, ASN1_R_INVALID_MIME_TYPE, 585 "type: %s", hdr->value); 586 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 587 return 0; 588 } 589 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 590 while ((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0) 591 BIO_write(out, iobuf, len); 592 if (len < 0) 593 return 0; 594 return 1; 595} 596 597/* 598 * Split a multipart/XXX message body into component parts: result is 599 * canonical parts in a STACK of bios 600 */ 601 602static int multi_split(BIO *bio, int flags, const char *bound, STACK_OF(BIO) **ret) 603{ 604 char linebuf[MAX_SMLEN]; 605 int len, blen; 606 int eol = 0, next_eol = 0; 607 BIO *bpart = NULL; 608 STACK_OF(BIO) *parts; 609 char state, part, first; 610 611 blen = strlen(bound); 612 part = 0; 613 state = 0; 614 first = 1; 615 parts = sk_BIO_new_null(); 616 *ret = parts; 617 if (*ret == NULL) 618 return 0; 619 while ((len = BIO_get_line(bio, linebuf, MAX_SMLEN)) > 0) { 620 state = mime_bound_check(linebuf, len, bound, blen); 621 if (state == 1) { 622 first = 1; 623 part++; 624 } else if (state == 2) { 625 if (!sk_BIO_push(parts, bpart)) { 626 BIO_free(bpart); 627 return 0; 628 } 629 return 1; 630 } else if (part != 0) { 631 /* Strip (possibly CR +) LF from linebuf */ 632 next_eol = strip_eol(linebuf, &len, flags); 633 if (first) { 634 first = 0; 635 if (bpart) 636 if (!sk_BIO_push(parts, bpart)) { 637 BIO_free(bpart); 638 return 0; 639 } 640 bpart = BIO_new(BIO_s_mem()); 641 if (bpart == NULL) 642 return 0; 643 BIO_set_mem_eof_return(bpart, 0); 644 } else if (eol) { 645 if ( 646#ifndef OPENSSL_NO_CMS 647 (flags & CMS_BINARY) == 0 648#else 649 1 650#endif 651 || (flags & SMIME_CRLFEOL) != 0) 652 BIO_write(bpart, "\r\n", 2); 653 else 654 BIO_write(bpart, "\n", 1); 655 } 656 eol = next_eol; 657 if (len > 0) 658 BIO_write(bpart, linebuf, len); 659 } 660 } 661 BIO_free(bpart); 662 return 0; 663} 664 665/* This is the big one: parse MIME header lines up to message body */ 666 667#define MIME_INVALID 0 668#define MIME_START 1 669#define MIME_TYPE 2 670#define MIME_NAME 3 671#define MIME_VALUE 4 672#define MIME_QUOTE 5 673#define MIME_COMMENT 6 674 675static STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio) 676{ 677 char *p, *q, c; 678 char *ntmp; 679 char linebuf[MAX_SMLEN]; 680 MIME_HEADER *mhdr = NULL, *new_hdr = NULL; 681 STACK_OF(MIME_HEADER) *headers; 682 int len, state, save_state = 0; 683 684 headers = sk_MIME_HEADER_new(mime_hdr_cmp); 685 if (headers == NULL) 686 return NULL; 687 while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) { 688 /* If whitespace at line start then continuation line */ 689 if (mhdr && ossl_isspace(linebuf[0])) 690 state = MIME_NAME; 691 else 692 state = MIME_START; 693 ntmp = NULL; 694 /* Go through all characters */ 695 for (p = linebuf, q = linebuf; (c = *p) && (c != '\r') && (c != '\n'); 696 p++) { 697 698 /* 699 * State machine to handle MIME headers if this looks horrible 700 * that's because it *is* 701 */ 702 703 switch (state) { 704 case MIME_START: 705 if (c == ':') { 706 state = MIME_TYPE; 707 *p = 0; 708 ntmp = strip_ends(q); 709 q = p + 1; 710 } 711 break; 712 713 case MIME_TYPE: 714 if (c == ';') { 715 mime_debug("Found End Value\n"); 716 *p = 0; 717 new_hdr = mime_hdr_new(ntmp, strip_ends(q)); 718 if (new_hdr == NULL) 719 goto err; 720 if (!sk_MIME_HEADER_push(headers, new_hdr)) 721 goto err; 722 mhdr = new_hdr; 723 new_hdr = NULL; 724 ntmp = NULL; 725 q = p + 1; 726 state = MIME_NAME; 727 } else if (c == '(') { 728 save_state = state; 729 state = MIME_COMMENT; 730 } 731 break; 732 733 case MIME_COMMENT: 734 if (c == ')') { 735 state = save_state; 736 } 737 break; 738 739 case MIME_NAME: 740 if (c == '=') { 741 state = MIME_VALUE; 742 *p = 0; 743 ntmp = strip_ends(q); 744 q = p + 1; 745 } 746 break; 747 748 case MIME_VALUE: 749 if (c == ';') { 750 state = MIME_NAME; 751 *p = 0; 752 mime_hdr_addparam(mhdr, ntmp, strip_ends(q)); 753 ntmp = NULL; 754 q = p + 1; 755 } else if (c == '"') { 756 mime_debug("Found Quote\n"); 757 state = MIME_QUOTE; 758 } else if (c == '(') { 759 save_state = state; 760 state = MIME_COMMENT; 761 } 762 break; 763 764 case MIME_QUOTE: 765 if (c == '"') { 766 mime_debug("Found Match Quote\n"); 767 state = MIME_VALUE; 768 } 769 break; 770 } 771 } 772 773 if (state == MIME_TYPE) { 774 new_hdr = mime_hdr_new(ntmp, strip_ends(q)); 775 if (new_hdr == NULL) 776 goto err; 777 if (!sk_MIME_HEADER_push(headers, new_hdr)) 778 goto err; 779 mhdr = new_hdr; 780 new_hdr = NULL; 781 } else if (state == MIME_VALUE) { 782 mime_hdr_addparam(mhdr, ntmp, strip_ends(q)); 783 } 784 if (p == linebuf) 785 break; /* Blank line means end of headers */ 786 } 787 788 return headers; 789 790 err: 791 mime_hdr_free(new_hdr); 792 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 793 return NULL; 794} 795 796static char *strip_ends(char *name) 797{ 798 return strip_end(strip_start(name)); 799} 800 801/* Strip a parameter of whitespace from start of param */ 802static char *strip_start(char *name) 803{ 804 char *p, c; 805 /* Look for first non whitespace or quote */ 806 for (p = name; (c = *p); p++) { 807 if (c == '"') { 808 /* Next char is start of string if non null */ 809 if (p[1]) 810 return p + 1; 811 /* Else null string */ 812 return NULL; 813 } 814 if (!ossl_isspace(c)) 815 return p; 816 } 817 return NULL; 818} 819 820/* As above but strip from end of string : maybe should handle brackets? */ 821static char *strip_end(char *name) 822{ 823 char *p, c; 824 if (!name) 825 return NULL; 826 /* Look for first non whitespace or quote */ 827 for (p = name + strlen(name) - 1; p >= name; p--) { 828 c = *p; 829 if (c == '"') { 830 if (p - 1 == name) 831 return NULL; 832 *p = 0; 833 return name; 834 } 835 if (ossl_isspace(c)) 836 *p = 0; 837 else 838 return name; 839 } 840 return NULL; 841} 842 843static MIME_HEADER *mime_hdr_new(const char *name, const char *value) 844{ 845 MIME_HEADER *mhdr = NULL; 846 char *tmpname = NULL, *tmpval = NULL, *p; 847 848 if (name) { 849 if ((tmpname = OPENSSL_strdup(name)) == NULL) 850 return NULL; 851 for (p = tmpname; *p; p++) 852 *p = ossl_tolower(*p); 853 } 854 if (value) { 855 if ((tmpval = OPENSSL_strdup(value)) == NULL) 856 goto err; 857 for (p = tmpval; *p; p++) 858 *p = ossl_tolower(*p); 859 } 860 mhdr = OPENSSL_malloc(sizeof(*mhdr)); 861 if (mhdr == NULL) 862 goto err; 863 mhdr->name = tmpname; 864 mhdr->value = tmpval; 865 if ((mhdr->params = sk_MIME_PARAM_new(mime_param_cmp)) == NULL) 866 goto err; 867 return mhdr; 868 869 err: 870 OPENSSL_free(tmpname); 871 OPENSSL_free(tmpval); 872 OPENSSL_free(mhdr); 873 return NULL; 874} 875 876static int mime_hdr_addparam(MIME_HEADER *mhdr, const char *name, const char *value) 877{ 878 char *tmpname = NULL, *tmpval = NULL, *p; 879 MIME_PARAM *mparam = NULL; 880 881 if (name) { 882 tmpname = OPENSSL_strdup(name); 883 if (!tmpname) 884 goto err; 885 for (p = tmpname; *p; p++) 886 *p = ossl_tolower(*p); 887 } 888 if (value) { 889 tmpval = OPENSSL_strdup(value); 890 if (!tmpval) 891 goto err; 892 } 893 /* Parameter values are case sensitive so leave as is */ 894 mparam = OPENSSL_malloc(sizeof(*mparam)); 895 if (mparam == NULL) 896 goto err; 897 mparam->param_name = tmpname; 898 mparam->param_value = tmpval; 899 if (!sk_MIME_PARAM_push(mhdr->params, mparam)) 900 goto err; 901 return 1; 902 err: 903 OPENSSL_free(tmpname); 904 OPENSSL_free(tmpval); 905 OPENSSL_free(mparam); 906 return 0; 907} 908 909static int mime_hdr_cmp(const MIME_HEADER *const *a, 910 const MIME_HEADER *const *b) 911{ 912 if ((*a)->name == NULL || (*b)->name == NULL) 913 return ((*a)->name != NULL) - ((*b)->name != NULL); 914 915 return strcmp((*a)->name, (*b)->name); 916} 917 918static int mime_param_cmp(const MIME_PARAM *const *a, 919 const MIME_PARAM *const *b) 920{ 921 if ((*a)->param_name == NULL || (*b)->param_name == NULL) 922 return ((*a)->param_name != NULL) - ((*b)->param_name != NULL); 923 return strcmp((*a)->param_name, (*b)->param_name); 924} 925 926/* Find a header with a given name (if possible) */ 927 928static MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, const char *name) 929{ 930 MIME_HEADER htmp; 931 int idx; 932 933 htmp.name = (char *)name; 934 htmp.value = NULL; 935 htmp.params = NULL; 936 937 idx = sk_MIME_HEADER_find(hdrs, &htmp); 938 return sk_MIME_HEADER_value(hdrs, idx); 939} 940 941static MIME_PARAM *mime_param_find(MIME_HEADER *hdr, const char *name) 942{ 943 MIME_PARAM param; 944 int idx; 945 946 param.param_name = (char *)name; 947 param.param_value = NULL; 948 idx = sk_MIME_PARAM_find(hdr->params, ¶m); 949 return sk_MIME_PARAM_value(hdr->params, idx); 950} 951 952static void mime_hdr_free(MIME_HEADER *hdr) 953{ 954 if (hdr == NULL) 955 return; 956 OPENSSL_free(hdr->name); 957 OPENSSL_free(hdr->value); 958 if (hdr->params) 959 sk_MIME_PARAM_pop_free(hdr->params, mime_param_free); 960 OPENSSL_free(hdr); 961} 962 963static void mime_param_free(MIME_PARAM *param) 964{ 965 OPENSSL_free(param->param_name); 966 OPENSSL_free(param->param_value); 967 OPENSSL_free(param); 968} 969 970/*- 971 * Check for a multipart boundary. Returns: 972 * 0 : no boundary 973 * 1 : part boundary 974 * 2 : final boundary 975 */ 976static int mime_bound_check(char *line, int linelen, const char *bound, int blen) 977{ 978 if (linelen == -1) 979 linelen = strlen(line); 980 if (blen == -1) 981 blen = strlen(bound); 982 /* Quickly eliminate if line length too short */ 983 if (blen + 2 > linelen) 984 return 0; 985 /* Check for part boundary */ 986 if ((strncmp(line, "--", 2) == 0) 987 && strncmp(line + 2, bound, blen) == 0) { 988 if (strncmp(line + blen + 2, "--", 2) == 0) 989 return 2; 990 else 991 return 1; 992 } 993 return 0; 994} 995 996static int strip_eol(char *linebuf, int *plen, int flags) 997{ 998 int len = *plen; 999 char *p, c; 1000 int is_eol = 0; 1001 1002#ifndef OPENSSL_NO_CMS 1003 if ((flags & CMS_BINARY) != 0) { 1004 if (len <= 0 || linebuf[len - 1] != '\n') 1005 return 0; 1006 if ((flags & SMIME_CRLFEOL) != 0) { 1007 if (len <= 1 || linebuf[len - 2] != '\r') 1008 return 0; 1009 len--; 1010 } 1011 len--; 1012 *plen = len; 1013 return 1; 1014 } 1015#endif 1016 1017 for (p = linebuf + len - 1; len > 0; len--, p--) { 1018 c = *p; 1019 if (c == '\n') { 1020 is_eol = 1; 1021 } else if (is_eol && (flags & SMIME_ASCIICRLF) != 0 && c == 32) { 1022 /* Strip trailing space on a line; 32 == ASCII for ' ' */ 1023 continue; 1024 } else if (c != '\r') { 1025 break; 1026 } 1027 } 1028 *plen = len; 1029 return is_eol; 1030} 1031