RetroZilla/security/nss/lib/jar/jar.c
2018-05-19 22:01:21 +08:00

685 lines
14 KiB
C

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* JAR.C
*
* Jarnature.
* Routines common to signing and validating.
*
*/
#include "jar.h"
#include "jarint.h"
#include "portreg.h"
static void
jar_destroy_list (ZZList *list);
static int
jar_find_first_cert(JAR_Signer *signer, int type, JAR_Item **it);
/*
* J A R _ n e w
*
* Create a new instantiation of a manifest representation.
* Use this as a token to any calls to this API.
*
*/
JAR *
JAR_new(void)
{
JAR *jar;
if ((jar = (JAR*)PORT_ZAlloc (sizeof (JAR))) == NULL)
goto loser;
if ((jar->manifest = ZZ_NewList()) == NULL)
goto loser;
if ((jar->hashes = ZZ_NewList()) == NULL)
goto loser;
if ((jar->phy = ZZ_NewList()) == NULL)
goto loser;
if ((jar->metainfo = ZZ_NewList()) == NULL)
goto loser;
if ((jar->signers = ZZ_NewList()) == NULL)
goto loser;
return jar;
loser:
if (jar) {
if (jar->manifest)
ZZ_DestroyList (jar->manifest);
if (jar->hashes)
ZZ_DestroyList (jar->hashes);
if (jar->phy)
ZZ_DestroyList (jar->phy);
if (jar->metainfo)
ZZ_DestroyList (jar->metainfo);
if (jar->signers)
ZZ_DestroyList (jar->signers);
PORT_Free (jar);
}
return NULL;
}
/*
* J A R _ d e s t r o y
*/
void PR_CALLBACK
JAR_destroy(JAR *jar)
{
PORT_Assert( jar != NULL );
if (jar == NULL)
return;
if (jar->fp)
JAR_FCLOSE ((PRFileDesc*)jar->fp);
if (jar->url)
PORT_Free (jar->url);
if (jar->filename)
PORT_Free (jar->filename);
/* Free the linked list elements */
jar_destroy_list (jar->manifest);
ZZ_DestroyList (jar->manifest);
jar_destroy_list (jar->hashes);
ZZ_DestroyList (jar->hashes);
jar_destroy_list (jar->phy);
ZZ_DestroyList (jar->phy);
jar_destroy_list (jar->metainfo);
ZZ_DestroyList (jar->metainfo);
jar_destroy_list (jar->signers);
ZZ_DestroyList (jar->signers);
PORT_Free (jar);
}
static void
jar_destroy_list(ZZList *list)
{
ZZLink *link, *oldlink;
JAR_Item *it;
JAR_Physical *phy;
JAR_Digest *dig;
JAR_Cert *fing;
JAR_Metainfo *met;
JAR_Signer *signer;
if (list && !ZZ_ListEmpty (list)) {
link = ZZ_ListHead (list);
while (!ZZ_ListIterDone (list, link)) {
it = link->thing;
if (!it)
goto next;
if (it->pathname)
PORT_Free (it->pathname);
switch (it->type) {
case jarTypeMeta:
met = (JAR_Metainfo *) it->data;
if (met) {
if (met->header)
PORT_Free (met->header);
if (met->info)
PORT_Free (met->info);
PORT_Free (met);
}
break;
case jarTypePhy:
phy = (JAR_Physical *) it->data;
if (phy)
PORT_Free (phy);
break;
case jarTypeSign:
fing = (JAR_Cert *) it->data;
if (fing) {
if (fing->cert)
CERT_DestroyCertificate (fing->cert);
if (fing->key)
PORT_Free (fing->key);
PORT_Free (fing);
}
break;
case jarTypeSect:
case jarTypeMF:
case jarTypeSF:
dig = (JAR_Digest *) it->data;
if (dig) {
PORT_Free (dig);
}
break;
case jarTypeOwner:
signer = (JAR_Signer *) it->data;
if (signer)
JAR_destroy_signer (signer);
break;
default:
/* PORT_Assert( 1 != 2 ); */
break;
}
PORT_Free (it);
next:
oldlink = link;
link = link->next;
ZZ_DestroyLink (oldlink);
}
}
}
/*
* J A R _ g e t _ m e t a i n f o
*
* Retrieve meta information from the manifest file.
* It doesn't matter whether it's from .MF or .SF, does it?
*
*/
int
JAR_get_metainfo(JAR *jar, char *name, char *header, void **info,
unsigned long *length)
{
JAR_Item *it;
ZZLink *link;
ZZList *list;
PORT_Assert( jar != NULL && header != NULL );
if (jar == NULL || header == NULL)
return JAR_ERR_PNF;
list = jar->metainfo;
if (ZZ_ListEmpty (list))
return JAR_ERR_PNF;
for (link = ZZ_ListHead (list);
!ZZ_ListIterDone (list, link);
link = link->next) {
it = link->thing;
if (it->type == jarTypeMeta) {
JAR_Metainfo *met;
if ((name && !it->pathname) || (!name && it->pathname))
continue;
if (name && it->pathname && strcmp (it->pathname, name))
continue;
met = (JAR_Metainfo *) it->data;
if (!PORT_Strcasecmp (met->header, header)) {
*info = PORT_Strdup (met->info);
*length = PORT_Strlen (met->info);
return 0;
}
}
}
return JAR_ERR_PNF;
}
/*
* J A R _ f i n d
*
* Establish the search pattern for use
* by JAR_find_next, to traverse the filenames
* or certificates in the JAR structure.
*
* See jar.h for a description on how to use.
*
*/
JAR_Context *
JAR_find (JAR *jar, char *pattern, jarType type)
{
JAR_Context *ctx;
PORT_Assert( jar != NULL );
if (!jar)
return NULL;
ctx = (JAR_Context *) PORT_ZAlloc (sizeof (JAR_Context));
if (ctx == NULL)
return NULL;
ctx->jar = jar;
if (pattern) {
if ((ctx->pattern = PORT_Strdup (pattern)) == NULL) {
PORT_Free (ctx);
return NULL;
}
}
ctx->finding = type;
switch (type) {
case jarTypeMF:
ctx->next = ZZ_ListHead (jar->hashes);
break;
case jarTypeSF:
case jarTypeSign:
ctx->next = NULL;
ctx->nextsign = ZZ_ListHead (jar->signers);
break;
case jarTypeSect:
ctx->next = ZZ_ListHead (jar->manifest);
break;
case jarTypePhy:
ctx->next = ZZ_ListHead (jar->phy);
break;
case jarTypeOwner:
if (jar->signers)
ctx->next = ZZ_ListHead (jar->signers);
else
ctx->next = NULL;
break;
case jarTypeMeta:
ctx->next = ZZ_ListHead (jar->metainfo);
break;
default:
PORT_Assert( 1 != 2);
break;
}
return ctx;
}
/*
* J A R _ f i n d _ e n d
*
* Destroy the find iterator context.
*
*/
void
JAR_find_end (JAR_Context *ctx)
{
PORT_Assert( ctx != NULL );
if (ctx) {
if (ctx->pattern)
PORT_Free (ctx->pattern);
PORT_Free (ctx);
}
}
/*
* J A R _ f i n d _ n e x t
*
* Return the next item of the given type
* from one of the JAR linked lists.
*
*/
int JAR_find_next (JAR_Context *ctx, JAR_Item **it)
{
JAR *jar;
ZZList *list = NULL;
int finding;
JAR_Signer *signer = NULL;
PORT_Assert( ctx != NULL );
PORT_Assert( ctx->jar != NULL );
jar = ctx->jar;
/* Internally, convert jarTypeSign to jarTypeSF, and return
the actual attached certificate later */
finding = (ctx->finding == jarTypeSign) ? jarTypeSF : ctx->finding;
if (ctx->nextsign) {
if (ZZ_ListIterDone (jar->signers, ctx->nextsign)) {
*it = NULL;
return -1;
}
PORT_Assert (ctx->nextsign->thing != NULL);
signer = (JAR_Signer*)ctx->nextsign->thing->data;
}
/* Find out which linked list to traverse. Then if
necessary, advance to the next linked list. */
while (1) {
switch (finding) {
case jarTypeSign: /* not any more */
PORT_Assert( finding != jarTypeSign );
list = signer->certs;
break;
case jarTypeSect:
list = jar->manifest;
break;
case jarTypePhy:
list = jar->phy;
break;
case jarTypeSF: /* signer, not jar */
PORT_Assert( signer != NULL );
list = signer ? signer->sf : NULL;
break;
case jarTypeMF:
list = jar->hashes;
break;
case jarTypeOwner:
list = jar->signers;
break;
case jarTypeMeta:
list = jar->metainfo;
break;
default:
PORT_Assert( 1 != 2 );
list = NULL;
break;
}
if (list == NULL) {
*it = NULL;
return -1;
}
/* When looping over lists of lists, advance to the next signer.
This is done when multiple signers are possible. */
if (ZZ_ListIterDone (list, ctx->next)) {
if (ctx->nextsign && jar->signers) {
ctx->nextsign = ctx->nextsign->next;
if (!ZZ_ListIterDone (jar->signers, ctx->nextsign)) {
PORT_Assert (ctx->nextsign->thing != NULL);
signer = (JAR_Signer*)ctx->nextsign->thing->data;
PORT_Assert( signer != NULL );
ctx->next = NULL;
continue;
}
}
*it = NULL;
return -1;
}
/* if the signer changed, still need to fill in the "next" link */
if (ctx->nextsign && ctx->next == NULL) {
switch (finding) {
case jarTypeSF:
ctx->next = ZZ_ListHead (signer->sf);
break;
case jarTypeSign:
ctx->next = ZZ_ListHead (signer->certs);
break;
}
}
PORT_Assert( ctx->next != NULL );
if (ctx->next == NULL) {
*it = NULL;
return -1;
}
while (!ZZ_ListIterDone (list, ctx->next)) {
*it = ctx->next->thing;
ctx->next = ctx->next->next;
if (!*it || (*it)->type != finding)
continue;
if (ctx->pattern && *ctx->pattern) {
if (PORT_RegExpSearch ((*it)->pathname, ctx->pattern))
continue;
}
/* We have a valid match. If this is a jarTypeSign
return the certificate instead.. */
if (ctx->finding == jarTypeSign) {
JAR_Item *itt;
/* just the first one for now */
if (jar_find_first_cert (signer, jarTypeSign, &itt) >= 0) {
*it = itt;
return 0;
}
continue;
}
return 0;
}
} /* end while */
}
static int
jar_find_first_cert (JAR_Signer *signer, int type, JAR_Item **it)
{
ZZLink *link;
ZZList *list = signer->certs;
int status = JAR_ERR_PNF;
*it = NULL;
if (ZZ_ListEmpty (list)) {
/* empty list */
return JAR_ERR_PNF;
}
for (link = ZZ_ListHead (list);
!ZZ_ListIterDone (list, link);
link = link->next) {
if (link->thing->type == type) {
*it = link->thing;
status = 0;
break;
}
}
return status;
}
JAR_Signer *
JAR_new_signer (void)
{
JAR_Signer *signer = (JAR_Signer *) PORT_ZAlloc (sizeof (JAR_Signer));
if (signer == NULL)
goto loser;
/* certs */
signer->certs = ZZ_NewList();
if (signer->certs == NULL)
goto loser;
/* sf */
signer->sf = ZZ_NewList();
if (signer->sf == NULL)
goto loser;
return signer;
loser:
if (signer) {
if (signer->certs)
ZZ_DestroyList (signer->certs);
if (signer->sf)
ZZ_DestroyList (signer->sf);
PORT_Free (signer);
}
return NULL;
}
void
JAR_destroy_signer(JAR_Signer *signer)
{
if (signer) {
if (signer->owner)
PORT_Free (signer->owner);
if (signer->digest)
PORT_Free (signer->digest);
jar_destroy_list (signer->sf);
ZZ_DestroyList (signer->sf);
jar_destroy_list (signer->certs);
ZZ_DestroyList (signer->certs);
PORT_Free (signer);
}
}
JAR_Signer *
jar_get_signer(JAR *jar, char *basename)
{
JAR_Item *it;
JAR_Context *ctx = JAR_find (jar, NULL, jarTypeOwner);
JAR_Signer *candidate;
JAR_Signer *signer = NULL;
if (ctx == NULL)
return NULL;
while (JAR_find_next (ctx, &it) >= 0) {
candidate = (JAR_Signer *) it->data;
if (*basename == '*' || !PORT_Strcmp (candidate->owner, basename)) {
signer = candidate;
break;
}
}
JAR_find_end (ctx);
return signer;
}
/*
* J A R _ g e t _ f i l e n a m e
*
* Returns the filename associated with
* a JAR structure.
*
*/
char *
JAR_get_filename(JAR *jar)
{
return jar->filename;
}
/*
* J A R _ g e t _ u r l
*
* Returns the URL associated with
* a JAR structure. Nobody really uses this now.
*
*/
char *
JAR_get_url(JAR *jar)
{
return jar->url;
}
/*
* J A R _ s e t _ c a l l b a c k
*
* Register some manner of callback function for this jar.
*
*/
int
JAR_set_callback(int type, JAR *jar, jar_settable_callback_fn *fn)
{
if (type == JAR_CB_SIGNAL) {
jar->signal = fn;
return 0;
}
return -1;
}
/*
* Callbacks
*
*/
/* To return an error string */
char *(*jar_fn_GetString) (int) = NULL;
/* To return an MWContext for Java */
void *(*jar_fn_FindSomeContext) (void) = NULL;
/* To fabricate an MWContext for FE_GetPassword */
void *(*jar_fn_GetInitContext) (void) = NULL;
void
JAR_init_callbacks(char *(*string_cb)(int),
void *(*find_cx)(void),
void *(*init_cx)(void))
{
jar_fn_GetString = string_cb;
jar_fn_FindSomeContext = find_cx;
jar_fn_GetInitContext = init_cx;
}
/*
* J A R _ g e t _ e r r o r
*
* This is provided to map internal JAR errors to strings for
* the Java console. Also, a DLL may call this function if it does
* not have access to the XP_GetString function.
*
* These strings aren't UI, since they are Java console only.
*
*/
char *
JAR_get_error(int status)
{
char *errstring = NULL;
switch (status) {
case JAR_ERR_GENERAL:
errstring = "General JAR file error";
break;
case JAR_ERR_FNF:
errstring = "JAR file not found";
break;
case JAR_ERR_CORRUPT:
errstring = "Corrupt JAR file";
break;
case JAR_ERR_MEMORY:
errstring = "Out of memory";
break;
case JAR_ERR_DISK:
errstring = "Disk error (perhaps out of space)";
break;
case JAR_ERR_ORDER:
errstring = "Inconsistent files in META-INF directory";
break;
case JAR_ERR_SIG:
errstring = "Invalid digital signature file";
break;
case JAR_ERR_METADATA:
errstring = "JAR metadata failed verification";
break;
case JAR_ERR_ENTRY:
errstring = "No Manifest entry for this JAR entry";
break;
case JAR_ERR_HASH:
errstring = "Invalid Hash of this JAR entry";
break;
case JAR_ERR_PK7:
errstring = "Strange PKCS7 or RSA failure";
break;
case JAR_ERR_PNF:
errstring = "Path not found inside JAR file";
break;
default:
if (jar_fn_GetString) {
errstring = jar_fn_GetString (status);
} else {
/* this is not a normal situation, and would only be
called in cases of improper initialization */
char *err = (char*)PORT_Alloc (40);
if (err)
PR_snprintf (err, 39, "Error %d\n", status); /* leak me! */
else
err = "Error! Bad! Out of memory!";
return err;
}
break;
}
return errstring;
}