jar.c (17049B)
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 * JAR.C 7 * 8 * Jarnature. 9 * Routines common to signing and validating. 10 * 11 */ 12 13 #include "jar.h" 14 #include "jarint.h" 15 #include "portreg.h" 16 17 static void 18 jar_destroy_list(ZZList *list); 19 20 static int 21 jar_find_first_cert(JAR_Signer *signer, jarType type, JAR_Item **it); 22 23 /* 24 * J A R _ n e w 25 * 26 * Create a new instantiation of a manifest representation. 27 * Use this as a token to any calls to this API. 28 * 29 */ 30 JAR * 31 JAR_new(void) 32 { 33 JAR *jar; 34 35 if ((jar = (JAR *)PORT_ZAlloc(sizeof(JAR))) == NULL) 36 goto loser; 37 if ((jar->manifest = ZZ_NewList()) == NULL) 38 goto loser; 39 if ((jar->hashes = ZZ_NewList()) == NULL) 40 goto loser; 41 if ((jar->phy = ZZ_NewList()) == NULL) 42 goto loser; 43 if ((jar->metainfo = ZZ_NewList()) == NULL) 44 goto loser; 45 if ((jar->signers = ZZ_NewList()) == NULL) 46 goto loser; 47 return jar; 48 49 loser: 50 if (jar) { 51 if (jar->manifest) 52 ZZ_DestroyList(jar->manifest); 53 if (jar->hashes) 54 ZZ_DestroyList(jar->hashes); 55 if (jar->phy) 56 ZZ_DestroyList(jar->phy); 57 if (jar->metainfo) 58 ZZ_DestroyList(jar->metainfo); 59 if (jar->signers) 60 ZZ_DestroyList(jar->signers); 61 PORT_Free(jar); 62 } 63 return NULL; 64 } 65 66 /* 67 * J A R _ d e s t r o y 68 */ 69 void PR_CALLBACK 70 JAR_destroy(JAR *jar) 71 { 72 PORT_Assert(jar != NULL); 73 74 if (jar == NULL) 75 return; 76 77 if (jar->fp) 78 JAR_FCLOSE((PRFileDesc *)jar->fp); 79 if (jar->url) 80 PORT_Free(jar->url); 81 if (jar->filename) 82 PORT_Free(jar->filename); 83 if (jar->globalmeta) 84 PORT_Free(jar->globalmeta); 85 86 /* Free the linked list elements */ 87 jar_destroy_list(jar->manifest); 88 ZZ_DestroyList(jar->manifest); 89 jar_destroy_list(jar->hashes); 90 ZZ_DestroyList(jar->hashes); 91 jar_destroy_list(jar->phy); 92 ZZ_DestroyList(jar->phy); 93 jar_destroy_list(jar->metainfo); 94 ZZ_DestroyList(jar->metainfo); 95 jar_destroy_list(jar->signers); 96 ZZ_DestroyList(jar->signers); 97 PORT_Free(jar); 98 } 99 100 static void 101 jar_destroy_list(ZZList *list) 102 { 103 ZZLink *link, *oldlink; 104 JAR_Item *it; 105 JAR_Physical *phy; 106 JAR_Digest *dig; 107 JAR_Cert *fing; 108 JAR_Metainfo *met; 109 JAR_Signer *signer; 110 111 if (list && !ZZ_ListEmpty(list)) { 112 link = ZZ_ListHead(list); 113 while (!ZZ_ListIterDone(list, link)) { 114 it = link->thing; 115 if (!it) 116 goto next; 117 if (it->pathname) 118 PORT_Free(it->pathname); 119 120 switch (it->type) { 121 case jarTypeMeta: 122 met = (JAR_Metainfo *)it->data; 123 if (met) { 124 if (met->header) 125 PORT_Free(met->header); 126 if (met->info) 127 PORT_Free(met->info); 128 PORT_Free(met); 129 } 130 break; 131 132 case jarTypePhy: 133 phy = (JAR_Physical *)it->data; 134 if (phy) 135 PORT_Free(phy); 136 break; 137 138 case jarTypeSign: 139 fing = (JAR_Cert *)it->data; 140 if (fing) { 141 if (fing->cert) 142 CERT_DestroyCertificate(fing->cert); 143 if (fing->key) 144 PORT_Free(fing->key); 145 PORT_Free(fing); 146 } 147 break; 148 149 case jarTypeSect: 150 case jarTypeMF: 151 case jarTypeSF: 152 dig = (JAR_Digest *)it->data; 153 if (dig) { 154 PORT_Free(dig); 155 } 156 break; 157 158 case jarTypeOwner: 159 signer = (JAR_Signer *)it->data; 160 if (signer) 161 JAR_destroy_signer(signer); 162 break; 163 164 default: 165 /* PORT_Assert( 1 != 2 ); */ 166 break; 167 } 168 PORT_Free(it); 169 170 next: 171 oldlink = link; 172 link = link->next; 173 ZZ_DestroyLink(oldlink); 174 } 175 } 176 } 177 178 /* 179 * J A R _ g e t _ m e t a i n f o 180 * 181 * Retrieve meta information from the manifest file. 182 * It doesn't matter whether it's from .MF or .SF, does it? 183 * 184 */ 185 186 int 187 JAR_get_metainfo(JAR *jar, char *name, char *header, void **info, 188 unsigned long *length) 189 { 190 JAR_Item *it; 191 ZZLink *link; 192 ZZList *list; 193 194 PORT_Assert(jar != NULL && header != NULL); 195 196 if (jar == NULL || header == NULL) 197 return JAR_ERR_PNF; 198 199 list = jar->metainfo; 200 201 if (ZZ_ListEmpty(list)) 202 return JAR_ERR_PNF; 203 204 for (link = ZZ_ListHead(list); 205 !ZZ_ListIterDone(list, link); 206 link = link->next) { 207 it = link->thing; 208 if (it->type == jarTypeMeta) { 209 JAR_Metainfo *met; 210 211 if ((name && !it->pathname) || (!name && it->pathname)) 212 continue; 213 if (name && it->pathname && strcmp(it->pathname, name)) 214 continue; 215 met = (JAR_Metainfo *)it->data; 216 if (!PORT_Strcasecmp(met->header, header)) { 217 *info = PORT_Strdup(met->info); 218 *length = PORT_Strlen(met->info); 219 return 0; 220 } 221 } 222 } 223 return JAR_ERR_PNF; 224 } 225 226 /* 227 * J A R _ f i n d 228 * 229 * Establish the search pattern for use 230 * by JAR_find_next, to traverse the filenames 231 * or certificates in the JAR structure. 232 * 233 * See jar.h for a description on how to use. 234 * 235 */ 236 JAR_Context * 237 JAR_find(JAR *jar, char *pattern, jarType type) 238 { 239 JAR_Context *ctx; 240 241 PORT_Assert(jar != NULL); 242 243 if (!jar) 244 return NULL; 245 246 ctx = (JAR_Context *)PORT_ZAlloc(sizeof(JAR_Context)); 247 if (ctx == NULL) 248 return NULL; 249 250 ctx->jar = jar; 251 if (pattern) { 252 if ((ctx->pattern = PORT_Strdup(pattern)) == NULL) { 253 PORT_Free(ctx); 254 return NULL; 255 } 256 } 257 ctx->finding = type; 258 259 switch (type) { 260 case jarTypeMF: 261 ctx->next = ZZ_ListHead(jar->hashes); 262 break; 263 264 case jarTypeSF: 265 case jarTypeSign: 266 ctx->next = NULL; 267 ctx->nextsign = ZZ_ListHead(jar->signers); 268 break; 269 270 case jarTypeSect: 271 ctx->next = ZZ_ListHead(jar->manifest); 272 break; 273 274 case jarTypePhy: 275 ctx->next = ZZ_ListHead(jar->phy); 276 break; 277 278 case jarTypeOwner: 279 if (jar->signers) 280 ctx->next = ZZ_ListHead(jar->signers); 281 else 282 ctx->next = NULL; 283 break; 284 285 case jarTypeMeta: 286 ctx->next = ZZ_ListHead(jar->metainfo); 287 break; 288 289 default: 290 PORT_Assert(1 != 2); 291 break; 292 } 293 return ctx; 294 } 295 296 /* 297 * J A R _ f i n d _ e n d 298 * 299 * Destroy the find iterator context. 300 * 301 */ 302 void 303 JAR_find_end(JAR_Context *ctx) 304 { 305 PORT_Assert(ctx != NULL); 306 if (ctx) { 307 if (ctx->pattern) 308 PORT_Free(ctx->pattern); 309 PORT_Free(ctx); 310 } 311 } 312 313 /* 314 * J A R _ f i n d _ n e x t 315 * 316 * Return the next item of the given type 317 * from one of the JAR linked lists. 318 * 319 */ 320 321 int 322 JAR_find_next(JAR_Context *ctx, JAR_Item **it) 323 { 324 JAR *jar; 325 ZZList *list = NULL; 326 jarType finding; 327 JAR_Signer *signer = NULL; 328 329 PORT_Assert(ctx != NULL); 330 PORT_Assert(ctx->jar != NULL); 331 332 jar = ctx->jar; 333 334 /* Internally, convert jarTypeSign to jarTypeSF, and return 335 the actual attached certificate later */ 336 finding = (ctx->finding == jarTypeSign) ? jarTypeSF : ctx->finding; 337 if (ctx->nextsign) { 338 if (ZZ_ListIterDone(jar->signers, ctx->nextsign)) { 339 *it = NULL; 340 return -1; 341 } 342 PORT_Assert(ctx->nextsign->thing != NULL); 343 signer = (JAR_Signer *)ctx->nextsign->thing->data; 344 } 345 346 /* Find out which linked list to traverse. Then if 347 necessary, advance to the next linked list. */ 348 while (1) { 349 switch (finding) { 350 case jarTypeSign: /* not any more */ 351 PORT_Assert(finding != jarTypeSign); 352 list = signer->certs; 353 break; 354 355 case jarTypeSect: 356 list = jar->manifest; 357 break; 358 359 case jarTypePhy: 360 list = jar->phy; 361 break; 362 363 case jarTypeSF: /* signer, not jar */ 364 PORT_Assert(signer != NULL); 365 list = signer ? signer->sf : NULL; 366 break; 367 368 case jarTypeMF: 369 list = jar->hashes; 370 break; 371 372 case jarTypeOwner: 373 list = jar->signers; 374 break; 375 376 case jarTypeMeta: 377 list = jar->metainfo; 378 break; 379 380 default: 381 PORT_Assert(1 != 2); 382 list = NULL; 383 break; 384 } 385 if (list == NULL) { 386 *it = NULL; 387 return -1; 388 } 389 /* When looping over lists of lists, advance to the next signer. 390 This is done when multiple signers are possible. */ 391 if (ZZ_ListIterDone(list, ctx->next)) { 392 if (ctx->nextsign && jar->signers) { 393 ctx->nextsign = ctx->nextsign->next; 394 if (!ZZ_ListIterDone(jar->signers, ctx->nextsign)) { 395 PORT_Assert(ctx->nextsign->thing != NULL); 396 signer = (JAR_Signer *)ctx->nextsign->thing->data; 397 PORT_Assert(signer != NULL); 398 ctx->next = NULL; 399 continue; 400 } 401 } 402 *it = NULL; 403 return -1; 404 } 405 406 /* if the signer changed, still need to fill in the "next" link */ 407 if (ctx->nextsign && ctx->next == NULL) { 408 switch (finding) { 409 case jarTypeSF: 410 ctx->next = ZZ_ListHead(signer->sf); 411 break; 412 413 case jarTypeSign: 414 ctx->next = ZZ_ListHead(signer->certs); 415 break; 416 417 case jarTypeMF: 418 case jarTypeMeta: 419 case jarTypePhy: 420 case jarTypeSect: 421 case jarTypeOwner: 422 break; 423 } 424 } 425 PORT_Assert(ctx->next != NULL); 426 if (ctx->next == NULL) { 427 *it = NULL; 428 return -1; 429 } 430 while (!ZZ_ListIterDone(list, ctx->next)) { 431 *it = ctx->next->thing; 432 ctx->next = ctx->next->next; 433 if (!*it || (*it)->type != finding) 434 continue; 435 if (ctx->pattern && *ctx->pattern) { 436 if (PORT_RegExpSearch((*it)->pathname, ctx->pattern)) 437 continue; 438 } 439 /* We have a valid match. If this is a jarTypeSign 440 return the certificate instead.. */ 441 if (ctx->finding == jarTypeSign) { 442 JAR_Item *itt; 443 444 /* just the first one for now */ 445 if (jar_find_first_cert(signer, jarTypeSign, &itt) >= 0) { 446 *it = itt; 447 return 0; 448 } 449 continue; 450 } 451 return 0; 452 } 453 } /* end while */ 454 } 455 456 static int 457 jar_find_first_cert(JAR_Signer *signer, jarType type, JAR_Item **it) 458 { 459 ZZLink *link; 460 ZZList *list = signer->certs; 461 int status = JAR_ERR_PNF; 462 463 *it = NULL; 464 if (ZZ_ListEmpty(list)) { 465 /* empty list */ 466 return JAR_ERR_PNF; 467 } 468 469 for (link = ZZ_ListHead(list); 470 !ZZ_ListIterDone(list, link); 471 link = link->next) { 472 if (link->thing->type == type) { 473 *it = link->thing; 474 status = 0; 475 break; 476 } 477 } 478 return status; 479 } 480 481 JAR_Signer * 482 JAR_new_signer(void) 483 { 484 JAR_Signer *signer = (JAR_Signer *)PORT_ZAlloc(sizeof(JAR_Signer)); 485 if (signer == NULL) 486 goto loser; 487 488 /* certs */ 489 signer->certs = ZZ_NewList(); 490 if (signer->certs == NULL) 491 goto loser; 492 493 /* sf */ 494 signer->sf = ZZ_NewList(); 495 if (signer->sf == NULL) 496 goto loser; 497 return signer; 498 499 loser: 500 if (signer) { 501 if (signer->certs) 502 ZZ_DestroyList(signer->certs); 503 if (signer->sf) 504 ZZ_DestroyList(signer->sf); 505 PORT_Free(signer); 506 } 507 return NULL; 508 } 509 510 void 511 JAR_destroy_signer(JAR_Signer *signer) 512 { 513 if (signer) { 514 if (signer->owner) 515 PORT_Free(signer->owner); 516 if (signer->digest) 517 PORT_Free(signer->digest); 518 jar_destroy_list(signer->sf); 519 ZZ_DestroyList(signer->sf); 520 jar_destroy_list(signer->certs); 521 ZZ_DestroyList(signer->certs); 522 PORT_Free(signer); 523 } 524 } 525 526 JAR_Signer * 527 jar_get_signer(JAR *jar, char *basename) 528 { 529 JAR_Item *it; 530 JAR_Context *ctx = JAR_find(jar, NULL, jarTypeOwner); 531 JAR_Signer *candidate; 532 JAR_Signer *signer = NULL; 533 534 if (ctx == NULL) 535 return NULL; 536 537 while (JAR_find_next(ctx, &it) >= 0) { 538 candidate = (JAR_Signer *)it->data; 539 if (*basename == '*' || !PORT_Strcmp(candidate->owner, basename)) { 540 signer = candidate; 541 break; 542 } 543 } 544 JAR_find_end(ctx); 545 return signer; 546 } 547 548 /* 549 * J A R _ g e t _ f i l e n a m e 550 * 551 * Returns the filename associated with 552 * a JAR structure. 553 * 554 */ 555 char * 556 JAR_get_filename(JAR *jar) 557 { 558 return jar->filename; 559 } 560 561 /* 562 * J A R _ g e t _ u r l 563 * 564 * Returns the URL associated with 565 * a JAR structure. Nobody really uses this now. 566 * 567 */ 568 char * 569 JAR_get_url(JAR *jar) 570 { 571 return jar->url; 572 } 573 574 /* 575 * J A R _ s e t _ c a l l b a c k 576 * 577 * Register some manner of callback function for this jar. 578 * 579 */ 580 int 581 JAR_set_callback(int type, JAR *jar, jar_settable_callback_fn *fn) 582 { 583 if (type == JAR_CB_SIGNAL) { 584 jar->signal = fn; 585 return 0; 586 } 587 return -1; 588 } 589 590 /* 591 * Callbacks 592 * 593 */ 594 595 /* To return an error string */ 596 char *(*jar_fn_GetString)(int) = NULL; 597 598 /* To return an MWContext for Java */ 599 void *(*jar_fn_FindSomeContext)(void) = NULL; 600 601 /* To fabricate an MWContext for FE_GetPassword */ 602 void *(*jar_fn_GetInitContext)(void) = NULL; 603 604 void 605 JAR_init_callbacks(char *(*string_cb)(int), 606 void *(*find_cx)(void), 607 void *(*init_cx)(void)) 608 { 609 jar_fn_GetString = string_cb; 610 jar_fn_FindSomeContext = find_cx; 611 jar_fn_GetInitContext = init_cx; 612 } 613 614 /* 615 * J A R _ g e t _ e r r o r 616 * 617 * This is provided to map internal JAR errors to strings for 618 * the Java console. Also, a DLL may call this function if it does 619 * not have access to the XP_GetString function. 620 * 621 * These strings aren't UI, since they are Java console only. 622 * 623 */ 624 char * 625 JAR_get_error(int status) 626 { 627 char *errstring = NULL; 628 629 switch (status) { 630 case JAR_ERR_GENERAL: 631 errstring = "General JAR file error"; 632 break; 633 634 case JAR_ERR_FNF: 635 errstring = "JAR file not found"; 636 break; 637 638 case JAR_ERR_CORRUPT: 639 errstring = "Corrupt JAR file"; 640 break; 641 642 case JAR_ERR_MEMORY: 643 errstring = "Out of memory"; 644 break; 645 646 case JAR_ERR_DISK: 647 errstring = "Disk error (perhaps out of space)"; 648 break; 649 650 case JAR_ERR_ORDER: 651 errstring = "Inconsistent files in META-INF directory"; 652 break; 653 654 case JAR_ERR_SIG: 655 errstring = "Invalid digital signature file"; 656 break; 657 658 case JAR_ERR_METADATA: 659 errstring = "JAR metadata failed verification"; 660 break; 661 662 case JAR_ERR_ENTRY: 663 errstring = "No Manifest entry for this JAR entry"; 664 break; 665 666 case JAR_ERR_HASH: 667 errstring = "Invalid Hash of this JAR entry"; 668 break; 669 670 case JAR_ERR_PK7: 671 errstring = "Strange PKCS7 or RSA failure"; 672 break; 673 674 case JAR_ERR_PNF: 675 errstring = "Path not found inside JAR file"; 676 break; 677 678 default: 679 if (jar_fn_GetString) { 680 errstring = jar_fn_GetString(status); 681 } else { 682 /* this is not a normal situation, and would only be 683 called in cases of improper initialization */ 684 char *err = (char *)PORT_Alloc(40); 685 if (err) 686 PR_snprintf(err, 39, "Error %d\n", status); /* leak me! */ 687 else 688 err = "Error! Bad! Out of memory!"; 689 return err; 690 } 691 break; 692 } 693 return errstring; 694 }