gecko-dev/security/nss/lib/jar/jar.c
2004-04-25 15:03:26 +00:00

835 lines
17 KiB
C

/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Netscape security libraries.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1994-2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
* JAR.C
*
* Jarnature.
* Routines common to signing and validating.
*
*/
#include "jar.h"
#include "jarint.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
*
* Godzilla.
*
*/
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;
JAR_Metainfo *met;
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)
{
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->sf;
break;
case jarTypeMF: list = jar->hashes;
break;
case jarTypeOwner: list = jar->signers;
break;
case jarTypeMeta: list = jar->metainfo;
break;
default: PORT_Assert( 1 != 2 );
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 );
while (!ZZ_ListIterDone (list, ctx->next))
{
*it = ctx->next->thing;
ctx->next = ctx->next->next;
if (!it || !*it || (*it)->type != finding)
continue;
if (ctx->pattern && *ctx->pattern)
{
if (PORT_Strcmp ((*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;
int status = JAR_ERR_PNF;
list = signer->certs;
*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;
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_Signer *candidate;
JAR_Signer *signer = NULL;
ctx = JAR_find (jar, NULL, jarTypeOwner);
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,
int (*fn) (int status, JAR *jar,
const char *metafile, char *pathname, char *errortext))
{
if (type == JAR_CB_SIGNAL)
{
jar->signal = fn;
return 0;
}
else
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;
err = (char*)PORT_Alloc (40);
if (err)
PR_snprintf (err, 39, "Error %d\n", status);
else
err = "Error! Bad! Out of memory!";
return err;
}
break;
}
return errstring;
}