1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/lib/jar/jarver.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1170 @@ 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 + * JARVER 1.10 + * 1.11 + * Jarnature Parsing & Verification 1.12 + */ 1.13 + 1.14 +#include "nssrenam.h" 1.15 +#include "jar.h" 1.16 +#include "jarint.h" 1.17 +#include "certdb.h" 1.18 +#include "certt.h" 1.19 +#include "secpkcs7.h" 1.20 +#include "secder.h" 1.21 + 1.22 +#define SZ 512 1.23 + 1.24 +static int 1.25 +jar_validate_pkcs7(JAR *jar, JAR_Signer *signer, char *data, long length); 1.26 + 1.27 +static void 1.28 +jar_catch_bytes(void *arg, const char *buf, unsigned long len); 1.29 + 1.30 +static int 1.31 +jar_gather_signers(JAR *jar, JAR_Signer *signer, SEC_PKCS7ContentInfo *cinfo); 1.32 + 1.33 +static char * 1.34 +jar_eat_line(int lines, int eating, char *data, long *len); 1.35 + 1.36 +static JAR_Digest * 1.37 +jar_digest_section(char *manifest, long length); 1.38 + 1.39 +static JAR_Digest *jar_get_mf_digest(JAR *jar, char *path); 1.40 + 1.41 +static int 1.42 +jar_parse_digital_signature(char *raw_manifest, JAR_Signer *signer, 1.43 + long length, JAR *jar); 1.44 + 1.45 +static int 1.46 +jar_add_cert(JAR *jar, JAR_Signer *signer, int type, CERTCertificate *cert); 1.47 + 1.48 +static char *jar_basename(const char *path); 1.49 + 1.50 +static int 1.51 +jar_signal(int status, JAR *jar, const char *metafile, char *pathname); 1.52 + 1.53 +#ifdef DEBUG 1.54 +static int jar_insanity_check(char *data, long length); 1.55 +#endif 1.56 + 1.57 +int 1.58 +jar_parse_mf(JAR *jar, char *raw_manifest, long length, 1.59 + const char *path, const char *url); 1.60 + 1.61 +int 1.62 +jar_parse_sf(JAR *jar, char *raw_manifest, long length, 1.63 + const char *path, const char *url); 1.64 + 1.65 +int 1.66 +jar_parse_sig(JAR *jar, const char *path, char *raw_manifest, 1.67 + long length); 1.68 + 1.69 +int 1.70 +jar_parse_any(JAR *jar, int type, JAR_Signer *signer, 1.71 + char *raw_manifest, long length, const char *path, 1.72 + const char *url); 1.73 + 1.74 +static int 1.75 +jar_internal_digest(JAR *jar, const char *path, char *x_name, JAR_Digest *dig); 1.76 + 1.77 +/* 1.78 + * J A R _ p a r s e _ m a n i f e s t 1.79 + * 1.80 + * Pass manifest files to this function. They are 1.81 + * decoded and placed into internal representations. 1.82 + * 1.83 + * Accepts both signature and manifest files. Use 1.84 + * the same "jar" for both. 1.85 + * 1.86 + */ 1.87 +int 1.88 +JAR_parse_manifest(JAR *jar, char *raw_manifest, long length, 1.89 + const char *path, const char *url) 1.90 +{ 1.91 + int filename_free = 0; 1.92 + 1.93 + /* fill in the path, if supplied. This is the location 1.94 + of the jar file on disk, if known */ 1.95 + 1.96 + if (jar->filename == NULL && path) { 1.97 + jar->filename = PORT_Strdup(path); 1.98 + if (jar->filename == NULL) 1.99 + return JAR_ERR_MEMORY; 1.100 + filename_free = 1; 1.101 + } 1.102 + 1.103 + /* fill in the URL, if supplied. This is the place 1.104 + from which the jar file was retrieved. */ 1.105 + 1.106 + if (jar->url == NULL && url) { 1.107 + jar->url = PORT_Strdup(url); 1.108 + if (jar->url == NULL) { 1.109 + if (filename_free) { 1.110 + PORT_Free(jar->filename); 1.111 + } 1.112 + return JAR_ERR_MEMORY; 1.113 + } 1.114 + } 1.115 + 1.116 + /* Determine what kind of file this is from the META-INF 1.117 + directory. It could be MF, SF, or a binary RSA/DSA file */ 1.118 + 1.119 + if (!PORT_Strncasecmp (raw_manifest, "Manifest-Version:", 17)) { 1.120 + return jar_parse_mf(jar, raw_manifest, length, path, url); 1.121 + } 1.122 + else if (!PORT_Strncasecmp (raw_manifest, "Signature-Version:", 18)) 1.123 + { 1.124 + return jar_parse_sf(jar, raw_manifest, length, path, url); 1.125 + } else { 1.126 + /* This is probably a binary signature */ 1.127 + return jar_parse_sig(jar, path, raw_manifest, length); 1.128 + } 1.129 +} 1.130 + 1.131 +/* 1.132 + * j a r _ p a r s e _ s i g 1.133 + * 1.134 + * Pass some manner of RSA or DSA digital signature 1.135 + * on, after checking to see if it comes at an appropriate state. 1.136 + * 1.137 + */ 1.138 +int 1.139 +jar_parse_sig(JAR *jar, const char *path, char *raw_manifest, 1.140 + long length) 1.141 +{ 1.142 + JAR_Signer *signer; 1.143 + int status = JAR_ERR_ORDER; 1.144 + 1.145 + if (length <= 128) { 1.146 + /* signature is way too small */ 1.147 + return JAR_ERR_SIG; 1.148 + } 1.149 + 1.150 + /* make sure that MF and SF have already been processed */ 1.151 + 1.152 + if (jar->globalmeta == NULL) 1.153 + return JAR_ERR_ORDER; 1.154 + 1.155 + /* Determine whether or not this RSA file has 1.156 + has an associated SF file */ 1.157 + 1.158 + if (path) { 1.159 + char *owner; 1.160 + owner = jar_basename(path); 1.161 + 1.162 + if (owner == NULL) 1.163 + return JAR_ERR_MEMORY; 1.164 + 1.165 + signer = jar_get_signer(jar, owner); 1.166 + PORT_Free(owner); 1.167 + } else 1.168 + signer = jar_get_signer(jar, "*"); 1.169 + 1.170 + if (signer == NULL) 1.171 + return JAR_ERR_ORDER; 1.172 + 1.173 + 1.174 + /* Do not pass a huge pointer to this function, 1.175 + since the underlying security code is unaware. We will 1.176 + never pass >64k through here. */ 1.177 + 1.178 + if (length > 64000) { 1.179 + /* this digital signature is way too big */ 1.180 + return JAR_ERR_SIG; 1.181 + } 1.182 + 1.183 + /* don't expense unneeded calloc overhead on non-win16 */ 1.184 + status = jar_parse_digital_signature(raw_manifest, signer, length, jar); 1.185 + 1.186 + return status; 1.187 +} 1.188 + 1.189 +/* 1.190 + * j a r _ p a r s e _ m f 1.191 + * 1.192 + * Parse the META-INF/manifest.mf file, whose 1.193 + * information applies to all signers. 1.194 + * 1.195 + */ 1.196 +int 1.197 +jar_parse_mf(JAR *jar, char *raw_manifest, long length, 1.198 + const char *path, const char *url) 1.199 +{ 1.200 + if (jar->globalmeta) { 1.201 + /* refuse a second manifest file, if passed for some reason */ 1.202 + return JAR_ERR_ORDER; 1.203 + } 1.204 + 1.205 + /* remember a digest for the global section */ 1.206 + jar->globalmeta = jar_digest_section(raw_manifest, length); 1.207 + if (jar->globalmeta == NULL) 1.208 + return JAR_ERR_MEMORY; 1.209 + return jar_parse_any(jar, jarTypeMF, NULL, raw_manifest, length, 1.210 + path, url); 1.211 +} 1.212 + 1.213 +/* 1.214 + * j a r _ p a r s e _ s f 1.215 + * 1.216 + * Parse META-INF/xxx.sf, a digitally signed file 1.217 + * pointing to a subset of MF sections. 1.218 + * 1.219 + */ 1.220 +int 1.221 +jar_parse_sf(JAR *jar, char *raw_manifest, long length, 1.222 + const char *path, const char *url) 1.223 +{ 1.224 + JAR_Signer *signer = NULL; 1.225 + int status = JAR_ERR_MEMORY; 1.226 + 1.227 + if (jar->globalmeta == NULL) { 1.228 + /* It is a requirement that the MF file be passed before the SF file */ 1.229 + return JAR_ERR_ORDER; 1.230 + } 1.231 + 1.232 + signer = JAR_new_signer(); 1.233 + if (signer == NULL) 1.234 + goto loser; 1.235 + 1.236 + if (path) { 1.237 + signer->owner = jar_basename(path); 1.238 + if (signer->owner == NULL) 1.239 + goto loser; 1.240 + } 1.241 + 1.242 + /* check for priors. When someone doctors a jar file 1.243 + to contain identical path entries, prevent the second 1.244 + one from affecting JAR functions */ 1.245 + if (jar_get_signer(jar, signer->owner)) { 1.246 + /* someone is trying to spoof us */ 1.247 + status = JAR_ERR_ORDER; 1.248 + goto loser; 1.249 + } 1.250 + 1.251 + /* remember its digest */ 1.252 + signer->digest = JAR_calculate_digest (raw_manifest, length); 1.253 + if (signer->digest == NULL) 1.254 + goto loser; 1.255 + 1.256 + /* Add this signer to the jar */ 1.257 + ADDITEM(jar->signers, jarTypeOwner, signer->owner, signer, 1.258 + sizeof (JAR_Signer)); 1.259 + 1.260 + return jar_parse_any(jar, jarTypeSF, signer, raw_manifest, length, 1.261 + path, url); 1.262 + 1.263 +loser: 1.264 + if (signer) 1.265 + JAR_destroy_signer (signer); 1.266 + return status; 1.267 +} 1.268 + 1.269 +/* 1.270 + * j a r _ p a r s e _ a n y 1.271 + * 1.272 + * Parse a MF or SF manifest file. 1.273 + * 1.274 + */ 1.275 +int 1.276 +jar_parse_any(JAR *jar, int type, JAR_Signer *signer, 1.277 + char *raw_manifest, long length, const char *path, 1.278 + const char *url) 1.279 +{ 1.280 + int status; 1.281 + long raw_len; 1.282 + JAR_Digest *dig, *mfdig = NULL; 1.283 + char line [SZ]; 1.284 + char x_name [SZ], x_md5 [SZ], x_sha [SZ]; 1.285 + char *x_info; 1.286 + char *sf_md5 = NULL, *sf_sha1 = NULL; 1.287 + 1.288 + *x_name = 0; 1.289 + *x_md5 = 0; 1.290 + *x_sha = 0; 1.291 + 1.292 + PORT_Assert( length > 0 ); 1.293 + raw_len = length; 1.294 + 1.295 +#ifdef DEBUG 1.296 + if ((status = jar_insanity_check(raw_manifest, raw_len)) < 0) 1.297 + return status; 1.298 +#endif 1.299 + 1.300 + /* null terminate the first line */ 1.301 + raw_manifest = jar_eat_line(0, PR_TRUE, raw_manifest, &raw_len); 1.302 + 1.303 + /* skip over the preliminary section */ 1.304 + /* This is one section at the top of the file with global metainfo */ 1.305 + while (raw_len > 0) { 1.306 + JAR_Metainfo *met; 1.307 + 1.308 + raw_manifest = jar_eat_line(1, PR_TRUE, raw_manifest, &raw_len); 1.309 + if (raw_len <= 0 || !*raw_manifest) 1.310 + break; 1.311 + 1.312 + met = PORT_ZNew(JAR_Metainfo); 1.313 + if (met == NULL) 1.314 + return JAR_ERR_MEMORY; 1.315 + 1.316 + /* Parse out the header & info */ 1.317 + if (PORT_Strlen (raw_manifest) >= SZ) { 1.318 + /* almost certainly nonsense */ 1.319 + PORT_Free(met); 1.320 + continue; 1.321 + } 1.322 + 1.323 + PORT_Strcpy (line, raw_manifest); 1.324 + x_info = line; 1.325 + 1.326 + while (*x_info && *x_info != ' ' && *x_info != '\t' && *x_info != ':') 1.327 + x_info++; 1.328 + 1.329 + if (*x_info) 1.330 + *x_info++ = 0; 1.331 + 1.332 + while (*x_info == ' ' || *x_info == '\t') 1.333 + x_info++; 1.334 + 1.335 + /* metainfo (name, value) pair is now (line, x_info) */ 1.336 + met->header = PORT_Strdup(line); 1.337 + met->info = PORT_Strdup(x_info); 1.338 + 1.339 + if (type == jarTypeMF) { 1.340 + ADDITEM (jar->metainfo, jarTypeMeta, 1.341 + /* pathname */ NULL, met, sizeof (JAR_Metainfo)); 1.342 + } 1.343 + 1.344 + /* For SF files, this metadata may be the digests 1.345 + of the MF file, still in the "met" structure. */ 1.346 + 1.347 + if (type == jarTypeSF) { 1.348 + if (!PORT_Strcasecmp(line, "MD5-Digest")) 1.349 + sf_md5 = (char *) met->info; 1.350 + 1.351 + if (!PORT_Strcasecmp(line, "SHA1-Digest") || 1.352 + !PORT_Strcasecmp(line, "SHA-Digest")) 1.353 + sf_sha1 = (char *) met->info; 1.354 + } 1.355 + 1.356 + if (type != jarTypeMF) { 1.357 + PORT_Free(met->header); 1.358 + if (type != jarTypeSF) { 1.359 + PORT_Free(met->info); 1.360 + } 1.361 + PORT_Free(met); 1.362 + } 1.363 + } 1.364 + 1.365 + if (type == jarTypeSF && jar->globalmeta) { 1.366 + /* this is a SF file which may contain a digest of the manifest.mf's 1.367 + global metainfo. */ 1.368 + 1.369 + int match = 0; 1.370 + JAR_Digest *glob = jar->globalmeta; 1.371 + 1.372 + if (sf_md5) { 1.373 + unsigned int md5_length; 1.374 + unsigned char *md5_digest; 1.375 + 1.376 + md5_digest = ATOB_AsciiToData (sf_md5, &md5_length); 1.377 + PORT_Assert( md5_length == MD5_LENGTH ); 1.378 + 1.379 + if (md5_length != MD5_LENGTH) 1.380 + return JAR_ERR_CORRUPT; 1.381 + 1.382 + match = PORT_Memcmp(md5_digest, glob->md5, MD5_LENGTH); 1.383 + } 1.384 + 1.385 + if (sf_sha1 && match == 0) { 1.386 + unsigned int sha1_length; 1.387 + unsigned char *sha1_digest; 1.388 + 1.389 + sha1_digest = ATOB_AsciiToData (sf_sha1, &sha1_length); 1.390 + PORT_Assert( sha1_length == SHA1_LENGTH ); 1.391 + 1.392 + if (sha1_length != SHA1_LENGTH) 1.393 + return JAR_ERR_CORRUPT; 1.394 + 1.395 + match = PORT_Memcmp(sha1_digest, glob->sha1, SHA1_LENGTH); 1.396 + } 1.397 + 1.398 + if (match != 0) { 1.399 + /* global digest doesn't match, SF file therefore invalid */ 1.400 + jar->valid = JAR_ERR_METADATA; 1.401 + return JAR_ERR_METADATA; 1.402 + } 1.403 + } 1.404 + 1.405 + /* done with top section of global data */ 1.406 + while (raw_len > 0) { 1.407 + *x_md5 = 0; 1.408 + *x_sha = 0; 1.409 + *x_name = 0; 1.410 + 1.411 + /* If this is a manifest file, attempt to get a digest of the following 1.412 + section, without damaging it. This digest will be saved later. */ 1.413 + 1.414 + if (type == jarTypeMF) { 1.415 + char *sec; 1.416 + long sec_len = raw_len; 1.417 + 1.418 + if (!*raw_manifest || *raw_manifest == '\n') { 1.419 + /* skip the blank line */ 1.420 + sec = jar_eat_line(1, PR_FALSE, raw_manifest, &sec_len); 1.421 + } else 1.422 + sec = raw_manifest; 1.423 + 1.424 + if (sec_len > 0 && !PORT_Strncasecmp(sec, "Name:", 5)) { 1.425 + if (type == jarTypeMF) 1.426 + mfdig = jar_digest_section(sec, sec_len); 1.427 + else 1.428 + mfdig = NULL; 1.429 + } 1.430 + } 1.431 + 1.432 + 1.433 + while (raw_len > 0) { 1.434 + raw_manifest = jar_eat_line(1, PR_TRUE, raw_manifest, &raw_len); 1.435 + if (raw_len <= 0 || !*raw_manifest) 1.436 + break; /* blank line, done with this entry */ 1.437 + 1.438 + if (PORT_Strlen(raw_manifest) >= SZ) { 1.439 + /* almost certainly nonsense */ 1.440 + continue; 1.441 + } 1.442 + 1.443 + /* Parse out the name/value pair */ 1.444 + PORT_Strcpy(line, raw_manifest); 1.445 + x_info = line; 1.446 + 1.447 + while (*x_info && *x_info != ' ' && *x_info != '\t' && 1.448 + *x_info != ':') 1.449 + x_info++; 1.450 + 1.451 + if (*x_info) 1.452 + *x_info++ = 0; 1.453 + 1.454 + while (*x_info == ' ' || *x_info == '\t') 1.455 + x_info++; 1.456 + 1.457 + if (!PORT_Strcasecmp(line, "Name")) 1.458 + PORT_Strcpy(x_name, x_info); 1.459 + else if (!PORT_Strcasecmp(line, "MD5-Digest")) 1.460 + PORT_Strcpy(x_md5, x_info); 1.461 + else if (!PORT_Strcasecmp(line, "SHA1-Digest") 1.462 + || !PORT_Strcasecmp(line, "SHA-Digest")) 1.463 + PORT_Strcpy(x_sha, x_info); 1.464 + 1.465 + /* Algorithm list is meta info we don't care about; keeping it out 1.466 + of metadata saves significant space for large jar files */ 1.467 + else if (!PORT_Strcasecmp(line, "Digest-Algorithms") 1.468 + || !PORT_Strcasecmp(line, "Hash-Algorithms")) 1.469 + continue; 1.470 + 1.471 + /* Meta info is only collected for the manifest.mf file, 1.472 + since the JAR_get_metainfo call does not support identity */ 1.473 + else if (type == jarTypeMF) { 1.474 + JAR_Metainfo *met; 1.475 + 1.476 + /* this is meta-data */ 1.477 + met = PORT_ZNew(JAR_Metainfo); 1.478 + if (met == NULL) 1.479 + return JAR_ERR_MEMORY; 1.480 + 1.481 + /* metainfo (name, value) pair is now (line, x_info) */ 1.482 + if ((met->header = PORT_Strdup(line)) == NULL) { 1.483 + PORT_Free(met); 1.484 + return JAR_ERR_MEMORY; 1.485 + } 1.486 + 1.487 + if ((met->info = PORT_Strdup(x_info)) == NULL) { 1.488 + PORT_Free(met->header); 1.489 + PORT_Free(met); 1.490 + return JAR_ERR_MEMORY; 1.491 + } 1.492 + 1.493 + ADDITEM (jar->metainfo, jarTypeMeta, 1.494 + x_name, met, sizeof (JAR_Metainfo)); 1.495 + } 1.496 + } 1.497 + 1.498 + if (!*x_name) { 1.499 + /* Whatever that was, it wasn't an entry, because we didn't get a 1.500 + name. We don't really have anything, so don't record this. */ 1.501 + continue; 1.502 + } 1.503 + 1.504 + dig = PORT_ZNew(JAR_Digest); 1.505 + if (dig == NULL) 1.506 + return JAR_ERR_MEMORY; 1.507 + 1.508 + if (*x_md5) { 1.509 + unsigned int binary_length; 1.510 + unsigned char *binary_digest; 1.511 + 1.512 + binary_digest = ATOB_AsciiToData (x_md5, &binary_length); 1.513 + PORT_Assert( binary_length == MD5_LENGTH ); 1.514 + if (binary_length != MD5_LENGTH) { 1.515 + PORT_Free(dig); 1.516 + return JAR_ERR_CORRUPT; 1.517 + } 1.518 + memcpy (dig->md5, binary_digest, MD5_LENGTH); 1.519 + dig->md5_status = jarHashPresent; 1.520 + } 1.521 + 1.522 + if (*x_sha ) { 1.523 + unsigned int binary_length; 1.524 + unsigned char *binary_digest; 1.525 + 1.526 + binary_digest = ATOB_AsciiToData (x_sha, &binary_length); 1.527 + PORT_Assert( binary_length == SHA1_LENGTH ); 1.528 + if (binary_length != SHA1_LENGTH) { 1.529 + PORT_Free(dig); 1.530 + return JAR_ERR_CORRUPT; 1.531 + } 1.532 + memcpy (dig->sha1, binary_digest, SHA1_LENGTH); 1.533 + dig->sha1_status = jarHashPresent; 1.534 + } 1.535 + 1.536 + PORT_Assert( type == jarTypeMF || type == jarTypeSF ); 1.537 + if (type == jarTypeMF) { 1.538 + ADDITEM (jar->hashes, jarTypeMF, x_name, dig, sizeof (JAR_Digest)); 1.539 + } else if (type == jarTypeSF) { 1.540 + ADDITEM (signer->sf, jarTypeSF, x_name, dig, sizeof (JAR_Digest)); 1.541 + } else { 1.542 + PORT_Free(dig); 1.543 + return JAR_ERR_ORDER; 1.544 + } 1.545 + 1.546 + /* we're placing these calculated digests of manifest.mf 1.547 + sections in a list where they can subsequently be forgotten */ 1.548 + if (type == jarTypeMF && mfdig) { 1.549 + ADDITEM (jar->manifest, jarTypeSect, 1.550 + x_name, mfdig, sizeof (JAR_Digest)); 1.551 + mfdig = NULL; 1.552 + } 1.553 + 1.554 + /* Retrieve our saved SHA1 digest from saved copy and check digests. 1.555 + This is just comparing the digest of the MF section as indicated in 1.556 + the SF file with the one we remembered from parsing the MF file */ 1.557 + 1.558 + if (type == jarTypeSF) { 1.559 + if ((status = jar_internal_digest(jar, path, x_name, dig)) < 0) 1.560 + return status; 1.561 + } 1.562 + } 1.563 + 1.564 + return 0; 1.565 +} 1.566 + 1.567 +static int 1.568 +jar_internal_digest(JAR *jar, const char *path, char *x_name, JAR_Digest *dig) 1.569 +{ 1.570 + int cv; 1.571 + int status; 1.572 + 1.573 + JAR_Digest *savdig; 1.574 + 1.575 + savdig = jar_get_mf_digest(jar, x_name); 1.576 + if (savdig == NULL) { 1.577 + /* no .mf digest for this pathname */ 1.578 + status = jar_signal(JAR_ERR_ENTRY, jar, path, x_name); 1.579 + if (status < 0) 1.580 + return 0; /* was continue; */ 1.581 + return status; 1.582 + } 1.583 + 1.584 + /* check for md5 consistency */ 1.585 + if (dig->md5_status) { 1.586 + cv = PORT_Memcmp(savdig->md5, dig->md5, MD5_LENGTH); 1.587 + /* md5 hash of .mf file is not what expected */ 1.588 + if (cv) { 1.589 + status = jar_signal(JAR_ERR_HASH, jar, path, x_name); 1.590 + 1.591 + /* bad hash, man */ 1.592 + dig->md5_status = jarHashBad; 1.593 + savdig->md5_status = jarHashBad; 1.594 + 1.595 + if (status < 0) 1.596 + return 0; /* was continue; */ 1.597 + return status; 1.598 + } 1.599 + } 1.600 + 1.601 + /* check for sha1 consistency */ 1.602 + if (dig->sha1_status) { 1.603 + cv = PORT_Memcmp(savdig->sha1, dig->sha1, SHA1_LENGTH); 1.604 + /* sha1 hash of .mf file is not what expected */ 1.605 + if (cv) { 1.606 + status = jar_signal(JAR_ERR_HASH, jar, path, x_name); 1.607 + 1.608 + /* bad hash, man */ 1.609 + dig->sha1_status = jarHashBad; 1.610 + savdig->sha1_status = jarHashBad; 1.611 + 1.612 + if (status < 0) 1.613 + return 0; /* was continue; */ 1.614 + return status; 1.615 + } 1.616 + } 1.617 + return 0; 1.618 +} 1.619 + 1.620 +#ifdef DEBUG 1.621 +/* 1.622 + * j a r _ i n s a n i t y _ c h e c k 1.623 + * 1.624 + * Check for illegal characters (or possibly so) 1.625 + * in the manifest files, to detect potential memory 1.626 + * corruption by our neighbors. Debug only, since 1.627 + * not I18N safe. 1.628 + * 1.629 + */ 1.630 +static int 1.631 +jar_insanity_check(char *data, long length) 1.632 +{ 1.633 + int c; 1.634 + long off; 1.635 + 1.636 + for (off = 0; off < length; off++) { 1.637 + c = data [off]; 1.638 + if (c == '\n' || c == '\r' || (c >= ' ' && c <= 128)) 1.639 + continue; 1.640 + return JAR_ERR_CORRUPT; 1.641 + } 1.642 + return 0; 1.643 +} 1.644 +#endif 1.645 + 1.646 +/* 1.647 + * j a r _ p a r s e _ d i g i t a l _ s i g n a t u r e 1.648 + * 1.649 + * Parse an RSA or DSA (or perhaps other) digital signature. 1.650 + * Right now everything is PKCS7. 1.651 + * 1.652 + */ 1.653 +static int 1.654 +jar_parse_digital_signature(char *raw_manifest, JAR_Signer *signer, 1.655 + long length, JAR *jar) 1.656 +{ 1.657 + return jar_validate_pkcs7 (jar, signer, raw_manifest, length); 1.658 +} 1.659 + 1.660 +/* 1.661 + * j a r _ a d d _ c e r t 1.662 + * 1.663 + * Add information for the given certificate 1.664 + * (or whatever) to the JAR linked list. A pointer 1.665 + * is passed for some relevant reference, say 1.666 + * for example the original certificate. 1.667 + * 1.668 + */ 1.669 +static int 1.670 +jar_add_cert(JAR *jar, JAR_Signer *signer, int type, CERTCertificate *cert) 1.671 +{ 1.672 + JAR_Cert *fing; 1.673 + unsigned char *keyData; 1.674 + 1.675 + if (cert == NULL) 1.676 + return JAR_ERR_ORDER; 1.677 + 1.678 + fing = PORT_ZNew(JAR_Cert); 1.679 + if (fing == NULL) 1.680 + goto loser; 1.681 + 1.682 + fing->cert = CERT_DupCertificate (cert); 1.683 + 1.684 + /* get the certkey */ 1.685 + fing->length = cert->derIssuer.len + 2 + cert->serialNumber.len; 1.686 + fing->key = keyData = (unsigned char *) PORT_ZAlloc(fing->length); 1.687 + if (fing->key == NULL) 1.688 + goto loser; 1.689 + keyData[0] = ((cert->derIssuer.len) >> 8) & 0xff; 1.690 + keyData[1] = ((cert->derIssuer.len) & 0xff); 1.691 + PORT_Memcpy(&keyData[2], cert->derIssuer.data, cert->derIssuer.len); 1.692 + PORT_Memcpy(&keyData[2+cert->derIssuer.len], cert->serialNumber.data, 1.693 + cert->serialNumber.len); 1.694 + 1.695 + ADDITEM (signer->certs, type, NULL, fing, sizeof (JAR_Cert)); 1.696 + return 0; 1.697 + 1.698 +loser: 1.699 + if (fing) { 1.700 + if (fing->cert) 1.701 + CERT_DestroyCertificate (fing->cert); 1.702 + PORT_Free(fing); 1.703 + } 1.704 + return JAR_ERR_MEMORY; 1.705 +} 1.706 + 1.707 +/* 1.708 + * e a t _ l i n e 1.709 + * 1.710 + * Reads and/or modifies input buffer "data" of length "*len". 1.711 + * This function does zero, one or two of the following tasks: 1.712 + * 1) if "lines" is non-zero, it reads and discards that many lines from 1.713 + * the input. NUL characters are treated as end-of-line characters, 1.714 + * not as end-of-input characters. The input is NOT NUL terminated. 1.715 + * Note: presently, all callers pass either 0 or 1 for lines. 1.716 + * 2) After skipping the specified number of input lines, if "eating" is 1.717 + * non-zero, it finds the end of the next line of input and replaces 1.718 + * the end of line character(s) with a NUL character. 1.719 + * This function modifies the input buffer, containing the file, in place. 1.720 + * This function handles PC, Mac, and Unix style text files. 1.721 + * On entry, *len contains the maximum number of characters that this 1.722 + * function should ever examine, starting with the character in *data. 1.723 + * On return, *len is reduced by the number of characters skipped by the 1.724 + * first task, if any; 1.725 + * If lines is zero and eating is false, this function returns 1.726 + * the value in the data argument, but otherwise does nothing. 1.727 + */ 1.728 +static char * 1.729 +jar_eat_line(int lines, int eating, char *data, long *len) 1.730 +{ 1.731 + char *start = data; 1.732 + long maxLen = *len; 1.733 + 1.734 + if (maxLen <= 0) 1.735 + return start; 1.736 + 1.737 +#define GO_ON ((data - start) < maxLen) 1.738 + 1.739 + /* Eat the requisite number of lines, if any; 1.740 + prior to terminating the current line with a 0. */ 1.741 + for (/* yip */ ; lines > 0; lines--) { 1.742 + while (GO_ON && *data && *data != '\r' && *data != '\n') 1.743 + data++; 1.744 + 1.745 + /* Eat any leading CR */ 1.746 + if (GO_ON && *data == '\r') 1.747 + data++; 1.748 + 1.749 + /* After the CR, ok to eat one LF */ 1.750 + if (GO_ON && *data == '\n') 1.751 + data++; 1.752 + 1.753 + /* If there are NULs, this function probably put them there */ 1.754 + while (GO_ON && !*data) 1.755 + data++; 1.756 + } 1.757 + maxLen -= data - start; /* we have this many characters left. */ 1.758 + *len = maxLen; 1.759 + start = data; /* now start again here. */ 1.760 + if (maxLen > 0 && eating) { 1.761 + /* Terminate this line with a 0 */ 1.762 + while (GO_ON && *data && *data != '\n' && *data != '\r') 1.763 + data++; 1.764 + 1.765 + /* If not past the end, we are allowed to eat one CR */ 1.766 + if (GO_ON && *data == '\r') 1.767 + *data++ = 0; 1.768 + 1.769 + /* After the CR (if any), if not past the end, ok to eat one LF */ 1.770 + if (GO_ON && *data == '\n') 1.771 + *data++ = 0; 1.772 + } 1.773 + return start; 1.774 +} 1.775 +#undef GO_ON 1.776 + 1.777 +/* 1.778 + * j a r _ d i g e s t _ s e c t i o n 1.779 + * 1.780 + * Return the digests of the next section of the manifest file. 1.781 + * Does not damage the manifest file, unlike parse_manifest. 1.782 + * 1.783 + */ 1.784 +static JAR_Digest * 1.785 +jar_digest_section(char *manifest, long length) 1.786 +{ 1.787 + long global_len; 1.788 + char *global_end; 1.789 + 1.790 + global_end = manifest; 1.791 + global_len = length; 1.792 + 1.793 + while (global_len > 0) { 1.794 + global_end = jar_eat_line(1, PR_FALSE, global_end, &global_len); 1.795 + if (global_len > 0 && (*global_end == 0 || *global_end == '\n')) 1.796 + break; 1.797 + } 1.798 + return JAR_calculate_digest (manifest, global_end - manifest); 1.799 +} 1.800 + 1.801 +/* 1.802 + * J A R _ v e r i f y _ d i g e s t 1.803 + * 1.804 + * Verifies that a precalculated digest matches the 1.805 + * expected value in the manifest. 1.806 + * 1.807 + */ 1.808 +int PR_CALLBACK 1.809 +JAR_verify_digest(JAR *jar, const char *name, JAR_Digest *dig) 1.810 +{ 1.811 + JAR_Item *it; 1.812 + JAR_Digest *shindig; 1.813 + ZZLink *link; 1.814 + ZZList *list = jar->hashes; 1.815 + int result1 = 0; 1.816 + int result2 = 0; 1.817 + 1.818 + 1.819 + if (jar->valid < 0) { 1.820 + /* signature not valid */ 1.821 + return JAR_ERR_SIG; 1.822 + } 1.823 + if (ZZ_ListEmpty (list)) { 1.824 + /* empty list */ 1.825 + return JAR_ERR_PNF; 1.826 + } 1.827 + 1.828 + for (link = ZZ_ListHead (list); 1.829 + !ZZ_ListIterDone (list, link); 1.830 + link = link->next) { 1.831 + it = link->thing; 1.832 + if (it->type == jarTypeMF 1.833 + && it->pathname && !PORT_Strcmp(it->pathname, name)) { 1.834 + shindig = (JAR_Digest *) it->data; 1.835 + if (shindig->md5_status) { 1.836 + if (shindig->md5_status == jarHashBad) 1.837 + return JAR_ERR_HASH; 1.838 + result1 = memcmp (dig->md5, shindig->md5, MD5_LENGTH); 1.839 + } 1.840 + if (shindig->sha1_status) { 1.841 + if (shindig->sha1_status == jarHashBad) 1.842 + return JAR_ERR_HASH; 1.843 + result2 = memcmp (dig->sha1, shindig->sha1, SHA1_LENGTH); 1.844 + } 1.845 + return (result1 == 0 && result2 == 0) ? 0 : JAR_ERR_HASH; 1.846 + } 1.847 + } 1.848 + return JAR_ERR_PNF; 1.849 +} 1.850 + 1.851 + 1.852 + 1.853 + 1.854 + 1.855 + 1.856 + 1.857 +/* 1.858 + * J A R _ f e t c h _ c e r t 1.859 + * 1.860 + * Given an opaque identifier of a certificate, 1.861 + * return the full certificate. 1.862 + * 1.863 + * The new function, which retrieves by key. 1.864 + * 1.865 + */ 1.866 +CERTCertificate * 1.867 +JAR_fetch_cert(long length, void *key) 1.868 +{ 1.869 + CERTIssuerAndSN issuerSN; 1.870 + CERTCertificate *cert = NULL; 1.871 + CERTCertDBHandle *certdb; 1.872 + 1.873 + certdb = JAR_open_database(); 1.874 + if (certdb) { 1.875 + unsigned char *keyData = (unsigned char *)key; 1.876 + issuerSN.derIssuer.len = (keyData[0] << 8) + keyData[0]; 1.877 + issuerSN.derIssuer.data = &keyData[2]; 1.878 + issuerSN.serialNumber.len = length - (2 + issuerSN.derIssuer.len); 1.879 + issuerSN.serialNumber.data = &keyData[2+issuerSN.derIssuer.len]; 1.880 + cert = CERT_FindCertByIssuerAndSN (certdb, &issuerSN); 1.881 + JAR_close_database (certdb); 1.882 + } 1.883 + return cert; 1.884 +} 1.885 + 1.886 +/* 1.887 + * j a r _ g e t _ m f _ d i g e s t 1.888 + * 1.889 + * Retrieve a corresponding saved digest over a section 1.890 + * of the main manifest file. 1.891 + * 1.892 + */ 1.893 +static JAR_Digest * 1.894 +jar_get_mf_digest(JAR *jar, char *pathname) 1.895 +{ 1.896 + JAR_Item *it; 1.897 + JAR_Digest *dig; 1.898 + ZZLink *link; 1.899 + ZZList *list = jar->manifest; 1.900 + 1.901 + if (ZZ_ListEmpty (list)) 1.902 + return NULL; 1.903 + 1.904 + for (link = ZZ_ListHead (list); 1.905 + !ZZ_ListIterDone (list, link); 1.906 + link = link->next) { 1.907 + it = link->thing; 1.908 + if (it->type == jarTypeSect 1.909 + && it->pathname && !PORT_Strcmp(it->pathname, pathname)) { 1.910 + dig = (JAR_Digest *) it->data; 1.911 + return dig; 1.912 + } 1.913 + } 1.914 + return NULL; 1.915 +} 1.916 + 1.917 +/* 1.918 + * j a r _ b a s e n a m e 1.919 + * 1.920 + * Return the basename -- leading components of path stripped off, 1.921 + * extension ripped off -- of a path. 1.922 + * 1.923 + */ 1.924 +static char * 1.925 +jar_basename(const char *path) 1.926 +{ 1.927 + char *pith, *e, *basename, *ext; 1.928 + 1.929 + if (path == NULL) 1.930 + return PORT_Strdup(""); 1.931 + 1.932 + pith = PORT_Strdup(path); 1.933 + basename = pith; 1.934 + while (1) { 1.935 + for (e = basename; *e && *e != '/' && *e != '\\'; e++) 1.936 + /* yip */ ; 1.937 + if (*e) 1.938 + basename = ++e; 1.939 + else 1.940 + break; 1.941 + } 1.942 + 1.943 + if ((ext = PORT_Strrchr(basename, '.')) != NULL) 1.944 + *ext = 0; 1.945 + 1.946 + /* We already have the space allocated */ 1.947 + PORT_Strcpy(pith, basename); 1.948 + return pith; 1.949 +} 1.950 + 1.951 +/* 1.952 + * + + + + + + + + + + + + + + + 1.953 + * 1.954 + * CRYPTO ROUTINES FOR JAR 1.955 + * 1.956 + * The following functions are the cryptographic 1.957 + * interface to PKCS7 for Jarnatures. 1.958 + * 1.959 + * + + + + + + + + + + + + + + + 1.960 + * 1.961 + */ 1.962 + 1.963 +/* 1.964 + * j a r _ c a t c h _ b y t e s 1.965 + * 1.966 + * In the event signatures contain enveloped data, it will show up here. 1.967 + * But note that the lib/pkcs7 routines aren't ready for it. 1.968 + * 1.969 + */ 1.970 +static void 1.971 +jar_catch_bytes(void *arg, const char *buf, unsigned long len) 1.972 +{ 1.973 + /* Actually this should never be called, since there is 1.974 + presumably no data in the signature itself. */ 1.975 +} 1.976 + 1.977 +/* 1.978 + * j a r _ v a l i d a t e _ p k c s 7 1.979 + * 1.980 + * Validate (and decode, if necessary) a binary pkcs7 1.981 + * signature in DER format. 1.982 + * 1.983 + */ 1.984 +static int 1.985 +jar_validate_pkcs7(JAR *jar, JAR_Signer *signer, char *data, long length) 1.986 +{ 1.987 + 1.988 + SEC_PKCS7ContentInfo *cinfo = NULL; 1.989 + SEC_PKCS7DecoderContext *dcx; 1.990 + PRBool goodSig; 1.991 + int status = 0; 1.992 + SECItem detdig; 1.993 + 1.994 + PORT_Assert( jar != NULL && signer != NULL ); 1.995 + 1.996 + if (jar == NULL || signer == NULL) 1.997 + return JAR_ERR_ORDER; 1.998 + 1.999 + signer->valid = JAR_ERR_SIG; 1.1000 + 1.1001 + /* We need a context if we can get one */ 1.1002 + dcx = SEC_PKCS7DecoderStart(jar_catch_bytes, NULL /*cb_arg*/, 1.1003 + NULL /*getpassword*/, jar->mw, 1.1004 + NULL, NULL, NULL); 1.1005 + if (dcx == NULL) { 1.1006 + /* strange pkcs7 failure */ 1.1007 + return JAR_ERR_PK7; 1.1008 + } 1.1009 + 1.1010 + SEC_PKCS7DecoderUpdate (dcx, data, length); 1.1011 + cinfo = SEC_PKCS7DecoderFinish (dcx); 1.1012 + if (cinfo == NULL) { 1.1013 + /* strange pkcs7 failure */ 1.1014 + return JAR_ERR_PK7; 1.1015 + } 1.1016 + if (SEC_PKCS7ContentIsEncrypted (cinfo)) { 1.1017 + /* content was encrypted, fail */ 1.1018 + return JAR_ERR_PK7; 1.1019 + } 1.1020 + if (SEC_PKCS7ContentIsSigned (cinfo) == PR_FALSE) { 1.1021 + /* content was not signed, fail */ 1.1022 + return JAR_ERR_PK7; 1.1023 + } 1.1024 + 1.1025 + PORT_SetError(0); 1.1026 + 1.1027 + /* use SHA1 only */ 1.1028 + detdig.len = SHA1_LENGTH; 1.1029 + detdig.data = signer->digest->sha1; 1.1030 + goodSig = SEC_PKCS7VerifyDetachedSignature(cinfo, 1.1031 + certUsageObjectSigner, 1.1032 + &detdig, HASH_AlgSHA1, 1.1033 + PR_FALSE); 1.1034 + jar_gather_signers(jar, signer, cinfo); 1.1035 + if (goodSig == PR_TRUE) { 1.1036 + /* signature is valid */ 1.1037 + signer->valid = 0; 1.1038 + } else { 1.1039 + status = PORT_GetError(); 1.1040 + PORT_Assert( status < 0 ); 1.1041 + if (status >= 0) 1.1042 + status = JAR_ERR_SIG; 1.1043 + jar->valid = status; 1.1044 + signer->valid = status; 1.1045 + } 1.1046 + jar->pkcs7 = PR_TRUE; 1.1047 + signer->pkcs7 = PR_TRUE; 1.1048 + SEC_PKCS7DestroyContentInfo(cinfo); 1.1049 + return status; 1.1050 +} 1.1051 + 1.1052 +/* 1.1053 + * j a r _ g a t h e r _ s i g n e r s 1.1054 + * 1.1055 + * Add the single signer of this signature to the 1.1056 + * certificate linked list. 1.1057 + * 1.1058 + */ 1.1059 +static int 1.1060 +jar_gather_signers(JAR *jar, JAR_Signer *signer, SEC_PKCS7ContentInfo *cinfo) 1.1061 +{ 1.1062 + int result; 1.1063 + CERTCertificate *cert; 1.1064 + CERTCertDBHandle *certdb; 1.1065 + SEC_PKCS7SignedData *sdp = cinfo->content.signedData; 1.1066 + SEC_PKCS7SignerInfo **pksigners, *pksigner; 1.1067 + 1.1068 + if (sdp == NULL) 1.1069 + return JAR_ERR_PK7; 1.1070 + 1.1071 + pksigners = sdp->signerInfos; 1.1072 + /* permit exactly one signer */ 1.1073 + if (pksigners == NULL || pksigners [0] == NULL || pksigners [1] != NULL) 1.1074 + return JAR_ERR_PK7; 1.1075 + 1.1076 + pksigner = *pksigners; 1.1077 + cert = pksigner->cert; 1.1078 + 1.1079 + if (cert == NULL) 1.1080 + return JAR_ERR_PK7; 1.1081 + 1.1082 + certdb = JAR_open_database(); 1.1083 + if (certdb == NULL) 1.1084 + return JAR_ERR_GENERAL; 1.1085 + 1.1086 + result = jar_add_cert(jar, signer, jarTypeSign, cert); 1.1087 + JAR_close_database (certdb); 1.1088 + return result; 1.1089 +} 1.1090 + 1.1091 +/* 1.1092 + * j a r _ o p e n _ d a t a b a s e 1.1093 + * 1.1094 + * Open the certificate database, 1.1095 + * for use by JAR functions. 1.1096 + * 1.1097 + */ 1.1098 +CERTCertDBHandle * 1.1099 +JAR_open_database(void) 1.1100 +{ 1.1101 + return CERT_GetDefaultCertDB(); 1.1102 +} 1.1103 + 1.1104 +/* 1.1105 + * j a r _ c l o s e _ d a t a b a s e 1.1106 + * 1.1107 + * Close the certificate database. 1.1108 + * For use by JAR functions. 1.1109 + * 1.1110 + */ 1.1111 +int 1.1112 +JAR_close_database(CERTCertDBHandle *certdb) 1.1113 +{ 1.1114 + return 0; 1.1115 +} 1.1116 + 1.1117 + 1.1118 +/* 1.1119 + * j a r _ s i g n a l 1.1120 + * 1.1121 + * Nonfatal errors come here to callback Java. 1.1122 + * 1.1123 + */ 1.1124 +static int 1.1125 +jar_signal(int status, JAR *jar, const char *metafile, char *pathname) 1.1126 +{ 1.1127 + char *errstring = JAR_get_error (status); 1.1128 + if (jar->signal) { 1.1129 + (*jar->signal) (status, jar, metafile, pathname, errstring); 1.1130 + return 0; 1.1131 + } 1.1132 + return status; 1.1133 +} 1.1134 + 1.1135 +/* 1.1136 + * j a r _ a p p e n d 1.1137 + * 1.1138 + * Tack on an element to one of a JAR's linked 1.1139 + * lists, with rudimentary error handling. 1.1140 + * 1.1141 + */ 1.1142 +int 1.1143 +jar_append(ZZList *list, int type, char *pathname, void *data, size_t size) 1.1144 +{ 1.1145 + JAR_Item *it = PORT_ZNew(JAR_Item); 1.1146 + ZZLink *entity; 1.1147 + 1.1148 + if (it == NULL) 1.1149 + goto loser; 1.1150 + 1.1151 + if (pathname) { 1.1152 + it->pathname = PORT_Strdup(pathname); 1.1153 + if (it->pathname == NULL) 1.1154 + goto loser; 1.1155 + } 1.1156 + 1.1157 + it->type = (jarType)type; 1.1158 + it->data = (unsigned char *) data; 1.1159 + it->size = size; 1.1160 + entity = ZZ_NewLink (it); 1.1161 + if (entity) { 1.1162 + ZZ_AppendLink (list, entity); 1.1163 + return 0; 1.1164 + } 1.1165 + 1.1166 +loser: 1.1167 + if (it) { 1.1168 + if (it->pathname) 1.1169 + PORT_Free(it->pathname); 1.1170 + PORT_Free(it); 1.1171 + } 1.1172 + return JAR_ERR_MEMORY; 1.1173 +}