jarver.c (32187B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* 6 * JARVER 7 * 8 * Jarnature Parsing & Verification 9 */ 10 11 #include "nssrenam.h" 12 #include "jar.h" 13 #include "jarint.h" 14 #include "certdb.h" 15 #include "certt.h" 16 #include "secpkcs7.h" 17 #include "secder.h" 18 19 #define SZ 512 20 21 static int 22 jar_validate_pkcs7(JAR *jar, JAR_Signer *signer, char *data, long length); 23 24 static void 25 jar_catch_bytes(void *arg, const char *buf, unsigned long len); 26 27 static int 28 jar_gather_signers(JAR *jar, JAR_Signer *signer, SEC_PKCS7ContentInfo *cinfo); 29 30 static char * 31 jar_eat_line(int lines, int eating, char *data, long *len); 32 33 static JAR_Digest * 34 jar_digest_section(char *manifest, long length); 35 36 static JAR_Digest *jar_get_mf_digest(JAR *jar, char *path); 37 38 static int 39 jar_parse_digital_signature(char *raw_manifest, JAR_Signer *signer, 40 long length, JAR *jar); 41 42 static int 43 jar_add_cert(JAR *jar, JAR_Signer *signer, int type, CERTCertificate *cert); 44 45 static char *jar_basename(const char *path); 46 47 static int 48 jar_signal(int status, JAR *jar, const char *metafile, char *pathname); 49 50 #ifdef DEBUG 51 static int jar_insanity_check(char *data, long length); 52 #endif 53 54 int 55 jar_parse_mf(JAR *jar, char *raw_manifest, long length, 56 const char *path, const char *url); 57 58 int 59 jar_parse_sf(JAR *jar, char *raw_manifest, long length, 60 const char *path, const char *url); 61 62 int 63 jar_parse_sig(JAR *jar, const char *path, char *raw_manifest, 64 long length); 65 66 int 67 jar_parse_any(JAR *jar, int type, JAR_Signer *signer, 68 char *raw_manifest, long length, const char *path, 69 const char *url); 70 71 static int 72 jar_internal_digest(JAR *jar, const char *path, char *x_name, JAR_Digest *dig); 73 74 /* 75 * J A R _ p a r s e _ m a n i f e s t 76 * 77 * Pass manifest files to this function. They are 78 * decoded and placed into internal representations. 79 * 80 * Accepts both signature and manifest files. Use 81 * the same "jar" for both. 82 * 83 */ 84 int 85 JAR_parse_manifest(JAR *jar, char *raw_manifest, long length, 86 const char *path, const char *url) 87 { 88 int filename_free = 0; 89 90 /* fill in the path, if supplied. This is the location 91 of the jar file on disk, if known */ 92 93 if (jar->filename == NULL && path) { 94 jar->filename = PORT_Strdup(path); 95 if (jar->filename == NULL) 96 return JAR_ERR_MEMORY; 97 filename_free = 1; 98 } 99 100 /* fill in the URL, if supplied. This is the place 101 from which the jar file was retrieved. */ 102 103 if (jar->url == NULL && url) { 104 jar->url = PORT_Strdup(url); 105 if (jar->url == NULL) { 106 if (filename_free) { 107 PORT_Free(jar->filename); 108 } 109 return JAR_ERR_MEMORY; 110 } 111 } 112 113 /* Determine what kind of file this is from the META-INF 114 directory. It could be MF, SF, or a binary RSA/DSA file */ 115 116 if (!PORT_Strncasecmp(raw_manifest, "Manifest-Version:", 17)) { 117 return jar_parse_mf(jar, raw_manifest, length, path, url); 118 } else if (!PORT_Strncasecmp(raw_manifest, "Signature-Version:", 18)) { 119 return jar_parse_sf(jar, raw_manifest, length, path, url); 120 } else { 121 /* This is probably a binary signature */ 122 return jar_parse_sig(jar, path, raw_manifest, length); 123 } 124 } 125 126 /* 127 * j a r _ p a r s e _ s i g 128 * 129 * Pass some manner of RSA or DSA digital signature 130 * on, after checking to see if it comes at an appropriate state. 131 * 132 */ 133 int 134 jar_parse_sig(JAR *jar, const char *path, char *raw_manifest, 135 long length) 136 { 137 JAR_Signer *signer; 138 int status = JAR_ERR_ORDER; 139 140 if (length <= 128) { 141 /* signature is way too small */ 142 return JAR_ERR_SIG; 143 } 144 145 /* make sure that MF and SF have already been processed */ 146 147 if (jar->globalmeta == NULL) 148 return JAR_ERR_ORDER; 149 150 /* Determine whether or not this RSA file has 151 has an associated SF file */ 152 153 if (path) { 154 char *owner; 155 owner = jar_basename(path); 156 157 if (owner == NULL) 158 return JAR_ERR_MEMORY; 159 160 signer = jar_get_signer(jar, owner); 161 PORT_Free(owner); 162 } else 163 signer = jar_get_signer(jar, "*"); 164 165 if (signer == NULL) 166 return JAR_ERR_ORDER; 167 168 /* Do not pass a huge pointer to this function, 169 since the underlying security code is unaware. We will 170 never pass >64k through here. */ 171 172 if (length > 64000) { 173 /* this digital signature is way too big */ 174 return JAR_ERR_SIG; 175 } 176 177 /* don't expense unneeded calloc overhead on non-win16 */ 178 status = jar_parse_digital_signature(raw_manifest, signer, length, jar); 179 180 return status; 181 } 182 183 /* 184 * j a r _ p a r s e _ m f 185 * 186 * Parse the META-INF/manifest.mf file, whose 187 * information applies to all signers. 188 * 189 */ 190 int 191 jar_parse_mf(JAR *jar, char *raw_manifest, long length, 192 const char *path, const char *url) 193 { 194 if (jar->globalmeta) { 195 /* refuse a second manifest file, if passed for some reason */ 196 return JAR_ERR_ORDER; 197 } 198 199 /* remember a digest for the global section */ 200 jar->globalmeta = jar_digest_section(raw_manifest, length); 201 if (jar->globalmeta == NULL) 202 return JAR_ERR_MEMORY; 203 return jar_parse_any(jar, jarTypeMF, NULL, raw_manifest, length, 204 path, url); 205 } 206 207 /* 208 * j a r _ p a r s e _ s f 209 * 210 * Parse META-INF/xxx.sf, a digitally signed file 211 * pointing to a subset of MF sections. 212 * 213 */ 214 int 215 jar_parse_sf(JAR *jar, char *raw_manifest, long length, 216 const char *path, const char *url) 217 { 218 JAR_Signer *signer = NULL; 219 int status = JAR_ERR_MEMORY; 220 221 if (jar->globalmeta == NULL) { 222 /* It is a requirement that the MF file be passed before the SF file */ 223 return JAR_ERR_ORDER; 224 } 225 226 signer = JAR_new_signer(); 227 if (signer == NULL) 228 goto loser; 229 230 if (path) { 231 signer->owner = jar_basename(path); 232 if (signer->owner == NULL) 233 goto loser; 234 } 235 236 /* check for priors. When someone doctors a jar file 237 to contain identical path entries, prevent the second 238 one from affecting JAR functions */ 239 if (jar_get_signer(jar, signer->owner)) { 240 /* someone is trying to spoof us */ 241 status = JAR_ERR_ORDER; 242 goto loser; 243 } 244 245 /* remember its digest */ 246 signer->digest = JAR_calculate_digest(raw_manifest, length); 247 if (signer->digest == NULL) 248 goto loser; 249 250 /* Add this signer to the jar */ 251 ADDITEM(jar->signers, jarTypeOwner, signer->owner, signer, 252 sizeof(JAR_Signer)); 253 254 return jar_parse_any(jar, jarTypeSF, signer, raw_manifest, length, 255 path, url); 256 257 loser: 258 if (signer) 259 JAR_destroy_signer(signer); 260 return status; 261 } 262 263 /* 264 * j a r _ p a r s e _ a n y 265 * 266 * Parse a MF or SF manifest file. 267 * 268 */ 269 int 270 jar_parse_any(JAR *jar, int type, JAR_Signer *signer, 271 char *raw_manifest, long length, const char *path, 272 const char *url) 273 { 274 int status; 275 long raw_len; 276 JAR_Digest *dig, *mfdig = NULL; 277 char line[SZ]; 278 char x_name[SZ], x_md5[SZ], x_sha[SZ]; 279 char *x_info; 280 char *sf_md5 = NULL, *sf_sha1 = NULL; 281 282 *x_name = 0; 283 *x_md5 = 0; 284 *x_sha = 0; 285 286 PORT_Assert(length > 0); 287 raw_len = length; 288 289 #ifdef DEBUG 290 if ((status = jar_insanity_check(raw_manifest, raw_len)) < 0) 291 return status; 292 #endif 293 294 /* null terminate the first line */ 295 raw_manifest = jar_eat_line(0, PR_TRUE, raw_manifest, &raw_len); 296 297 /* skip over the preliminary section */ 298 /* This is one section at the top of the file with global metainfo */ 299 while (raw_len > 0) { 300 JAR_Metainfo *met; 301 302 raw_manifest = jar_eat_line(1, PR_TRUE, raw_manifest, &raw_len); 303 if (raw_len <= 0 || !*raw_manifest) 304 break; 305 306 met = PORT_ZNew(JAR_Metainfo); 307 if (met == NULL) 308 return JAR_ERR_MEMORY; 309 310 /* Parse out the header & info */ 311 if (PORT_Strlen(raw_manifest) >= SZ) { 312 /* almost certainly nonsense */ 313 PORT_Free(met); 314 continue; 315 } 316 317 PORT_Strcpy(line, raw_manifest); 318 x_info = line; 319 320 while (*x_info && *x_info != ' ' && *x_info != '\t' && *x_info != ':') 321 x_info++; 322 323 if (*x_info) 324 *x_info++ = 0; 325 326 while (*x_info == ' ' || *x_info == '\t') 327 x_info++; 328 329 /* metainfo (name, value) pair is now (line, x_info) */ 330 met->header = PORT_Strdup(line); 331 met->info = PORT_Strdup(x_info); 332 333 if (type == jarTypeMF) { 334 ADDITEM(jar->metainfo, jarTypeMeta, 335 /* pathname */ NULL, met, sizeof(JAR_Metainfo)); 336 } 337 338 /* For SF files, this metadata may be the digests 339 of the MF file, still in the "met" structure. */ 340 341 if (type == jarTypeSF) { 342 if (!PORT_Strcasecmp(line, "MD5-Digest")) { 343 sf_md5 = (char *)met->info; 344 } else if (!PORT_Strcasecmp(line, "SHA1-Digest") || 345 !PORT_Strcasecmp(line, "SHA-Digest")) { 346 sf_sha1 = (char *)met->info; 347 } else { 348 PORT_Free(met->info); 349 met->info = NULL; 350 } 351 } 352 353 if (type != jarTypeMF) { 354 PORT_Free(met->header); 355 if ((type != jarTypeSF || !jar->globalmeta) && met->info) { 356 PORT_Free(met->info); 357 } 358 PORT_Free(met); 359 } 360 } 361 362 if (type == jarTypeSF && jar->globalmeta) { 363 /* this is a SF file which may contain a digest of the manifest.mf's 364 global metainfo. */ 365 366 int match = 0; 367 JAR_Digest *glob = jar->globalmeta; 368 369 if (sf_md5) { 370 unsigned int md5_length; 371 unsigned char *md5_digest; 372 373 md5_digest = ATOB_AsciiToData(sf_md5, &md5_length); 374 PORT_Assert(md5_length == MD5_LENGTH); 375 PORT_Free(sf_md5); 376 377 if (md5_length != MD5_LENGTH) 378 return JAR_ERR_CORRUPT; 379 380 match = PORT_Memcmp(md5_digest, glob->md5, MD5_LENGTH); 381 PORT_Free(md5_digest); 382 } 383 384 if (sf_sha1 && match == 0) { 385 unsigned int sha1_length; 386 unsigned char *sha1_digest; 387 388 sha1_digest = ATOB_AsciiToData(sf_sha1, &sha1_length); 389 PORT_Assert(sha1_length == SHA1_LENGTH); 390 PORT_Free(sf_sha1); 391 392 if (sha1_length != SHA1_LENGTH) 393 return JAR_ERR_CORRUPT; 394 395 match = PORT_Memcmp(sha1_digest, glob->sha1, SHA1_LENGTH); 396 PORT_Free(sha1_digest); 397 } 398 399 if (match != 0) { 400 /* global digest doesn't match, SF file therefore invalid */ 401 jar->valid = JAR_ERR_METADATA; 402 return JAR_ERR_METADATA; 403 } 404 } 405 406 /* done with top section of global data */ 407 while (raw_len > 0) { 408 *x_md5 = 0; 409 *x_sha = 0; 410 *x_name = 0; 411 412 /* If this is a manifest file, attempt to get a digest of the following 413 section, without damaging it. This digest will be saved later. */ 414 415 if (type == jarTypeMF) { 416 char *sec; 417 long sec_len = raw_len; 418 419 if (!*raw_manifest || *raw_manifest == '\n') { 420 /* skip the blank line */ 421 sec = jar_eat_line(1, PR_FALSE, raw_manifest, &sec_len); 422 } else 423 sec = raw_manifest; 424 425 if (sec_len > 0 && !PORT_Strncasecmp(sec, "Name:", 5)) { 426 if (type == jarTypeMF) 427 mfdig = jar_digest_section(sec, sec_len); 428 else 429 mfdig = NULL; 430 } 431 } 432 433 while (raw_len > 0) { 434 raw_manifest = jar_eat_line(1, PR_TRUE, raw_manifest, &raw_len); 435 if (raw_len <= 0 || !*raw_manifest) 436 break; /* blank line, done with this entry */ 437 438 if (PORT_Strlen(raw_manifest) >= SZ) { 439 /* almost certainly nonsense */ 440 continue; 441 } 442 443 /* Parse out the name/value pair */ 444 PORT_Strcpy(line, raw_manifest); 445 x_info = line; 446 447 while (*x_info && *x_info != ' ' && *x_info != '\t' && 448 *x_info != ':') 449 x_info++; 450 451 if (*x_info) 452 *x_info++ = 0; 453 454 while (*x_info == ' ' || *x_info == '\t') 455 x_info++; 456 457 if (!PORT_Strcasecmp(line, "Name")) 458 PORT_Strcpy(x_name, x_info); 459 else if (!PORT_Strcasecmp(line, "MD5-Digest")) 460 PORT_Strcpy(x_md5, x_info); 461 else if (!PORT_Strcasecmp(line, "SHA1-Digest") || 462 !PORT_Strcasecmp(line, "SHA-Digest")) 463 PORT_Strcpy(x_sha, x_info); 464 465 /* Algorithm list is meta info we don't care about; keeping it out 466 of metadata saves significant space for large jar files */ 467 else if (!PORT_Strcasecmp(line, "Digest-Algorithms") || 468 !PORT_Strcasecmp(line, "Hash-Algorithms")) 469 continue; 470 471 /* Meta info is only collected for the manifest.mf file, 472 since the JAR_get_metainfo call does not support identity */ 473 else if (type == jarTypeMF) { 474 JAR_Metainfo *met; 475 476 /* this is meta-data */ 477 met = PORT_ZNew(JAR_Metainfo); 478 if (met == NULL) 479 return JAR_ERR_MEMORY; 480 481 /* metainfo (name, value) pair is now (line, x_info) */ 482 if ((met->header = PORT_Strdup(line)) == NULL) { 483 PORT_Free(met); 484 return JAR_ERR_MEMORY; 485 } 486 487 if ((met->info = PORT_Strdup(x_info)) == NULL) { 488 PORT_Free(met->header); 489 PORT_Free(met); 490 return JAR_ERR_MEMORY; 491 } 492 493 ADDITEM(jar->metainfo, jarTypeMeta, 494 x_name, met, sizeof(JAR_Metainfo)); 495 } 496 } 497 498 if (!*x_name) { 499 /* Whatever that was, it wasn't an entry, because we didn't get a 500 name. We don't really have anything, so don't record this. */ 501 continue; 502 } 503 504 dig = PORT_ZNew(JAR_Digest); 505 if (dig == NULL) 506 return JAR_ERR_MEMORY; 507 508 if (*x_md5) { 509 unsigned int binary_length; 510 unsigned char *binary_digest; 511 512 binary_digest = ATOB_AsciiToData(x_md5, &binary_length); 513 PORT_Assert(binary_length == MD5_LENGTH); 514 if (binary_length != MD5_LENGTH) { 515 PORT_Free(dig); 516 return JAR_ERR_CORRUPT; 517 } 518 memcpy(dig->md5, binary_digest, MD5_LENGTH); 519 dig->md5_status = jarHashPresent; 520 PORT_Free(binary_digest); 521 } 522 523 if (*x_sha) { 524 unsigned int binary_length; 525 unsigned char *binary_digest; 526 527 binary_digest = ATOB_AsciiToData(x_sha, &binary_length); 528 PORT_Assert(binary_length == SHA1_LENGTH); 529 if (binary_length != SHA1_LENGTH) { 530 PORT_Free(dig); 531 return JAR_ERR_CORRUPT; 532 } 533 memcpy(dig->sha1, binary_digest, SHA1_LENGTH); 534 dig->sha1_status = jarHashPresent; 535 PORT_Free(binary_digest); 536 } 537 538 PORT_Assert(type == jarTypeMF || type == jarTypeSF); 539 if (type == jarTypeMF) { 540 ADDITEM(jar->hashes, jarTypeMF, x_name, dig, sizeof(JAR_Digest)); 541 } else if (type == jarTypeSF) { 542 ADDITEM(signer->sf, jarTypeSF, x_name, dig, sizeof(JAR_Digest)); 543 } else { 544 PORT_Free(dig); 545 return JAR_ERR_ORDER; 546 } 547 548 /* we're placing these calculated digests of manifest.mf 549 sections in a list where they can subsequently be forgotten */ 550 if (type == jarTypeMF && mfdig) { 551 ADDITEM(jar->manifest, jarTypeSect, 552 x_name, mfdig, sizeof(JAR_Digest)); 553 mfdig = NULL; 554 } 555 556 /* Retrieve our saved SHA1 digest from saved copy and check digests. 557 This is just comparing the digest of the MF section as indicated in 558 the SF file with the one we remembered from parsing the MF file */ 559 560 if (type == jarTypeSF) { 561 if ((status = jar_internal_digest(jar, path, x_name, dig)) < 0) 562 return status; 563 } 564 } 565 566 return 0; 567 } 568 569 static int 570 jar_internal_digest(JAR *jar, const char *path, char *x_name, JAR_Digest *dig) 571 { 572 int cv; 573 int status; 574 575 JAR_Digest *savdig; 576 577 savdig = jar_get_mf_digest(jar, x_name); 578 if (savdig == NULL) { 579 /* no .mf digest for this pathname */ 580 status = jar_signal(JAR_ERR_ENTRY, jar, path, x_name); 581 if (status < 0) 582 return 0; /* was continue; */ 583 return status; 584 } 585 586 /* check for md5 consistency */ 587 if (dig->md5_status) { 588 cv = PORT_Memcmp(savdig->md5, dig->md5, MD5_LENGTH); 589 /* md5 hash of .mf file is not what expected */ 590 if (cv) { 591 status = jar_signal(JAR_ERR_HASH, jar, path, x_name); 592 593 /* bad hash, man */ 594 dig->md5_status = jarHashBad; 595 savdig->md5_status = jarHashBad; 596 597 if (status < 0) 598 return 0; /* was continue; */ 599 return status; 600 } 601 } 602 603 /* check for sha1 consistency */ 604 if (dig->sha1_status) { 605 cv = PORT_Memcmp(savdig->sha1, dig->sha1, SHA1_LENGTH); 606 /* sha1 hash of .mf file is not what expected */ 607 if (cv) { 608 status = jar_signal(JAR_ERR_HASH, jar, path, x_name); 609 610 /* bad hash, man */ 611 dig->sha1_status = jarHashBad; 612 savdig->sha1_status = jarHashBad; 613 614 if (status < 0) 615 return 0; /* was continue; */ 616 return status; 617 } 618 } 619 return 0; 620 } 621 622 #ifdef DEBUG 623 /* 624 * j a r _ i n s a n i t y _ c h e c k 625 * 626 * Check for illegal characters (or possibly so) 627 * in the manifest files, to detect potential memory 628 * corruption by our neighbors. Debug only, since 629 * not I18N safe. 630 * 631 */ 632 static int 633 jar_insanity_check(char *data, long length) 634 { 635 int c; 636 long off; 637 638 for (off = 0; off < length; off++) { 639 c = data[off]; 640 if (c == '\n' || c == '\r' || (c >= ' ' && c <= 128)) 641 continue; 642 return JAR_ERR_CORRUPT; 643 } 644 return 0; 645 } 646 #endif 647 648 /* 649 * j a r _ p a r s e _ d i g i t a l _ s i g n a t u r e 650 * 651 * Parse an RSA or DSA (or perhaps other) digital signature. 652 * Right now everything is PKCS7. 653 * 654 */ 655 static int 656 jar_parse_digital_signature(char *raw_manifest, JAR_Signer *signer, 657 long length, JAR *jar) 658 { 659 return jar_validate_pkcs7(jar, signer, raw_manifest, length); 660 } 661 662 /* 663 * j a r _ a d d _ c e r t 664 * 665 * Add information for the given certificate 666 * (or whatever) to the JAR linked list. A pointer 667 * is passed for some relevant reference, say 668 * for example the original certificate. 669 * 670 */ 671 static int 672 jar_add_cert(JAR *jar, JAR_Signer *signer, int type, CERTCertificate *cert) 673 { 674 JAR_Cert *fing; 675 unsigned char *keyData; 676 677 if (cert == NULL) 678 return JAR_ERR_ORDER; 679 680 fing = PORT_ZNew(JAR_Cert); 681 if (fing == NULL) 682 goto loser; 683 684 fing->cert = CERT_DupCertificate(cert); 685 686 /* get the certkey */ 687 fing->length = cert->derIssuer.len + 2 + cert->serialNumber.len; 688 fing->key = keyData = (unsigned char *)PORT_ZAlloc(fing->length); 689 if (fing->key == NULL) 690 goto loser; 691 keyData[0] = ((cert->derIssuer.len) >> 8) & 0xff; 692 keyData[1] = ((cert->derIssuer.len) & 0xff); 693 PORT_Memcpy(&keyData[2], cert->derIssuer.data, cert->derIssuer.len); 694 PORT_Memcpy(&keyData[2 + cert->derIssuer.len], cert->serialNumber.data, 695 cert->serialNumber.len); 696 697 ADDITEM(signer->certs, type, NULL, fing, sizeof(JAR_Cert)); 698 return 0; 699 700 loser: 701 if (fing) { 702 if (fing->cert) 703 CERT_DestroyCertificate(fing->cert); 704 PORT_Free(fing); 705 } 706 return JAR_ERR_MEMORY; 707 } 708 709 /* 710 * e a t _ l i n e 711 * 712 * Reads and/or modifies input buffer "data" of length "*len". 713 * This function does zero, one or two of the following tasks: 714 * 1) if "lines" is non-zero, it reads and discards that many lines from 715 * the input. NUL characters are treated as end-of-line characters, 716 * not as end-of-input characters. The input is NOT NUL terminated. 717 * Note: presently, all callers pass either 0 or 1 for lines. 718 * 2) After skipping the specified number of input lines, if "eating" is 719 * non-zero, it finds the end of the next line of input and replaces 720 * the end of line character(s) with a NUL character. 721 * This function modifies the input buffer, containing the file, in place. 722 * This function handles PC, Mac, and Unix style text files. 723 * On entry, *len contains the maximum number of characters that this 724 * function should ever examine, starting with the character in *data. 725 * On return, *len is reduced by the number of characters skipped by the 726 * first task, if any; 727 * If lines is zero and eating is false, this function returns 728 * the value in the data argument, but otherwise does nothing. 729 */ 730 static char * 731 jar_eat_line(int lines, int eating, char *data, long *len) 732 { 733 char *start = data; 734 long maxLen = *len; 735 736 if (maxLen <= 0) 737 return start; 738 739 #define GO_ON ((data - start) < maxLen) 740 741 /* Eat the requisite number of lines, if any; 742 prior to terminating the current line with a 0. */ 743 for (/* yip */; lines > 0; lines--) { 744 while (GO_ON && *data && *data != '\r' && *data != '\n') 745 data++; 746 747 /* Eat any leading CR */ 748 if (GO_ON && *data == '\r') 749 data++; 750 751 /* After the CR, ok to eat one LF */ 752 if (GO_ON && *data == '\n') 753 data++; 754 755 /* If there are NULs, this function probably put them there */ 756 while (GO_ON && !*data) 757 data++; 758 } 759 maxLen -= data - start; /* we have this many characters left. */ 760 *len = maxLen; 761 start = data; /* now start again here. */ 762 if (maxLen > 0 && eating) { 763 /* Terminate this line with a 0 */ 764 while (GO_ON && *data && *data != '\n' && *data != '\r') 765 data++; 766 767 /* If not past the end, we are allowed to eat one CR */ 768 if (GO_ON && *data == '\r') 769 *data++ = 0; 770 771 /* After the CR (if any), if not past the end, ok to eat one LF */ 772 if (GO_ON && *data == '\n') 773 *data++ = 0; 774 } 775 return start; 776 } 777 #undef GO_ON 778 779 /* 780 * j a r _ d i g e s t _ s e c t i o n 781 * 782 * Return the digests of the next section of the manifest file. 783 * Does not damage the manifest file, unlike parse_manifest. 784 * 785 */ 786 static JAR_Digest * 787 jar_digest_section(char *manifest, long length) 788 { 789 long global_len; 790 char *global_end; 791 792 global_end = manifest; 793 global_len = length; 794 795 while (global_len > 0) { 796 global_end = jar_eat_line(1, PR_FALSE, global_end, &global_len); 797 if (global_len > 0 && (*global_end == 0 || *global_end == '\n')) 798 break; 799 } 800 return JAR_calculate_digest(manifest, global_end - manifest); 801 } 802 803 /* 804 * J A R _ v e r i f y _ d i g e s t 805 * 806 * Verifies that a precalculated digest matches the 807 * expected value in the manifest. 808 * 809 */ 810 int PR_CALLBACK 811 JAR_verify_digest(JAR *jar, const char *name, JAR_Digest *dig) 812 { 813 JAR_Item *it; 814 JAR_Digest *shindig; 815 ZZLink *link; 816 ZZList *list = jar->hashes; 817 int result1 = 0; 818 int result2 = 0; 819 820 if (jar->valid < 0) { 821 /* signature not valid */ 822 return JAR_ERR_SIG; 823 } 824 if (ZZ_ListEmpty(list)) { 825 /* empty list */ 826 return JAR_ERR_PNF; 827 } 828 829 for (link = ZZ_ListHead(list); 830 !ZZ_ListIterDone(list, link); 831 link = link->next) { 832 it = link->thing; 833 if (it->type == jarTypeMF && 834 it->pathname && !PORT_Strcmp(it->pathname, name)) { 835 shindig = (JAR_Digest *)it->data; 836 if (shindig->md5_status) { 837 if (shindig->md5_status == jarHashBad) 838 return JAR_ERR_HASH; 839 result1 = memcmp(dig->md5, shindig->md5, MD5_LENGTH); 840 } 841 if (shindig->sha1_status) { 842 if (shindig->sha1_status == jarHashBad) 843 return JAR_ERR_HASH; 844 result2 = memcmp(dig->sha1, shindig->sha1, SHA1_LENGTH); 845 } 846 return (result1 == 0 && result2 == 0) ? 0 : JAR_ERR_HASH; 847 } 848 } 849 return JAR_ERR_PNF; 850 } 851 852 /* 853 * J A R _ f e t c h _ c e r t 854 * 855 * Given an opaque identifier of a certificate, 856 * return the full certificate. 857 * 858 * The new function, which retrieves by key. 859 * 860 */ 861 CERTCertificate * 862 JAR_fetch_cert(long length, void *key) 863 { 864 CERTIssuerAndSN issuerSN; 865 CERTCertificate *cert = NULL; 866 CERTCertDBHandle *certdb; 867 868 certdb = JAR_open_database(); 869 if (certdb) { 870 unsigned char *keyData = (unsigned char *)key; 871 issuerSN.derIssuer.len = (keyData[0] << 8) + keyData[0]; 872 issuerSN.derIssuer.data = &keyData[2]; 873 issuerSN.serialNumber.len = length - (2 + issuerSN.derIssuer.len); 874 issuerSN.serialNumber.data = &keyData[2 + issuerSN.derIssuer.len]; 875 cert = CERT_FindCertByIssuerAndSN(certdb, &issuerSN); 876 JAR_close_database(certdb); 877 } 878 return cert; 879 } 880 881 /* 882 * j a r _ g e t _ m f _ d i g e s t 883 * 884 * Retrieve a corresponding saved digest over a section 885 * of the main manifest file. 886 * 887 */ 888 static JAR_Digest * 889 jar_get_mf_digest(JAR *jar, char *pathname) 890 { 891 JAR_Item *it; 892 JAR_Digest *dig; 893 ZZLink *link; 894 ZZList *list = jar->manifest; 895 896 if (ZZ_ListEmpty(list)) 897 return NULL; 898 899 for (link = ZZ_ListHead(list); 900 !ZZ_ListIterDone(list, link); 901 link = link->next) { 902 it = link->thing; 903 if (it->type == jarTypeSect && 904 it->pathname && !PORT_Strcmp(it->pathname, pathname)) { 905 dig = (JAR_Digest *)it->data; 906 return dig; 907 } 908 } 909 return NULL; 910 } 911 912 /* 913 * j a r _ b a s e n a m e 914 * 915 * Return the basename -- leading components of path stripped off, 916 * extension ripped off -- of a path. 917 * 918 */ 919 static char * 920 jar_basename(const char *path) 921 { 922 char *pith, *e, *basename, *ext; 923 924 if (path == NULL) 925 return PORT_Strdup(""); 926 927 pith = PORT_Strdup(path); 928 basename = pith; 929 while (1) { 930 for (e = basename; *e && *e != '/' && *e != '\\'; e++) 931 /* yip */; 932 if (*e) 933 basename = ++e; 934 else 935 break; 936 } 937 938 if ((ext = PORT_Strrchr(basename, '.')) != NULL) 939 *ext = 0; 940 941 /* We already have the space allocated */ 942 PORT_Strcpy(pith, basename); 943 return pith; 944 } 945 946 /* 947 * + + + + + + + + + + + + + + + 948 * 949 * CRYPTO ROUTINES FOR JAR 950 * 951 * The following functions are the cryptographic 952 * interface to PKCS7 for Jarnatures. 953 * 954 * + + + + + + + + + + + + + + + 955 * 956 */ 957 958 /* 959 * j a r _ c a t c h _ b y t e s 960 * 961 * In the event signatures contain enveloped data, it will show up here. 962 * But note that the lib/pkcs7 routines aren't ready for it. 963 * 964 */ 965 static void 966 jar_catch_bytes(void *arg, const char *buf, unsigned long len) 967 { 968 /* Actually this should never be called, since there is 969 presumably no data in the signature itself. */ 970 } 971 972 /* 973 * j a r _ v a l i d a t e _ p k c s 7 974 * 975 * Validate (and decode, if necessary) a binary pkcs7 976 * signature in DER format. 977 * 978 */ 979 static int 980 jar_validate_pkcs7(JAR *jar, JAR_Signer *signer, char *data, long length) 981 { 982 983 SEC_PKCS7ContentInfo *cinfo = NULL; 984 SEC_PKCS7DecoderContext *dcx; 985 PRBool goodSig; 986 int status = 0; 987 SECItem detdig; 988 989 PORT_Assert(jar != NULL && signer != NULL); 990 991 if (jar == NULL || signer == NULL) 992 return JAR_ERR_ORDER; 993 994 signer->valid = JAR_ERR_SIG; 995 996 /* We need a context if we can get one */ 997 dcx = SEC_PKCS7DecoderStart(jar_catch_bytes, NULL /*cb_arg*/, 998 NULL /*getpassword*/, jar->mw, 999 NULL, NULL, NULL); 1000 if (dcx == NULL) { 1001 /* strange pkcs7 failure */ 1002 return JAR_ERR_PK7; 1003 } 1004 1005 SEC_PKCS7DecoderUpdate(dcx, data, length); 1006 cinfo = SEC_PKCS7DecoderFinish(dcx); 1007 if (cinfo == NULL) { 1008 /* strange pkcs7 failure */ 1009 return JAR_ERR_PK7; 1010 } 1011 if (SEC_PKCS7ContentIsEncrypted(cinfo)) { 1012 /* content was encrypted, fail */ 1013 return JAR_ERR_PK7; 1014 } 1015 if (SEC_PKCS7ContentIsSigned(cinfo) == PR_FALSE) { 1016 /* content was not signed, fail */ 1017 return JAR_ERR_PK7; 1018 } 1019 1020 PORT_SetError(0); 1021 1022 /* use SHA1 only */ 1023 detdig.len = SHA1_LENGTH; 1024 detdig.data = signer->digest->sha1; 1025 goodSig = SEC_PKCS7VerifyDetachedSignature(cinfo, 1026 certUsageObjectSigner, 1027 &detdig, HASH_AlgSHA1, 1028 PR_FALSE); 1029 jar_gather_signers(jar, signer, cinfo); 1030 if (goodSig == PR_TRUE) { 1031 /* signature is valid */ 1032 signer->valid = 0; 1033 } else { 1034 status = PORT_GetError(); 1035 PORT_Assert(status < 0); 1036 if (status >= 0) 1037 status = JAR_ERR_SIG; 1038 jar->valid = status; 1039 signer->valid = status; 1040 } 1041 jar->pkcs7 = PR_TRUE; 1042 signer->pkcs7 = PR_TRUE; 1043 SEC_PKCS7DestroyContentInfo(cinfo); 1044 return status; 1045 } 1046 1047 /* 1048 * j a r _ g a t h e r _ s i g n e r s 1049 * 1050 * Add the single signer of this signature to the 1051 * certificate linked list. 1052 * 1053 */ 1054 static int 1055 jar_gather_signers(JAR *jar, JAR_Signer *signer, SEC_PKCS7ContentInfo *cinfo) 1056 { 1057 int result; 1058 CERTCertificate *cert; 1059 CERTCertDBHandle *certdb; 1060 SEC_PKCS7SignedData *sdp = cinfo->content.signedData; 1061 SEC_PKCS7SignerInfo **pksigners, *pksigner; 1062 1063 if (sdp == NULL) 1064 return JAR_ERR_PK7; 1065 1066 pksigners = sdp->signerInfos; 1067 /* permit exactly one signer */ 1068 if (pksigners == NULL || pksigners[0] == NULL || pksigners[1] != NULL) 1069 return JAR_ERR_PK7; 1070 1071 pksigner = *pksigners; 1072 cert = pksigner->cert; 1073 1074 if (cert == NULL) 1075 return JAR_ERR_PK7; 1076 1077 certdb = JAR_open_database(); 1078 if (certdb == NULL) 1079 return JAR_ERR_GENERAL; 1080 1081 result = jar_add_cert(jar, signer, jarTypeSign, cert); 1082 JAR_close_database(certdb); 1083 return result; 1084 } 1085 1086 /* 1087 * j a r _ o p e n _ d a t a b a s e 1088 * 1089 * Open the certificate database, 1090 * for use by JAR functions. 1091 * 1092 */ 1093 CERTCertDBHandle * 1094 JAR_open_database(void) 1095 { 1096 return CERT_GetDefaultCertDB(); 1097 } 1098 1099 /* 1100 * j a r _ c l o s e _ d a t a b a s e 1101 * 1102 * Close the certificate database. 1103 * For use by JAR functions. 1104 * 1105 */ 1106 int 1107 JAR_close_database(CERTCertDBHandle *certdb) 1108 { 1109 return 0; 1110 } 1111 1112 /* 1113 * j a r _ s i g n a l 1114 * 1115 * Nonfatal errors come here to callback Java. 1116 * 1117 */ 1118 static int 1119 jar_signal(int status, JAR *jar, const char *metafile, char *pathname) 1120 { 1121 char *errstring = JAR_get_error(status); 1122 if (jar->signal) { 1123 (*jar->signal)(status, jar, metafile, pathname, errstring); 1124 return 0; 1125 } 1126 return status; 1127 } 1128 1129 /* 1130 * j a r _ a p p e n d 1131 * 1132 * Tack on an element to one of a JAR's linked 1133 * lists, with rudimentary error handling. 1134 * 1135 */ 1136 int 1137 jar_append(ZZList *list, int type, char *pathname, void *data, size_t size) 1138 { 1139 JAR_Item *it = PORT_ZNew(JAR_Item); 1140 ZZLink *entity; 1141 1142 if (it == NULL) 1143 goto loser; 1144 1145 if (pathname) { 1146 it->pathname = PORT_Strdup(pathname); 1147 if (it->pathname == NULL) 1148 goto loser; 1149 } 1150 1151 it->type = (jarType)type; 1152 it->data = (unsigned char *)data; 1153 it->size = size; 1154 entity = ZZ_NewLink(it); 1155 if (entity) { 1156 ZZ_AppendLink(list, entity); 1157 return 0; 1158 } 1159 1160 loser: 1161 if (it) { 1162 if (it->pathname) 1163 PORT_Free(it->pathname); 1164 PORT_Free(it); 1165 } 1166 return JAR_ERR_MEMORY; 1167 }