mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-14 03:30:17 +01:00
685 lines
14 KiB
C
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;
|
|
}
|