diff options
Diffstat (limited to 'security/nss/lib/jar/jar.c')
-rw-r--r-- | security/nss/lib/jar/jar.c | 687 |
1 files changed, 687 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..00a55463e --- /dev/null +++ b/security/nss/lib/jar/jar.c @@ -0,0 +1,687 @@ +/* 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); + if (jar->globalmeta) + PORT_Free(jar->globalmeta); + + /* 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; +} |