security/nss/lib/jar/jarver.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial