diff options
Diffstat (limited to 'security/nss/lib/jar/jar.c')
-rw-r--r-- | security/nss/lib/jar/jar.c | 831 |
1 files changed, 831 insertions, 0 deletions
diff --git a/security/nss/lib/jar/jar.c b/security/nss/lib/jar/jar.c new file mode 100644 index 000000000..cfd46b195 --- /dev/null +++ b/security/nss/lib/jar/jar.c @@ -0,0 +1,831 @@ +/* + * 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 Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * 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; + } |