security/nss/lib/jar/jarfile.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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 * JARFILE
michael@0 7 *
michael@0 8 * Parsing of a Jar file
michael@0 9 */
michael@0 10 #define JAR_SIZE 256
michael@0 11
michael@0 12 #include "jar.h"
michael@0 13 #include "jarint.h"
michael@0 14 #include "jarfile.h"
michael@0 15
michael@0 16 /* commercial compression */
michael@0 17 #include "jzlib.h"
michael@0 18
michael@0 19 #if defined(XP_UNIX) || defined(XP_BEOS)
michael@0 20 #include "sys/stat.h"
michael@0 21 #endif
michael@0 22
michael@0 23 #include "sechash.h" /* for HASH_GetHashObject() */
michael@0 24
michael@0 25 PR_STATIC_ASSERT(46 == sizeof(struct ZipCentral));
michael@0 26 PR_STATIC_ASSERT(30 == sizeof(struct ZipLocal));
michael@0 27 PR_STATIC_ASSERT(22 == sizeof(struct ZipEnd));
michael@0 28 PR_STATIC_ASSERT(512 == sizeof(union TarEntry));
michael@0 29
michael@0 30 /* extracting */
michael@0 31 static int
michael@0 32 jar_guess_jar(const char *filename, JAR_FILE fp);
michael@0 33
michael@0 34 static int
michael@0 35 jar_inflate_memory(unsigned int method, long *length, long expected_out_len,
michael@0 36 char **data);
michael@0 37
michael@0 38 static int
michael@0 39 jar_physical_extraction(JAR_FILE fp, char *outpath, long offset, long length);
michael@0 40
michael@0 41 static int
michael@0 42 jar_physical_inflate(JAR_FILE fp, char *outpath, long offset, long length,
michael@0 43 unsigned int method);
michael@0 44
michael@0 45 static int
michael@0 46 jar_verify_extract(JAR *jar, char *path, char *physical_path);
michael@0 47
michael@0 48 static JAR_Physical *
michael@0 49 jar_get_physical(JAR *jar, char *pathname);
michael@0 50
michael@0 51 static int
michael@0 52 jar_extract_manifests(JAR *jar, jarArch format, JAR_FILE fp);
michael@0 53
michael@0 54 static int
michael@0 55 jar_extract_mf(JAR *jar, jarArch format, JAR_FILE fp, char *ext);
michael@0 56
michael@0 57
michael@0 58 /* indexing */
michael@0 59 static int
michael@0 60 jar_gen_index(JAR *jar, jarArch format, JAR_FILE fp);
michael@0 61
michael@0 62 static int
michael@0 63 jar_listtar(JAR *jar, JAR_FILE fp);
michael@0 64
michael@0 65 static int
michael@0 66 jar_listzip(JAR *jar, JAR_FILE fp);
michael@0 67
michael@0 68
michael@0 69 /* conversions */
michael@0 70 static int
michael@0 71 dosdate(char *date, const char *s);
michael@0 72
michael@0 73 static int
michael@0 74 dostime(char *time, const char *s);
michael@0 75
michael@0 76 #ifdef NSS_X86_OR_X64
michael@0 77 #define x86ShortToUint32(ii) ((const PRUint32)*((const PRUint16 *)(ii)))
michael@0 78 #define x86LongToUint32(ii) (*(const PRUint32 *)(ii))
michael@0 79 #else
michael@0 80 static PRUint32
michael@0 81 x86ShortToUint32(const void *ii);
michael@0 82
michael@0 83 static PRUint32
michael@0 84 x86LongToUint32(const void *ll);
michael@0 85 #endif
michael@0 86
michael@0 87 static long
michael@0 88 octalToLong(const char *s);
michael@0 89
michael@0 90 /*
michael@0 91 * J A R _ p a s s _ a r c h i v e
michael@0 92 *
michael@0 93 * For use by naive clients. Slam an entire archive file
michael@0 94 * into this function. We extract manifests, parse, index
michael@0 95 * the archive file, and do whatever nastiness.
michael@0 96 *
michael@0 97 */
michael@0 98 int
michael@0 99 JAR_pass_archive(JAR *jar, jarArch format, char *filename, const char *url)
michael@0 100 {
michael@0 101 JAR_FILE fp;
michael@0 102 int status = 0;
michael@0 103
michael@0 104 if (filename == NULL)
michael@0 105 return JAR_ERR_GENERAL;
michael@0 106
michael@0 107 if ((fp = JAR_FOPEN (filename, "rb")) != NULL) {
michael@0 108 if (format == jarArchGuess)
michael@0 109 format = (jarArch)jar_guess_jar (filename, fp);
michael@0 110
michael@0 111 jar->format = format;
michael@0 112 jar->url = url ? PORT_Strdup (url) : NULL;
michael@0 113 jar->filename = PORT_Strdup (filename);
michael@0 114
michael@0 115 status = jar_gen_index (jar, format, fp);
michael@0 116 if (status == 0)
michael@0 117 status = jar_extract_manifests (jar, format, fp);
michael@0 118
michael@0 119 JAR_FCLOSE (fp);
michael@0 120 if (status < 0)
michael@0 121 return status;
michael@0 122
michael@0 123 /* people were expecting it this way */
michael@0 124 return jar->valid;
michael@0 125 }
michael@0 126 /* file not found */
michael@0 127 return JAR_ERR_FNF;
michael@0 128 }
michael@0 129
michael@0 130 /*
michael@0 131 * J A R _ p a s s _ a r c h i v e _ u n v e r i f i e d
michael@0 132 *
michael@0 133 * Same as JAR_pass_archive, but doesn't parse signatures.
michael@0 134 *
michael@0 135 */
michael@0 136 int
michael@0 137 JAR_pass_archive_unverified(JAR *jar, jarArch format, char *filename,
michael@0 138 const char *url)
michael@0 139 {
michael@0 140 JAR_FILE fp;
michael@0 141 int status = 0;
michael@0 142
michael@0 143 if (filename == NULL) {
michael@0 144 return JAR_ERR_GENERAL;
michael@0 145 }
michael@0 146
michael@0 147 if ((fp = JAR_FOPEN (filename, "rb")) != NULL) {
michael@0 148 if (format == jarArchGuess) {
michael@0 149 format = (jarArch)jar_guess_jar (filename, fp);
michael@0 150 }
michael@0 151
michael@0 152 jar->format = format;
michael@0 153 jar->url = url ? PORT_Strdup (url) : NULL;
michael@0 154 jar->filename = PORT_Strdup (filename);
michael@0 155
michael@0 156 status = jar_gen_index (jar, format, fp);
michael@0 157 if (status == 0) {
michael@0 158 status = jar_extract_mf(jar, format, fp, "mf");
michael@0 159 }
michael@0 160
michael@0 161 JAR_FCLOSE (fp);
michael@0 162 if (status < 0) {
michael@0 163 return status;
michael@0 164 }
michael@0 165
michael@0 166 /* people were expecting it this way */
michael@0 167 return jar->valid;
michael@0 168 }
michael@0 169 /* file not found */
michael@0 170 return JAR_ERR_FNF;
michael@0 171 }
michael@0 172
michael@0 173 /*
michael@0 174 * J A R _ v e r i f i e d _ e x t r a c t
michael@0 175 *
michael@0 176 * Optimization: keep a file descriptor open
michael@0 177 * inside the JAR structure, so we don't have to
michael@0 178 * open the file 25 times to run java.
michael@0 179 *
michael@0 180 */
michael@0 181
michael@0 182 int
michael@0 183 JAR_verified_extract(JAR *jar, char *path, char *outpath)
michael@0 184 {
michael@0 185 int status = JAR_extract (jar, path, outpath);
michael@0 186
michael@0 187 if (status >= 0)
michael@0 188 return jar_verify_extract(jar, path, outpath);
michael@0 189 return status;
michael@0 190 }
michael@0 191
michael@0 192 int
michael@0 193 JAR_extract(JAR *jar, char *path, char *outpath)
michael@0 194 {
michael@0 195 int result;
michael@0 196 JAR_Physical *phy;
michael@0 197
michael@0 198 if (jar->fp == NULL && jar->filename) {
michael@0 199 jar->fp = (FILE*)JAR_FOPEN (jar->filename, "rb");
michael@0 200 }
michael@0 201 if (jar->fp == NULL) {
michael@0 202 /* file not found */
michael@0 203 return JAR_ERR_FNF;
michael@0 204 }
michael@0 205
michael@0 206 phy = jar_get_physical (jar, path);
michael@0 207 if (phy) {
michael@0 208 if (phy->compression != 0 && phy->compression != 8) {
michael@0 209 /* unsupported compression method */
michael@0 210 result = JAR_ERR_CORRUPT;
michael@0 211 }
michael@0 212 if (phy->compression == 0) {
michael@0 213 result = jar_physical_extraction
michael@0 214 ((PRFileDesc*)jar->fp, outpath, phy->offset, phy->length);
michael@0 215 } else {
michael@0 216 result = jar_physical_inflate((PRFileDesc*)jar->fp, outpath,
michael@0 217 phy->offset, phy->length,
michael@0 218 (unsigned int) phy->compression);
michael@0 219 }
michael@0 220
michael@0 221 #if defined(XP_UNIX) || defined(XP_BEOS)
michael@0 222 if (phy->mode)
michael@0 223 chmod (outpath, 0400 | (mode_t) phy->mode);
michael@0 224 #endif
michael@0 225 } else {
michael@0 226 /* pathname not found in archive */
michael@0 227 result = JAR_ERR_PNF;
michael@0 228 }
michael@0 229 return result;
michael@0 230 }
michael@0 231
michael@0 232 /*
michael@0 233 * p h y s i c a l _ e x t r a c t i o n
michael@0 234 *
michael@0 235 * This needs to be done in chunks of say 32k, instead of
michael@0 236 * in one bulk calloc. (Necessary under Win16 platform.)
michael@0 237 * This is done for uncompressed entries only.
michael@0 238 *
michael@0 239 */
michael@0 240
michael@0 241 #define CHUNK 32768
michael@0 242
michael@0 243 static int
michael@0 244 jar_physical_extraction(JAR_FILE fp, char *outpath, long offset, long length)
michael@0 245 {
michael@0 246 JAR_FILE out;
michael@0 247 char *buffer = (char *)PORT_ZAlloc(CHUNK);
michael@0 248 int status = 0;
michael@0 249
michael@0 250 if (buffer == NULL)
michael@0 251 return JAR_ERR_MEMORY;
michael@0 252
michael@0 253 if ((out = JAR_FOPEN (outpath, "wb")) != NULL) {
michael@0 254 long at = 0;
michael@0 255
michael@0 256 JAR_FSEEK (fp, offset, (PRSeekWhence)0);
michael@0 257 while (at < length) {
michael@0 258 long chunk = (at + CHUNK <= length) ? CHUNK : length - at;
michael@0 259 if (JAR_FREAD (fp, buffer, chunk) != chunk) {
michael@0 260 status = JAR_ERR_DISK;
michael@0 261 break;
michael@0 262 }
michael@0 263 at += chunk;
michael@0 264 if (JAR_FWRITE (out, buffer, chunk) < chunk) {
michael@0 265 /* most likely a disk full error */
michael@0 266 status = JAR_ERR_DISK;
michael@0 267 break;
michael@0 268 }
michael@0 269 }
michael@0 270 JAR_FCLOSE (out);
michael@0 271 } else {
michael@0 272 /* error opening output file */
michael@0 273 status = JAR_ERR_DISK;
michael@0 274 }
michael@0 275 PORT_Free (buffer);
michael@0 276 return status;
michael@0 277 }
michael@0 278
michael@0 279 /*
michael@0 280 * j a r _ p h y s i c a l _ i n f l a t e
michael@0 281 *
michael@0 282 * Inflate a range of bytes in a file, writing the inflated
michael@0 283 * result to "outpath". Chunk based.
michael@0 284 *
michael@0 285 */
michael@0 286 /* input and output chunks differ, assume 4x compression */
michael@0 287
michael@0 288 #define ICHUNK 8192
michael@0 289 #define OCHUNK 32768
michael@0 290
michael@0 291 static int
michael@0 292 jar_physical_inflate(JAR_FILE fp, char *outpath, long offset, long length,
michael@0 293 unsigned int method)
michael@0 294 {
michael@0 295 char *inbuf, *outbuf;
michael@0 296 int status = 0;
michael@0 297 z_stream zs;
michael@0 298 JAR_FILE out;
michael@0 299
michael@0 300 /* Raw inflate in zlib 1.1.4 needs an extra dummy byte at the end */
michael@0 301 if ((inbuf = (char *)PORT_ZAlloc(ICHUNK + 1)) == NULL)
michael@0 302 return JAR_ERR_MEMORY;
michael@0 303
michael@0 304 if ((outbuf = (char *)PORT_ZAlloc(OCHUNK)) == NULL) {
michael@0 305 PORT_Free (inbuf);
michael@0 306 return JAR_ERR_MEMORY;
michael@0 307 }
michael@0 308
michael@0 309 PORT_Memset (&zs, 0, sizeof (zs));
michael@0 310 status = inflateInit2 (&zs, -MAX_WBITS);
michael@0 311 if (status != Z_OK) {
michael@0 312 PORT_Free (inbuf);
michael@0 313 PORT_Free (outbuf);
michael@0 314 return JAR_ERR_GENERAL;
michael@0 315 }
michael@0 316
michael@0 317 if ((out = JAR_FOPEN (outpath, "wb")) != NULL) {
michael@0 318 long at = 0;
michael@0 319
michael@0 320 JAR_FSEEK (fp, offset, (PRSeekWhence)0);
michael@0 321 while (at < length) {
michael@0 322 long chunk = (at + ICHUNK <= length) ? ICHUNK : length - at;
michael@0 323 unsigned long tin;
michael@0 324
michael@0 325 if (JAR_FREAD (fp, inbuf, chunk) != chunk) {
michael@0 326 /* incomplete read */
michael@0 327 JAR_FCLOSE (out);
michael@0 328 PORT_Free (inbuf);
michael@0 329 PORT_Free (outbuf);
michael@0 330 return JAR_ERR_CORRUPT;
michael@0 331 }
michael@0 332 at += chunk;
michael@0 333 if (at == length) {
michael@0 334 /* add an extra dummy byte at the end */
michael@0 335 inbuf[chunk++] = 0xDD;
michael@0 336 }
michael@0 337 zs.next_in = (Bytef *) inbuf;
michael@0 338 zs.avail_in = chunk;
michael@0 339 zs.avail_out = OCHUNK;
michael@0 340 tin = zs.total_in;
michael@0 341 while ((zs.total_in - tin < chunk) || (zs.avail_out == 0)) {
michael@0 342 unsigned long prev_total = zs.total_out;
michael@0 343 unsigned long ochunk;
michael@0 344
michael@0 345 zs.next_out = (Bytef *) outbuf;
michael@0 346 zs.avail_out = OCHUNK;
michael@0 347 status = inflate (&zs, Z_NO_FLUSH);
michael@0 348 if (status != Z_OK && status != Z_STREAM_END) {
michael@0 349 /* error during decompression */
michael@0 350 JAR_FCLOSE (out);
michael@0 351 PORT_Free (inbuf);
michael@0 352 PORT_Free (outbuf);
michael@0 353 return JAR_ERR_CORRUPT;
michael@0 354 }
michael@0 355 ochunk = zs.total_out - prev_total;
michael@0 356 if (JAR_FWRITE (out, outbuf, ochunk) < ochunk) {
michael@0 357 /* most likely a disk full error */
michael@0 358 status = JAR_ERR_DISK;
michael@0 359 break;
michael@0 360 }
michael@0 361 if (status == Z_STREAM_END)
michael@0 362 break;
michael@0 363 }
michael@0 364 }
michael@0 365 JAR_FCLOSE (out);
michael@0 366 status = inflateEnd (&zs);
michael@0 367 } else {
michael@0 368 /* error opening output file */
michael@0 369 status = JAR_ERR_DISK;
michael@0 370 }
michael@0 371 PORT_Free (inbuf);
michael@0 372 PORT_Free (outbuf);
michael@0 373 return status;
michael@0 374 }
michael@0 375
michael@0 376 /*
michael@0 377 * j a r _ i n f l a t e _ m e m o r y
michael@0 378 *
michael@0 379 * Call zlib to inflate the given memory chunk. It is re-XP_ALLOC'd,
michael@0 380 * and thus appears to operate inplace to the caller.
michael@0 381 *
michael@0 382 */
michael@0 383 static int
michael@0 384 jar_inflate_memory(unsigned int method, long *length, long expected_out_len,
michael@0 385 char **data)
michael@0 386 {
michael@0 387 char *inbuf = *data;
michael@0 388 char *outbuf = (char*)PORT_ZAlloc(expected_out_len);
michael@0 389 long insz = *length;
michael@0 390 int status;
michael@0 391 z_stream zs;
michael@0 392
michael@0 393 if (outbuf == NULL)
michael@0 394 return JAR_ERR_MEMORY;
michael@0 395
michael@0 396 PORT_Memset(&zs, 0, sizeof zs);
michael@0 397 status = inflateInit2 (&zs, -MAX_WBITS);
michael@0 398 if (status < 0) {
michael@0 399 /* error initializing zlib stream */
michael@0 400 PORT_Free (outbuf);
michael@0 401 return JAR_ERR_GENERAL;
michael@0 402 }
michael@0 403
michael@0 404 zs.next_in = (Bytef *) inbuf;
michael@0 405 zs.next_out = (Bytef *) outbuf;
michael@0 406 zs.avail_in = insz;
michael@0 407 zs.avail_out = expected_out_len;
michael@0 408
michael@0 409 status = inflate (&zs, Z_FINISH);
michael@0 410 if (status != Z_OK && status != Z_STREAM_END) {
michael@0 411 /* error during deflation */
michael@0 412 PORT_Free (outbuf);
michael@0 413 return JAR_ERR_GENERAL;
michael@0 414 }
michael@0 415
michael@0 416 status = inflateEnd (&zs);
michael@0 417 if (status != Z_OK) {
michael@0 418 /* error during deflation */
michael@0 419 PORT_Free (outbuf);
michael@0 420 return JAR_ERR_GENERAL;
michael@0 421 }
michael@0 422 PORT_Free(*data);
michael@0 423 *data = outbuf;
michael@0 424 *length = zs.total_out;
michael@0 425 return 0;
michael@0 426 }
michael@0 427
michael@0 428 /*
michael@0 429 * v e r i f y _ e x t r a c t
michael@0 430 *
michael@0 431 * Validate signature on the freshly extracted file.
michael@0 432 *
michael@0 433 */
michael@0 434 static int
michael@0 435 jar_verify_extract(JAR *jar, char *path, char *physical_path)
michael@0 436 {
michael@0 437 int status;
michael@0 438 JAR_Digest dig;
michael@0 439
michael@0 440 PORT_Memset (&dig, 0, sizeof dig);
michael@0 441 status = JAR_digest_file (physical_path, &dig);
michael@0 442 if (!status)
michael@0 443 status = JAR_verify_digest (jar, path, &dig);
michael@0 444 return status;
michael@0 445 }
michael@0 446
michael@0 447 /*
michael@0 448 * g e t _ p h y s i c a l
michael@0 449 *
michael@0 450 * Let's get physical.
michael@0 451 * Obtains the offset and length of this file in the jar file.
michael@0 452 *
michael@0 453 */
michael@0 454 static JAR_Physical *
michael@0 455 jar_get_physical(JAR *jar, char *pathname)
michael@0 456 {
michael@0 457 ZZLink *link;
michael@0 458 ZZList *list = jar->phy;
michael@0 459
michael@0 460 if (ZZ_ListEmpty (list))
michael@0 461 return NULL;
michael@0 462
michael@0 463 for (link = ZZ_ListHead (list);
michael@0 464 !ZZ_ListIterDone (list, link);
michael@0 465 link = link->next) {
michael@0 466 JAR_Item *it = link->thing;
michael@0 467
michael@0 468 if (it->type == jarTypePhy &&
michael@0 469 it->pathname && !PORT_Strcmp (it->pathname, pathname)) {
michael@0 470 JAR_Physical *phy = (JAR_Physical *)it->data;
michael@0 471 return phy;
michael@0 472 }
michael@0 473 }
michael@0 474 return NULL;
michael@0 475 }
michael@0 476
michael@0 477 /*
michael@0 478 * j a r _ e x t r a c t _ m a n i f e s t s
michael@0 479 *
michael@0 480 * Extract the manifest files and parse them,
michael@0 481 * from an open archive file whose contents are known.
michael@0 482 *
michael@0 483 */
michael@0 484 static int
michael@0 485 jar_extract_manifests(JAR *jar, jarArch format, JAR_FILE fp)
michael@0 486 {
michael@0 487 int status, signatures;
michael@0 488
michael@0 489 if (format != jarArchZip && format != jarArchTar)
michael@0 490 return JAR_ERR_CORRUPT;
michael@0 491
michael@0 492 if ((status = jar_extract_mf (jar, format, fp, "mf")) < 0)
michael@0 493 return status;
michael@0 494 if (!status)
michael@0 495 return JAR_ERR_ORDER;
michael@0 496 if ((status = jar_extract_mf (jar, format, fp, "sf")) < 0)
michael@0 497 return status;
michael@0 498 if (!status)
michael@0 499 return JAR_ERR_ORDER;
michael@0 500 if ((status = jar_extract_mf (jar, format, fp, "rsa")) < 0)
michael@0 501 return status;
michael@0 502 signatures = status;
michael@0 503 if ((status = jar_extract_mf (jar, format, fp, "dsa")) < 0)
michael@0 504 return status;
michael@0 505 if (!(signatures += status))
michael@0 506 return JAR_ERR_SIG;
michael@0 507 return 0;
michael@0 508 }
michael@0 509
michael@0 510 /*
michael@0 511 * j a r _ e x t r a c t _ m f
michael@0 512 *
michael@0 513 * Extracts manifest files based on an extension, which
michael@0 514 * should be .MF, .SF, .RSA, etc. Order of the files is now no
michael@0 515 * longer important when zipping jar files.
michael@0 516 *
michael@0 517 */
michael@0 518 static int
michael@0 519 jar_extract_mf(JAR *jar, jarArch format, JAR_FILE fp, char *ext)
michael@0 520 {
michael@0 521 ZZLink *link;
michael@0 522 ZZList *list = jar->phy;
michael@0 523 int ret = 0;
michael@0 524
michael@0 525 if (ZZ_ListEmpty (list))
michael@0 526 return JAR_ERR_PNF;
michael@0 527
michael@0 528 for (link = ZZ_ListHead (list);
michael@0 529 ret >= 0 && !ZZ_ListIterDone (list, link);
michael@0 530 link = link->next) {
michael@0 531 JAR_Item *it = link->thing;
michael@0 532
michael@0 533 if (it->type == jarTypePhy &&
michael@0 534 !PORT_Strncmp (it->pathname, "META-INF", 8))
michael@0 535 {
michael@0 536 JAR_Physical *phy = (JAR_Physical *) it->data;
michael@0 537 char *fn = it->pathname + 8;
michael@0 538 char *e;
michael@0 539 char *manifest;
michael@0 540 long length;
michael@0 541 int num, status;
michael@0 542
michael@0 543 if (PORT_Strlen (it->pathname) < 8)
michael@0 544 continue;
michael@0 545
michael@0 546 if (*fn == '/' || *fn == '\\')
michael@0 547 fn++;
michael@0 548 if (*fn == 0) {
michael@0 549 /* just a directory entry */
michael@0 550 continue;
michael@0 551 }
michael@0 552
michael@0 553 /* skip to extension */
michael@0 554 for (e = fn; *e && *e != '.'; e++)
michael@0 555 /* yip */ ;
michael@0 556
michael@0 557 /* and skip dot */
michael@0 558 if (*e == '.')
michael@0 559 e++;
michael@0 560 if (PORT_Strcasecmp (ext, e)) {
michael@0 561 /* not the right extension */
michael@0 562 continue;
michael@0 563 }
michael@0 564 if (phy->length == 0 || phy->length > 0xFFFF) {
michael@0 565 /* manifest files cannot be zero length or too big! */
michael@0 566 /* the 0xFFFF limit is per J2SE SDK */
michael@0 567 return JAR_ERR_CORRUPT;
michael@0 568 }
michael@0 569
michael@0 570 /* Read in the manifest and parse it */
michael@0 571 /* Raw inflate in zlib 1.1.4 needs an extra dummy byte at the end */
michael@0 572 manifest = (char *)PORT_ZAlloc(phy->length + 1);
michael@0 573 if (!manifest)
michael@0 574 return JAR_ERR_MEMORY;
michael@0 575
michael@0 576 JAR_FSEEK (fp, phy->offset, (PRSeekWhence)0);
michael@0 577 num = JAR_FREAD (fp, manifest, phy->length);
michael@0 578 if (num != phy->length) {
michael@0 579 /* corrupt archive file */
michael@0 580 PORT_Free (manifest);
michael@0 581 return JAR_ERR_CORRUPT;
michael@0 582 }
michael@0 583
michael@0 584 if (phy->compression == 8) {
michael@0 585 length = phy->length;
michael@0 586 /* add an extra dummy byte at the end */
michael@0 587 manifest[length++] = 0xDD;
michael@0 588 status = jar_inflate_memory((unsigned int)phy->compression,
michael@0 589 &length,
michael@0 590 phy->uncompressed_length,
michael@0 591 &manifest);
michael@0 592 if (status < 0) {
michael@0 593 PORT_Free (manifest);
michael@0 594 return status;
michael@0 595 }
michael@0 596 } else if (phy->compression) {
michael@0 597 /* unsupported compression method */
michael@0 598 PORT_Free (manifest);
michael@0 599 return JAR_ERR_CORRUPT;
michael@0 600 } else
michael@0 601 length = phy->length;
michael@0 602
michael@0 603 status = JAR_parse_manifest(jar, manifest, length,
michael@0 604 it->pathname, "url");
michael@0 605 PORT_Free (manifest);
michael@0 606 if (status < 0)
michael@0 607 ret = status;
michael@0 608 else
michael@0 609 ++ret;
michael@0 610 } else if (it->type == jarTypePhy) {
michael@0 611 /* ordinary file */
michael@0 612 }
michael@0 613 }
michael@0 614 return ret;
michael@0 615 }
michael@0 616
michael@0 617 /*
michael@0 618 * j a r _ g e n _ i n d e x
michael@0 619 *
michael@0 620 * Generate an index for the various types of
michael@0 621 * known archive files. Right now .ZIP and .TAR
michael@0 622 *
michael@0 623 */
michael@0 624 static int
michael@0 625 jar_gen_index(JAR *jar, jarArch format, JAR_FILE fp)
michael@0 626 {
michael@0 627 int result = JAR_ERR_CORRUPT;
michael@0 628
michael@0 629 JAR_FSEEK (fp, 0, (PRSeekWhence)0);
michael@0 630 switch (format) {
michael@0 631 case jarArchZip:
michael@0 632 result = jar_listzip (jar, fp);
michael@0 633 break;
michael@0 634
michael@0 635 case jarArchTar:
michael@0 636 result = jar_listtar (jar, fp);
michael@0 637 break;
michael@0 638
michael@0 639 case jarArchGuess:
michael@0 640 case jarArchNone:
michael@0 641 return JAR_ERR_GENERAL;
michael@0 642 }
michael@0 643 JAR_FSEEK (fp, 0, (PRSeekWhence)0);
michael@0 644 return result;
michael@0 645 }
michael@0 646
michael@0 647 /*
michael@0 648 * j a r _ l i s t z i p
michael@0 649 *
michael@0 650 * List the physical contents of a Phil Katz
michael@0 651 * style .ZIP file into the JAR linked list.
michael@0 652 *
michael@0 653 */
michael@0 654 static int
michael@0 655 jar_listzip(JAR *jar, JAR_FILE fp)
michael@0 656 {
michael@0 657 ZZLink *ent;
michael@0 658 JAR_Item *it;
michael@0 659 JAR_Physical *phy;
michael@0 660 struct ZipLocal *Local = PORT_ZNew(struct ZipLocal);
michael@0 661 struct ZipCentral *Central = PORT_ZNew(struct ZipCentral);
michael@0 662 struct ZipEnd *End = PORT_ZNew(struct ZipEnd);
michael@0 663
michael@0 664 int err = 0;
michael@0 665 long pos = 0L;
michael@0 666 unsigned int compression;
michael@0 667 unsigned int filename_len, extra_len;
michael@0 668
michael@0 669 char filename[JAR_SIZE];
michael@0 670 char date[9], time[9];
michael@0 671 char sig[4];
michael@0 672
michael@0 673 if (!Local || !Central || !End) {
michael@0 674 /* out of memory */
michael@0 675 err = JAR_ERR_MEMORY;
michael@0 676 goto loser;
michael@0 677 }
michael@0 678
michael@0 679 while (1) {
michael@0 680 PRUint32 sigVal;
michael@0 681 JAR_FSEEK (fp, pos, (PRSeekWhence)0);
michael@0 682
michael@0 683 if (JAR_FREAD(fp, sig, sizeof sig) != sizeof sig) {
michael@0 684 /* zip file ends prematurely */
michael@0 685 err = JAR_ERR_CORRUPT;
michael@0 686 goto loser;
michael@0 687 }
michael@0 688
michael@0 689 JAR_FSEEK (fp, pos, (PRSeekWhence)0);
michael@0 690 sigVal = x86LongToUint32(sig);
michael@0 691 if (sigVal == LSIG) {
michael@0 692 JAR_FREAD (fp, Local, sizeof *Local);
michael@0 693
michael@0 694 filename_len = x86ShortToUint32(Local->filename_len);
michael@0 695 extra_len = x86ShortToUint32(Local->extrafield_len);
michael@0 696 if (filename_len >= JAR_SIZE) {
michael@0 697 /* corrupt zip file */
michael@0 698 err = JAR_ERR_CORRUPT;
michael@0 699 goto loser;
michael@0 700 }
michael@0 701
michael@0 702 if (JAR_FREAD (fp, filename, filename_len) != filename_len) {
michael@0 703 /* truncated archive file */
michael@0 704 err = JAR_ERR_CORRUPT;
michael@0 705 goto loser;
michael@0 706 }
michael@0 707 filename [filename_len] = 0;
michael@0 708 /* Add this to our jar chain */
michael@0 709 phy = PORT_ZNew(JAR_Physical);
michael@0 710 if (phy == NULL) {
michael@0 711 err = JAR_ERR_MEMORY;
michael@0 712 goto loser;
michael@0 713 }
michael@0 714
michael@0 715 /* We will index any file that comes our way, but when it comes
michael@0 716 to actually extraction, compression must be 0 or 8 */
michael@0 717 compression = x86ShortToUint32(Local->method);
michael@0 718 phy->compression = (compression <= 255) ? compression : 222;
michael@0 719 /* XXX 222 is bad magic. */
michael@0 720
michael@0 721 phy->offset = pos + (sizeof *Local) + filename_len + extra_len;
michael@0 722 phy->length = x86LongToUint32(Local->size);
michael@0 723 phy->uncompressed_length = x86LongToUint32(Local->orglen);
michael@0 724
michael@0 725 dosdate (date, Local->date);
michael@0 726 dostime (time, Local->time);
michael@0 727
michael@0 728 it = PORT_ZNew(JAR_Item);
michael@0 729 if (it == NULL) {
michael@0 730 err = JAR_ERR_MEMORY;
michael@0 731 goto loser;
michael@0 732 }
michael@0 733
michael@0 734 it->pathname = PORT_Strdup(filename);
michael@0 735 it->type = jarTypePhy;
michael@0 736 it->data = (unsigned char *) phy;
michael@0 737 it->size = sizeof (JAR_Physical);
michael@0 738
michael@0 739 ent = ZZ_NewLink (it);
michael@0 740 if (ent == NULL) {
michael@0 741 err = JAR_ERR_MEMORY;
michael@0 742 goto loser;
michael@0 743 }
michael@0 744
michael@0 745 ZZ_AppendLink (jar->phy, ent);
michael@0 746 pos = phy->offset + phy->length;
michael@0 747 } else if (sigVal == CSIG) {
michael@0 748 unsigned int attr = 0;
michael@0 749 if (JAR_FREAD(fp, Central, sizeof *Central) != sizeof *Central) {
michael@0 750 /* apparently truncated archive */
michael@0 751 err = JAR_ERR_CORRUPT;
michael@0 752 goto loser;
michael@0 753 }
michael@0 754
michael@0 755 #if defined(XP_UNIX) || defined(XP_BEOS)
michael@0 756 /* with unix we need to locate any bits from
michael@0 757 the protection mask in the external attributes. */
michael@0 758 attr = Central->external_attributes [2]; /* magic */
michael@0 759 if (attr) {
michael@0 760 /* we have to read the filename, again */
michael@0 761 filename_len = x86ShortToUint32(Central->filename_len);
michael@0 762 if (filename_len >= JAR_SIZE) {
michael@0 763 /* corrupt in central directory */
michael@0 764 err = JAR_ERR_CORRUPT;
michael@0 765 goto loser;
michael@0 766 }
michael@0 767
michael@0 768 if (JAR_FREAD(fp, filename, filename_len) != filename_len) {
michael@0 769 /* truncated in central directory */
michael@0 770 err = JAR_ERR_CORRUPT;
michael@0 771 goto loser;
michael@0 772 }
michael@0 773 filename [filename_len] = 0;
michael@0 774
michael@0 775 /* look up this name again */
michael@0 776 phy = jar_get_physical (jar, filename);
michael@0 777 if (phy) {
michael@0 778 /* always allow access by self */
michael@0 779 phy->mode = 0400 | attr;
michael@0 780 }
michael@0 781 }
michael@0 782 #endif
michael@0 783 pos += sizeof(struct ZipCentral)
michael@0 784 + x86ShortToUint32(Central->filename_len)
michael@0 785 + x86ShortToUint32(Central->commentfield_len)
michael@0 786 + x86ShortToUint32(Central->extrafield_len);
michael@0 787 } else if (sigVal == ESIG) {
michael@0 788 if (JAR_FREAD(fp, End, sizeof *End) != sizeof *End) {
michael@0 789 err = JAR_ERR_CORRUPT;
michael@0 790 goto loser;
michael@0 791 }
michael@0 792 break;
michael@0 793 } else {
michael@0 794 /* garbage in archive */
michael@0 795 err = JAR_ERR_CORRUPT;
michael@0 796 goto loser;
michael@0 797 }
michael@0 798 }
michael@0 799
michael@0 800 loser:
michael@0 801 if (Local)
michael@0 802 PORT_Free(Local);
michael@0 803 if (Central)
michael@0 804 PORT_Free(Central);
michael@0 805 if (End)
michael@0 806 PORT_Free(End);
michael@0 807 return err;
michael@0 808 }
michael@0 809
michael@0 810 /*
michael@0 811 * j a r _ l i s t t a r
michael@0 812 *
michael@0 813 * List the physical contents of a Unix
michael@0 814 * .tar file into the JAR linked list.
michael@0 815 *
michael@0 816 */
michael@0 817 static int
michael@0 818 jar_listtar(JAR *jar, JAR_FILE fp)
michael@0 819 {
michael@0 820 char *s;
michael@0 821 JAR_Physical *phy;
michael@0 822 long pos = 0L;
michael@0 823 long sz, mode;
michael@0 824 time_t when;
michael@0 825 union TarEntry tarball;
michael@0 826
michael@0 827 while (1) {
michael@0 828 JAR_FSEEK (fp, pos, (PRSeekWhence)0);
michael@0 829
michael@0 830 if (JAR_FREAD (fp, &tarball, sizeof tarball) < sizeof tarball)
michael@0 831 break;
michael@0 832
michael@0 833 if (!*tarball.val.filename)
michael@0 834 break;
michael@0 835
michael@0 836 when = octalToLong (tarball.val.time);
michael@0 837 sz = octalToLong (tarball.val.size);
michael@0 838 mode = octalToLong (tarball.val.mode);
michael@0 839
michael@0 840 /* Tag the end of filename */
michael@0 841 s = tarball.val.filename;
michael@0 842 while (*s && *s != ' ')
michael@0 843 s++;
michael@0 844 *s = 0;
michael@0 845
michael@0 846 /* Add to our linked list */
michael@0 847 phy = PORT_ZNew(JAR_Physical);
michael@0 848 if (phy == NULL)
michael@0 849 return JAR_ERR_MEMORY;
michael@0 850
michael@0 851 phy->compression = 0;
michael@0 852 phy->offset = pos + sizeof tarball;
michael@0 853 phy->length = sz;
michael@0 854
michael@0 855 ADDITEM(jar->phy, jarTypePhy, tarball.val.filename, phy,
michael@0 856 sizeof *phy);
michael@0 857
michael@0 858 /* Advance to next file entry */
michael@0 859 sz = PR_ROUNDUP(sz,sizeof tarball);
michael@0 860 pos += sz + sizeof tarball;
michael@0 861 }
michael@0 862
michael@0 863 return 0;
michael@0 864 }
michael@0 865
michael@0 866 /*
michael@0 867 * d o s d a t e
michael@0 868 *
michael@0 869 * Not used right now, but keep it in here because
michael@0 870 * it will be needed.
michael@0 871 *
michael@0 872 */
michael@0 873 static int
michael@0 874 dosdate(char *date, const char *s)
michael@0 875 {
michael@0 876 PRUint32 num = x86ShortToUint32(s);
michael@0 877
michael@0 878 PR_snprintf(date, 9, "%02d-%02d-%02d", ((num >> 5) & 0x0F), (num & 0x1F),
michael@0 879 ((num >> 9) + 80));
michael@0 880 return 0;
michael@0 881 }
michael@0 882
michael@0 883 /*
michael@0 884 * d o s t i m e
michael@0 885 *
michael@0 886 * Not used right now, but keep it in here because
michael@0 887 * it will be needed.
michael@0 888 *
michael@0 889 */
michael@0 890 static int
michael@0 891 dostime (char *time, const char *s)
michael@0 892 {
michael@0 893 PRUint32 num = x86ShortToUint32(s);
michael@0 894
michael@0 895 PR_snprintf (time, 6, "%02d:%02d", ((num >> 11) & 0x1F),
michael@0 896 ((num >> 5) & 0x3F));
michael@0 897 return 0;
michael@0 898 }
michael@0 899
michael@0 900 #ifndef NSS_X86_OR_X64
michael@0 901 /*
michael@0 902 * Simulates an x86 (little endian, unaligned) ushort fetch from any address.
michael@0 903 */
michael@0 904 static PRUint32
michael@0 905 x86ShortToUint32(const void * v)
michael@0 906 {
michael@0 907 const unsigned char *ii = (const unsigned char *)v;
michael@0 908 PRUint32 ret = (PRUint32)(ii[0]) | ((PRUint32)(ii[1]) << 8);
michael@0 909 return ret;
michael@0 910 }
michael@0 911
michael@0 912 /*
michael@0 913 * Simulates an x86 (little endian, unaligned) uint fetch from any address.
michael@0 914 */
michael@0 915 static PRUint32
michael@0 916 x86LongToUint32(const void *v)
michael@0 917 {
michael@0 918 const unsigned char *ll = (const unsigned char *)v;
michael@0 919 PRUint32 ret;
michael@0 920
michael@0 921 ret = ((((PRUint32)(ll[0])) << 0) |
michael@0 922 (((PRUint32)(ll[1])) << 8) |
michael@0 923 (((PRUint32)(ll[2])) << 16) |
michael@0 924 (((PRUint32)(ll[3])) << 24));
michael@0 925 return ret;
michael@0 926 }
michael@0 927 #endif
michael@0 928
michael@0 929 /*
michael@0 930 * ASCII octal to binary long.
michael@0 931 * Used for integer encoding inside tar files.
michael@0 932 *
michael@0 933 */
michael@0 934 static long
michael@0 935 octalToLong(const char *s)
michael@0 936 {
michael@0 937 long num = 0L;
michael@0 938
michael@0 939 while (*s == ' ')
michael@0 940 s++;
michael@0 941 while (*s >= '0' && *s <= '7') {
michael@0 942 num <<= 3;
michael@0 943 num += *s++ - '0';
michael@0 944 }
michael@0 945 return num;
michael@0 946 }
michael@0 947
michael@0 948 /*
michael@0 949 * g u e s s _ j a r
michael@0 950 *
michael@0 951 * Try to guess what kind of JAR file this is.
michael@0 952 * Maybe tar, maybe zip. Look in the file for magic
michael@0 953 * or at its filename.
michael@0 954 *
michael@0 955 */
michael@0 956 static int
michael@0 957 jar_guess_jar(const char *filename, JAR_FILE fp)
michael@0 958 {
michael@0 959 PRInt32 len = PORT_Strlen(filename);
michael@0 960 const char *ext = filename + len - 4; /* 4 for ".tar" */
michael@0 961
michael@0 962 if (len >= 4 && !PL_strcasecmp(ext, ".tar"))
michael@0 963 return jarArchTar;
michael@0 964 return jarArchZip;
michael@0 965 }

mercurial