diff options
author | Lorry <lorry@roadtrain.codethink.co.uk> | 2012-07-18 20:31:20 +0100 |
---|---|---|
committer | Lorry <lorry@roadtrain.codethink.co.uk> | 2012-07-18 20:31:20 +0100 |
commit | e43ad1f4ce7f1504e6f01fc8a90d5c0398013383 (patch) | |
tree | 03504d9d81336081b899c9f34cc0f66801caf67c /mozilla/security/nss/lib/jar/jar.c | |
download | nss-e43ad1f4ce7f1504e6f01fc8a90d5c0398013383.tar.gz |
Tarball conversion
Diffstat (limited to 'mozilla/security/nss/lib/jar/jar.c')
-rw-r--r-- | mozilla/security/nss/lib/jar/jar.c | 716 |
1 files changed, 716 insertions, 0 deletions
diff --git a/mozilla/security/nss/lib/jar/jar.c b/mozilla/security/nss/lib/jar/jar.c new file mode 100644 index 0000000..cabd948 --- /dev/null +++ b/mozilla/security/nss/lib/jar/jar.c @@ -0,0 +1,716 @@ +/* ***** 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" +#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; +} |