Blame nss/lib/jar/jarver.c

Packit 40b132
/* This Source Code Form is subject to the terms of the Mozilla Public
Packit 40b132
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit 40b132
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  JARVER
Packit 40b132
 *
Packit 40b132
 *  Jarnature Parsing & Verification
Packit 40b132
 */
Packit 40b132
Packit 40b132
#include "nssrenam.h"
Packit 40b132
#include "jar.h"
Packit 40b132
#include "jarint.h"
Packit 40b132
#include "certdb.h"
Packit 40b132
#include "certt.h"
Packit 40b132
#include "secpkcs7.h"
Packit 40b132
#include "secder.h"
Packit 40b132
Packit 40b132
#define SZ 512
Packit 40b132
Packit 40b132
static int
Packit 40b132
jar_validate_pkcs7(JAR *jar, JAR_Signer *signer, char *data, long length);
Packit 40b132
Packit 40b132
static void
Packit 40b132
jar_catch_bytes(void *arg, const char *buf, unsigned long len);
Packit 40b132
Packit 40b132
static int
Packit 40b132
jar_gather_signers(JAR *jar, JAR_Signer *signer, SEC_PKCS7ContentInfo *cinfo);
Packit 40b132
Packit 40b132
static char *
Packit 40b132
jar_eat_line(int lines, int eating, char *data, long *len);
Packit 40b132
Packit 40b132
static JAR_Digest *
Packit 40b132
jar_digest_section(char *manifest, long length);
Packit 40b132
Packit 40b132
static JAR_Digest *jar_get_mf_digest(JAR *jar, char *path);
Packit 40b132
Packit 40b132
static int
Packit 40b132
jar_parse_digital_signature(char *raw_manifest, JAR_Signer *signer,
Packit 40b132
			    long length, JAR *jar);
Packit 40b132
Packit 40b132
static int
Packit 40b132
jar_add_cert(JAR *jar, JAR_Signer *signer, int type, CERTCertificate *cert);
Packit 40b132
Packit 40b132
static char *jar_basename(const char *path);
Packit 40b132
Packit 40b132
static int
Packit 40b132
jar_signal(int status, JAR *jar, const char *metafile, char *pathname);
Packit 40b132
Packit 40b132
#ifdef DEBUG
Packit 40b132
static int jar_insanity_check(char *data, long length);
Packit 40b132
#endif
Packit 40b132
Packit 40b132
int
Packit 40b132
jar_parse_mf(JAR *jar, char *raw_manifest, long length,
Packit 40b132
	     const char *path, const char *url);
Packit 40b132
Packit 40b132
int
Packit 40b132
jar_parse_sf(JAR *jar, char *raw_manifest, long length,
Packit 40b132
	     const char *path, const char *url);
Packit 40b132
Packit 40b132
int
Packit 40b132
jar_parse_sig(JAR *jar, const char *path, char *raw_manifest,
Packit 40b132
	      long length);
Packit 40b132
Packit 40b132
int
Packit 40b132
jar_parse_any(JAR *jar, int type, JAR_Signer *signer,
Packit 40b132
	      char *raw_manifest, long length, const char *path,
Packit 40b132
	  const char *url);
Packit 40b132
Packit 40b132
static int
Packit 40b132
jar_internal_digest(JAR *jar, const char *path, char *x_name, JAR_Digest *dig);
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  J A R _ p a r s e _ m a n i f e s t
Packit 40b132
 *
Packit 40b132
 *  Pass manifest files to this function. They are
Packit 40b132
 *  decoded and placed into internal representations.
Packit 40b132
 *
Packit 40b132
 *  Accepts both signature and manifest files. Use
Packit 40b132
 *  the same "jar" for both.
Packit 40b132
 *
Packit 40b132
 */
Packit 40b132
int
Packit 40b132
JAR_parse_manifest(JAR *jar, char *raw_manifest, long length,
Packit 40b132
		   const char *path, const char *url)
