security/nss/lib/jar/jarver.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

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

mercurial