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