Packit 40b132
{
Packit 40b132
    int filename_free = 0;
Packit 40b132
Packit 40b132
    /* fill in the path, if supplied. This is the location
Packit 40b132
	 of the jar file on disk, if known */
Packit 40b132
Packit 40b132
    if (jar->filename == NULL && path) {
Packit 40b132
	jar->filename = PORT_Strdup(path);
Packit 40b132
	if (jar->filename == NULL)
Packit 40b132
	    return JAR_ERR_MEMORY;
Packit 40b132
	filename_free = 1;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* fill in the URL, if supplied. This is the place
Packit 40b132
	 from which the jar file was retrieved. */
Packit 40b132
Packit 40b132
    if (jar->url == NULL && url) {
Packit 40b132
	jar->url = PORT_Strdup(url);
Packit 40b132
	if (jar->url == NULL) {
Packit 40b132
	    if (filename_free) {
Packit 40b132
		PORT_Free(jar->filename);
Packit 40b132
	    }
Packit 40b132
	    return JAR_ERR_MEMORY;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* Determine what kind of file this is from the META-INF
Packit 40b132
	 directory. It could be MF, SF, or a binary RSA/DSA file */
Packit 40b132
Packit 40b132
    if (!PORT_Strncasecmp (raw_manifest, "Manifest-Version:", 17)) {
Packit 40b132
	return jar_parse_mf(jar, raw_manifest, length, path, url);
Packit 40b132
    }
Packit 40b132
    else if (!PORT_Strncasecmp (raw_manifest, "Signature-Version:", 18))
Packit 40b132
    {
Packit 40b132
	return jar_parse_sf(jar, raw_manifest, length, path, url);
Packit 40b132
    } else {
Packit 40b132
	/* This is probably a binary signature */
Packit 40b132
	return jar_parse_sig(jar, path, raw_manifest, length);
Packit 40b132
    }
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  j a r _ p a r s e _ s i g
Packit 40b132
 *
Packit 40b132
 *  Pass some manner of RSA or DSA digital signature
Packit 40b132
 *  on, after checking to see if it comes at an appropriate state.
Packit 40b132
 *
Packit 40b132
 */
Packit 40b132
int
Packit 40b132
jar_parse_sig(JAR *jar, const char *path, char *raw_manifest,
Packit 40b132
	      long length)
Packit 40b132
{
Packit 40b132
    JAR_Signer *signer;
Packit 40b132
    int status = JAR_ERR_ORDER;
Packit 40b132
Packit 40b132
    if (length <= 128) {
Packit 40b132
	/* signature is way too small */
Packit 40b132
	return JAR_ERR_SIG;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* make sure that MF and SF have already been processed */
Packit 40b132
Packit 40b132
    if (jar->globalmeta == NULL)
Packit 40b132
	return JAR_ERR_ORDER;
Packit 40b132
Packit 40b132
    /* Determine whether or not this RSA file has
Packit 40b132
	 has an associated SF file */
Packit 40b132
Packit 40b132
    if (path) {
Packit 40b132
	char *owner;
Packit 40b132
	owner = jar_basename(path);
Packit 40b132
Packit 40b132
	if (owner == NULL)
Packit 40b132
	    return JAR_ERR_MEMORY;
Packit 40b132
Packit 40b132
	signer = jar_get_signer(jar, owner);
Packit 40b132
	PORT_Free(owner);
Packit 40b132
    } else
Packit 40b132
	signer = jar_get_signer(jar, "*");
Packit 40b132
Packit 40b132
    if (signer == NULL)
Packit 40b132
	return JAR_ERR_ORDER;
Packit 40b132
Packit 40b132
Packit 40b132
    /* Do not pass a huge pointer to this function,
Packit 40b132
	 since the underlying security code is unaware. We will
Packit 40b132
	 never pass >64k through here. */
Packit 40b132
Packit 40b132
    if (length > 64000) {
Packit 40b132
	/* this digital signature is way too big */
Packit 40b132
	return JAR_ERR_SIG;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* don't expense unneeded calloc overhead on non-win16 */
Packit 40b132
    status = jar_parse_digital_signature(raw_manifest, signer, length, jar);
Packit 40b132
Packit 40b132
    return status;
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  j a r _ p a r s e _ m f
Packit 40b132
 *
Packit 40b132
 *  Parse the META-INF/manifest.mf file, whose
Packit 40b132
 *  information applies to all signers.
Packit 40b132
 *
Packit 40b132
 */
Packit 40b132
int
Packit 40b132
jar_parse_mf(JAR *jar, char *raw_manifest, long length,
Packit 40b132
	     const char *path, const char *url)
Packit 40b132
{
Packit 40b132
    if (jar->globalmeta) {
Packit 40b132
	/* refuse a second manifest file, if passed for some reason */
Packit 40b132
	return JAR_ERR_ORDER;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* remember a digest for the global section */
Packit 40b132
    jar->globalmeta = jar_digest_section(raw_manifest, length);
Packit 40b132
    if (jar->globalmeta == NULL)
Packit 40b132
	return JAR_ERR_MEMORY;
Packit 40b132
    return jar_parse_any(jar, jarTypeMF, NULL, raw_manifest, length,
Packit 40b132
			 path, url);
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  j a r _ p a r s e _ s f
Packit 40b132
 *
Packit 40b132
 *  Parse META-INF/xxx.sf, a digitally signed file
Packit 40b132
 *  pointing to a subset of MF sections.
Packit 40b132
 *
Packit 40b132
 */
Packit 40b132
int
Packit 40b132
jar_parse_sf(JAR *jar, char *raw_manifest, long length,
Packit 40b132
	     const char *path, const char *url)
Packit 40b132
{
Packit 40b132
    JAR_Signer *signer = NULL;
Packit 40b132
    int status = JAR_ERR_MEMORY;
Packit 40b132
Packit 40b132
    if (jar->globalmeta == NULL) {
Packit 40b132
	/* It is a requirement that the MF file be passed before the SF file */
Packit 40b132
	return JAR_ERR_ORDER;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    signer = JAR_new_signer();
Packit 40b132
    if (signer == NULL)
Packit 40b132
	goto loser;
Packit 40b132
Packit 40b132
    if (path) {
Packit 40b132
	signer->owner = jar_basename(path);
Packit 40b132
	if (signer->owner == NULL)
Packit 40b132
	    goto loser;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* check for priors. When someone doctors a jar file
Packit 40b132
	 to contain identical path entries, prevent the second
Packit 40b132
	 one from affecting JAR functions */
Packit 40b132
    if (jar_get_signer(jar, signer->owner)) {
Packit 40b132
	/* someone is trying to spoof us */
Packit 40b132
	status = JAR_ERR_ORDER;
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* remember its digest */
Packit 40b132
    signer->digest = JAR_calculate_digest (raw_manifest, length);
Packit 40b132
    if (signer->digest == NULL)
Packit 40b132
	goto loser;
Packit 40b132
Packit 40b132
    /* Add this signer to the jar */
Packit 40b132
    ADDITEM(jar->signers, jarTypeOwner, signer->owner, signer,
Packit 40b132
	    sizeof (JAR_Signer));
Packit 40b132
Packit 40b132
    return jar_parse_any(jar, jarTypeSF, signer, raw_manifest, length,
Packit 40b132
			 path, url);
Packit 40b132
Packit 40b132
loser:
Packit 40b132
    if (signer)
Packit 40b132
	JAR_destroy_signer (signer);
Packit 40b132
    return status;
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  j a r _ p a r s e _ a n y
Packit 40b132
 *
Packit 40b132
 *  Parse a MF or SF manifest file.
Packit 40b132
 *
Packit 40b132
 */
Packit 40b132
int 
Packit 40b132
jar_parse_any(JAR *jar, int type, JAR_Signer *signer, 
Packit 40b132
              char *raw_manifest, long length, const char *path, 
Packit 40b132
	      const char *url)
Packit 40b132
{
Packit 40b132
    int status;
Packit 40b132
    long raw_len;
Packit 40b132
    JAR_Digest *dig, *mfdig = NULL;
Packit 40b132
    char line [SZ];
Packit 40b132
    char x_name [SZ], x_md5 [SZ], x_sha [SZ];
Packit 40b132
    char *x_info;
Packit 40b132
    char *sf_md5 = NULL, *sf_sha1 = NULL;
Packit 40b132
Packit 40b132
    *x_name = 0;
Packit 40b132
    *x_md5 = 0;
Packit 40b132
    *x_sha = 0;
Packit 40b132
Packit 40b132
    PORT_Assert( length > 0 );
Packit 40b132
    raw_len = length;
Packit 40b132
Packit 40b132
#ifdef DEBUG
Packit 40b132
    if ((status = jar_insanity_check(raw_manifest, raw_len)) < 0)
Packit 40b132
	return status;
Packit 40b132
#endif
Packit 40b132
Packit 40b132
    /* null terminate the first line */
Packit 40b132
    raw_manifest = jar_eat_line(0, PR_TRUE, raw_manifest, &raw_len);
Packit 40b132
Packit 40b132
    /* skip over the preliminary section */
Packit 40b132
    /* This is one section at the top of the file with global metainfo */
Packit 40b132
    while (raw_len > 0) {
Packit 40b132
	JAR_Metainfo *met;
Packit 40b132
Packit 40b132
	raw_manifest = jar_eat_line(1, PR_TRUE, raw_manifest, &raw_len);
Packit 40b132
	if (raw_len <= 0 || !*raw_manifest)
Packit 40b132
	    break;
Packit 40b132
Packit 40b132
	met = PORT_ZNew(JAR_Metainfo);
Packit 40b132
	if (met == NULL)
Packit 40b132
	    return JAR_ERR_MEMORY;
Packit 40b132
Packit 40b132
	/* Parse out the header & info */
Packit 40b132
	if (PORT_Strlen (raw_manifest) >= SZ) {
Packit 40b132
	    /* almost certainly nonsense */
Packit 40b132
	    PORT_Free(met);
Packit 40b132
	    continue;
Packit 40b132
	}
Packit 40b132
Packit 40b132
	PORT_Strcpy (line, raw_manifest);
Packit 40b132
	x_info = line;
Packit 40b132
Packit 40b132
	while (*x_info && *x_info != ' ' && *x_info != '\t' && *x_info != ':')
Packit 40b132
	    x_info++;
Packit 40b132
Packit 40b132
	if (*x_info)
Packit 40b132
	    *x_info++ = 0;
Packit 40b132
Packit 40b132
	while (*x_info == ' ' || *x_info == '\t')
Packit 40b132
	    x_info++;
Packit 40b132
Packit 40b132
	/* metainfo (name, value) pair is now (line, x_info) */
Packit 40b132
	met->header = PORT_Strdup(line);
Packit 40b132
	met->info = PORT_Strdup(x_info);
Packit 40b132
Packit 40b132
	if (type == jarTypeMF) {
Packit 40b132
	    ADDITEM (jar->metainfo, jarTypeMeta,
Packit 40b132
	    /* pathname */ NULL, met, sizeof (JAR_Metainfo));
Packit 40b132
	}
Packit 40b132
Packit 40b132
	/* For SF files, this metadata may be the digests
Packit 40b132
	       of the MF file, still in the "met" structure. */
Packit 40b132
Packit 40b132
	if (type == jarTypeSF) {
Packit 40b132
	    if (!PORT_Strcasecmp(line, "MD5-Digest"))
Packit 40b132
		sf_md5 = (char *) met->info;
Packit 40b132
Packit 40b132
	    if (!PORT_Strcasecmp(line, "SHA1-Digest") || 
Packit 40b132
	        !PORT_Strcasecmp(line, "SHA-Digest"))
Packit 40b132
		sf_sha1 = (char *) met->info;
Packit 40b132
	}
Packit 40b132
Packit 40b132
	if (type != jarTypeMF) {
Packit 40b132
	    PORT_Free(met->header);
Packit 40b132
	    if (type != jarTypeSF) {
Packit 40b132
		PORT_Free(met->info);
Packit 40b132
	    }
Packit 40b132
	    PORT_Free(met);
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (type == jarTypeSF && jar->globalmeta) {
Packit 40b132
	/* this is a SF file which may contain a digest of the manifest.mf's
Packit 40b132
	       global metainfo. */
Packit 40b132
Packit 40b132
	int match = 0;
Packit 40b132
	JAR_Digest *glob = jar->globalmeta;
Packit 40b132
Packit 40b132
	if (sf_md5) {
Packit 40b132
	    unsigned int md5_length;
Packit 40b132
	    unsigned char *md5_digest;
Packit 40b132
Packit 40b132
	    md5_digest = ATOB_AsciiToData (sf_md5, &md5_length);
Packit 40b132
	    PORT_Assert( md5_length == MD5_LENGTH );
Packit 40b132
Packit 40b132
	    if (md5_length != MD5_LENGTH)
Packit 40b132
		return JAR_ERR_CORRUPT;
Packit 40b132
Packit 40b132
	    match = PORT_Memcmp(md5_digest, glob->md5, MD5_LENGTH);
Packit 40b132
	}
Packit 40b132
Packit 40b132
	if (sf_sha1 && match == 0) {
Packit 40b132
	    unsigned int sha1_length;
Packit 40b132
	    unsigned char *sha1_digest;
Packit 40b132
Packit 40b132
	    sha1_digest = ATOB_AsciiToData (sf_sha1, &sha1_length);
Packit 40b132
	    PORT_Assert( sha1_length == SHA1_LENGTH );
Packit 40b132
Packit 40b132
	    if (sha1_length != SHA1_LENGTH)
Packit 40b132
		return JAR_ERR_CORRUPT;
Packit 40b132
Packit 40b132
	    match = PORT_Memcmp(sha1_digest, glob->sha1, SHA1_LENGTH);
Packit 40b132
	}
Packit 40b132
Packit 40b132
	if (match != 0) {
Packit 40b132
	    /* global digest doesn't match, SF file therefore invalid */
Packit 40b132
	    jar->valid = JAR_ERR_METADATA;
Packit 40b132
	    return JAR_ERR_METADATA;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* done with top section of global data */
Packit 40b132
    while (raw_len > 0) {
Packit 40b132
	*x_md5 = 0;
Packit 40b132
	*x_sha = 0;
Packit 40b132
	*x_name = 0;
Packit 40b132
Packit 40b132
	/* If this is a manifest file, attempt to get a digest of the following
Packit 40b132
	   section, without damaging it. This digest will be saved later. */
Packit 40b132
Packit 40b132
	if (type == jarTypeMF) {
Packit 40b132
	    char *sec;
Packit 40b132
	    long sec_len = raw_len;
Packit 40b132
Packit 40b132
	    if (!*raw_manifest || *raw_manifest == '\n') {
Packit 40b132
		/* skip the blank line */
Packit 40b132
		sec = jar_eat_line(1, PR_FALSE, raw_manifest, &sec_len);
Packit 40b132
	    } else
Packit 40b132
		sec = raw_manifest;
Packit 40b132
Packit 40b132
	    if (sec_len > 0 && !PORT_Strncasecmp(sec, "Name:", 5)) {
Packit 40b132
		if (type == jarTypeMF)
Packit 40b132
		    mfdig = jar_digest_section(sec, sec_len);
Packit 40b132
		else
Packit 40b132
		    mfdig = NULL;
Packit 40b132
	    }
Packit 40b132
	}
Packit 40b132
Packit 40b132
Packit 40b132
	while (raw_len > 0) {
Packit 40b132
	    raw_manifest = jar_eat_line(1, PR_TRUE, raw_manifest, &raw_len);
Packit 40b132
	    if (raw_len <= 0 || !*raw_manifest)
Packit 40b132
		break; /* blank line, done with this entry */
Packit 40b132
Packit 40b132
	    if (PORT_Strlen(raw_manifest) >= SZ) {
Packit 40b132
		/* almost certainly nonsense */
Packit 40b132
		continue;
Packit 40b132
	    }
Packit 40b132
Packit 40b132
	    /* Parse out the name/value pair */
Packit 40b132
	    PORT_Strcpy(line, raw_manifest);
Packit 40b132
	    x_info = line;
Packit 40b132
Packit 40b132
	    while (*x_info && *x_info != ' ' && *x_info != '\t' && 
Packit 40b132
	           *x_info != ':')
Packit 40b132
		x_info++;
Packit 40b132
Packit 40b132
	    if (*x_info)
Packit 40b132
		*x_info++ = 0;
Packit 40b132
Packit 40b132
	    while (*x_info == ' ' || *x_info == '\t')
Packit 40b132
		x_info++;
Packit 40b132
Packit 40b132
	    if (!PORT_Strcasecmp(line, "Name"))
Packit 40b132
		PORT_Strcpy(x_name, x_info);
Packit 40b132
	    else if (!PORT_Strcasecmp(line, "MD5-Digest"))
Packit 40b132
		PORT_Strcpy(x_md5, x_info);
Packit 40b132
	    else if (!PORT_Strcasecmp(line, "SHA1-Digest")
Packit 40b132
		  || !PORT_Strcasecmp(line, "SHA-Digest"))
Packit 40b132
		PORT_Strcpy(x_sha, x_info);
Packit 40b132
Packit 40b132
	    /* Algorithm list is meta info we don't care about; keeping it out
Packit 40b132
		 of metadata saves significant space for large jar files */
Packit 40b132
	    else if (!PORT_Strcasecmp(line, "Digest-Algorithms")
Packit 40b132
		  || !PORT_Strcasecmp(line, "Hash-Algorithms"))
Packit 40b132
		continue;
Packit 40b132
Packit 40b132
	    /* Meta info is only collected for the manifest.mf file,
Packit 40b132
		 since the JAR_get_metainfo call does not support identity */
Packit 40b132
	    else if (type == jarTypeMF) {
Packit 40b132
		JAR_Metainfo *met;
Packit 40b132
Packit 40b132
		/* this is meta-data */
Packit 40b132
		met = PORT_ZNew(JAR_Metainfo);
Packit 40b132
		if (met == NULL)
Packit 40b132
		    return JAR_ERR_MEMORY;
Packit 40b132
Packit 40b132
		/* metainfo (name, value) pair is now (line, x_info) */
Packit 40b132
		if ((met->header = PORT_Strdup(line)) == NULL) {
Packit 40b132
		    PORT_Free(met);
Packit 40b132
		    return JAR_ERR_MEMORY;
Packit 40b132
		}
Packit 40b132
Packit 40b132
		if ((met->info = PORT_Strdup(x_info)) == NULL) {
Packit 40b132
		    PORT_Free(met->header);
Packit 40b132
		    PORT_Free(met);
Packit 40b132
		    return JAR_ERR_MEMORY;
Packit 40b132
		}
Packit 40b132
Packit 40b132
		ADDITEM (jar->metainfo, jarTypeMeta,
Packit 40b132
		x_name, met, sizeof (JAR_Metainfo));
Packit 40b132
	    }
Packit 40b132
	}
Packit 40b132
Packit 40b132
	if (!*x_name) {
Packit 40b132
	    /* Whatever that was, it wasn't an entry, because we didn't get a 
Packit 40b132
	       name. We don't really have anything, so don't record this. */
Packit 40b132
	    continue;
Packit 40b132
	}
Packit 40b132
Packit 40b132
	dig = PORT_ZNew(JAR_Digest);
Packit 40b132
	if (dig == NULL)
Packit 40b132
	    return JAR_ERR_MEMORY;
Packit 40b132
Packit 40b132
	if (*x_md5) {
Packit 40b132
	    unsigned int binary_length;
Packit 40b132
	    unsigned char *binary_digest;
Packit 40b132
Packit 40b132
	    binary_digest = ATOB_AsciiToData (x_md5, &binary_length);
Packit 40b132
	    PORT_Assert( binary_length == MD5_LENGTH );
Packit 40b132
	    if (binary_length != MD5_LENGTH) {
Packit 40b132
		PORT_Free(dig);
Packit 40b132
		return JAR_ERR_CORRUPT;
Packit 40b132
	    }
Packit 40b132
	    memcpy (dig->md5, binary_digest, MD5_LENGTH);
Packit 40b132
	    dig->md5_status = jarHashPresent;
Packit 40b132
	}
Packit 40b132
Packit 40b132
	if (*x_sha ) {
Packit 40b132
	    unsigned int binary_length;
Packit 40b132
	    unsigned char *binary_digest;
Packit 40b132
Packit 40b132
	    binary_digest = ATOB_AsciiToData (x_sha, &binary_length);
Packit 40b132
	    PORT_Assert( binary_length == SHA1_LENGTH );
Packit 40b132
	    if (binary_length != SHA1_LENGTH) {
Packit 40b132
		PORT_Free(dig);
Packit 40b132
		return JAR_ERR_CORRUPT;
Packit 40b132
	    }
Packit 40b132
	    memcpy (dig->sha1, binary_digest, SHA1_LENGTH);
Packit 40b132
	    dig->sha1_status = jarHashPresent;
Packit 40b132
	}
Packit 40b132
Packit 40b132
	PORT_Assert( type == jarTypeMF || type == jarTypeSF );
Packit 40b132
	if (type == jarTypeMF) {
Packit 40b132
	    ADDITEM (jar->hashes, jarTypeMF, x_name, dig, sizeof (JAR_Digest));
Packit 40b132
	} else if (type == jarTypeSF) {
Packit 40b132
	    ADDITEM (signer->sf, jarTypeSF, x_name, dig, sizeof (JAR_Digest));
Packit 40b132
	} else {
Packit 40b132
	    PORT_Free(dig);
Packit 40b132
	    return JAR_ERR_ORDER;
Packit 40b132
	}
Packit 40b132
Packit 40b132
	/* we're placing these calculated digests of manifest.mf
Packit 40b132
	   sections in a list where they can subsequently be forgotten */
Packit 40b132
	if (type == jarTypeMF && mfdig) {
Packit 40b132
	    ADDITEM (jar->manifest, jarTypeSect,
Packit 40b132
	    x_name, mfdig, sizeof (JAR_Digest));
Packit 40b132
	    mfdig = NULL;
Packit 40b132
	}
Packit 40b132
Packit 40b132
	/* Retrieve our saved SHA1 digest from saved copy and check digests.
Packit 40b132
	   This is just comparing the digest of the MF section as indicated in
Packit 40b132
	   the SF file with the one we remembered from parsing the MF file */
Packit 40b132
Packit 40b132
	if (type == jarTypeSF) {
Packit 40b132
	    if ((status = jar_internal_digest(jar, path, x_name, dig)) < 0)
Packit 40b132
		return status;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
Packit 40b132
    return 0;
Packit 40b132
}
Packit 40b132
Packit 40b132
static int
Packit 40b132
jar_internal_digest(JAR *jar, const char *path, char *x_name, JAR_Digest *dig)
Packit 40b132
{
Packit 40b132
    int cv;
Packit 40b132
    int status;
Packit 40b132
Packit 40b132
    JAR_Digest *savdig;
Packit 40b132
Packit 40b132
    savdig = jar_get_mf_digest(jar, x_name);
Packit 40b132
    if (savdig == NULL) {
Packit 40b132
	/* no .mf digest for this pathname */
Packit 40b132
	status = jar_signal(JAR_ERR_ENTRY, jar, path, x_name);
Packit 40b132
	if (status < 0)
Packit 40b132
	    return 0; /* was continue; */
Packit 40b132
	return status;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* check for md5 consistency */
Packit 40b132
    if (dig->md5_status) {
Packit 40b132
	cv = PORT_Memcmp(savdig->md5, dig->md5, MD5_LENGTH);
Packit 40b132
	/* md5 hash of .mf file is not what expected */
Packit 40b132
	if (cv) {
Packit 40b132
	    status = jar_signal(JAR_ERR_HASH, jar, path, x_name);
Packit 40b132
Packit 40b132
	    /* bad hash, man */
Packit 40b132
	    dig->md5_status = jarHashBad;
Packit 40b132
	    savdig->md5_status = jarHashBad;
Packit 40b132
Packit 40b132
	    if (status < 0)
Packit 40b132
		return 0; /* was continue; */
Packit 40b132
	    return status;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* check for sha1 consistency */
Packit 40b132
    if (dig->sha1_status) {
Packit 40b132
	cv = PORT_Memcmp(savdig->sha1, dig->sha1, SHA1_LENGTH);
Packit 40b132
	/* sha1 hash of .mf file is not what expected */
Packit 40b132
	if (cv) {
Packit 40b132
	    status = jar_signal(JAR_ERR_HASH, jar, path, x_name);
Packit 40b132
Packit 40b132
	    /* bad hash, man */
Packit 40b132
	    dig->sha1_status = jarHashBad;
Packit 40b132
	    savdig->sha1_status = jarHashBad;
Packit 40b132
Packit 40b132
	    if (status < 0)
Packit 40b132
		return 0; /* was continue; */
Packit 40b132
	    return status;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
    return 0;
Packit 40b132
}
Packit 40b132
Packit 40b132
#ifdef DEBUG
Packit 40b132
/*
Packit 40b132
 *  j a r _ i n s a n i t y _ c h e c k
Packit 40b132
 *
Packit 40b132
 *  Check for illegal characters (or possibly so)
Packit 40b132
 *  in the manifest files, to detect potential memory
Packit 40b132
 *  corruption by our neighbors. Debug only, since
Packit 40b132
 *  not I18N safe.
Packit 40b132
 *
Packit 40b132
 */
Packit 40b132
static int
Packit 40b132
jar_insanity_check(char *data, long length)
Packit 40b132
{
Packit 40b132
    int c;
Packit 40b132
    long off;
Packit 40b132
Packit 40b132
    for (off = 0; off < length; off++) {
Packit 40b132
	c = data [off];
Packit 40b132
	if (c == '\n' || c == '\r' || (c >= ' ' && c <= 128))
Packit 40b132
	    continue;
Packit 40b132
	return JAR_ERR_CORRUPT;
Packit 40b132
    }
Packit 40b132
    return 0;
Packit 40b132
}
Packit 40b132
#endif
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  j a r _ p a r s e _ d i g i t a l _ s i g n a t u r e
Packit 40b132
 *
Packit 40b132
 *  Parse an RSA or DSA (or perhaps other) digital signature.
Packit 40b132
 *  Right now everything is PKCS7.
Packit 40b132
 *
Packit 40b132
 */
Packit 40b132
static int
Packit 40b132
jar_parse_digital_signature(char *raw_manifest, JAR_Signer *signer,
Packit 40b132
			    long length, JAR *jar)
Packit 40b132
{
Packit 40b132
    return jar_validate_pkcs7 (jar, signer, raw_manifest, length);
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  j a r _ a d d _ c e r t
Packit 40b132
 *
Packit 40b132
 *  Add information for the given certificate
Packit 40b132
 *  (or whatever) to the JAR linked list. A pointer
Packit 40b132
 *  is passed for some relevant reference, say
Packit 40b132
 *  for example the original certificate.
Packit 40b132
 *
Packit 40b132
 */
Packit 40b132
static int
Packit 40b132
jar_add_cert(JAR *jar, JAR_Signer *signer, int type, CERTCertificate *cert)
Packit 40b132
{
Packit 40b132
    JAR_Cert *fing;
Packit 40b132
    unsigned char *keyData;
Packit 40b132
Packit 40b132
    if (cert == NULL)
Packit 40b132
	return JAR_ERR_ORDER;
Packit 40b132
Packit 40b132
    fing = PORT_ZNew(JAR_Cert);
Packit 40b132
    if (fing == NULL)
Packit 40b132
	goto loser;
Packit 40b132
Packit 40b132
    fing->cert = CERT_DupCertificate (cert);
Packit 40b132
Packit 40b132
    /* get the certkey */
Packit 40b132
    fing->length = cert->derIssuer.len + 2 + cert->serialNumber.len;
Packit 40b132
    fing->key = keyData = (unsigned char *) PORT_ZAlloc(fing->length);
Packit 40b132
    if (fing->key == NULL)
Packit 40b132
	goto loser;
Packit 40b132
    keyData[0] = ((cert->derIssuer.len) >> 8) & 0xff;
Packit 40b132
    keyData[1] = ((cert->derIssuer.len) & 0xff);
Packit 40b132
    PORT_Memcpy(&keyData[2], cert->derIssuer.data, cert->derIssuer.len);
Packit 40b132
    PORT_Memcpy(&keyData[2+cert->derIssuer.len], cert->serialNumber.data,
Packit 40b132
		cert->serialNumber.len);
Packit 40b132
Packit 40b132
    ADDITEM (signer->certs, type, NULL, fing, sizeof (JAR_Cert));
Packit 40b132
    return 0;
Packit 40b132
Packit 40b132
loser:
Packit 40b132
    if (fing) {
Packit 40b132
	if (fing->cert)
Packit 40b132
	    CERT_DestroyCertificate (fing->cert);
Packit 40b132
	PORT_Free(fing);
Packit 40b132
    }
Packit 40b132
    return JAR_ERR_MEMORY;
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  e a t _ l i n e
Packit 40b132
 *
Packit 40b132
 * Reads and/or modifies input buffer "data" of length "*len".
Packit 40b132
 * This function does zero, one or two of the following tasks:
Packit 40b132
 * 1) if "lines" is non-zero, it reads and discards that many lines from
Packit 40b132
 *    the input.  NUL characters are treated as end-of-line characters,
Packit 40b132
 *    not as end-of-input characters.  The input is NOT NUL terminated.
Packit 40b132
 *    Note: presently, all callers pass either 0 or 1 for lines.
Packit 40b132
 * 2) After skipping the specified number of input lines, if "eating" is 
Packit 40b132
 *    non-zero, it finds the end of the next line of input and replaces
Packit 40b132
 *    the end of line character(s) with a NUL character.
Packit 40b132
 *  This function modifies the input buffer, containing the file, in place. 
Packit 40b132
 *  This function handles PC, Mac, and Unix style text files.
Packit 40b132
 *  On entry, *len contains the maximum number of characters that this
Packit 40b132
 *  function should ever examine, starting with the character in *data.
Packit 40b132
 *  On return, *len is reduced by the number of characters skipped by the
Packit 40b132
 *  first task, if any;
Packit 40b132
 *  If lines is zero and eating is false, this function returns
Packit 40b132
 *  the value in the data argument, but otherwise does nothing.
Packit 40b132
 */
Packit 40b132
static char *
Packit 40b132
jar_eat_line(int lines, int eating, char *data, long *len)
Packit 40b132
{
Packit 40b132
    char *start = data;
Packit 40b132
    long maxLen = *len;
Packit 40b132
Packit 40b132
    if (maxLen <= 0)
Packit 40b132
	return start;
Packit 40b132
Packit 40b132
#define GO_ON ((data - start) < maxLen)
Packit 40b132
Packit 40b132
    /* Eat the requisite number of lines, if any;
Packit 40b132
       prior to terminating the current line with a 0. */
Packit 40b132
    for (/* yip */ ; lines > 0; lines--) {
Packit 40b132
	while (GO_ON && *data && *data != '\r' && *data != '\n')
Packit 40b132
	    data++;
Packit 40b132
Packit 40b132
	/* Eat any leading CR */
Packit 40b132
	if (GO_ON && *data == '\r')
Packit 40b132
	    data++;
Packit 40b132
Packit 40b132
	/* After the CR, ok to eat one LF */
Packit 40b132
	if (GO_ON && *data == '\n')
Packit 40b132
	    data++;
Packit 40b132
Packit 40b132
	/* If there are NULs, this function probably put them there */
Packit 40b132
	while (GO_ON && !*data)
Packit 40b132
	    data++;
Packit 40b132
    }
Packit 40b132
    maxLen -= data - start;           /* we have this many characters left. */
Packit 40b132
    *len  = maxLen;
Packit 40b132
    start = data;                     /* now start again here.            */
Packit 40b132
    if (maxLen > 0 && eating) {
Packit 40b132
	/* Terminate this line with a 0 */
Packit 40b132
	while (GO_ON && *data && *data != '\n' && *data != '\r')
Packit 40b132
	    data++;
Packit 40b132
Packit 40b132
	/* If not past the end, we are allowed to eat one CR */
Packit 40b132
	if (GO_ON && *data == '\r')
Packit 40b132
	    *data++ = 0;
Packit 40b132
Packit 40b132
	/* After the CR (if any), if not past the end, ok to eat one LF */
Packit 40b132
	if (GO_ON && *data == '\n')
Packit 40b132
	    *data++ = 0;
Packit 40b132
    }
Packit 40b132
    return start;
Packit 40b132
}
Packit 40b132
#undef GO_ON
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  j a r _ d i g e s t _ s e c t i o n
Packit 40b132
 *
Packit 40b132
 *  Return the digests of the next section of the manifest file.
Packit 40b132
 *  Does not damage the manifest file, unlike parse_manifest.
Packit 40b132
 *
Packit 40b132
 */
Packit 40b132
static JAR_Digest *
Packit 40b132
jar_digest_section(char *manifest, long length)
Packit 40b132
{
Packit 40b132
    long global_len;
Packit 40b132
    char *global_end;
Packit 40b132
Packit 40b132
    global_end = manifest;
Packit 40b132
    global_len = length;
Packit 40b132
Packit 40b132
    while (global_len > 0) {
Packit 40b132
	global_end = jar_eat_line(1, PR_FALSE, global_end, &global_len);
Packit 40b132
	if (global_len > 0 && (*global_end == 0 || *global_end == '\n'))
Packit 40b132
	    break;
Packit 40b132
    }
Packit 40b132
    return JAR_calculate_digest (manifest, global_end - manifest);
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  J A R _ v e r i f y _ d i g e s t
Packit 40b132
 *
Packit 40b132
 *  Verifies that a precalculated digest matches the
Packit 40b132
 *  expected value in the manifest.
Packit 40b132
 *
Packit 40b132
 */
Packit 40b132
int PR_CALLBACK
Packit 40b132
JAR_verify_digest(JAR *jar, const char *name, JAR_Digest *dig)
Packit 40b132
{
Packit 40b132
    JAR_Item *it;
Packit 40b132
    JAR_Digest *shindig;
Packit 40b132
    ZZLink *link;
Packit 40b132
    ZZList *list = jar->hashes;
Packit 40b132
    int result1 = 0;
Packit 40b132
    int result2 = 0;
Packit 40b132
Packit 40b132
Packit 40b132
    if (jar->valid < 0) {
Packit 40b132
	/* signature not valid */
Packit 40b132
	return JAR_ERR_SIG;
Packit 40b132
    }
Packit 40b132
    if (ZZ_ListEmpty (list)) {
Packit 40b132
	/* empty list */
Packit 40b132
	return JAR_ERR_PNF;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    for (link = ZZ_ListHead (list);
Packit 40b132
	     !ZZ_ListIterDone (list, link);
Packit 40b132
	     link = link->next) {
Packit 40b132
	it = link->thing;
Packit 40b132
	if (it->type == jarTypeMF
Packit 40b132
	    && it->pathname && !PORT_Strcmp(it->pathname, name)) {
Packit 40b132
	    shindig = (JAR_Digest *) it->data;
Packit 40b132
	    if (shindig->md5_status) {
Packit 40b132
		if (shindig->md5_status == jarHashBad)
Packit 40b132
		    return JAR_ERR_HASH;
Packit 40b132
		result1 = memcmp (dig->md5, shindig->md5, MD5_LENGTH);
Packit 40b132
	    }
Packit 40b132
	    if (shindig->sha1_status) {
Packit 40b132
		if (shindig->sha1_status == jarHashBad)
Packit 40b132
		    return JAR_ERR_HASH;
Packit 40b132
		result2 = memcmp (dig->sha1, shindig->sha1, SHA1_LENGTH);
Packit 40b132
	    }
Packit 40b132
	    return (result1 == 0 && result2 == 0) ? 0 : JAR_ERR_HASH;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
    return JAR_ERR_PNF;
Packit 40b132
}
Packit 40b132
Packit 40b132
Packit 40b132
Packit 40b132
Packit 40b132
Packit 40b132
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  J A R _ f e t c h _ c e r t
Packit 40b132
 *
Packit 40b132
 *  Given an opaque identifier of a certificate,
Packit 40b132
 *  return the full certificate.
Packit 40b132
 *
Packit 40b132
 * The new function, which retrieves by key.
Packit 40b132
 *
Packit 40b132
 */
Packit 40b132
CERTCertificate *
Packit 40b132
JAR_fetch_cert(long length, void *key)
Packit 40b132
{
Packit 40b132
    CERTIssuerAndSN issuerSN;
Packit 40b132
    CERTCertificate *cert = NULL;
Packit 40b132
    CERTCertDBHandle *certdb;
Packit 40b132
Packit 40b132
    certdb = JAR_open_database();
Packit 40b132
    if (certdb) {
Packit 40b132
	unsigned char *keyData = (unsigned char *)key;
Packit 40b132
	issuerSN.derIssuer.len = (keyData[0] << 8) + keyData[0];
Packit 40b132
	issuerSN.derIssuer.data = &keyData[2];
Packit 40b132
	issuerSN.serialNumber.len = length - (2 + issuerSN.derIssuer.len);
Packit 40b132
	issuerSN.serialNumber.data = &keyData[2+issuerSN.derIssuer.len];
Packit 40b132
	cert = CERT_FindCertByIssuerAndSN (certdb, &issuerSN);
Packit 40b132
	JAR_close_database (certdb);
Packit 40b132
    }
Packit 40b132
    return cert;
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  j a r _ g e t _ m f _ d i g e s t
Packit 40b132
 *
Packit 40b132
 *  Retrieve a corresponding saved digest over a section
Packit 40b132
 *  of the main manifest file.
Packit 40b132
 *
Packit 40b132
 */
Packit 40b132
static JAR_Digest *
Packit 40b132
jar_get_mf_digest(JAR *jar, char *pathname)
Packit 40b132
{
Packit 40b132
    JAR_Item *it;
Packit 40b132
    JAR_Digest *dig;
Packit 40b132
    ZZLink *link;
Packit 40b132
    ZZList *list = jar->manifest;
Packit 40b132
Packit 40b132
    if (ZZ_ListEmpty (list))
Packit 40b132
	return NULL;
Packit 40b132
Packit 40b132
    for (link = ZZ_ListHead (list);
Packit 40b132
	 !ZZ_ListIterDone (list, link);
Packit 40b132
	 link = link->next) {
Packit 40b132
	it = link->thing;
Packit 40b132
	if (it->type == jarTypeSect
Packit 40b132
	    && it->pathname && !PORT_Strcmp(it->pathname, pathname)) {
Packit 40b132
	    dig = (JAR_Digest *) it->data;
Packit 40b132
	    return dig;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
    return NULL;
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  j a r _ b a s e n a m e
Packit 40b132
 *
Packit 40b132
 *  Return the basename -- leading components of path stripped off,
Packit 40b132
 *  extension ripped off -- of a path.
Packit 40b132
 *
Packit 40b132
 */
Packit 40b132
static char *
Packit 40b132
jar_basename(const char *path)
Packit 40b132
{
Packit 40b132
    char *pith, *e, *basename, *ext;
Packit 40b132
Packit 40b132
    if (path == NULL)
Packit 40b132
	return PORT_Strdup("");
Packit 40b132
Packit 40b132
    pith = PORT_Strdup(path);
Packit 40b132
    basename = pith;
Packit 40b132
    while (1) {
Packit 40b132
	for (e = basename; *e && *e != '/' && *e != '\\'; e++)
Packit 40b132
	    /* yip */ ;
Packit 40b132
	if (*e)
Packit 40b132
	    basename = ++e;
Packit 40b132
	else
Packit 40b132
	    break;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if ((ext = PORT_Strrchr(basename, '.')) != NULL)
Packit 40b132
	*ext = 0;
Packit 40b132
Packit 40b132
    /* We already have the space allocated */
Packit 40b132
    PORT_Strcpy(pith, basename);
Packit 40b132
    return pith;
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  + + + + + + + + + + + + + + +
Packit 40b132
 *
Packit 40b132
 *  CRYPTO ROUTINES FOR JAR
Packit 40b132
 *
Packit 40b132
 *  The following functions are the cryptographic
Packit 40b132
 *  interface to PKCS7 for Jarnatures.
Packit 40b132
 *
Packit 40b132
 *  + + + + + + + + + + + + + + +
Packit 40b132
 *
Packit 40b132
 */
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  j a r _ c a t c h _ b y t e s
Packit 40b132
 *
Packit 40b132
 *  In the event signatures contain enveloped data, it will show up here.
Packit 40b132
 *  But note that the lib/pkcs7 routines aren't ready for it.
Packit 40b132
 *
Packit 40b132
 */
Packit 40b132
static void
Packit 40b132
jar_catch_bytes(void *arg, const char *buf, unsigned long len)
Packit 40b132
{
Packit 40b132
    /* Actually this should never be called, since there is
Packit 40b132
	 presumably no data in the signature itself. */
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  j a r _ v a l i d a t e _ p k c s 7
Packit 40b132
 *
Packit 40b132
 *  Validate (and decode, if necessary) a binary pkcs7
Packit 40b132
 *  signature in DER format.
Packit 40b132
 *
Packit 40b132
 */
Packit 40b132
static int 
Packit 40b132
jar_validate_pkcs7(JAR *jar, JAR_Signer *signer, char *data, long length)
Packit 40b132
{
Packit 40b132
Packit 40b132
    SEC_PKCS7ContentInfo *cinfo = NULL;
Packit 40b132
    SEC_PKCS7DecoderContext *dcx;
Packit 40b132
    PRBool goodSig;
Packit 40b132
    int status = 0;
Packit 40b132
    SECItem detdig;
Packit 40b132
Packit 40b132
    PORT_Assert( jar != NULL && signer != NULL );
Packit 40b132
Packit 40b132
    if (jar == NULL || signer == NULL)
Packit 40b132
	return JAR_ERR_ORDER;
Packit 40b132
Packit 40b132
    signer->valid = JAR_ERR_SIG;
Packit 40b132
Packit 40b132
    /* We need a context if we can get one */
Packit 40b132
    dcx = SEC_PKCS7DecoderStart(jar_catch_bytes, NULL /*cb_arg*/,
Packit 40b132
				NULL /*getpassword*/, jar->mw,
Packit 40b132
				NULL, NULL, NULL);
Packit 40b132
    if (dcx == NULL) {
Packit 40b132
	/* strange pkcs7 failure */
Packit 40b132
	return JAR_ERR_PK7;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    SEC_PKCS7DecoderUpdate (dcx, data, length);
Packit 40b132
    cinfo = SEC_PKCS7DecoderFinish (dcx);
Packit 40b132
    if (cinfo == NULL) {
Packit 40b132
	/* strange pkcs7 failure */
Packit 40b132
	return JAR_ERR_PK7;
Packit 40b132
    }
Packit 40b132
    if (SEC_PKCS7ContentIsEncrypted (cinfo)) {
Packit 40b132
	/* content was encrypted, fail */
Packit 40b132
	return JAR_ERR_PK7;
Packit 40b132
    }
Packit 40b132
    if (SEC_PKCS7ContentIsSigned (cinfo) == PR_FALSE) {
Packit 40b132
	/* content was not signed, fail */
Packit 40b132
	return JAR_ERR_PK7;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    PORT_SetError(0);
Packit 40b132
Packit 40b132
    /* use SHA1 only */
Packit 40b132
    detdig.len = SHA1_LENGTH;
Packit 40b132
    detdig.data = signer->digest->sha1;
Packit 40b132
    goodSig = SEC_PKCS7VerifyDetachedSignature(cinfo,
Packit 40b132
					       certUsageObjectSigner,
Packit 40b132
					       &detdig, HASH_AlgSHA1,
Packit 40b132
					       PR_FALSE);
Packit 40b132
    jar_gather_signers(jar, signer, cinfo);
Packit 40b132
    if (goodSig == PR_TRUE) {
Packit 40b132
	/* signature is valid */
Packit 40b132
	signer->valid = 0;
Packit 40b132
    } else {
Packit 40b132
	status = PORT_GetError();
Packit 40b132
	PORT_Assert( status < 0 );
Packit 40b132
	if (status >= 0)
Packit 40b132
	    status = JAR_ERR_SIG;
Packit 40b132
	jar->valid = status;
Packit 40b132
	signer->valid = status;
Packit 40b132
    }
Packit 40b132
    jar->pkcs7 = PR_TRUE;
Packit 40b132
    signer->pkcs7 = PR_TRUE;
Packit 40b132
    SEC_PKCS7DestroyContentInfo(cinfo);
Packit 40b132
    return status;
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  j a r _ g a t h e r _ s i g n e r s
Packit 40b132
 *
Packit 40b132
 *  Add the single signer of this signature to the
Packit 40b132
 *  certificate linked list.
Packit 40b132
 *
Packit 40b132
 */
Packit 40b132
static int
Packit 40b132
jar_gather_signers(JAR *jar, JAR_Signer *signer, SEC_PKCS7ContentInfo *cinfo)
Packit 40b132
{
Packit 40b132
    int result;
Packit 40b132
    CERTCertificate *cert;
Packit 40b132
    CERTCertDBHandle *certdb;
Packit 40b132
    SEC_PKCS7SignedData *sdp = cinfo->content.signedData;
Packit 40b132
    SEC_PKCS7SignerInfo **pksigners, *pksigner;
Packit 40b132
Packit 40b132
    if (sdp == NULL)
Packit 40b132
	return JAR_ERR_PK7;
Packit 40b132
Packit 40b132
    pksigners = sdp->signerInfos;
Packit 40b132
    /* permit exactly one signer */
Packit 40b132
    if (pksigners == NULL || pksigners [0] == NULL || pksigners [1] != NULL)
Packit 40b132
	return JAR_ERR_PK7;
Packit 40b132
Packit 40b132
    pksigner = *pksigners;
Packit 40b132
    cert = pksigner->cert;
Packit 40b132
Packit 40b132
    if (cert == NULL)
Packit 40b132
	return JAR_ERR_PK7;
Packit 40b132
Packit 40b132
    certdb = JAR_open_database();
Packit 40b132
    if (certdb == NULL)
Packit 40b132
	return JAR_ERR_GENERAL;
Packit 40b132
Packit 40b132
    result = jar_add_cert(jar, signer, jarTypeSign, cert);
Packit 40b132
    JAR_close_database (certdb);
Packit 40b132
    return result;
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  j a r _ o p e n _ d a t a b a s e
Packit 40b132
 *
Packit 40b132
 *  Open the certificate database,
Packit 40b132
 *  for use by JAR functions.
Packit 40b132
 *
Packit 40b132
 */
Packit 40b132
CERTCertDBHandle *
Packit 40b132
JAR_open_database(void)
Packit 40b132
{
Packit 40b132
    return CERT_GetDefaultCertDB();
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  j a r _ c l o s e _ d a t a b a s e
Packit 40b132
 *
Packit 40b132
 *  Close the certificate database.
Packit 40b132
 *  For use by JAR functions.
Packit 40b132
 *
Packit 40b132
 */
Packit 40b132
int 
Packit 40b132
JAR_close_database(CERTCertDBHandle *certdb)
Packit 40b132
{
Packit 40b132
    return 0;
Packit 40b132
}
Packit 40b132
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  j a r _ s i g n a l
Packit 40b132
 *
Packit 40b132
 *  Nonfatal errors come here to callback Java.
Packit 40b132
 *
Packit 40b132
 */
Packit 40b132
static int
Packit 40b132
jar_signal(int status, JAR *jar, const char *metafile, char *pathname)
Packit 40b132
{
Packit 40b132
    char *errstring = JAR_get_error (status);
Packit 40b132
    if (jar->signal) {
Packit 40b132
	(*jar->signal) (status, jar, metafile, pathname, errstring);
Packit 40b132
	return 0;
Packit 40b132
    }
Packit 40b132
    return status;
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 *  j a r _ a p p e n d
Packit 40b132
 *
Packit 40b132
 *  Tack on an element to one of a JAR's linked
Packit 40b132
 *  lists, with rudimentary error handling.
Packit 40b132
 *
Packit 40b132
 */
Packit 40b132
int
Packit 40b132
jar_append(ZZList *list, int type, char *pathname, void *data, size_t size)
Packit 40b132
{
Packit 40b132
    JAR_Item *it = PORT_ZNew(JAR_Item);
Packit 40b132
    ZZLink *entity;
Packit 40b132
Packit 40b132
    if (it == NULL)
Packit 40b132
	goto loser;
Packit 40b132
Packit 40b132
    if (pathname) {
Packit 40b132
	it->pathname = PORT_Strdup(pathname);
Packit 40b132
	if (it->pathname == NULL)
Packit 40b132
	    goto loser;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    it->type = (jarType)type;
Packit 40b132
    it->data = (unsigned char *) data;
Packit 40b132
    it->size = size;
Packit 40b132
    entity = ZZ_NewLink (it);
Packit 40b132
    if (entity) {
Packit 40b132
	ZZ_AppendLink (list, entity);
Packit 40b132
	return 0;
Packit 40b132
    }
Packit 40b132
Packit 40b132
loser:
Packit 40b132
    if (it) {
Packit 40b132
	if (it->pathname) 
Packit 40b132
	    PORT_Free(it->pathname);
Packit 40b132
	PORT_Free(it);
Packit 40b132
    }
Packit 40b132
    return JAR_ERR_MEMORY;
Packit 40b132
}