/* 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; }