1 /* 2 * Copyright 2008-2018 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 "internal/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 "internal/evp_int.h" 18 #include "internal/bio.h" 19 #include "asn1_locl.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 default: 202 if (have_unknown) 203 write_comma = 0; 204 else { 205 BIO_puts(out, "unknown"); 206 have_unknown = 1; 207 } 208 break; 209 210 } 211 } 212 213 ret = 1; 214 err: 215 216 return ret; 217 218 } 219 220 /* SMIME sender */ 221 222 int SMIME_write_ASN1(BIO *bio, ASN1_VALUE *val, BIO *data, int flags, 223 int ctype_nid, int econt_nid, 224 STACK_OF(X509_ALGOR) *mdalgs, const ASN1_ITEM *it) 225 { 226 char bound[33], c; 227 int i; 228 const char *mime_prefix, *mime_eol, *cname = "smime.p7m"; 229 const char *msg_type = NULL; 230 if (flags & SMIME_OLDMIME) 231 mime_prefix = "application/x-pkcs7-"; 232 else 233 mime_prefix = "application/pkcs7-"; 234 235 if (flags & SMIME_CRLFEOL) 236 mime_eol = "\r\n"; 237 else 238 mime_eol = "\n"; 239 if ((flags & SMIME_DETACHED) && data) { 240 /* We want multipart/signed */ 241 /* Generate a random boundary */ 242 if (RAND_bytes((unsigned char *)bound, 32) <= 0) 243 return 0; 244 for (i = 0; i < 32; i++) { 245 c = bound[i] & 0xf; 246 if (c < 10) 247 c += '0'; 248 else 249 c += 'A' - 10; 250 bound[i] = c; 251 } 252 bound[32] = 0; 253 BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol); 254 BIO_printf(bio, "Content-Type: multipart/signed;"); 255 BIO_printf(bio, " protocol=\"%ssignature\";", mime_prefix); 256 BIO_puts(bio, " micalg=\""); 257 asn1_write_micalg(bio, mdalgs); 258 BIO_printf(bio, "\"; boundary=\"----%s\"%s%s", 259 bound, mime_eol, mime_eol); 260 BIO_printf(bio, "This is an S/MIME signed message%s%s", 261 mime_eol, mime_eol); 262 /* Now write out the first part */ 263 BIO_printf(bio, "------%s%s", bound, mime_eol); 264 if (!asn1_output_data(bio, data, val, flags, it)) 265 return 0; 266 BIO_printf(bio, "%s------%s%s", mime_eol, bound, mime_eol); 267 268 /* Headers for signature */ 269 270 BIO_printf(bio, "Content-Type: %ssignature;", mime_prefix); 271 BIO_printf(bio, " name=\"smime.p7s\"%s", mime_eol); 272 BIO_printf(bio, "Content-Transfer-Encoding: base64%s", mime_eol); 273 BIO_printf(bio, "Content-Disposition: attachment;"); 274 BIO_printf(bio, " filename=\"smime.p7s\"%s%s", mime_eol, mime_eol); 275 B64_write_ASN1(bio, val, NULL, 0, it); 276 BIO_printf(bio, "%s------%s--%s%s", mime_eol, bound, 277 mime_eol, mime_eol); 278 return 1; 279 } 280 281 /* Determine smime-type header */ 282 283 if (ctype_nid == NID_pkcs7_enveloped) 284 msg_type = "enveloped-data"; 285 else if (ctype_nid == NID_pkcs7_signed) { 286 if (econt_nid == NID_id_smime_ct_receipt) 287 msg_type = "signed-receipt"; 288 else if (sk_X509_ALGOR_num(mdalgs) >= 0) 289 msg_type = "signed-data"; 290 else 291 msg_type = "certs-only"; 292 } else if (ctype_nid == NID_id_smime_ct_compressedData) { 293 msg_type = "compressed-data"; 294 cname = "smime.p7z"; 295 } 296 /* MIME headers */ 297 BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol); 298 BIO_printf(bio, "Content-Disposition: attachment;"); 299 BIO_printf(bio, " filename=\"%s\"%s", cname, mime_eol); 300 BIO_printf(bio, "Content-Type: %smime;", mime_prefix); 301 if (msg_type) 302 BIO_printf(bio, " smime-type=%s;", msg_type); 303 BIO_printf(bio, " name=\"%s\"%s", cname, mime_eol); 304 BIO_printf(bio, "Content-Transfer-Encoding: base64%s%s", 305 mime_eol, mime_eol); 306 if (!B64_write_ASN1(bio, val, data, flags, it)) 307 return 0; 308 BIO_printf(bio, "%s", mime_eol); 309 return 1; 310 } 311 312 /* Handle output of ASN1 data */ 313 314 static int asn1_output_data(BIO *out, BIO *data, ASN1_VALUE *val, int flags, 315 const ASN1_ITEM *it) 316 { 317 BIO *tmpbio; 318 const ASN1_AUX *aux = it->funcs; 319 ASN1_STREAM_ARG sarg; 320 int rv = 1; 321 322 /* 323 * If data is not detached or resigning then the output BIO is already 324 * set up to finalise when it is written through. 325 */ 326 if (!(flags & SMIME_DETACHED) || (flags & PKCS7_REUSE_DIGEST)) { 327 SMIME_crlf_copy(data, out, flags); 328 return 1; 329 } 330 331 if (!aux || !aux->asn1_cb) { 332 ASN1err(ASN1_F_ASN1_OUTPUT_DATA, ASN1_R_STREAMING_NOT_SUPPORTED); 333 return 0; 334 } 335 336 sarg.out = out; 337 sarg.ndef_bio = NULL; 338 sarg.boundary = NULL; 339 340 /* Let ASN1 code prepend any needed BIOs */ 341 342 if (aux->asn1_cb(ASN1_OP_DETACHED_PRE, &val, it, &sarg) <= 0) 343 return 0; 344 345 /* Copy data across, passing through filter BIOs for processing */ 346 SMIME_crlf_copy(data, sarg.ndef_bio, flags); 347 348 /* Finalize structure */ 349 if (aux->asn1_cb(ASN1_OP_DETACHED_POST, &val, it, &sarg) <= 0) 350 rv = 0; 351 352 /* Now remove any digests prepended to the BIO */ 353 354 while (sarg.ndef_bio != out) { 355 tmpbio = BIO_pop(sarg.ndef_bio); 356 BIO_free(sarg.ndef_bio); 357 sarg.ndef_bio = tmpbio; 358 } 359 360 return rv; 361 362 } 363 364 /* 365 * SMIME reader: handle multipart/signed and opaque signing. in multipart 366 * case the content is placed in a memory BIO pointed to by "bcont". In 367 * opaque this is set to NULL 368 */ 369 370 ASN1_VALUE *SMIME_read_ASN1(BIO *bio, BIO **bcont, const ASN1_ITEM *it) 371 { 372 BIO *asnin; 373 STACK_OF(MIME_HEADER) *headers = NULL; 374 STACK_OF(BIO) *parts = NULL; 375 MIME_HEADER *hdr; 376 MIME_PARAM *prm; 377 ASN1_VALUE *val; 378 int ret; 379 380 if (bcont) 381 *bcont = NULL; 382 383 if ((headers = mime_parse_hdr(bio)) == NULL) { 384 ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_MIME_PARSE_ERROR); 385 return NULL; 386 } 387 388 if ((hdr = mime_hdr_find(headers, "content-type")) == NULL 389 || hdr->value == NULL) { 390 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 391 ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_CONTENT_TYPE); 392 return NULL; 393 } 394 395 /* Handle multipart/signed */ 396 397 if (strcmp(hdr->value, "multipart/signed") == 0) { 398 /* Split into two parts */ 399 prm = mime_param_find(hdr, "boundary"); 400 if (!prm || !prm->param_value) { 401 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 402 ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_MULTIPART_BOUNDARY); 403 return NULL; 404 } 405 ret = multi_split(bio, prm->param_value, &parts); 406 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 407 if (!ret || (sk_BIO_num(parts) != 2)) { 408 ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_MULTIPART_BODY_FAILURE); 409 sk_BIO_pop_free(parts, BIO_vfree); 410 return NULL; 411 } 412 413 /* Parse the signature piece */ 414 asnin = sk_BIO_value(parts, 1); 415 416 if ((headers = mime_parse_hdr(asnin)) == NULL) { 417 ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_MIME_SIG_PARSE_ERROR); 418 sk_BIO_pop_free(parts, BIO_vfree); 419 return NULL; 420 } 421 422 /* Get content type */ 423 424 if ((hdr = mime_hdr_find(headers, "content-type")) == NULL 425 || hdr->value == NULL) { 426 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 427 ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_NO_SIG_CONTENT_TYPE); 428 sk_BIO_pop_free(parts, BIO_vfree); 429 return NULL; 430 } 431 432 if (strcmp(hdr->value, "application/x-pkcs7-signature") && 433 strcmp(hdr->value, "application/pkcs7-signature")) { 434 ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_SIG_INVALID_MIME_TYPE); 435 ERR_add_error_data(2, "type: ", hdr->value); 436 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 437 sk_BIO_pop_free(parts, BIO_vfree); 438 return NULL; 439 } 440 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 441 /* Read in ASN1 */ 442 if ((val = b64_read_asn1(asnin, it)) == NULL) { 443 ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_ASN1_SIG_PARSE_ERROR); 444 sk_BIO_pop_free(parts, BIO_vfree); 445 return NULL; 446 } 447 448 if (bcont) { 449 *bcont = sk_BIO_value(parts, 0); 450 BIO_free(asnin); 451 sk_BIO_free(parts); 452 } else 453 sk_BIO_pop_free(parts, BIO_vfree); 454 return val; 455 } 456 457 /* OK, if not multipart/signed try opaque signature */ 458 459 if (strcmp(hdr->value, "application/x-pkcs7-mime") && 460 strcmp(hdr->value, "application/pkcs7-mime")) { 461 ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_INVALID_MIME_TYPE); 462 ERR_add_error_data(2, "type: ", hdr->value); 463 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 464 return NULL; 465 } 466 467 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 468 469 if ((val = b64_read_asn1(bio, it)) == NULL) { 470 ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_ASN1_PARSE_ERROR); 471 return NULL; 472 } 473 return val; 474 475 } 476 477 /* Copy text from one BIO to another making the output CRLF at EOL */ 478 int SMIME_crlf_copy(BIO *in, BIO *out, int flags) 479 { 480 BIO *bf; 481 char eol; 482 int len; 483 char linebuf[MAX_SMLEN]; 484 /* 485 * Buffer output so we don't write one line at a time. This is useful 486 * when streaming as we don't end up with one OCTET STRING per line. 487 */ 488 bf = BIO_new(BIO_f_buffer()); 489 if (bf == NULL) 490 return 0; 491 out = BIO_push(bf, out); 492 if (flags & SMIME_BINARY) { 493 while ((len = BIO_read(in, linebuf, MAX_SMLEN)) > 0) 494 BIO_write(out, linebuf, len); 495 } else { 496 int eolcnt = 0; 497 if (flags & SMIME_TEXT) 498 BIO_printf(out, "Content-Type: text/plain\r\n\r\n"); 499 while ((len = BIO_gets(in, linebuf, MAX_SMLEN)) > 0) { 500 eol = strip_eol(linebuf, &len, flags); 501 if (len) { 502 /* Not EOF: write out all CRLF */ 503 if (flags & SMIME_ASCIICRLF) { 504 int i; 505 for (i = 0; i < eolcnt; i++) 506 BIO_write(out, "\r\n", 2); 507 eolcnt = 0; 508 } 509 BIO_write(out, linebuf, len); 510 if (eol) 511 BIO_write(out, "\r\n", 2); 512 } else if (flags & SMIME_ASCIICRLF) 513 eolcnt++; 514 else if (eol) 515 BIO_write(out, "\r\n", 2); 516 } 517 } 518 (void)BIO_flush(out); 519 BIO_pop(out); 520 BIO_free(bf); 521 return 1; 522 } 523 524 /* Strip off headers if they are text/plain */ 525 int SMIME_text(BIO *in, BIO *out) 526 { 527 char iobuf[4096]; 528 int len; 529 STACK_OF(MIME_HEADER) *headers; 530 MIME_HEADER *hdr; 531 532 if ((headers = mime_parse_hdr(in)) == NULL) { 533 ASN1err(ASN1_F_SMIME_TEXT, ASN1_R_MIME_PARSE_ERROR); 534 return 0; 535 } 536 if ((hdr = mime_hdr_find(headers, "content-type")) == NULL 537 || hdr->value == NULL) { 538 ASN1err(ASN1_F_SMIME_TEXT, ASN1_R_MIME_NO_CONTENT_TYPE); 539 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 540 return 0; 541 } 542 if (strcmp(hdr->value, "text/plain")) { 543 ASN1err(ASN1_F_SMIME_TEXT, ASN1_R_INVALID_MIME_TYPE); 544 ERR_add_error_data(2, "type: ", hdr->value); 545 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 546 return 0; 547 } 548 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 549 while ((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0) 550 BIO_write(out, iobuf, len); 551 if (len < 0) 552 return 0; 553 return 1; 554 } 555 556 /* 557 * Split a multipart/XXX message body into component parts: result is 558 * canonical parts in a STACK of bios 559 */ 560 561 static int multi_split(BIO *bio, const char *bound, STACK_OF(BIO) **ret) 562 { 563 char linebuf[MAX_SMLEN]; 564 int len, blen; 565 int eol = 0, next_eol = 0; 566 BIO *bpart = NULL; 567 STACK_OF(BIO) *parts; 568 char state, part, first; 569 570 blen = strlen(bound); 571 part = 0; 572 state = 0; 573 first = 1; 574 parts = sk_BIO_new_null(); 575 *ret = parts; 576 if (*ret == NULL) 577 return 0; 578 while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) { 579 state = mime_bound_check(linebuf, len, bound, blen); 580 if (state == 1) { 581 first = 1; 582 part++; 583 } else if (state == 2) { 584 if (!sk_BIO_push(parts, bpart)) { 585 BIO_free(bpart); 586 return 0; 587 } 588 return 1; 589 } else if (part) { 590 /* Strip CR+LF from linebuf */ 591 next_eol = strip_eol(linebuf, &len, 0); 592 if (first) { 593 first = 0; 594 if (bpart) 595 if (!sk_BIO_push(parts, bpart)) { 596 BIO_free(bpart); 597 return 0; 598 } 599 bpart = BIO_new(BIO_s_mem()); 600 if (bpart == NULL) 601 return 0; 602 BIO_set_mem_eof_return(bpart, 0); 603 } else if (eol) 604 BIO_write(bpart, "\r\n", 2); 605 eol = next_eol; 606 if (len) 607 BIO_write(bpart, linebuf, len); 608 } 609 } 610 BIO_free(bpart); 611 return 0; 612 } 613 614 /* This is the big one: parse MIME header lines up to message body */ 615 616 #define MIME_INVALID 0 617 #define MIME_START 1 618 #define MIME_TYPE 2 619 #define MIME_NAME 3 620 #define MIME_VALUE 4 621 #define MIME_QUOTE 5 622 #define MIME_COMMENT 6 623 624 static STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio) 625 { 626 char *p, *q, c; 627 char *ntmp; 628 char linebuf[MAX_SMLEN]; 629 MIME_HEADER *mhdr = NULL, *new_hdr = NULL; 630 STACK_OF(MIME_HEADER) *headers; 631 int len, state, save_state = 0; 632 633 headers = sk_MIME_HEADER_new(mime_hdr_cmp); 634 if (headers == NULL) 635 return NULL; 636 while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) { 637 /* If whitespace at line start then continuation line */ 638 if (mhdr && ossl_isspace(linebuf[0])) 639 state = MIME_NAME; 640 else 641 state = MIME_START; 642 ntmp = NULL; 643 /* Go through all characters */ 644 for (p = linebuf, q = linebuf; (c = *p) && (c != '\r') && (c != '\n'); 645 p++) { 646 647 /* 648 * State machine to handle MIME headers if this looks horrible 649 * that's because it *is* 650 */ 651 652 switch (state) { 653 case MIME_START: 654 if (c == ':') { 655 state = MIME_TYPE; 656 *p = 0; 657 ntmp = strip_ends(q); 658 q = p + 1; 659 } 660 break; 661 662 case MIME_TYPE: 663 if (c == ';') { 664 mime_debug("Found End Value\n"); 665 *p = 0; 666 new_hdr = mime_hdr_new(ntmp, strip_ends(q)); 667 if (new_hdr == NULL) 668 goto err; 669 if (!sk_MIME_HEADER_push(headers, new_hdr)) 670 goto err; 671 mhdr = new_hdr; 672 new_hdr = NULL; 673 ntmp = NULL; 674 q = p + 1; 675 state = MIME_NAME; 676 } else if (c == '(') { 677 save_state = state; 678 state = MIME_COMMENT; 679 } 680 break; 681 682 case MIME_COMMENT: 683 if (c == ')') { 684 state = save_state; 685 } 686 break; 687 688 case MIME_NAME: 689 if (c == '=') { 690 state = MIME_VALUE; 691 *p = 0; 692 ntmp = strip_ends(q); 693 q = p + 1; 694 } 695 break; 696 697 case MIME_VALUE: 698 if (c == ';') { 699 state = MIME_NAME; 700 *p = 0; 701 mime_hdr_addparam(mhdr, ntmp, strip_ends(q)); 702 ntmp = NULL; 703 q = p + 1; 704 } else if (c == '"') { 705 mime_debug("Found Quote\n"); 706 state = MIME_QUOTE; 707 } else if (c == '(') { 708 save_state = state; 709 state = MIME_COMMENT; 710 } 711 break; 712 713 case MIME_QUOTE: 714 if (c == '"') { 715 mime_debug("Found Match Quote\n"); 716 state = MIME_VALUE; 717 } 718 break; 719 } 720 } 721 722 if (state == MIME_TYPE) { 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 } else if (state == MIME_VALUE) 731 mime_hdr_addparam(mhdr, ntmp, strip_ends(q)); 732 if (p == linebuf) 733 break; /* Blank line means end of headers */ 734 } 735 736 return headers; 737 738 err: 739 mime_hdr_free(new_hdr); 740 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 741 return NULL; 742 } 743 744 static char *strip_ends(char *name) 745 { 746 return strip_end(strip_start(name)); 747 } 748 749 /* Strip a parameter of whitespace from start of param */ 750 static char *strip_start(char *name) 751 { 752 char *p, c; 753 /* Look for first non white space or quote */ 754 for (p = name; (c = *p); p++) { 755 if (c == '"') { 756 /* Next char is start of string if non null */ 757 if (p[1]) 758 return p + 1; 759 /* Else null string */ 760 return NULL; 761 } 762 if (!ossl_isspace(c)) 763 return p; 764 } 765 return NULL; 766 } 767 768 /* As above but strip from end of string : maybe should handle brackets? */ 769 static char *strip_end(char *name) 770 { 771 char *p, c; 772 if (!name) 773 return NULL; 774 /* Look for first non white space or quote */ 775 for (p = name + strlen(name) - 1; p >= name; p--) { 776 c = *p; 777 if (c == '"') { 778 if (p - 1 == name) 779 return NULL; 780 *p = 0; 781 return name; 782 } 783 if (ossl_isspace(c)) 784 *p = 0; 785 else 786 return name; 787 } 788 return NULL; 789 } 790 791 static MIME_HEADER *mime_hdr_new(const char *name, const char *value) 792 { 793 MIME_HEADER *mhdr = NULL; 794 char *tmpname = NULL, *tmpval = NULL, *p; 795 796 if (name) { 797 if ((tmpname = OPENSSL_strdup(name)) == NULL) 798 return NULL; 799 for (p = tmpname; *p; p++) 800 *p = ossl_tolower(*p); 801 } 802 if (value) { 803 if ((tmpval = OPENSSL_strdup(value)) == NULL) 804 goto err; 805 for (p = tmpval; *p; p++) 806 *p = ossl_tolower(*p); 807 } 808 mhdr = OPENSSL_malloc(sizeof(*mhdr)); 809 if (mhdr == NULL) 810 goto err; 811 mhdr->name = tmpname; 812 mhdr->value = tmpval; 813 if ((mhdr->params = sk_MIME_PARAM_new(mime_param_cmp)) == NULL) 814 goto err; 815 return mhdr; 816 817 err: 818 OPENSSL_free(tmpname); 819 OPENSSL_free(tmpval); 820 OPENSSL_free(mhdr); 821 return NULL; 822 } 823 824 static int mime_hdr_addparam(MIME_HEADER *mhdr, const char *name, const char *value) 825 { 826 char *tmpname = NULL, *tmpval = NULL, *p; 827 MIME_PARAM *mparam = NULL; 828 829 if (name) { 830 tmpname = OPENSSL_strdup(name); 831 if (!tmpname) 832 goto err; 833 for (p = tmpname; *p; p++) 834 *p = ossl_tolower(*p); 835 } 836 if (value) { 837 tmpval = OPENSSL_strdup(value); 838 if (!tmpval) 839 goto err; 840 } 841 /* Parameter values are case sensitive so leave as is */ 842 mparam = OPENSSL_malloc(sizeof(*mparam)); 843 if (mparam == NULL) 844 goto err; 845 mparam->param_name = tmpname; 846 mparam->param_value = tmpval; 847 if (!sk_MIME_PARAM_push(mhdr->params, mparam)) 848 goto err; 849 return 1; 850 err: 851 OPENSSL_free(tmpname); 852 OPENSSL_free(tmpval); 853 OPENSSL_free(mparam); 854 return 0; 855 } 856 857 static int mime_hdr_cmp(const MIME_HEADER *const *a, 858 const MIME_HEADER *const *b) 859 { 860 if (!(*a)->name || !(*b)->name) 861 return ! !(*a)->name - ! !(*b)->name; 862 863 return strcmp((*a)->name, (*b)->name); 864 } 865 866 static int mime_param_cmp(const MIME_PARAM *const *a, 867 const MIME_PARAM *const *b) 868 { 869 if (!(*a)->param_name || !(*b)->param_name) 870 return ! !(*a)->param_name - ! !(*b)->param_name; 871 return strcmp((*a)->param_name, (*b)->param_name); 872 } 873 874 /* Find a header with a given name (if possible) */ 875 876 static MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, const char *name) 877 { 878 MIME_HEADER htmp; 879 int idx; 880 881 htmp.name = (char *)name; 882 htmp.value = NULL; 883 htmp.params = NULL; 884 885 idx = sk_MIME_HEADER_find(hdrs, &htmp); 886 return sk_MIME_HEADER_value(hdrs, idx); 887 } 888 889 static MIME_PARAM *mime_param_find(MIME_HEADER *hdr, const char *name) 890 { 891 MIME_PARAM param; 892 int idx; 893 894 param.param_name = (char *)name; 895 param.param_value = NULL; 896 idx = sk_MIME_PARAM_find(hdr->params, ¶m); 897 return sk_MIME_PARAM_value(hdr->params, idx); 898 } 899 900 static void mime_hdr_free(MIME_HEADER *hdr) 901 { 902 if (hdr == NULL) 903 return; 904 OPENSSL_free(hdr->name); 905 OPENSSL_free(hdr->value); 906 if (hdr->params) 907 sk_MIME_PARAM_pop_free(hdr->params, mime_param_free); 908 OPENSSL_free(hdr); 909 } 910 911 static void mime_param_free(MIME_PARAM *param) 912 { 913 OPENSSL_free(param->param_name); 914 OPENSSL_free(param->param_value); 915 OPENSSL_free(param); 916 } 917 918 /*- 919 * Check for a multipart boundary. Returns: 920 * 0 : no boundary 921 * 1 : part boundary 922 * 2 : final boundary 923 */ 924 static int mime_bound_check(char *line, int linelen, const char *bound, int blen) 925 { 926 if (linelen == -1) 927 linelen = strlen(line); 928 if (blen == -1) 929 blen = strlen(bound); 930 /* Quickly eliminate if line length too short */ 931 if (blen + 2 > linelen) 932 return 0; 933 /* Check for part boundary */ 934 if ((strncmp(line, "--", 2) == 0) 935 && strncmp(line + 2, bound, blen) == 0) { 936 if (strncmp(line + blen + 2, "--", 2) == 0) 937 return 2; 938 else 939 return 1; 940 } 941 return 0; 942 } 943 944 static int strip_eol(char *linebuf, int *plen, int flags) 945 { 946 int len = *plen; 947 char *p, c; 948 int is_eol = 0; 949 950 for (p = linebuf + len - 1; len > 0; len--, p--) { 951 c = *p; 952 if (c == '\n') { 953 is_eol = 1; 954 } else if (is_eol && flags & SMIME_ASCIICRLF && c == 32) { 955 /* Strip trailing space on a line; 32 == ASCII for ' ' */ 956 continue; 957 } else if (c != '\r') { 958 break; 959 } 960 } 961 *plen = len; 962 return is_eol; 963 } 964