1 /* pk7_mime.c */ 2 /* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL 3 * project 1999. 4 */ 5 /* ==================================================================== 6 * Copyright (c) 1999 The OpenSSL Project. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in 17 * the documentation and/or other materials provided with the 18 * distribution. 19 * 20 * 3. All advertising materials mentioning features or use of this 21 * software must display the following acknowledgment: 22 * "This product includes software developed by the OpenSSL Project 23 * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" 24 * 25 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 26 * endorse or promote products derived from this software without 27 * prior written permission. For written permission, please contact 28 * licensing@OpenSSL.org. 29 * 30 * 5. Products derived from this software may not be called "OpenSSL" 31 * nor may "OpenSSL" appear in their names without prior written 32 * permission of the OpenSSL Project. 33 * 34 * 6. Redistributions of any form whatsoever must retain the following 35 * acknowledgment: 36 * "This product includes software developed by the OpenSSL Project 37 * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" 38 * 39 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 40 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 41 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 42 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 43 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 44 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 45 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 46 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 48 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 49 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 50 * OF THE POSSIBILITY OF SUCH DAMAGE. 51 * ==================================================================== 52 * 53 * This product includes cryptographic software written by Eric Young 54 * (eay@cryptsoft.com). This product includes software written by Tim 55 * Hudson (tjh@cryptsoft.com). 56 * 57 */ 58 59 #include <stdio.h> 60 #include <ctype.h> 61 #include "cryptlib.h" 62 #include <openssl/rand.h> 63 #include <openssl/x509.h> 64 65 /* MIME and related routines */ 66 67 /* MIME format structures 68 * Note that all are translated to lower case apart from 69 * parameter values. Quotes are stripped off 70 */ 71 72 typedef struct { 73 char *param_name; /* Param name e.g. "micalg" */ 74 char *param_value; /* Param value e.g. "sha1" */ 75 } MIME_PARAM; 76 77 DECLARE_STACK_OF(MIME_PARAM) 78 IMPLEMENT_STACK_OF(MIME_PARAM) 79 80 typedef struct { 81 char *name; /* Name of line e.g. "content-type" */ 82 char *value; /* Value of line e.g. "text/plain" */ 83 STACK_OF(MIME_PARAM) *params; /* Zero or more parameters */ 84 } MIME_HEADER; 85 86 DECLARE_STACK_OF(MIME_HEADER) 87 IMPLEMENT_STACK_OF(MIME_HEADER) 88 89 static int B64_write_PKCS7(BIO *bio, PKCS7 *p7); 90 static PKCS7 *B64_read_PKCS7(BIO *bio); 91 static char * strip_ends(char *name); 92 static char * strip_start(char *name); 93 static char * strip_end(char *name); 94 static MIME_HEADER *mime_hdr_new(char *name, char *value); 95 static int mime_hdr_addparam(MIME_HEADER *mhdr, char *name, char *value); 96 static STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio); 97 static int mime_hdr_cmp(const MIME_HEADER * const *a, 98 const MIME_HEADER * const *b); 99 static int mime_param_cmp(const MIME_PARAM * const *a, 100 const MIME_PARAM * const *b); 101 static void mime_param_free(MIME_PARAM *param); 102 static int mime_bound_check(char *line, int linelen, char *bound, int blen); 103 static int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret); 104 static int iscrlf(char c); 105 static MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, char *name); 106 static MIME_PARAM *mime_param_find(MIME_HEADER *hdr, char *name); 107 static void mime_hdr_free(MIME_HEADER *hdr); 108 109 #define MAX_SMLEN 1024 110 #define mime_debug(x) /* x */ 111 112 113 typedef void (*stkfree)(); 114 115 /* Base 64 read and write of PKCS#7 structure */ 116 117 static int B64_write_PKCS7(BIO *bio, PKCS7 *p7) 118 { 119 BIO *b64; 120 if(!(b64 = BIO_new(BIO_f_base64()))) { 121 PKCS7err(PKCS7_F_B64_WRITE_PKCS7,ERR_R_MALLOC_FAILURE); 122 return 0; 123 } 124 bio = BIO_push(b64, bio); 125 i2d_PKCS7_bio(bio, p7); 126 BIO_flush(bio); 127 bio = BIO_pop(bio); 128 BIO_free(b64); 129 return 1; 130 } 131 132 static PKCS7 *B64_read_PKCS7(BIO *bio) 133 { 134 BIO *b64; 135 PKCS7 *p7; 136 if(!(b64 = BIO_new(BIO_f_base64()))) { 137 PKCS7err(PKCS7_F_B64_READ_PKCS7,ERR_R_MALLOC_FAILURE); 138 return 0; 139 } 140 bio = BIO_push(b64, bio); 141 if(!(p7 = d2i_PKCS7_bio(bio, NULL))) 142 PKCS7err(PKCS7_F_B64_READ_PKCS7,PKCS7_R_DECODE_ERROR); 143 BIO_flush(bio); 144 bio = BIO_pop(bio); 145 BIO_free(b64); 146 return p7; 147 } 148 149 /* SMIME sender */ 150 151 int SMIME_write_PKCS7(BIO *bio, PKCS7 *p7, BIO *data, int flags) 152 { 153 char linebuf[MAX_SMLEN]; 154 char bound[33], c; 155 int i; 156 if((flags & PKCS7_DETACHED) && data) { 157 /* We want multipart/signed */ 158 /* Generate a random boundary */ 159 RAND_pseudo_bytes((unsigned char *)bound, 32); 160 for(i = 0; i < 32; i++) { 161 c = bound[i] & 0xf; 162 if(c < 10) c += '0'; 163 else c += 'A' - 10; 164 bound[i] = c; 165 } 166 bound[32] = 0; 167 BIO_printf(bio, "MIME-Version: 1.0\n"); 168 BIO_printf(bio, "Content-Type: multipart/signed;"); 169 BIO_printf(bio, " protocol=\"application/x-pkcs7-signature\";"); 170 BIO_printf(bio, " micalg=sha1; boundary=\"----%s\"\n\n", bound); 171 BIO_printf(bio, "This is an S/MIME signed message\n\n"); 172 /* Now write out the first part */ 173 BIO_printf(bio, "------%s\n", bound); 174 if(flags & PKCS7_TEXT) BIO_printf(bio, "Content-Type: text/plain\n\n"); 175 while((i = BIO_read(data, linebuf, MAX_SMLEN)) > 0) 176 BIO_write(bio, linebuf, i); 177 BIO_printf(bio, "\n------%s\n", bound); 178 179 /* Headers for signature */ 180 181 BIO_printf(bio, "Content-Type: application/x-pkcs7-signature; name=\"smime.p7s\"\n"); 182 BIO_printf(bio, "Content-Transfer-Encoding: base64\n"); 183 BIO_printf(bio, "Content-Disposition: attachment; filename=\"smime.p7s\"\n\n"); 184 B64_write_PKCS7(bio, p7); 185 BIO_printf(bio,"\n------%s--\n\n", bound); 186 return 1; 187 } 188 /* MIME headers */ 189 BIO_printf(bio, "MIME-Version: 1.0\n"); 190 BIO_printf(bio, "Content-Disposition: attachment; filename=\"smime.p7m\"\n"); 191 BIO_printf(bio, "Content-Type: application/x-pkcs7-mime; name=\"smime.p7m\"\n"); 192 BIO_printf(bio, "Content-Transfer-Encoding: base64\n\n"); 193 B64_write_PKCS7(bio, p7); 194 BIO_printf(bio, "\n"); 195 return 1; 196 } 197 198 /* SMIME reader: handle multipart/signed and opaque signing. 199 * in multipart case the content is placed in a memory BIO 200 * pointed to by "bcont". In opaque this is set to NULL 201 */ 202 203 PKCS7 *SMIME_read_PKCS7(BIO *bio, BIO **bcont) 204 { 205 BIO *p7in; 206 STACK_OF(MIME_HEADER) *headers = NULL; 207 STACK_OF(BIO) *parts = NULL; 208 MIME_HEADER *hdr; 209 MIME_PARAM *prm; 210 PKCS7 *p7; 211 int ret; 212 213 if(bcont) *bcont = NULL; 214 215 if (!(headers = mime_parse_hdr(bio))) { 216 PKCS7err(PKCS7_F_SMIME_READ_PKCS7,PKCS7_R_MIME_PARSE_ERROR); 217 return NULL; 218 } 219 220 if(!(hdr = mime_hdr_find(headers, "content-type")) || !hdr->value) { 221 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 222 PKCS7err(PKCS7_F_SMIME_READ_PKCS7, PKCS7_R_NO_CONTENT_TYPE); 223 return NULL; 224 } 225 226 /* Handle multipart/signed */ 227 228 if(!strcmp(hdr->value, "multipart/signed")) { 229 /* Split into two parts */ 230 prm = mime_param_find(hdr, "boundary"); 231 if(!prm || !prm->param_value) { 232 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 233 PKCS7err(PKCS7_F_SMIME_READ_PKCS7, PKCS7_R_NO_MULTIPART_BOUNDARY); 234 return NULL; 235 } 236 ret = multi_split(bio, prm->param_value, &parts); 237 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 238 if(!ret || (sk_BIO_num(parts) != 2) ) { 239 PKCS7err(PKCS7_F_SMIME_READ_PKCS7, PKCS7_R_NO_MULTIPART_BODY_FAILURE); 240 sk_BIO_pop_free(parts, BIO_vfree); 241 return NULL; 242 } 243 244 /* Parse the signature piece */ 245 p7in = sk_BIO_value(parts, 1); 246 247 if (!(headers = mime_parse_hdr(p7in))) { 248 PKCS7err(PKCS7_F_SMIME_READ_PKCS7,PKCS7_R_MIME_SIG_PARSE_ERROR); 249 sk_BIO_pop_free(parts, BIO_vfree); 250 return NULL; 251 } 252 253 /* Get content type */ 254 255 if(!(hdr = mime_hdr_find(headers, "content-type")) || 256 !hdr->value) { 257 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 258 PKCS7err(PKCS7_F_SMIME_READ_PKCS7, PKCS7_R_NO_SIG_CONTENT_TYPE); 259 return NULL; 260 } 261 262 if(strcmp(hdr->value, "application/x-pkcs7-signature") && 263 strcmp(hdr->value, "application/pkcs7-signature")) { 264 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 265 PKCS7err(PKCS7_F_SMIME_READ_PKCS7,PKCS7_R_SIG_INVALID_MIME_TYPE); 266 ERR_add_error_data(2, "type: ", hdr->value); 267 sk_BIO_pop_free(parts, BIO_vfree); 268 return NULL; 269 } 270 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 271 /* Read in PKCS#7 */ 272 if(!(p7 = B64_read_PKCS7(p7in))) { 273 PKCS7err(PKCS7_F_SMIME_READ_PKCS7,PKCS7_R_PKCS7_SIG_PARSE_ERROR); 274 sk_BIO_pop_free(parts, BIO_vfree); 275 return NULL; 276 } 277 278 if(bcont) { 279 *bcont = sk_BIO_value(parts, 0); 280 BIO_free(p7in); 281 sk_BIO_free(parts); 282 } else sk_BIO_pop_free(parts, BIO_vfree); 283 return p7; 284 } 285 286 /* OK, if not multipart/signed try opaque signature */ 287 288 if (strcmp (hdr->value, "application/x-pkcs7-mime") && 289 strcmp (hdr->value, "application/pkcs7-mime")) { 290 PKCS7err(PKCS7_F_SMIME_READ_PKCS7,PKCS7_R_INVALID_MIME_TYPE); 291 ERR_add_error_data(2, "type: ", hdr->value); 292 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 293 return NULL; 294 } 295 296 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 297 298 if(!(p7 = B64_read_PKCS7(bio))) { 299 PKCS7err(PKCS7_F_SMIME_READ_PKCS7, PKCS7_R_PKCS7_PARSE_ERROR); 300 return NULL; 301 } 302 return p7; 303 304 } 305 306 /* Copy text from one BIO to another making the output CRLF at EOL */ 307 int SMIME_crlf_copy(BIO *in, BIO *out, int flags) 308 { 309 char eol; 310 int len; 311 char linebuf[MAX_SMLEN]; 312 if(flags & PKCS7_BINARY) { 313 while((len = BIO_read(in, linebuf, MAX_SMLEN)) > 0) 314 BIO_write(out, linebuf, len); 315 return 1; 316 } 317 if(flags & PKCS7_TEXT) BIO_printf(out, "Content-Type: text/plain\r\n\r\n"); 318 while ((len = BIO_gets(in, linebuf, MAX_SMLEN)) > 0) { 319 eol = 0; 320 while(iscrlf(linebuf[len - 1])) { 321 len--; 322 eol = 1; 323 } 324 BIO_write(out, linebuf, len); 325 if(eol) BIO_write(out, "\r\n", 2); 326 } 327 return 1; 328 } 329 330 /* Strip off headers if they are text/plain */ 331 int SMIME_text(BIO *in, BIO *out) 332 { 333 char iobuf[4096]; 334 int len; 335 STACK_OF(MIME_HEADER) *headers; 336 MIME_HEADER *hdr; 337 338 if (!(headers = mime_parse_hdr(in))) { 339 PKCS7err(PKCS7_F_SMIME_TEXT,PKCS7_R_MIME_PARSE_ERROR); 340 return 0; 341 } 342 if(!(hdr = mime_hdr_find(headers, "content-type")) || !hdr->value) { 343 PKCS7err(PKCS7_F_SMIME_TEXT,PKCS7_R_MIME_NO_CONTENT_TYPE); 344 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 345 return 0; 346 } 347 if (strcmp (hdr->value, "text/plain")) { 348 PKCS7err(PKCS7_F_SMIME_TEXT,PKCS7_R_INVALID_MIME_TYPE); 349 ERR_add_error_data(2, "type: ", hdr->value); 350 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 351 return 0; 352 } 353 sk_MIME_HEADER_pop_free(headers, mime_hdr_free); 354 while ((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0) 355 BIO_write(out, iobuf, len); 356 return 1; 357 } 358 359 /* Split a multipart/XXX message body into component parts: result is 360 * canonical parts in a STACK of bios 361 */ 362 363 static int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret) 364 { 365 char linebuf[MAX_SMLEN]; 366 int len, blen; 367 BIO *bpart = NULL; 368 STACK_OF(BIO) *parts; 369 char state, part, first; 370 371 blen = strlen(bound); 372 part = 0; 373 state = 0; 374 first = 1; 375 parts = sk_BIO_new_null(); 376 *ret = parts; 377 while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) { 378 state = mime_bound_check(linebuf, len, bound, blen); 379 if(state == 1) { 380 first = 1; 381 part++; 382 } else if(state == 2) { 383 sk_BIO_push(parts, bpart); 384 return 1; 385 } else if(part) { 386 if(first) { 387 first = 0; 388 if(bpart) sk_BIO_push(parts, bpart); 389 bpart = BIO_new(BIO_s_mem()); 390 391 } else BIO_write(bpart, "\r\n", 2); 392 /* Strip CR+LF from linebuf */ 393 while(iscrlf(linebuf[len - 1])) len--; 394 BIO_write(bpart, linebuf, len); 395 } 396 } 397 return 0; 398 } 399 400 static int iscrlf(char c) 401 { 402 if(c == '\r' || c == '\n') return 1; 403 return 0; 404 } 405 406 /* This is the big one: parse MIME header lines up to message body */ 407 408 #define MIME_INVALID 0 409 #define MIME_START 1 410 #define MIME_TYPE 2 411 #define MIME_NAME 3 412 #define MIME_VALUE 4 413 #define MIME_QUOTE 5 414 #define MIME_COMMENT 6 415 416 417 static STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio) 418 { 419 char *p, *q, c; 420 char *ntmp; 421 char linebuf[MAX_SMLEN]; 422 MIME_HEADER *mhdr = NULL; 423 STACK_OF(MIME_HEADER) *headers; 424 int len, state, save_state = 0; 425 426 headers = sk_MIME_HEADER_new(mime_hdr_cmp); 427 while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) { 428 /* If whitespace at line start then continuation line */ 429 if(mhdr && isspace((unsigned char)linebuf[0])) state = MIME_NAME; 430 else state = MIME_START; 431 ntmp = NULL; 432 /* Go through all characters */ 433 for(p = linebuf, q = linebuf; (c = *p) && (c!='\r') && (c!='\n'); p++) { 434 435 /* State machine to handle MIME headers 436 * if this looks horrible that's because it *is* 437 */ 438 439 switch(state) { 440 case MIME_START: 441 if(c == ':') { 442 state = MIME_TYPE; 443 *p = 0; 444 ntmp = strip_ends(q); 445 q = p + 1; 446 } 447 break; 448 449 case MIME_TYPE: 450 if(c == ';') { 451 mime_debug("Found End Value\n"); 452 *p = 0; 453 mhdr = mime_hdr_new(ntmp, strip_ends(q)); 454 sk_MIME_HEADER_push(headers, mhdr); 455 ntmp = NULL; 456 q = p + 1; 457 state = MIME_NAME; 458 } else if(c == '(') { 459 save_state = state; 460 state = MIME_COMMENT; 461 } 462 break; 463 464 case MIME_COMMENT: 465 if(c == ')') { 466 state = save_state; 467 } 468 break; 469 470 case MIME_NAME: 471 if(c == '=') { 472 state = MIME_VALUE; 473 *p = 0; 474 ntmp = strip_ends(q); 475 q = p + 1; 476 } 477 break ; 478 479 case MIME_VALUE: 480 if(c == ';') { 481 state = MIME_NAME; 482 *p = 0; 483 mime_hdr_addparam(mhdr, ntmp, strip_ends(q)); 484 ntmp = NULL; 485 q = p + 1; 486 } else if (c == '"') { 487 mime_debug("Found Quote\n"); 488 state = MIME_QUOTE; 489 } else if(c == '(') { 490 save_state = state; 491 state = MIME_COMMENT; 492 } 493 break; 494 495 case MIME_QUOTE: 496 if(c == '"') { 497 mime_debug("Found Match Quote\n"); 498 state = MIME_VALUE; 499 } 500 break; 501 } 502 } 503 504 if(state == MIME_TYPE) { 505 mhdr = mime_hdr_new(ntmp, strip_ends(q)); 506 sk_MIME_HEADER_push(headers, mhdr); 507 } else if(state == MIME_VALUE) 508 mime_hdr_addparam(mhdr, ntmp, strip_ends(q)); 509 if(p == linebuf) break; /* Blank line means end of headers */ 510 } 511 512 return headers; 513 514 } 515 516 static char *strip_ends(char *name) 517 { 518 return strip_end(strip_start(name)); 519 } 520 521 /* Strip a parameter of whitespace from start of param */ 522 static char *strip_start(char *name) 523 { 524 char *p, c; 525 /* Look for first non white space or quote */ 526 for(p = name; (c = *p) ;p++) { 527 if(c == '"') { 528 /* Next char is start of string if non null */ 529 if(p[1]) return p + 1; 530 /* Else null string */ 531 return NULL; 532 } 533 if(!isspace((unsigned char)c)) return p; 534 } 535 return NULL; 536 } 537 538 /* As above but strip from end of string : maybe should handle brackets? */ 539 static char *strip_end(char *name) 540 { 541 char *p, c; 542 if(!name) return NULL; 543 /* Look for first non white space or quote */ 544 for(p = name + strlen(name) - 1; p >= name ;p--) { 545 c = *p; 546 if(c == '"') { 547 if(p - 1 == name) return NULL; 548 *p = 0; 549 return name; 550 } 551 if(isspace((unsigned char)c)) *p = 0; 552 else return name; 553 } 554 return NULL; 555 } 556 557 static MIME_HEADER *mime_hdr_new(char *name, char *value) 558 { 559 MIME_HEADER *mhdr; 560 char *tmpname, *tmpval, *p; 561 int c; 562 if(name) { 563 if(!(tmpname = BUF_strdup(name))) return NULL; 564 for(p = tmpname ; *p; p++) { 565 c = *p; 566 if(isupper(c)) { 567 c = tolower(c); 568 *p = c; 569 } 570 } 571 } else tmpname = NULL; 572 if(value) { 573 if(!(tmpval = BUF_strdup(value))) return NULL; 574 for(p = tmpval ; *p; p++) { 575 c = *p; 576 if(isupper(c)) { 577 c = tolower(c); 578 *p = c; 579 } 580 } 581 } else tmpval = NULL; 582 mhdr = (MIME_HEADER *) OPENSSL_malloc(sizeof(MIME_HEADER)); 583 if(!mhdr) return NULL; 584 mhdr->name = tmpname; 585 mhdr->value = tmpval; 586 if(!(mhdr->params = sk_MIME_PARAM_new(mime_param_cmp))) return NULL; 587 return mhdr; 588 } 589 590 static int mime_hdr_addparam(MIME_HEADER *mhdr, char *name, char *value) 591 { 592 char *tmpname, *tmpval, *p; 593 int c; 594 MIME_PARAM *mparam; 595 if(name) { 596 tmpname = BUF_strdup(name); 597 if(!tmpname) return 0; 598 for(p = tmpname ; *p; p++) { 599 c = *p; 600 if(isupper(c)) { 601 c = tolower(c); 602 *p = c; 603 } 604 } 605 } else tmpname = NULL; 606 if(value) { 607 tmpval = BUF_strdup(value); 608 if(!tmpval) return 0; 609 } else tmpval = NULL; 610 /* Parameter values are case sensitive so leave as is */ 611 mparam = (MIME_PARAM *) OPENSSL_malloc(sizeof(MIME_PARAM)); 612 if(!mparam) return 0; 613 mparam->param_name = tmpname; 614 mparam->param_value = tmpval; 615 sk_MIME_PARAM_push(mhdr->params, mparam); 616 return 1; 617 } 618 619 static int mime_hdr_cmp(const MIME_HEADER * const *a, 620 const MIME_HEADER * const *b) 621 { 622 return(strcmp((*a)->name, (*b)->name)); 623 } 624 625 static int mime_param_cmp(const MIME_PARAM * const *a, 626 const MIME_PARAM * const *b) 627 { 628 return(strcmp((*a)->param_name, (*b)->param_name)); 629 } 630 631 /* Find a header with a given name (if possible) */ 632 633 static MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, char *name) 634 { 635 MIME_HEADER htmp; 636 int idx; 637 htmp.name = name; 638 idx = sk_MIME_HEADER_find(hdrs, &htmp); 639 if(idx < 0) return NULL; 640 return sk_MIME_HEADER_value(hdrs, idx); 641 } 642 643 static MIME_PARAM *mime_param_find(MIME_HEADER *hdr, char *name) 644 { 645 MIME_PARAM param; 646 int idx; 647 param.param_name = name; 648 idx = sk_MIME_PARAM_find(hdr->params, ¶m); 649 if(idx < 0) return NULL; 650 return sk_MIME_PARAM_value(hdr->params, idx); 651 } 652 653 static void mime_hdr_free(MIME_HEADER *hdr) 654 { 655 if(hdr->name) OPENSSL_free(hdr->name); 656 if(hdr->value) OPENSSL_free(hdr->value); 657 if(hdr->params) sk_MIME_PARAM_pop_free(hdr->params, mime_param_free); 658 OPENSSL_free(hdr); 659 } 660 661 static void mime_param_free(MIME_PARAM *param) 662 { 663 if(param->param_name) OPENSSL_free(param->param_name); 664 if(param->param_value) OPENSSL_free(param->param_value); 665 OPENSSL_free(param); 666 } 667 668 /* Check for a multipart boundary. Returns: 669 * 0 : no boundary 670 * 1 : part boundary 671 * 2 : final boundary 672 */ 673 static int mime_bound_check(char *line, int linelen, char *bound, int blen) 674 { 675 if(linelen == -1) linelen = strlen(line); 676 if(blen == -1) blen = strlen(bound); 677 /* Quickly eliminate if line length too short */ 678 if(blen + 2 > linelen) return 0; 679 /* Check for part boundary */ 680 if(!strncmp(line, "--", 2) && !strncmp(line + 2, bound, blen)) { 681 if(!strncmp(line + blen + 2, "--", 2)) return 2; 682 else return 1; 683 } 684 return 0; 685 } 686