1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/lib/jar/jarfile.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,965 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +/* 1.9 + * JARFILE 1.10 + * 1.11 + * Parsing of a Jar file 1.12 + */ 1.13 +#define JAR_SIZE 256 1.14 + 1.15 +#include "jar.h" 1.16 +#include "jarint.h" 1.17 +#include "jarfile.h" 1.18 + 1.19 +/* commercial compression */ 1.20 +#include "jzlib.h" 1.21 + 1.22 +#if defined(XP_UNIX) || defined(XP_BEOS) 1.23 +#include "sys/stat.h" 1.24 +#endif 1.25 + 1.26 +#include "sechash.h" /* for HASH_GetHashObject() */ 1.27 + 1.28 +PR_STATIC_ASSERT(46 == sizeof(struct ZipCentral)); 1.29 +PR_STATIC_ASSERT(30 == sizeof(struct ZipLocal)); 1.30 +PR_STATIC_ASSERT(22 == sizeof(struct ZipEnd)); 1.31 +PR_STATIC_ASSERT(512 == sizeof(union TarEntry)); 1.32 + 1.33 +/* extracting */ 1.34 +static int 1.35 +jar_guess_jar(const char *filename, JAR_FILE fp); 1.36 + 1.37 +static int 1.38 +jar_inflate_memory(unsigned int method, long *length, long expected_out_len, 1.39 + char **data); 1.40 + 1.41 +static int 1.42 +jar_physical_extraction(JAR_FILE fp, char *outpath, long offset, long length); 1.43 + 1.44 +static int 1.45 +jar_physical_inflate(JAR_FILE fp, char *outpath, long offset, long length, 1.46 + unsigned int method); 1.47 + 1.48 +static int 1.49 +jar_verify_extract(JAR *jar, char *path, char *physical_path); 1.50 + 1.51 +static JAR_Physical * 1.52 +jar_get_physical(JAR *jar, char *pathname); 1.53 + 1.54 +static int 1.55 +jar_extract_manifests(JAR *jar, jarArch format, JAR_FILE fp); 1.56 + 1.57 +static int 1.58 +jar_extract_mf(JAR *jar, jarArch format, JAR_FILE fp, char *ext); 1.59 + 1.60 + 1.61 +/* indexing */ 1.62 +static int 1.63 +jar_gen_index(JAR *jar, jarArch format, JAR_FILE fp); 1.64 + 1.65 +static int 1.66 +jar_listtar(JAR *jar, JAR_FILE fp); 1.67 + 1.68 +static int 1.69 +jar_listzip(JAR *jar, JAR_FILE fp); 1.70 + 1.71 + 1.72 +/* conversions */ 1.73 +static int 1.74 +dosdate(char *date, const char *s); 1.75 + 1.76 +static int 1.77 +dostime(char *time, const char *s); 1.78 + 1.79 +#ifdef NSS_X86_OR_X64 1.80 +#define x86ShortToUint32(ii) ((const PRUint32)*((const PRUint16 *)(ii))) 1.81 +#define x86LongToUint32(ii) (*(const PRUint32 *)(ii)) 1.82 +#else 1.83 +static PRUint32 1.84 +x86ShortToUint32(const void *ii); 1.85 + 1.86 +static PRUint32 1.87 +x86LongToUint32(const void *ll); 1.88 +#endif 1.89 + 1.90 +static long 1.91 +octalToLong(const char *s); 1.92 + 1.93 +/* 1.94 + * J A R _ p a s s _ a r c h i v e 1.95 + * 1.96 + * For use by naive clients. Slam an entire archive file 1.97 + * into this function. We extract manifests, parse, index 1.98 + * the archive file, and do whatever nastiness. 1.99 + * 1.100 + */ 1.101 +int 1.102 +JAR_pass_archive(JAR *jar, jarArch format, char *filename, const char *url) 1.103 +{ 1.104 + JAR_FILE fp; 1.105 + int status = 0; 1.106 + 1.107 + if (filename == NULL) 1.108 + return JAR_ERR_GENERAL; 1.109 + 1.110 + if ((fp = JAR_FOPEN (filename, "rb")) != NULL) { 1.111 + if (format == jarArchGuess) 1.112 + format = (jarArch)jar_guess_jar (filename, fp); 1.113 + 1.114 + jar->format = format; 1.115 + jar->url = url ? PORT_Strdup (url) : NULL; 1.116 + jar->filename = PORT_Strdup (filename); 1.117 + 1.118 + status = jar_gen_index (jar, format, fp); 1.119 + if (status == 0) 1.120 + status = jar_extract_manifests (jar, format, fp); 1.121 + 1.122 + JAR_FCLOSE (fp); 1.123 + if (status < 0) 1.124 + return status; 1.125 + 1.126 + /* people were expecting it this way */ 1.127 + return jar->valid; 1.128 + } 1.129 + /* file not found */ 1.130 + return JAR_ERR_FNF; 1.131 +} 1.132 + 1.133 +/* 1.134 + * J A R _ p a s s _ a r c h i v e _ u n v e r i f i e d 1.135 + * 1.136 + * Same as JAR_pass_archive, but doesn't parse signatures. 1.137 + * 1.138 + */ 1.139 +int 1.140 +JAR_pass_archive_unverified(JAR *jar, jarArch format, char *filename, 1.141 + const char *url) 1.142 +{ 1.143 + JAR_FILE fp; 1.144 + int status = 0; 1.145 + 1.146 + if (filename == NULL) { 1.147 + return JAR_ERR_GENERAL; 1.148 + } 1.149 + 1.150 + if ((fp = JAR_FOPEN (filename, "rb")) != NULL) { 1.151 + if (format == jarArchGuess) { 1.152 + format = (jarArch)jar_guess_jar (filename, fp); 1.153 + } 1.154 + 1.155 + jar->format = format; 1.156 + jar->url = url ? PORT_Strdup (url) : NULL; 1.157 + jar->filename = PORT_Strdup (filename); 1.158 + 1.159 + status = jar_gen_index (jar, format, fp); 1.160 + if (status == 0) { 1.161 + status = jar_extract_mf(jar, format, fp, "mf"); 1.162 + } 1.163 + 1.164 + JAR_FCLOSE (fp); 1.165 + if (status < 0) { 1.166 + return status; 1.167 + } 1.168 + 1.169 + /* people were expecting it this way */ 1.170 + return jar->valid; 1.171 + } 1.172 + /* file not found */ 1.173 + return JAR_ERR_FNF; 1.174 +} 1.175 + 1.176 +/* 1.177 + * J A R _ v e r i f i e d _ e x t r a c t 1.178 + * 1.179 + * Optimization: keep a file descriptor open 1.180 + * inside the JAR structure, so we don't have to 1.181 + * open the file 25 times to run java. 1.182 + * 1.183 + */ 1.184 + 1.185 +int 1.186 +JAR_verified_extract(JAR *jar, char *path, char *outpath) 1.187 +{ 1.188 + int status = JAR_extract (jar, path, outpath); 1.189 + 1.190 + if (status >= 0) 1.191 + return jar_verify_extract(jar, path, outpath); 1.192 + return status; 1.193 +} 1.194 + 1.195 +int 1.196 +JAR_extract(JAR *jar, char *path, char *outpath) 1.197 +{ 1.198 + int result; 1.199 + JAR_Physical *phy; 1.200 + 1.201 + if (jar->fp == NULL && jar->filename) { 1.202 + jar->fp = (FILE*)JAR_FOPEN (jar->filename, "rb"); 1.203 + } 1.204 + if (jar->fp == NULL) { 1.205 + /* file not found */ 1.206 + return JAR_ERR_FNF; 1.207 + } 1.208 + 1.209 + phy = jar_get_physical (jar, path); 1.210 + if (phy) { 1.211 + if (phy->compression != 0 && phy->compression != 8) { 1.212 + /* unsupported compression method */ 1.213 + result = JAR_ERR_CORRUPT; 1.214 + } 1.215 + if (phy->compression == 0) { 1.216 + result = jar_physical_extraction 1.217 + ((PRFileDesc*)jar->fp, outpath, phy->offset, phy->length); 1.218 + } else { 1.219 + result = jar_physical_inflate((PRFileDesc*)jar->fp, outpath, 1.220 + phy->offset, phy->length, 1.221 + (unsigned int) phy->compression); 1.222 + } 1.223 + 1.224 +#if defined(XP_UNIX) || defined(XP_BEOS) 1.225 + if (phy->mode) 1.226 + chmod (outpath, 0400 | (mode_t) phy->mode); 1.227 +#endif 1.228 + } else { 1.229 + /* pathname not found in archive */ 1.230 + result = JAR_ERR_PNF; 1.231 + } 1.232 + return result; 1.233 +} 1.234 + 1.235 +/* 1.236 + * p h y s i c a l _ e x t r a c t i o n 1.237 + * 1.238 + * This needs to be done in chunks of say 32k, instead of 1.239 + * in one bulk calloc. (Necessary under Win16 platform.) 1.240 + * This is done for uncompressed entries only. 1.241 + * 1.242 + */ 1.243 + 1.244 +#define CHUNK 32768 1.245 + 1.246 +static int 1.247 +jar_physical_extraction(JAR_FILE fp, char *outpath, long offset, long length) 1.248 +{ 1.249 + JAR_FILE out; 1.250 + char *buffer = (char *)PORT_ZAlloc(CHUNK); 1.251 + int status = 0; 1.252 + 1.253 + if (buffer == NULL) 1.254 + return JAR_ERR_MEMORY; 1.255 + 1.256 + if ((out = JAR_FOPEN (outpath, "wb")) != NULL) { 1.257 + long at = 0; 1.258 + 1.259 + JAR_FSEEK (fp, offset, (PRSeekWhence)0); 1.260 + while (at < length) { 1.261 + long chunk = (at + CHUNK <= length) ? CHUNK : length - at; 1.262 + if (JAR_FREAD (fp, buffer, chunk) != chunk) { 1.263 + status = JAR_ERR_DISK; 1.264 + break; 1.265 + } 1.266 + at += chunk; 1.267 + if (JAR_FWRITE (out, buffer, chunk) < chunk) { 1.268 + /* most likely a disk full error */ 1.269 + status = JAR_ERR_DISK; 1.270 + break; 1.271 + } 1.272 + } 1.273 + JAR_FCLOSE (out); 1.274 + } else { 1.275 + /* error opening output file */ 1.276 + status = JAR_ERR_DISK; 1.277 + } 1.278 + PORT_Free (buffer); 1.279 + return status; 1.280 +} 1.281 + 1.282 +/* 1.283 + * j a r _ p h y s i c a l _ i n f l a t e 1.284 + * 1.285 + * Inflate a range of bytes in a file, writing the inflated 1.286 + * result to "outpath". Chunk based. 1.287 + * 1.288 + */ 1.289 +/* input and output chunks differ, assume 4x compression */ 1.290 + 1.291 +#define ICHUNK 8192 1.292 +#define OCHUNK 32768 1.293 + 1.294 +static int 1.295 +jar_physical_inflate(JAR_FILE fp, char *outpath, long offset, long length, 1.296 + unsigned int method) 1.297 +{ 1.298 + char *inbuf, *outbuf; 1.299 + int status = 0; 1.300 + z_stream zs; 1.301 + JAR_FILE out; 1.302 + 1.303 + /* Raw inflate in zlib 1.1.4 needs an extra dummy byte at the end */ 1.304 + if ((inbuf = (char *)PORT_ZAlloc(ICHUNK + 1)) == NULL) 1.305 + return JAR_ERR_MEMORY; 1.306 + 1.307 + if ((outbuf = (char *)PORT_ZAlloc(OCHUNK)) == NULL) { 1.308 + PORT_Free (inbuf); 1.309 + return JAR_ERR_MEMORY; 1.310 + } 1.311 + 1.312 + PORT_Memset (&zs, 0, sizeof (zs)); 1.313 + status = inflateInit2 (&zs, -MAX_WBITS); 1.314 + if (status != Z_OK) { 1.315 + PORT_Free (inbuf); 1.316 + PORT_Free (outbuf); 1.317 + return JAR_ERR_GENERAL; 1.318 + } 1.319 + 1.320 + if ((out = JAR_FOPEN (outpath, "wb")) != NULL) { 1.321 + long at = 0; 1.322 + 1.323 + JAR_FSEEK (fp, offset, (PRSeekWhence)0); 1.324 + while (at < length) { 1.325 + long chunk = (at + ICHUNK <= length) ? ICHUNK : length - at; 1.326 + unsigned long tin; 1.327 + 1.328 + if (JAR_FREAD (fp, inbuf, chunk) != chunk) { 1.329 + /* incomplete read */ 1.330 + JAR_FCLOSE (out); 1.331 + PORT_Free (inbuf); 1.332 + PORT_Free (outbuf); 1.333 + return JAR_ERR_CORRUPT; 1.334 + } 1.335 + at += chunk; 1.336 + if (at == length) { 1.337 + /* add an extra dummy byte at the end */ 1.338 + inbuf[chunk++] = 0xDD; 1.339 + } 1.340 + zs.next_in = (Bytef *) inbuf; 1.341 + zs.avail_in = chunk; 1.342 + zs.avail_out = OCHUNK; 1.343 + tin = zs.total_in; 1.344 + while ((zs.total_in - tin < chunk) || (zs.avail_out == 0)) { 1.345 + unsigned long prev_total = zs.total_out; 1.346 + unsigned long ochunk; 1.347 + 1.348 + zs.next_out = (Bytef *) outbuf; 1.349 + zs.avail_out = OCHUNK; 1.350 + status = inflate (&zs, Z_NO_FLUSH); 1.351 + if (status != Z_OK && status != Z_STREAM_END) { 1.352 + /* error during decompression */ 1.353 + JAR_FCLOSE (out); 1.354 + PORT_Free (inbuf); 1.355 + PORT_Free (outbuf); 1.356 + return JAR_ERR_CORRUPT; 1.357 + } 1.358 + ochunk = zs.total_out - prev_total; 1.359 + if (JAR_FWRITE (out, outbuf, ochunk) < ochunk) { 1.360 + /* most likely a disk full error */ 1.361 + status = JAR_ERR_DISK; 1.362 + break; 1.363 + } 1.364 + if (status == Z_STREAM_END) 1.365 + break; 1.366 + } 1.367 + } 1.368 + JAR_FCLOSE (out); 1.369 + status = inflateEnd (&zs); 1.370 + } else { 1.371 + /* error opening output file */ 1.372 + status = JAR_ERR_DISK; 1.373 + } 1.374 + PORT_Free (inbuf); 1.375 + PORT_Free (outbuf); 1.376 + return status; 1.377 +} 1.378 + 1.379 +/* 1.380 + * j a r _ i n f l a t e _ m e m o r y 1.381 + * 1.382 + * Call zlib to inflate the given memory chunk. It is re-XP_ALLOC'd, 1.383 + * and thus appears to operate inplace to the caller. 1.384 + * 1.385 + */ 1.386 +static int 1.387 +jar_inflate_memory(unsigned int method, long *length, long expected_out_len, 1.388 + char **data) 1.389 +{ 1.390 + char *inbuf = *data; 1.391 + char *outbuf = (char*)PORT_ZAlloc(expected_out_len); 1.392 + long insz = *length; 1.393 + int status; 1.394 + z_stream zs; 1.395 + 1.396 + if (outbuf == NULL) 1.397 + return JAR_ERR_MEMORY; 1.398 + 1.399 + PORT_Memset(&zs, 0, sizeof zs); 1.400 + status = inflateInit2 (&zs, -MAX_WBITS); 1.401 + if (status < 0) { 1.402 + /* error initializing zlib stream */ 1.403 + PORT_Free (outbuf); 1.404 + return JAR_ERR_GENERAL; 1.405 + } 1.406 + 1.407 + zs.next_in = (Bytef *) inbuf; 1.408 + zs.next_out = (Bytef *) outbuf; 1.409 + zs.avail_in = insz; 1.410 + zs.avail_out = expected_out_len; 1.411 + 1.412 + status = inflate (&zs, Z_FINISH); 1.413 + if (status != Z_OK && status != Z_STREAM_END) { 1.414 + /* error during deflation */ 1.415 + PORT_Free (outbuf); 1.416 + return JAR_ERR_GENERAL; 1.417 + } 1.418 + 1.419 + status = inflateEnd (&zs); 1.420 + if (status != Z_OK) { 1.421 + /* error during deflation */ 1.422 + PORT_Free (outbuf); 1.423 + return JAR_ERR_GENERAL; 1.424 + } 1.425 + PORT_Free(*data); 1.426 + *data = outbuf; 1.427 + *length = zs.total_out; 1.428 + return 0; 1.429 +} 1.430 + 1.431 +/* 1.432 + * v e r i f y _ e x t r a c t 1.433 + * 1.434 + * Validate signature on the freshly extracted file. 1.435 + * 1.436 + */ 1.437 +static int 1.438 +jar_verify_extract(JAR *jar, char *path, char *physical_path) 1.439 +{ 1.440 + int status; 1.441 + JAR_Digest dig; 1.442 + 1.443 + PORT_Memset (&dig, 0, sizeof dig); 1.444 + status = JAR_digest_file (physical_path, &dig); 1.445 + if (!status) 1.446 + status = JAR_verify_digest (jar, path, &dig); 1.447 + return status; 1.448 +} 1.449 + 1.450 +/* 1.451 + * g e t _ p h y s i c a l 1.452 + * 1.453 + * Let's get physical. 1.454 + * Obtains the offset and length of this file in the jar file. 1.455 + * 1.456 + */ 1.457 +static JAR_Physical * 1.458 +jar_get_physical(JAR *jar, char *pathname) 1.459 +{ 1.460 + ZZLink *link; 1.461 + ZZList *list = jar->phy; 1.462 + 1.463 + if (ZZ_ListEmpty (list)) 1.464 + return NULL; 1.465 + 1.466 + for (link = ZZ_ListHead (list); 1.467 + !ZZ_ListIterDone (list, link); 1.468 + link = link->next) { 1.469 + JAR_Item *it = link->thing; 1.470 + 1.471 + if (it->type == jarTypePhy && 1.472 + it->pathname && !PORT_Strcmp (it->pathname, pathname)) { 1.473 + JAR_Physical *phy = (JAR_Physical *)it->data; 1.474 + return phy; 1.475 + } 1.476 + } 1.477 + return NULL; 1.478 +} 1.479 + 1.480 +/* 1.481 + * j a r _ e x t r a c t _ m a n i f e s t s 1.482 + * 1.483 + * Extract the manifest files and parse them, 1.484 + * from an open archive file whose contents are known. 1.485 + * 1.486 + */ 1.487 +static int 1.488 +jar_extract_manifests(JAR *jar, jarArch format, JAR_FILE fp) 1.489 +{ 1.490 + int status, signatures; 1.491 + 1.492 + if (format != jarArchZip && format != jarArchTar) 1.493 + return JAR_ERR_CORRUPT; 1.494 + 1.495 + if ((status = jar_extract_mf (jar, format, fp, "mf")) < 0) 1.496 + return status; 1.497 + if (!status) 1.498 + return JAR_ERR_ORDER; 1.499 + if ((status = jar_extract_mf (jar, format, fp, "sf")) < 0) 1.500 + return status; 1.501 + if (!status) 1.502 + return JAR_ERR_ORDER; 1.503 + if ((status = jar_extract_mf (jar, format, fp, "rsa")) < 0) 1.504 + return status; 1.505 + signatures = status; 1.506 + if ((status = jar_extract_mf (jar, format, fp, "dsa")) < 0) 1.507 + return status; 1.508 + if (!(signatures += status)) 1.509 + return JAR_ERR_SIG; 1.510 + return 0; 1.511 +} 1.512 + 1.513 +/* 1.514 + * j a r _ e x t r a c t _ m f 1.515 + * 1.516 + * Extracts manifest files based on an extension, which 1.517 + * should be .MF, .SF, .RSA, etc. Order of the files is now no 1.518 + * longer important when zipping jar files. 1.519 + * 1.520 + */ 1.521 +static int 1.522 +jar_extract_mf(JAR *jar, jarArch format, JAR_FILE fp, char *ext) 1.523 +{ 1.524 + ZZLink *link; 1.525 + ZZList *list = jar->phy; 1.526 + int ret = 0; 1.527 + 1.528 + if (ZZ_ListEmpty (list)) 1.529 + return JAR_ERR_PNF; 1.530 + 1.531 + for (link = ZZ_ListHead (list); 1.532 + ret >= 0 && !ZZ_ListIterDone (list, link); 1.533 + link = link->next) { 1.534 + JAR_Item *it = link->thing; 1.535 + 1.536 + if (it->type == jarTypePhy && 1.537 + !PORT_Strncmp (it->pathname, "META-INF", 8)) 1.538 + { 1.539 + JAR_Physical *phy = (JAR_Physical *) it->data; 1.540 + char *fn = it->pathname + 8; 1.541 + char *e; 1.542 + char *manifest; 1.543 + long length; 1.544 + int num, status; 1.545 + 1.546 + if (PORT_Strlen (it->pathname) < 8) 1.547 + continue; 1.548 + 1.549 + if (*fn == '/' || *fn == '\\') 1.550 + fn++; 1.551 + if (*fn == 0) { 1.552 + /* just a directory entry */ 1.553 + continue; 1.554 + } 1.555 + 1.556 + /* skip to extension */ 1.557 + for (e = fn; *e && *e != '.'; e++) 1.558 + /* yip */ ; 1.559 + 1.560 + /* and skip dot */ 1.561 + if (*e == '.') 1.562 + e++; 1.563 + if (PORT_Strcasecmp (ext, e)) { 1.564 + /* not the right extension */ 1.565 + continue; 1.566 + } 1.567 + if (phy->length == 0 || phy->length > 0xFFFF) { 1.568 + /* manifest files cannot be zero length or too big! */ 1.569 + /* the 0xFFFF limit is per J2SE SDK */ 1.570 + return JAR_ERR_CORRUPT; 1.571 + } 1.572 + 1.573 + /* Read in the manifest and parse it */ 1.574 + /* Raw inflate in zlib 1.1.4 needs an extra dummy byte at the end */ 1.575 + manifest = (char *)PORT_ZAlloc(phy->length + 1); 1.576 + if (!manifest) 1.577 + return JAR_ERR_MEMORY; 1.578 + 1.579 + JAR_FSEEK (fp, phy->offset, (PRSeekWhence)0); 1.580 + num = JAR_FREAD (fp, manifest, phy->length); 1.581 + if (num != phy->length) { 1.582 + /* corrupt archive file */ 1.583 + PORT_Free (manifest); 1.584 + return JAR_ERR_CORRUPT; 1.585 + } 1.586 + 1.587 + if (phy->compression == 8) { 1.588 + length = phy->length; 1.589 + /* add an extra dummy byte at the end */ 1.590 + manifest[length++] = 0xDD; 1.591 + status = jar_inflate_memory((unsigned int)phy->compression, 1.592 + &length, 1.593 + phy->uncompressed_length, 1.594 + &manifest); 1.595 + if (status < 0) { 1.596 + PORT_Free (manifest); 1.597 + return status; 1.598 + } 1.599 + } else if (phy->compression) { 1.600 + /* unsupported compression method */ 1.601 + PORT_Free (manifest); 1.602 + return JAR_ERR_CORRUPT; 1.603 + } else 1.604 + length = phy->length; 1.605 + 1.606 + status = JAR_parse_manifest(jar, manifest, length, 1.607 + it->pathname, "url"); 1.608 + PORT_Free (manifest); 1.609 + if (status < 0) 1.610 + ret = status; 1.611 + else 1.612 + ++ret; 1.613 + } else if (it->type == jarTypePhy) { 1.614 + /* ordinary file */ 1.615 + } 1.616 + } 1.617 + return ret; 1.618 +} 1.619 + 1.620 +/* 1.621 + * j a r _ g e n _ i n d e x 1.622 + * 1.623 + * Generate an index for the various types of 1.624 + * known archive files. Right now .ZIP and .TAR 1.625 + * 1.626 + */ 1.627 +static int 1.628 +jar_gen_index(JAR *jar, jarArch format, JAR_FILE fp) 1.629 +{ 1.630 + int result = JAR_ERR_CORRUPT; 1.631 + 1.632 + JAR_FSEEK (fp, 0, (PRSeekWhence)0); 1.633 + switch (format) { 1.634 + case jarArchZip: 1.635 + result = jar_listzip (jar, fp); 1.636 + break; 1.637 + 1.638 + case jarArchTar: 1.639 + result = jar_listtar (jar, fp); 1.640 + break; 1.641 + 1.642 + case jarArchGuess: 1.643 + case jarArchNone: 1.644 + return JAR_ERR_GENERAL; 1.645 + } 1.646 + JAR_FSEEK (fp, 0, (PRSeekWhence)0); 1.647 + return result; 1.648 +} 1.649 + 1.650 +/* 1.651 + * j a r _ l i s t z i p 1.652 + * 1.653 + * List the physical contents of a Phil Katz 1.654 + * style .ZIP file into the JAR linked list. 1.655 + * 1.656 + */ 1.657 +static int 1.658 +jar_listzip(JAR *jar, JAR_FILE fp) 1.659 +{ 1.660 + ZZLink *ent; 1.661 + JAR_Item *it; 1.662 + JAR_Physical *phy; 1.663 + struct ZipLocal *Local = PORT_ZNew(struct ZipLocal); 1.664 + struct ZipCentral *Central = PORT_ZNew(struct ZipCentral); 1.665 + struct ZipEnd *End = PORT_ZNew(struct ZipEnd); 1.666 + 1.667 + int err = 0; 1.668 + long pos = 0L; 1.669 + unsigned int compression; 1.670 + unsigned int filename_len, extra_len; 1.671 + 1.672 + char filename[JAR_SIZE]; 1.673 + char date[9], time[9]; 1.674 + char sig[4]; 1.675 + 1.676 + if (!Local || !Central || !End) { 1.677 + /* out of memory */ 1.678 + err = JAR_ERR_MEMORY; 1.679 + goto loser; 1.680 + } 1.681 + 1.682 + while (1) { 1.683 + PRUint32 sigVal; 1.684 + JAR_FSEEK (fp, pos, (PRSeekWhence)0); 1.685 + 1.686 + if (JAR_FREAD(fp, sig, sizeof sig) != sizeof sig) { 1.687 + /* zip file ends prematurely */ 1.688 + err = JAR_ERR_CORRUPT; 1.689 + goto loser; 1.690 + } 1.691 + 1.692 + JAR_FSEEK (fp, pos, (PRSeekWhence)0); 1.693 + sigVal = x86LongToUint32(sig); 1.694 + if (sigVal == LSIG) { 1.695 + JAR_FREAD (fp, Local, sizeof *Local); 1.696 + 1.697 + filename_len = x86ShortToUint32(Local->filename_len); 1.698 + extra_len = x86ShortToUint32(Local->extrafield_len); 1.699 + if (filename_len >= JAR_SIZE) { 1.700 + /* corrupt zip file */ 1.701 + err = JAR_ERR_CORRUPT; 1.702 + goto loser; 1.703 + } 1.704 + 1.705 + if (JAR_FREAD (fp, filename, filename_len) != filename_len) { 1.706 + /* truncated archive file */ 1.707 + err = JAR_ERR_CORRUPT; 1.708 + goto loser; 1.709 + } 1.710 + filename [filename_len] = 0; 1.711 + /* Add this to our jar chain */ 1.712 + phy = PORT_ZNew(JAR_Physical); 1.713 + if (phy == NULL) { 1.714 + err = JAR_ERR_MEMORY; 1.715 + goto loser; 1.716 + } 1.717 + 1.718 + /* We will index any file that comes our way, but when it comes 1.719 + to actually extraction, compression must be 0 or 8 */ 1.720 + compression = x86ShortToUint32(Local->method); 1.721 + phy->compression = (compression <= 255) ? compression : 222; 1.722 + /* XXX 222 is bad magic. */ 1.723 + 1.724 + phy->offset = pos + (sizeof *Local) + filename_len + extra_len; 1.725 + phy->length = x86LongToUint32(Local->size); 1.726 + phy->uncompressed_length = x86LongToUint32(Local->orglen); 1.727 + 1.728 + dosdate (date, Local->date); 1.729 + dostime (time, Local->time); 1.730 + 1.731 + it = PORT_ZNew(JAR_Item); 1.732 + if (it == NULL) { 1.733 + err = JAR_ERR_MEMORY; 1.734 + goto loser; 1.735 + } 1.736 + 1.737 + it->pathname = PORT_Strdup(filename); 1.738 + it->type = jarTypePhy; 1.739 + it->data = (unsigned char *) phy; 1.740 + it->size = sizeof (JAR_Physical); 1.741 + 1.742 + ent = ZZ_NewLink (it); 1.743 + if (ent == NULL) { 1.744 + err = JAR_ERR_MEMORY; 1.745 + goto loser; 1.746 + } 1.747 + 1.748 + ZZ_AppendLink (jar->phy, ent); 1.749 + pos = phy->offset + phy->length; 1.750 + } else if (sigVal == CSIG) { 1.751 + unsigned int attr = 0; 1.752 + if (JAR_FREAD(fp, Central, sizeof *Central) != sizeof *Central) { 1.753 + /* apparently truncated archive */ 1.754 + err = JAR_ERR_CORRUPT; 1.755 + goto loser; 1.756 + } 1.757 + 1.758 +#if defined(XP_UNIX) || defined(XP_BEOS) 1.759 + /* with unix we need to locate any bits from 1.760 + the protection mask in the external attributes. */ 1.761 + attr = Central->external_attributes [2]; /* magic */ 1.762 + if (attr) { 1.763 + /* we have to read the filename, again */ 1.764 + filename_len = x86ShortToUint32(Central->filename_len); 1.765 + if (filename_len >= JAR_SIZE) { 1.766 + /* corrupt in central directory */ 1.767 + err = JAR_ERR_CORRUPT; 1.768 + goto loser; 1.769 + } 1.770 + 1.771 + if (JAR_FREAD(fp, filename, filename_len) != filename_len) { 1.772 + /* truncated in central directory */ 1.773 + err = JAR_ERR_CORRUPT; 1.774 + goto loser; 1.775 + } 1.776 + filename [filename_len] = 0; 1.777 + 1.778 + /* look up this name again */ 1.779 + phy = jar_get_physical (jar, filename); 1.780 + if (phy) { 1.781 + /* always allow access by self */ 1.782 + phy->mode = 0400 | attr; 1.783 + } 1.784 + } 1.785 +#endif 1.786 + pos += sizeof(struct ZipCentral) 1.787 + + x86ShortToUint32(Central->filename_len) 1.788 + + x86ShortToUint32(Central->commentfield_len) 1.789 + + x86ShortToUint32(Central->extrafield_len); 1.790 + } else if (sigVal == ESIG) { 1.791 + if (JAR_FREAD(fp, End, sizeof *End) != sizeof *End) { 1.792 + err = JAR_ERR_CORRUPT; 1.793 + goto loser; 1.794 + } 1.795 + break; 1.796 + } else { 1.797 + /* garbage in archive */ 1.798 + err = JAR_ERR_CORRUPT; 1.799 + goto loser; 1.800 + } 1.801 + } 1.802 + 1.803 +loser: 1.804 + if (Local) 1.805 + PORT_Free(Local); 1.806 + if (Central) 1.807 + PORT_Free(Central); 1.808 + if (End) 1.809 + PORT_Free(End); 1.810 + return err; 1.811 +} 1.812 + 1.813 +/* 1.814 + * j a r _ l i s t t a r 1.815 + * 1.816 + * List the physical contents of a Unix 1.817 + * .tar file into the JAR linked list. 1.818 + * 1.819 + */ 1.820 +static int 1.821 +jar_listtar(JAR *jar, JAR_FILE fp) 1.822 +{ 1.823 + char *s; 1.824 + JAR_Physical *phy; 1.825 + long pos = 0L; 1.826 + long sz, mode; 1.827 + time_t when; 1.828 + union TarEntry tarball; 1.829 + 1.830 + while (1) { 1.831 + JAR_FSEEK (fp, pos, (PRSeekWhence)0); 1.832 + 1.833 + if (JAR_FREAD (fp, &tarball, sizeof tarball) < sizeof tarball) 1.834 + break; 1.835 + 1.836 + if (!*tarball.val.filename) 1.837 + break; 1.838 + 1.839 + when = octalToLong (tarball.val.time); 1.840 + sz = octalToLong (tarball.val.size); 1.841 + mode = octalToLong (tarball.val.mode); 1.842 + 1.843 + /* Tag the end of filename */ 1.844 + s = tarball.val.filename; 1.845 + while (*s && *s != ' ') 1.846 + s++; 1.847 + *s = 0; 1.848 + 1.849 + /* Add to our linked list */ 1.850 + phy = PORT_ZNew(JAR_Physical); 1.851 + if (phy == NULL) 1.852 + return JAR_ERR_MEMORY; 1.853 + 1.854 + phy->compression = 0; 1.855 + phy->offset = pos + sizeof tarball; 1.856 + phy->length = sz; 1.857 + 1.858 + ADDITEM(jar->phy, jarTypePhy, tarball.val.filename, phy, 1.859 + sizeof *phy); 1.860 + 1.861 + /* Advance to next file entry */ 1.862 + sz = PR_ROUNDUP(sz,sizeof tarball); 1.863 + pos += sz + sizeof tarball; 1.864 + } 1.865 + 1.866 + return 0; 1.867 +} 1.868 + 1.869 +/* 1.870 + * d o s d a t e 1.871 + * 1.872 + * Not used right now, but keep it in here because 1.873 + * it will be needed. 1.874 + * 1.875 + */ 1.876 +static int 1.877 +dosdate(char *date, const char *s) 1.878 +{ 1.879 + PRUint32 num = x86ShortToUint32(s); 1.880 + 1.881 + PR_snprintf(date, 9, "%02d-%02d-%02d", ((num >> 5) & 0x0F), (num & 0x1F), 1.882 + ((num >> 9) + 80)); 1.883 + return 0; 1.884 +} 1.885 + 1.886 +/* 1.887 + * d o s t i m e 1.888 + * 1.889 + * Not used right now, but keep it in here because 1.890 + * it will be needed. 1.891 + * 1.892 + */ 1.893 +static int 1.894 +dostime (char *time, const char *s) 1.895 +{ 1.896 + PRUint32 num = x86ShortToUint32(s); 1.897 + 1.898 + PR_snprintf (time, 6, "%02d:%02d", ((num >> 11) & 0x1F), 1.899 + ((num >> 5) & 0x3F)); 1.900 + return 0; 1.901 +} 1.902 + 1.903 +#ifndef NSS_X86_OR_X64 1.904 +/* 1.905 + * Simulates an x86 (little endian, unaligned) ushort fetch from any address. 1.906 + */ 1.907 +static PRUint32 1.908 +x86ShortToUint32(const void * v) 1.909 +{ 1.910 + const unsigned char *ii = (const unsigned char *)v; 1.911 + PRUint32 ret = (PRUint32)(ii[0]) | ((PRUint32)(ii[1]) << 8); 1.912 + return ret; 1.913 +} 1.914 + 1.915 +/* 1.916 + * Simulates an x86 (little endian, unaligned) uint fetch from any address. 1.917 + */ 1.918 +static PRUint32 1.919 +x86LongToUint32(const void *v) 1.920 +{ 1.921 + const unsigned char *ll = (const unsigned char *)v; 1.922 + PRUint32 ret; 1.923 + 1.924 + ret = ((((PRUint32)(ll[0])) << 0) | 1.925 + (((PRUint32)(ll[1])) << 8) | 1.926 + (((PRUint32)(ll[2])) << 16) | 1.927 + (((PRUint32)(ll[3])) << 24)); 1.928 + return ret; 1.929 +} 1.930 +#endif 1.931 + 1.932 +/* 1.933 + * ASCII octal to binary long. 1.934 + * Used for integer encoding inside tar files. 1.935 + * 1.936 + */ 1.937 +static long 1.938 +octalToLong(const char *s) 1.939 +{ 1.940 + long num = 0L; 1.941 + 1.942 + while (*s == ' ') 1.943 + s++; 1.944 + while (*s >= '0' && *s <= '7') { 1.945 + num <<= 3; 1.946 + num += *s++ - '0'; 1.947 + } 1.948 + return num; 1.949 +} 1.950 + 1.951 +/* 1.952 + * g u e s s _ j a r 1.953 + * 1.954 + * Try to guess what kind of JAR file this is. 1.955 + * Maybe tar, maybe zip. Look in the file for magic 1.956 + * or at its filename. 1.957 + * 1.958 + */ 1.959 +static int 1.960 +jar_guess_jar(const char *filename, JAR_FILE fp) 1.961 +{ 1.962 + PRInt32 len = PORT_Strlen(filename); 1.963 + const char *ext = filename + len - 4; /* 4 for ".tar" */ 1.964 + 1.965 + if (len >= 4 && !PL_strcasecmp(ext, ".tar")) 1.966 + return jarArchTar; 1.967 + return jarArchZip; 1.968 +}