sign.c (23132B)
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 #include "signtool.h" 6 #include "zip.h" 7 #include "prmem.h" 8 #include "blapi.h" 9 #include "sechash.h" /* for HASH_GetHashObject() */ 10 11 static int create_pk7(char *dir, char *keyName, KeyType *keyType); 12 static KeyType jar_find_key_type(CERTCertificate *cert); 13 static int manifesto(char *dirname, char *install_script, PRBool recurse); 14 static int manifesto_fn(char *relpath, char *basedir, char *reldir, 15 char *filename, void *arg); 16 static int manifesto_xpi_fn(char *relpath, char *basedir, char *reldir, 17 char *filename, void *arg); 18 static int sign_all_arc_fn(char *relpath, char *basedir, char *reldir, 19 char *filename, void *arg); 20 static int add_meta(FILE *fp, char *name); 21 static int SignFile(FILE *outFile, FILE *inFile, CERTCertificate *cert); 22 static int generate_SF_file(char *manifile, char *who); 23 static int calculate_MD5_range(FILE *fp, long r1, long r2, 24 JAR_Digest *dig); 25 static void SignOut(void *arg, const char *buf, unsigned long len); 26 27 static char *metafile = NULL; 28 static int optimize = 0; 29 static FILE *mf; 30 static ZIPfile *zipfile = NULL; 31 32 /* 33 * S i g n A r c h i v e 34 * 35 * Sign an individual archive tree. A directory 36 * called META-INF is created underneath this. 37 * 38 */ 39 int 40 SignArchive(char *tree, char *keyName, char *zip_file, int javascript, 41 char *meta_file, char *install_script, int _optimize, PRBool recurse) 42 { 43 int status; 44 char tempfn[FNSIZE], fullfn[FNSIZE]; 45 KeyType keyType = rsaKey; 46 int count; 47 48 metafile = meta_file; 49 optimize = _optimize; 50 51 /* To create XPI compatible Archive manifesto() must be run before 52 * the zipfile is opened. This is so the signed files are not added 53 * the archive before the crucial rsa/dsa file*/ 54 if (xpi_arc) { 55 manifesto(tree, install_script, recurse); 56 } 57 58 if (zip_file) { 59 zipfile = JzipOpen(zip_file, NULL /*no comment*/); 60 } 61 62 /*Sign and add files to the archive normally with manifesto()*/ 63 if (!xpi_arc) { 64 manifesto(tree, install_script, recurse); 65 } 66 67 if (keyName) { 68 status = create_pk7(tree, keyName, &keyType); 69 if (status < 0) { 70 PR_fprintf(errorFD, "the tree \"%s\" was NOT SUCCESSFULLY SIGNED\n", 71 tree); 72 errorCount++; 73 exit(ERRX); 74 } 75 } 76 77 /* Add the rsa/dsa file as the first file in the archive. This is crucial 78 * for a XPInstall compatible archive */ 79 if (xpi_arc) { 80 if (verbosity >= 0) { 81 PR_fprintf(outputFD, "%s \n", XPI_TEXT); 82 } 83 84 /* rsa/dsa to zip */ 85 count = snprintf(tempfn, sizeof(tempfn), "META-INF/%s.%s", base, 86 SECKEY_GetKeyTypeString(keyType)); 87 if (count >= sizeof(tempfn)) { 88 PR_fprintf(errorFD, "unable to write key metadata\n"); 89 errorCount++; 90 exit(ERRX); 91 } 92 count = snprintf(fullfn, sizeof(fullfn), "%s/%s", tree, tempfn); 93 if (count >= sizeof(fullfn)) { 94 PR_fprintf(errorFD, "unable to write key metadata\n"); 95 errorCount++; 96 exit(ERRX); 97 } 98 JzipAdd(fullfn, tempfn, zipfile, compression_level); 99 100 /* Loop through all files & subdirectories, add to archive */ 101 foreach (tree, "", manifesto_xpi_fn, recurse, PR_FALSE /*include dirs */, 102 (void *)NULL) 103 ; 104 } 105 /* mf to zip */ 106 strcpy(tempfn, "META-INF/manifest.mf"); 107 count = snprintf(fullfn, sizeof(fullfn), "%s/%s", tree, tempfn); 108 if (count >= sizeof(fullfn)) { 109 PR_fprintf(errorFD, "unable to write manifest\n"); 110 errorCount++; 111 exit(ERRX); 112 } 113 JzipAdd(fullfn, tempfn, zipfile, compression_level); 114 115 /* sf to zip */ 116 count = snprintf(tempfn, sizeof(tempfn), "META-INF/%s.sf", base); 117 if (count >= sizeof(tempfn)) { 118 PR_fprintf(errorFD, "unable to write sf metadata\n"); 119 errorCount++; 120 exit(ERRX); 121 } 122 count = snprintf(fullfn, sizeof(fullfn), "%s/%s", tree, tempfn); 123 if (count >= sizeof(fullfn)) { 124 PR_fprintf(errorFD, "unable to write sf metadata\n"); 125 errorCount++; 126 exit(ERRX); 127 } 128 JzipAdd(fullfn, tempfn, zipfile, compression_level); 129 130 /* Add the rsa/dsa file to the zip archive normally */ 131 if (!xpi_arc) { 132 /* rsa/dsa to zip */ 133 count = snprintf(tempfn, sizeof(tempfn), "META-INF/%s.%s", base, 134 SECKEY_GetKeyTypeString(keyType)); 135 if (count >= sizeof(tempfn)) { 136 PR_fprintf(errorFD, "unable to write key metadata\n"); 137 errorCount++; 138 exit(ERRX); 139 } 140 count = snprintf(fullfn, sizeof(fullfn), "%s/%s", tree, tempfn); 141 if (count >= sizeof(fullfn)) { 142 PR_fprintf(errorFD, "unable to write key metadata\n"); 143 errorCount++; 144 exit(ERRX); 145 } 146 JzipAdd(fullfn, tempfn, zipfile, compression_level); 147 } 148 149 JzipClose(zipfile); 150 151 if (verbosity >= 0) { 152 if (javascript) { 153 PR_fprintf(outputFD, "jarfile \"%s\" signed successfully\n", 154 zip_file); 155 } else { 156 PR_fprintf(outputFD, "tree \"%s\" signed successfully\n", 157 tree); 158 } 159 } 160 161 return 0; 162 } 163 164 typedef struct { 165 char *keyName; 166 int javascript; 167 char *metafile; 168 char *install_script; 169 int optimize; 170 } SignArcInfo; 171 172 /* 173 * S i g n A l l A r c 174 * 175 * Javascript may generate multiple .arc directories, one 176 * for each jar archive needed. Sign them all. 177 * 178 */ 179 int 180 SignAllArc(char *jartree, char *keyName, int javascript, char *metafilename, 181 char *install_script, int optimize_level, PRBool recurse) 182 { 183 SignArcInfo info; 184 185 info.keyName = keyName; 186 info.javascript = javascript; 187 info.metafile = metafilename; 188 info.install_script = install_script; 189 info.optimize = optimize_level; 190 191 return foreach (jartree, "", sign_all_arc_fn, recurse, 192 PR_TRUE /*include dirs*/, (void *)&info); 193 } 194 195 static int 196 sign_all_arc_fn(char *relpath, char *basedir, char *reldir, char *filename, 197 void *arg) 198 { 199 char *zipfilename = NULL; 200 char *arc = NULL, *archive = NULL; 201 int retval = 0; 202 SignArcInfo *infop = (SignArcInfo *)arg; 203 204 /* Make sure there is one and only one ".arc" in the relative path, 205 * and that it is at the end of the path (don't sign .arcs within .arcs) */ 206 if ((PL_strcaserstr(relpath, ".arc") == relpath + strlen(relpath) - 4) && 207 (PL_strcasestr(relpath, ".arc") == relpath + strlen(relpath) - 4)) { 208 209 if (!infop) { 210 PR_fprintf(errorFD, "%s: Internal failure\n", PROGRAM_NAME); 211 errorCount++; 212 retval = -1; 213 goto finish; 214 } 215 archive = PR_smprintf("%s/%s", basedir, relpath); 216 217 zipfilename = PL_strdup(archive); 218 arc = PORT_Strrchr(zipfilename, '.'); 219 220 if (arc == NULL) { 221 PR_fprintf(errorFD, "%s: Internal failure\n", PROGRAM_NAME); 222 errorCount++; 223 retval = -1; 224 goto finish; 225 } 226 227 PL_strcpy(arc, ".jar"); 228 229 if (verbosity >= 0) { 230 PR_fprintf(outputFD, "\nsigning: %s\n", zipfilename); 231 } 232 retval = SignArchive(archive, infop->keyName, zipfilename, 233 infop->javascript, infop->metafile, infop->install_script, 234 infop->optimize, PR_TRUE /* recurse */); 235 } 236 finish: 237 if (archive) 238 PR_Free(archive); 239 if (zipfilename) 240 PR_Free(zipfilename); 241 242 return retval; 243 } 244 245 /********************************************************************* 246 * 247 * c r e a t e _ p k 7 248 */ 249 static int 250 create_pk7(char *dir, char *keyName, KeyType *keyType) 251 { 252 int status = 0; 253 const char *file_ext; 254 255 CERTCertificate *cert; 256 CERTCertDBHandle *db; 257 258 FILE *in, *out; 259 260 char sf_file[FNSIZE]; 261 char pk7_file[FNSIZE]; 262 263 /* open cert database */ 264 db = CERT_GetDefaultCertDB(); 265 266 if (db == NULL) 267 return -1; 268 269 /* find cert */ 270 /*cert = CERT_FindCertByNicknameOrEmailAddr(db, keyName);*/ 271 cert = PK11_FindCertFromNickname(keyName, &pwdata); 272 273 if (cert == NULL) { 274 SECU_PrintError(PROGRAM_NAME, 275 "Cannot find the cert \"%s\"", keyName); 276 return -1; 277 } 278 279 /* determine the key type, which sets the extension for pkcs7 object */ 280 281 *keyType = jar_find_key_type(cert); 282 file_ext = SECKEY_GetKeyTypeString(*keyType); 283 284 snprintf(sf_file, sizeof(sf_file), "%s/META-INF/%s.sf", dir, base); 285 snprintf(pk7_file, sizeof(pk7_file), "%s/META-INF/%s.%s", dir, base, file_ext); 286 287 if ((in = fopen(sf_file, "rb")) == NULL) { 288 PR_fprintf(errorFD, "%s: Can't open %s for reading\n", PROGRAM_NAME, 289 sf_file); 290 errorCount++; 291 exit(ERRX); 292 } 293 294 if ((out = fopen(pk7_file, "wb")) == NULL) { 295 PR_fprintf(errorFD, "%s: Can't open %s for writing\n", PROGRAM_NAME, 296 sf_file); 297 errorCount++; 298 exit(ERRX); 299 } 300 301 status = SignFile(out, in, cert); 302 303 CERT_DestroyCertificate(cert); 304 fclose(in); 305 fclose(out); 306 307 if (status) { 308 PR_fprintf(errorFD, "%s: PROBLEM signing data (%s)\n", 309 PROGRAM_NAME, SECU_Strerror(PORT_GetError())); 310 errorCount++; 311 return -1; 312 } 313 314 return 0; 315 } 316 317 /* 318 * j a r _ f i n d _ k e y _ t y p e 319 * 320 * Determine the key type for a given cert, which 321 * should be rsaKey or dsaKey. Any error return 0. 322 * 323 */ 324 static KeyType 325 jar_find_key_type(CERTCertificate *cert) 326 { 327 SECKEYPrivateKey *privk = NULL; 328 KeyType keyType; 329 330 /* determine its type */ 331 privk = PK11_FindKeyByAnyCert(cert, &pwdata); 332 if (privk == NULL) { 333 PR_fprintf(errorFD, "warning - can't find private key for this cert\n"); 334 warningCount++; 335 return 0; 336 } 337 338 keyType = privk->keyType; 339 SECKEY_DestroyPrivateKey(privk); 340 return keyType; 341 } 342 343 /* 344 * m a n i f e s t o 345 * 346 * Run once for every subdirectory in which a 347 * manifest is to be created -- usually exactly once. 348 * 349 */ 350 static int 351 manifesto(char *dirname, char *install_script, PRBool recurse) 352 { 353 char metadir[FNSIZE], sfname[FNSIZE]; 354 355 /* Create the META-INF directory to hold signing info */ 356 357 if (PR_Access(dirname, PR_ACCESS_READ_OK)) { 358 PR_fprintf(errorFD, "%s: unable to read your directory: %s\n", 359 PROGRAM_NAME, dirname); 360 errorCount++; 361 perror(dirname); 362 exit(ERRX); 363 } 364 365 if (PR_Access(dirname, PR_ACCESS_WRITE_OK)) { 366 PR_fprintf(errorFD, "%s: unable to write to your directory: %s\n", 367 PROGRAM_NAME, dirname); 368 errorCount++; 369 perror(dirname); 370 exit(ERRX); 371 } 372 373 snprintf(metadir, sizeof(metadir), "%s/META-INF", dirname); 374 375 strcpy(sfname, metadir); 376 377 PR_MkDir(metadir, 0777); 378 379 strcat(metadir, "/"); 380 strcat(metadir, MANIFEST); 381 382 if ((mf = fopen(metadir, "wb")) == NULL) { 383 perror(MANIFEST); 384 PR_fprintf(errorFD, "%s: Probably, the directory you are trying to" 385 " sign has\n", 386 PROGRAM_NAME); 387 PR_fprintf(errorFD, "%s: permissions problems or may not exist.\n", 388 PROGRAM_NAME); 389 errorCount++; 390 exit(ERRX); 391 } 392 393 if (verbosity >= 0) { 394 PR_fprintf(outputFD, "Generating %s file..\n", metadir); 395 } 396 397 fprintf(mf, "Manifest-Version: 1.0\n"); 398 fprintf(mf, "Created-By: %s\n", CREATOR); 399 fprintf(mf, "Comments: %s\n", BREAKAGE); 400 401 if (scriptdir) { 402 fprintf(mf, "Comments: --\n"); 403 fprintf(mf, "Comments: --\n"); 404 fprintf(mf, "Comments: -- This archive signs Javascripts which may not necessarily\n"); 405 fprintf(mf, "Comments: -- be included in the physical jar file.\n"); 406 fprintf(mf, "Comments: --\n"); 407 fprintf(mf, "Comments: --\n"); 408 } 409 410 if (install_script) 411 fprintf(mf, "Install-Script: %s\n", install_script); 412 413 if (metafile) 414 add_meta(mf, "+"); 415 416 /* Loop through all files & subdirectories */ 417 foreach (dirname, "", manifesto_fn, recurse, PR_FALSE /*include dirs */, 418 (void *)NULL) 419 ; 420 421 fclose(mf); 422 423 strcat(sfname, "/"); 424 strcat(sfname, base); 425 strcat(sfname, ".sf"); 426 427 if (verbosity >= 0) { 428 PR_fprintf(outputFD, "Generating %s.sf file..\n", base); 429 } 430 generate_SF_file(metadir, sfname); 431 432 return 0; 433 } 434 435 /* 436 * m a n i f e s t o _ x p i _ f n 437 * 438 * Called by pointer from SignArchive(), once for 439 * each file within the directory. This function 440 * is only used for adding to XPI compatible archive 441 * 442 */ 443 static int 444 manifesto_xpi_fn(char *relpath, char *basedir, char *reldir, char *filename, void *arg) 445 { 446 char fullname[FNSIZE]; 447 int count; 448 449 if (verbosity >= 0) { 450 PR_fprintf(outputFD, "--> %s\n", relpath); 451 } 452 453 /* extension matching */ 454 if (extensionsGiven) { 455 char *ext = PL_strrchr(relpath, '.'); 456 if (!ext) 457 return 0; 458 if (!PL_HashTableLookup(extensions, ext)) 459 return 0; 460 } 461 count = snprintf(fullname, sizeof(fullname), "%s/%s", basedir, relpath); 462 if (count >= sizeof(fullname)) { 463 return 1; 464 } 465 JzipAdd(fullname, relpath, zipfile, compression_level); 466 467 return 0; 468 } 469 470 /* 471 * m a n i f e s t o _ f n 472 * 473 * Called by pointer from manifesto(), once for 474 * each file within the directory. 475 * 476 */ 477 static int 478 manifesto_fn(char *relpath, char *basedir, char *reldir, char *filename, void *arg) 479 { 480 int use_js; 481 char *md5, *sha1; 482 483 JAR_Digest dig; 484 char fullname[FNSIZE]; 485 486 if (verbosity >= 0) { 487 PR_fprintf(outputFD, "--> %s\n", relpath); 488 } 489 490 /* extension matching */ 491 if (extensionsGiven) { 492 char *ext = PL_strrchr(relpath, '.'); 493 if (!ext) 494 return 0; 495 if (!PL_HashTableLookup(extensions, ext)) 496 return 0; 497 } 498 499 snprintf(fullname, sizeof(fullname), "%s/%s", basedir, relpath); 500 501 fprintf(mf, "\n"); 502 503 use_js = 0; 504 505 if (scriptdir && !PORT_Strcmp(scriptdir, reldir)) 506 use_js++; 507 508 /* sign non-.js files inside .arc directories using the javascript magic */ 509 510 if ((PL_strcaserstr(filename, ".js") != filename + strlen(filename) - 3) && 511 (PL_strcaserstr(reldir, ".arc") == reldir + strlen(filename) - 4)) 512 use_js++; 513 514 if (use_js) { 515 fprintf(mf, "Name: %s\n", filename); 516 fprintf(mf, "Magic: javascript\n"); 517 518 if (optimize == 0) 519 fprintf(mf, "javascript.id: %s\n", filename); 520 521 if (metafile) 522 add_meta(mf, filename); 523 } else { 524 fprintf(mf, "Name: %s\n", relpath); 525 if (metafile) 526 add_meta(mf, relpath); 527 } 528 529 JAR_digest_file(fullname, &dig); 530 531 if (optimize == 0) { 532 fprintf(mf, "Digest-Algorithms: MD5 SHA1\n"); 533 534 md5 = BTOA_DataToAscii(dig.md5, MD5_LENGTH); 535 fprintf(mf, "MD5-Digest: %s\n", md5); 536 PORT_Free(md5); 537 } 538 539 sha1 = BTOA_DataToAscii(dig.sha1, SHA1_LENGTH); 540 fprintf(mf, "SHA1-Digest: %s\n", sha1); 541 PORT_Free(sha1); 542 543 if (!use_js) { 544 JzipAdd(fullname, relpath, zipfile, compression_level); 545 } 546 547 return 0; 548 } 549 550 /* 551 * a d d _ m e t a 552 * 553 * Parse the metainfo file, and add any details 554 * necessary to the manifest file. In most cases you 555 * should be using the -i option (ie, for SmartUpdate). 556 * 557 */ 558 static int 559 add_meta(FILE *fp, char *name) 560 { 561 FILE *met; 562 char buf[BUFSIZ]; 563 564 int place; 565 char *pattern, *meta; 566 567 int num = 0; 568 569 if ((met = fopen(metafile, "r")) != NULL) { 570 while (fgets(buf, BUFSIZ, met)) { 571 char *s; 572 573 for (s = buf; *s && *s != '\n' && *s != '\r'; s++) 574 ; 575 *s = 0; 576 577 if (*buf == 0) 578 continue; 579 580 pattern = buf; 581 582 /* skip to whitespace */ 583 for (s = buf; *s && *s != ' ' && *s != '\t'; s++) 584 ; 585 586 /* terminate pattern */ 587 if (*s == ' ' || *s == '\t') 588 *s++ = 0; 589 590 /* eat through whitespace */ 591 while (*s == ' ' || *s == '\t') 592 s++; 593 594 meta = s; 595 596 /* this will eventually be regexp matching */ 597 598 place = 0; 599 if (!PORT_Strcmp(pattern, name)) 600 place = 1; 601 602 if (place) { 603 num++; 604 if (verbosity >= 0) { 605 PR_fprintf(outputFD, "[%s] %s\n", name, meta); 606 } 607 fprintf(fp, "%s\n", meta); 608 } 609 } 610 fclose(met); 611 } else { 612 PR_fprintf(errorFD, "%s: can't open metafile: %s\n", PROGRAM_NAME, 613 metafile); 614 errorCount++; 615 exit(ERRX); 616 } 617 618 return num; 619 } 620 621 /********************************************************************** 622 * 623 * S i g n F i l e 624 */ 625 static int 626 SignFile(FILE *outFile, FILE *inFile, CERTCertificate *cert) 627 { 628 int nb; 629 char ibuf[4096], digestdata[32]; 630 const SECHashObject *hashObj; 631 void *hashcx; 632 unsigned int len; 633 634 SECItem digest; 635 SEC_PKCS7ContentInfo *cinfo; 636 SECStatus rv; 637 638 if (outFile == NULL || inFile == NULL || cert == NULL) 639 return -1; 640 641 /* XXX probably want to extend interface to allow other hash algorithms */ 642 hashObj = HASH_GetHashObject(HASH_AlgSHA1); 643 644 hashcx = (*hashObj->create)(); 645 if (hashcx == NULL) 646 return -1; 647 648 (*hashObj->begin)(hashcx); 649 650 for (;;) { 651 if (feof(inFile)) 652 break; 653 nb = fread(ibuf, 1, sizeof(ibuf), inFile); 654 if (nb == 0) { 655 if (ferror(inFile)) { 656 PORT_SetError(SEC_ERROR_IO); 657 (*hashObj->destroy)(hashcx, PR_TRUE); 658 return -1; 659 } 660 /* eof */ 661 break; 662 } 663 (*hashObj->update)(hashcx, (unsigned char *)ibuf, nb); 664 } 665 666 (*hashObj->end)(hashcx, (unsigned char *)digestdata, &len, 32); 667 (*hashObj->destroy)(hashcx, PR_TRUE); 668 669 digest.data = (unsigned char *)digestdata; 670 digest.len = len; 671 672 cinfo = SEC_PKCS7CreateSignedData(cert, certUsageObjectSigner, NULL, 673 SEC_OID_SHA1, &digest, NULL, NULL); 674 675 if (cinfo == NULL) 676 return -1; 677 678 rv = SEC_PKCS7IncludeCertChain(cinfo, NULL); 679 if (rv != SECSuccess) { 680 SEC_PKCS7DestroyContentInfo(cinfo); 681 return -1; 682 } 683 684 if (no_time == 0) { 685 rv = SEC_PKCS7AddSigningTime(cinfo); 686 if (rv != SECSuccess) { 687 /* don't check error */ 688 } 689 } 690 691 rv = SEC_PKCS7Encode(cinfo, SignOut, outFile, NULL, NULL, &pwdata); 692 693 SEC_PKCS7DestroyContentInfo(cinfo); 694 695 if (rv != SECSuccess) 696 return -1; 697 698 return 0; 699 } 700 701 /* 702 * g e n e r a t e _ S F _ f i l e 703 * 704 * From the supplied manifest file, calculates 705 * digests on the various sections, creating a .SF 706 * file in the process. 707 * 708 */ 709 static int 710 generate_SF_file(char *manifile, char *who) 711 { 712 FILE *sfFile; 713 FILE *mfFile; 714 long r1, r2, r3; 715 char whofile[FNSIZE]; 716 char *buf, *name = NULL; 717 char *md5, *sha1; 718 JAR_Digest dig; 719 int line = 0; 720 721 strcpy(whofile, who); 722 723 if ((mfFile = fopen(manifile, "rb")) == NULL) { 724 perror(manifile); 725 exit(ERRX); 726 } 727 728 if ((sfFile = fopen(whofile, "wb")) == NULL) { 729 perror(who); 730 exit(ERRX); 731 } 732 733 buf = (char *)PORT_ZAlloc(BUFSIZ); 734 735 if (buf) 736 name = (char *)PORT_ZAlloc(BUFSIZ); 737 738 if (buf == NULL || name == NULL) 739 out_of_memory(); 740 741 fprintf(sfFile, "Signature-Version: 1.0\n"); 742 fprintf(sfFile, "Created-By: %s\n", CREATOR); 743 fprintf(sfFile, "Comments: %s\n", BREAKAGE); 744 745 if (fgets(buf, BUFSIZ, mfFile) == NULL) { 746 PR_fprintf(errorFD, "%s: empty manifest file!\n", PROGRAM_NAME); 747 errorCount++; 748 exit(ERRX); 749 } 750 751 if (strncmp(buf, "Manifest-Version:", 17)) { 752 PR_fprintf(errorFD, "%s: not a manifest file!\n", PROGRAM_NAME); 753 errorCount++; 754 exit(ERRX); 755 } 756 757 fseek(mfFile, 0L, SEEK_SET); 758 759 /* Process blocks of headers, and calculate their hashen */ 760 761 while (1) { 762 /* Beginning range */ 763 r1 = ftell(mfFile); 764 765 if (fgets(name, BUFSIZ, mfFile) == NULL) 766 break; 767 768 line++; 769 770 if (r1 != 0 && strncmp(name, "Name:", 5)) { 771 PR_fprintf(errorFD, 772 "warning: unexpected input in manifest file \"%s\" at line %d:\n", 773 manifile, line); 774 PR_fprintf(errorFD, "%s\n", name); 775 warningCount++; 776 } 777 778 r2 = r1; 779 while (fgets(buf, BUFSIZ, mfFile)) { 780 if (*buf == 0 || *buf == '\n' || *buf == '\r') 781 break; 782 783 line++; 784 785 /* Ending range for hashing */ 786 r2 = ftell(mfFile); 787 } 788 789 r3 = ftell(mfFile); 790 791 if (r1) { 792 fprintf(sfFile, "\n"); 793 fprintf(sfFile, "%s", name); 794 } 795 796 calculate_MD5_range(mfFile, r1, r2, &dig); 797 798 if (optimize == 0) { 799 fprintf(sfFile, "Digest-Algorithms: MD5 SHA1\n"); 800 801 md5 = BTOA_DataToAscii(dig.md5, MD5_LENGTH); 802 fprintf(sfFile, "MD5-Digest: %s\n", md5); 803 PORT_Free(md5); 804 } 805 806 sha1 = BTOA_DataToAscii(dig.sha1, SHA1_LENGTH); 807 fprintf(sfFile, "SHA1-Digest: %s\n", sha1); 808 PORT_Free(sha1); 809 810 /* restore normalcy after changing offset position */ 811 fseek(mfFile, r3, SEEK_SET); 812 } 813 814 PORT_Free(buf); 815 PORT_Free(name); 816 817 fclose(sfFile); 818 fclose(mfFile); 819 820 return 0; 821 } 822 823 /* 824 * c a l c u l a t e _ M D 5 _ r a n g e 825 * 826 * Calculate the MD5 digest on a range of bytes in 827 * the specified fopen'd file. Returns base64. 828 * 829 */ 830 static int 831 calculate_MD5_range(FILE *fp, long r1, long r2, JAR_Digest *dig) 832 { 833 int num; 834 int range; 835 unsigned char *buf; 836 SECStatus rv; 837 838 range = r2 - r1; 839 840 /* position to the beginning of range */ 841 fseek(fp, r1, SEEK_SET); 842 843 buf = (unsigned char *)PORT_ZAlloc(range); 844 if (buf == NULL) 845 out_of_memory(); 846 847 if ((num = fread(buf, 1, range, fp)) != range) { 848 PR_fprintf(errorFD, "%s: expected %d bytes, got %d\n", PROGRAM_NAME, 849 range, num); 850 errorCount++; 851 exit(ERRX); 852 } 853 854 rv = PK11_HashBuf(SEC_OID_MD5, dig->md5, buf, range); 855 if (rv == SECSuccess) { 856 rv = PK11_HashBuf(SEC_OID_SHA1, dig->sha1, buf, range); 857 } 858 if (rv != SECSuccess) { 859 PR_fprintf(errorFD, "%s: can't generate digest context\n", 860 PROGRAM_NAME); 861 errorCount++; 862 exit(ERRX); 863 } 864 865 PORT_Free(buf); 866 867 return 0; 868 } 869 870 static void 871 SignOut(void *arg, const char *buf, unsigned long len) 872 { 873 fwrite(buf, len, 1, (FILE *)arg); 874 }