diff options
Diffstat (limited to 'security/nss/lib/jar')
-rw-r--r-- | security/nss/lib/jar/Makefile | 23 | ||||
-rw-r--r-- | security/nss/lib/jar/exports.gyp | 27 | ||||
-rw-r--r-- | security/nss/lib/jar/jar-ds.c | 36 | ||||
-rw-r--r-- | security/nss/lib/jar/jar-ds.h | 77 | ||||
-rw-r--r-- | security/nss/lib/jar/jar.c | 694 | ||||
-rw-r--r-- | security/nss/lib/jar/jar.gyp | 33 | ||||
-rw-r--r-- | security/nss/lib/jar/jar.h | 372 | ||||
-rw-r--r-- | security/nss/lib/jar/jarfile.c | 976 | ||||
-rw-r--r-- | security/nss/lib/jar/jarfile.h | 76 | ||||
-rw-r--r-- | security/nss/lib/jar/jarint.c | 52 | ||||
-rw-r--r-- | security/nss/lib/jar/jarint.h | 54 | ||||
-rw-r--r-- | security/nss/lib/jar/jarnav.c | 63 | ||||
-rw-r--r-- | security/nss/lib/jar/jarsign.c | 250 | ||||
-rw-r--r-- | security/nss/lib/jar/jarver.c | 1167 | ||||
-rw-r--r-- | security/nss/lib/jar/jzconf.h | 189 | ||||
-rw-r--r-- | security/nss/lib/jar/jzlib.h | 916 | ||||
-rw-r--r-- | security/nss/lib/jar/manifest.mn | 26 |
17 files changed, 5031 insertions, 0 deletions
diff --git a/security/nss/lib/jar/Makefile b/security/nss/lib/jar/Makefile new file mode 100644 index 0000000000..650c2ae861 --- /dev/null +++ b/security/nss/lib/jar/Makefile @@ -0,0 +1,23 @@ +#! gmake +# +# 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/. + +include manifest.mn + +include $(CORE_DEPTH)/coreconf/config.mk + +# NSS_X86 means the target is a 32-bits x86 CPU architecture +# NSS_X64 means the target is a 64-bits x64 CPU architecture +# NSS_X86_OR_X64 means the target is either x86 or x64 +ifeq (,$(filter-out i386 x386 x86 x86_64,$(CPU_ARCH))) + DEFINES += -DNSS_X86_OR_X64 +ifdef USE_64 + DEFINES += -DNSS_X64 +else + DEFINES += -DNSS_X86 +endif +endif + +include $(CORE_DEPTH)/coreconf/rules.mk diff --git a/security/nss/lib/jar/exports.gyp b/security/nss/lib/jar/exports.gyp new file mode 100644 index 0000000000..94ee144e86 --- /dev/null +++ b/security/nss/lib/jar/exports.gyp @@ -0,0 +1,27 @@ +# 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/. +{ + 'includes': [ + '../../coreconf/config.gypi' + ], + 'targets': [ + { + 'target_name': 'lib_jar_exports', + 'type': 'none', + 'copies': [ + { + 'files': [ + 'jar-ds.h', + 'jar.h', + 'jarfile.h' + ], + 'destination': '<(nss_public_dist_dir)/<(module)' + } + ] + } + ], + 'variables': { + 'module': 'nss' + } +} diff --git a/security/nss/lib/jar/jar-ds.c b/security/nss/lib/jar/jar-ds.c new file mode 100644 index 0000000000..8962305cab --- /dev/null +++ b/security/nss/lib/jar/jar-ds.c @@ -0,0 +1,36 @@ +/* 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/. */ + +#include "jar.h" + +/* These are old DS_* routines renamed to ZZ_* */ +ZZList * +ZZ_NewList(void) +{ + ZZList *list = (ZZList *)PORT_ZAlloc(sizeof(ZZList)); + if (list) + ZZ_InitList(list); + return list; +} + +ZZLink * +ZZ_NewLink(JAR_Item *thing) +{ + ZZLink *link = (ZZLink *)PORT_ZAlloc(sizeof(ZZLink)); + if (link) + link->thing = thing; + return link; +} + +void +ZZ_DestroyLink(ZZLink *link) +{ + PORT_Free(link); +} + +void +ZZ_DestroyList(ZZList *list) +{ + PORT_Free(list); +} diff --git a/security/nss/lib/jar/jar-ds.h b/security/nss/lib/jar/jar-ds.h new file mode 100644 index 0000000000..9818c66c9a --- /dev/null +++ b/security/nss/lib/jar/jar-ds.h @@ -0,0 +1,77 @@ +/* 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/. */ + +#ifndef __JAR_DS_h_ +#define __JAR_DS_h_ + +/* Typedefs */ +typedef struct ZZLinkStr ZZLink; +typedef struct ZZListStr ZZList; + +/* +** Circular linked list. Each link contains a pointer to the object that +** is actually in the list. +*/ +struct ZZLinkStr { + ZZLink *next; + ZZLink *prev; + JAR_Item *thing; +}; + +struct ZZListStr { + ZZLink link; +}; + +#define ZZ_InitList(lst) \ + { \ + (lst)->link.next = &(lst)->link; \ + (lst)->link.prev = &(lst)->link; \ + (lst)->link.thing = 0; \ + } + +#define ZZ_ListEmpty(lst) ((lst)->link.next == &(lst)->link) + +#define ZZ_ListHead(lst) ((lst)->link.next) + +#define ZZ_ListTail(lst) ((lst)->link.prev) + +#define ZZ_ListIterDone(lst, lnk) ((lnk) == &(lst)->link) + +#define ZZ_AppendLink(lst, lnk) \ + { \ + (lnk)->next = &(lst)->link; \ + (lnk)->prev = (lst)->link.prev; \ + (lst)->link.prev->next = (lnk); \ + (lst)->link.prev = (lnk); \ + } + +#define ZZ_InsertLink(lst, lnk) \ + { \ + (lnk)->next = (lst)->link.next; \ + (lnk)->prev = &(lst)->link; \ + (lst)->link.next->prev = (lnk); \ + (lst)->link.next = (lnk); \ + } + +#define ZZ_RemoveLink(lnk) \ + { \ + (lnk)->next->prev = (lnk)->prev; \ + (lnk)->prev->next = (lnk)->next; \ + (lnk)->next = 0; \ + (lnk)->prev = 0; \ + } + +extern ZZLink * +ZZ_NewLink(JAR_Item *thing); + +extern void +ZZ_DestroyLink(ZZLink *link); + +extern ZZList * +ZZ_NewList(void); + +extern void +ZZ_DestroyList(ZZList *list); + +#endif /* __JAR_DS_h_ */ diff --git a/security/nss/lib/jar/jar.c b/security/nss/lib/jar/jar.c new file mode 100644 index 0000000000..5a38def9d7 --- /dev/null +++ b/security/nss/lib/jar/jar.c @@ -0,0 +1,694 @@ +/* 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, jarType 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; + jarType 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; + + case jarTypeMF: + case jarTypeMeta: + case jarTypePhy: + case jarTypeSect: + case jarTypeOwner: + 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, jarType 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; +} diff --git a/security/nss/lib/jar/jar.gyp b/security/nss/lib/jar/jar.gyp new file mode 100644 index 0000000000..ee8734aca8 --- /dev/null +++ b/security/nss/lib/jar/jar.gyp @@ -0,0 +1,33 @@ +# 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/. +{ + 'includes': [ + '../../coreconf/config.gypi' + ], + 'targets': [ + { + 'target_name': 'jar', + 'type': 'static_library', + 'sources': [ + 'jar-ds.c', + 'jar.c', + 'jarfile.c', + 'jarint.c', + 'jarsign.c', + 'jarver.c' + ], + 'dependencies': [ + '<(DEPTH)/exports.gyp:nss_exports' + ] + } + ], + 'target_defaults': { + 'defines': [ + 'MOZILLA_CLIENT=1', + ], + }, + 'variables': { + 'module': 'nss' + } +} diff --git a/security/nss/lib/jar/jar.h b/security/nss/lib/jar/jar.h new file mode 100644 index 0000000000..8b0c26d955 --- /dev/null +++ b/security/nss/lib/jar/jar.h @@ -0,0 +1,372 @@ +/* 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/. */ + +#ifndef __JAR_h_ +#define __JAR_h_ + +/* + * In general, any functions that return pointers + * have memory owned by the caller. + * + */ + +/* security includes */ +#include "cert.h" +#include "hasht.h" + +/* nspr 2.0 includes */ +#include "prio.h" + +#define ZHUGEP + +#include <stdio.h> + +/* various types */ + +typedef enum { + jarTypeMF = 2, + jarTypeSF = 3, + jarTypeMeta = 6, + jarTypePhy = 7, + jarTypeSign = 10, + jarTypeSect = 11, + jarTypeOwner = 13 +} jarType; + +/* void data in ZZList's contain JAR_Item type */ +typedef struct JAR_Item_ { + char *pathname; /* relative. inside zip file */ + jarType type; /* various types */ + size_t size; /* size of data below */ + void *data; /* totally opaque */ +} JAR_Item; + +/* hashes */ +typedef enum { + jarHashNone = 0, + jarHashBad = 1, + jarHashPresent = 2 +} jarHash; + +typedef struct JAR_Digest_ { + jarHash md5_status; + unsigned char md5[MD5_LENGTH]; + jarHash sha1_status; + unsigned char sha1[SHA1_LENGTH]; +} JAR_Digest; + +/* physical archive formats */ +typedef enum { + jarArchGuess = 0, + jarArchNone = 1, + jarArchZip = 2, + jarArchTar = 3 +} jarArch; + +#include "jar-ds.h" + +struct JAR_; + +typedef int jar_settable_callback_fn(int status, struct JAR_ *jar, + const char *metafile, char *pathname, + char *errortext); + +/* jar object */ +typedef struct JAR_ { + jarArch format; /* physical archive format */ + + char *url; /* Where it came from */ + char *filename; /* Disk location */ + FILE *fp; /* For multiple extractions */ + /* JAR_FILE */ + + /* various linked lists */ + ZZList *manifest; /* Digests of MF sections */ + ZZList *hashes; /* Digests of actual signed files */ + ZZList *phy; /* Physical layout of JAR file */ + ZZList *metainfo; /* Global metainfo */ + + JAR_Digest *globalmeta; /* digest of .MF global portion */ + + /* Below will change to a linked list to support multiple sigs */ + int pkcs7; /* Enforced opaqueness */ + int valid; /* PKCS7 signature validated */ + + ZZList *signers; /* the above, per signer */ + + /* Window context, very necessary for PKCS11 now */ + void *mw; /* MWContext window context */ + + /* Signal callback function */ + jar_settable_callback_fn *signal; +} JAR; + +/* + * Iterator + * + * Context for iterative operations. Certain operations + * require iterating multiple linked lists because of + * multiple signers. "nextsign" is used for this purpose. + * + */ +typedef struct JAR_Context_ { + JAR *jar; /* Jar we are searching */ + char *pattern; /* Regular expression */ + jarType finding; /* Type of item to find */ + ZZLink *next; /* Next item in find */ + ZZLink *nextsign; /* Next signer, sometimes */ +} JAR_Context; + +typedef struct JAR_Signer_ { + int pkcs7; /* Enforced opaqueness */ + int valid; /* PKCS7 signature validated */ + char *owner; /* name of .RSA file */ + JAR_Digest *digest; /* of .SF file */ + ZZList *sf; /* Linked list of .SF file contents */ + ZZList *certs; /* Signing information */ +} JAR_Signer; + +/* Meta informaton, or "policy", from the manifest file. + Right now just one tuple per JAR_Item. */ +typedef struct JAR_Metainfo_ { + char *header; + char *info; +} JAR_Metainfo; + +/* This should not be global */ +typedef struct JAR_Physical_ { + unsigned char compression; + unsigned long offset; + unsigned long length; + unsigned long uncompressed_length; +#if defined(XP_UNIX) + PRUint16 mode; +#endif +} JAR_Physical; + +typedef struct JAR_Cert_ { + size_t length; + void *key; + CERTCertificate *cert; +} JAR_Cert; + +/* certificate stuff */ +typedef enum { + jarCertCompany = 1, + jarCertCA = 2, + jarCertSerial = 3, + jarCertExpires = 4, + jarCertNickname = 5, + jarCertFinger = 6, + jarCertJavaHack = 100 +} jarCert; + +/* callback types */ +#define JAR_CB_SIGNAL 1 + +/* + * This is the base for the JAR error codes. It will + * change when these are incorporated into allxpstr.c, + * but right now they won't let me put them there. + * + */ +#ifndef SEC_ERR_BASE +#define SEC_ERR_BASE (-0x2000) +#endif + +#define JAR_BASE SEC_ERR_BASE + 300 + +/* Jar specific error definitions */ + +#define JAR_ERR_GENERAL (JAR_BASE + 1) +#define JAR_ERR_FNF (JAR_BASE + 2) +#define JAR_ERR_CORRUPT (JAR_BASE + 3) +#define JAR_ERR_MEMORY (JAR_BASE + 4) +#define JAR_ERR_DISK (JAR_BASE + 5) +#define JAR_ERR_ORDER (JAR_BASE + 6) +#define JAR_ERR_SIG (JAR_BASE + 7) +#define JAR_ERR_METADATA (JAR_BASE + 8) +#define JAR_ERR_ENTRY (JAR_BASE + 9) +#define JAR_ERR_HASH (JAR_BASE + 10) +#define JAR_ERR_PK7 (JAR_BASE + 11) +#define JAR_ERR_PNF (JAR_BASE + 12) + +/* Function declarations */ + +extern JAR *JAR_new(void); + +extern void PR_CALLBACK JAR_destroy(JAR *jar); + +extern char *JAR_get_error(int status); + +extern int JAR_set_callback(int type, JAR *jar, jar_settable_callback_fn *fn); + +extern void +JAR_init_callbacks(char *(*string_cb)(int), + void *(*find_cx)(void), + void *(*init_cx)(void)); + +/* + * JAR_set_context + * + * PKCS11 may require a password to be entered by the user + * before any crypto routines may be called. This will require + * a window context if used from inside Mozilla. + * + * Call this routine with your context before calling + * verifying or signing. If you have no context, call with NULL + * and one will be chosen for you. + * + */ +int JAR_set_context(JAR *jar, void /*MWContext*/ *mw); + +/* + * Iterative operations + * + * JAR_find sets up for repeated calls with JAR_find_next. + * I never liked findfirst and findnext, this is nicer. + * + * Pattern contains a relative pathname to match inside the + * archive. It is currently assumed to be "*". + * + * To use: + * + * JAR_Item *item; + * JAR_find (jar, "*.class", jarTypeMF); + * while (JAR_find_next (jar, &item) >= 0) + * { do stuff } + * + */ + +/* Replacement functions with an external context */ + +extern JAR_Context *JAR_find(JAR *jar, char *pattern, jarType type); + +extern int JAR_find_next(JAR_Context *ctx, JAR_Item **it); + +extern void JAR_find_end(JAR_Context *ctx); + +/* + * Function to parse manifest file: + * + * Many signatures may be attached to a single filename located + * inside the zip file. We only support one. + * + * Several manifests may be included in the zip file. + * + * You must pass the MANIFEST.MF file before any .SF files. + * + * Right now this returns a big ole list, privately in the jar structure. + * If you need to traverse it, use JAR_find if possible. + * + * The path is needed to determine what type of binary signature is + * being passed, though it is technically not needed for manifest files. + * + * When parsing an ASCII file, null terminate the ASCII raw_manifest + * prior to sending it, and indicate a length of 0. For binary digital + * signatures only, indicate the true length of the signature. + * (This is legacy behavior.) + * + * You may free the manifest after parsing it. + * + */ + +extern int +JAR_parse_manifest(JAR *jar, char *raw_manifest, long length, const char *path, + const char *url); + +/* + * Verify data (nonstreaming). The signature is actually + * checked by JAR_parse_manifest or JAR_pass_archive. + * + */ + +extern JAR_Digest *PR_CALLBACK +JAR_calculate_digest(void *data, long length); + +extern int PR_CALLBACK +JAR_verify_digest(JAR *jar, const char *name, JAR_Digest *dig); + +extern int +JAR_digest_file(char *filename, JAR_Digest *dig); + +/* + * Meta information + * + * Currently, since this call does not support passing of an owner + * (certificate, or physical name of the .sf file), it is restricted to + * returning information located in the manifest.mf file. + * + * Meta information is a name/value pair inside the archive file. Here, + * the name is passed in *header and value returned in **info. + * + * Pass a NULL as the name to retrieve metainfo from the global section. + * + * Data is returned in **info, of size *length. The return value + * will indicate if no data was found. + * + */ + +extern int +JAR_get_metainfo(JAR *jar, char *name, char *header, void **info, + unsigned long *length); + +extern char *JAR_get_filename(JAR *jar); + +extern char *JAR_get_url(JAR *jar); + +/* save the certificate with this fingerprint in persistent + storage, somewhere, for retrieval in a future session when there + is no corresponding JAR structure. */ +extern int PR_CALLBACK +JAR_stash_cert(JAR *jar, long keylen, void *key); + +/* retrieve a certificate presumably stashed with the above + function, but may be any certificate. Type is &CERTCertificate */ +CERTCertificate * +JAR_fetch_cert(long length, void *key); + +/* + * New functions to handle archives alone + * (call JAR_new beforehand) + * + * JAR_pass_archive acts much like parse_manifest. Certificates + * are returned in the JAR structure but as opaque data. When calling + * JAR_verified_extract you still need to decide which of these + * certificates to honor. + * + * Code to examine a JAR structure is in jarbert.c. You can obtain both + * a list of filenames and certificates from traversing the linked list. + * + */ +extern int +JAR_pass_archive(JAR *jar, jarArch format, char *filename, const char *url); + +/* + * Same thing, but don't check signatures + */ +extern int +JAR_pass_archive_unverified(JAR *jar, jarArch format, char *filename, + const char *url); + +/* + * Extracts a relative pathname from the archive and places it + * in the filename specified. + * + * Call JAR_set_nailed if you want to keep the file descriptors + * open between multiple calls to JAR_verify_extract. + * + */ +extern int +JAR_verified_extract(JAR *jar, char *path, char *outpath); + +/* + * JAR_extract does no crypto checking. This can be used if you + * need to extract a manifest file or signature, etc. + * + */ +extern int +JAR_extract(JAR *jar, char *path, char *outpath); + +#endif /* __JAR_h_ */ diff --git a/security/nss/lib/jar/jarfile.c b/security/nss/lib/jar/jarfile.c new file mode 100644 index 0000000000..504ceec2de --- /dev/null +++ b/security/nss/lib/jar/jarfile.c @@ -0,0 +1,976 @@ +/* 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/. */ + +/* + * JARFILE + * + * Parsing of a Jar file + */ +#define JAR_SIZE 256 + +#include "jar.h" +#include "jarint.h" +#include "jarfile.h" + +/* commercial compression */ +#include "jzlib.h" + +#if defined(XP_UNIX) +#include "sys/stat.h" +#endif + +#include "sechash.h" /* for HASH_GetHashObject() */ + +PR_STATIC_ASSERT(46 == sizeof(struct ZipCentral)); +PR_STATIC_ASSERT(30 == sizeof(struct ZipLocal)); +PR_STATIC_ASSERT(22 == sizeof(struct ZipEnd)); +PR_STATIC_ASSERT(512 == sizeof(union TarEntry)); + +/* extracting */ +static int +jar_guess_jar(const char *filename, JAR_FILE fp); + +static int +jar_inflate_memory(unsigned int method, long *length, long expected_out_len, + char **data); + +static int +jar_physical_extraction(JAR_FILE fp, char *outpath, unsigned long offset, + unsigned long length); + +static int +jar_physical_inflate(JAR_FILE fp, char *outpath, unsigned long offset, + unsigned long length, unsigned int method); + +static int +jar_verify_extract(JAR *jar, char *path, char *physical_path); + +static JAR_Physical * +jar_get_physical(JAR *jar, char *pathname); + +static int +jar_extract_manifests(JAR *jar, jarArch format, JAR_FILE fp); + +static int +jar_extract_mf(JAR *jar, jarArch format, JAR_FILE fp, char *ext); + +/* indexing */ +static int +jar_gen_index(JAR *jar, jarArch format, JAR_FILE fp); + +static int +jar_listtar(JAR *jar, JAR_FILE fp); + +static int +jar_listzip(JAR *jar, JAR_FILE fp); + +/* conversions */ +static int +dosdate(char *date, const char *s); + +static int +dostime(char *time, const char *s); + +#ifdef NSS_X86_OR_X64 +/* The following macros throw up warnings. */ +#if defined(__GNUC__) && !defined(NSS_NO_GCC48) +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif +#define x86ShortToUint32(ii) ((const PRUint32) * ((const PRUint16 *)(ii))) +#define x86LongToUint32(ii) (*(const PRUint32 *)(ii)) +#else +static PRUint32 +x86ShortToUint32(const void *ii); + +static PRUint32 +x86LongToUint32(const void *ll); +#endif + +static long +octalToLong(const char *s); + +/* + * J A R _ p a s s _ a r c h i v e + * + * For use by naive clients. Slam an entire archive file + * into this function. We extract manifests, parse, index + * the archive file, and do whatever nastiness. + * + */ +int +JAR_pass_archive(JAR *jar, jarArch format, char *filename, const char *url) +{ + JAR_FILE fp; + int status = 0; + + if (filename == NULL) + return JAR_ERR_GENERAL; + + if ((fp = JAR_FOPEN(filename, "rb")) != NULL) { + if (format == jarArchGuess) + format = (jarArch)jar_guess_jar(filename, fp); + + jar->format = format; + jar->url = url ? PORT_Strdup(url) : NULL; + jar->filename = PORT_Strdup(filename); + + status = jar_gen_index(jar, format, fp); + if (status == 0) + status = jar_extract_manifests(jar, format, fp); + + JAR_FCLOSE(fp); + if (status < 0) + return status; + + /* people were expecting it this way */ + return jar->valid; + } + /* file not found */ + return JAR_ERR_FNF; +} + +/* + * J A R _ p a s s _ a r c h i v e _ u n v e r i f i e d + * + * Same as JAR_pass_archive, but doesn't parse signatures. + * + */ +int +JAR_pass_archive_unverified(JAR *jar, jarArch format, char *filename, + const char *url) +{ + JAR_FILE fp; + int status = 0; + + if (filename == NULL) { + return JAR_ERR_GENERAL; + } + + if ((fp = JAR_FOPEN(filename, "rb")) != NULL) { + if (format == jarArchGuess) { + format = (jarArch)jar_guess_jar(filename, fp); + } + + jar->format = format; + jar->url = url ? PORT_Strdup(url) : NULL; + jar->filename = PORT_Strdup(filename); + + status = jar_gen_index(jar, format, fp); + if (status == 0) { + status = jar_extract_mf(jar, format, fp, "mf"); + } + + JAR_FCLOSE(fp); + if (status < 0) { + return status; + } + + /* people were expecting it this way */ + return jar->valid; + } + /* file not found */ + return JAR_ERR_FNF; +} + +/* + * J A R _ v e r i f i e d _ e x t r a c t + * + * Optimization: keep a file descriptor open + * inside the JAR structure, so we don't have to + * open the file 25 times to run java. + * + */ + +int +JAR_verified_extract(JAR *jar, char *path, char *outpath) +{ + int status = JAR_extract(jar, path, outpath); + + if (status >= 0) + return jar_verify_extract(jar, path, outpath); + return status; +} + +int +JAR_extract(JAR *jar, char *path, char *outpath) +{ + int result; + JAR_Physical *phy; + + if (jar->fp == NULL && jar->filename) { + jar->fp = (FILE *)JAR_FOPEN(jar->filename, "rb"); + } + if (jar->fp == NULL) { + /* file not found */ + return JAR_ERR_FNF; + } + + phy = jar_get_physical(jar, path); + if (phy) { + if (phy->compression == 0) { + result = jar_physical_extraction((PRFileDesc *)jar->fp, outpath, phy->offset, phy->length); + } else { + /* compression methods other than 8 are unsupported, + * but for historical reasons, jar_physical_inflate will be called for + * unsupported compression method constants too. */ + result = jar_physical_inflate((PRFileDesc *)jar->fp, outpath, + phy->offset, phy->length, + (unsigned int)phy->compression); + } + +#if defined(XP_UNIX) + if (phy->mode) + chmod(outpath, 0400 | (mode_t)phy->mode); +#endif + } else { + /* pathname not found in archive */ + result = JAR_ERR_PNF; + } + return result; +} + +/* + * p h y s i c a l _ e x t r a c t i o n + * + * This needs to be done in chunks of say 32k, instead of + * in one bulk calloc. (Necessary under Win16 platform.) + * This is done for uncompressed entries only. + * + */ + +#define CHUNK 32768 + +static int +jar_physical_extraction(JAR_FILE fp, char *outpath, unsigned long offset, + unsigned long length) +{ + JAR_FILE out; + char *buffer = (char *)PORT_ZAlloc(CHUNK); + int status = 0; + + if (buffer == NULL) + return JAR_ERR_MEMORY; + + if ((out = JAR_FOPEN(outpath, "wb")) != NULL) { + unsigned long at = 0; + + JAR_FSEEK(fp, offset, (PRSeekWhence)0); + while (at < length) { + long chunk = (at + CHUNK <= length) ? CHUNK : length - at; + if (JAR_FREAD(fp, buffer, chunk) != chunk) { + status = JAR_ERR_DISK; + break; + } + at += chunk; + if (JAR_FWRITE(out, buffer, chunk) < chunk) { + /* most likely a disk full error */ + status = JAR_ERR_DISK; + break; + } + } + JAR_FCLOSE(out); + } else { + /* error opening output file */ + status = JAR_ERR_DISK; + } + PORT_Free(buffer); + return status; +} + +/* + * j a r _ p h y s i c a l _ i n f l a t e + * + * Inflate a range of bytes in a file, writing the inflated + * result to "outpath". Chunk based. + * + */ +/* input and output chunks differ, assume 4x compression */ + +#define ICHUNK 8192 +#define OCHUNK 32768 + +static int +jar_physical_inflate(JAR_FILE fp, char *outpath, unsigned long offset, unsigned long length, + unsigned int method) +{ + char *inbuf, *outbuf; + int status = 0; + z_stream zs; + JAR_FILE out; + + /* Raw inflate in zlib 1.1.4 needs an extra dummy byte at the end */ + if ((inbuf = (char *)PORT_ZAlloc(ICHUNK + 1)) == NULL) + return JAR_ERR_MEMORY; + + if ((outbuf = (char *)PORT_ZAlloc(OCHUNK)) == NULL) { + status = JAR_ERR_MEMORY; + goto loser; + } + + PORT_Memset(&zs, 0, sizeof(zs)); + status = inflateInit2(&zs, -MAX_WBITS); + if (status != Z_OK) { + status = JAR_ERR_GENERAL; + goto loser; + } + + if ((out = JAR_FOPEN(outpath, "wb")) != NULL) { + int status2 = 0; + unsigned long at = 0; + + JAR_FSEEK(fp, offset, (PRSeekWhence)0); + while (at < length) { + unsigned long chunk = (at + ICHUNK <= length) ? ICHUNK : length - at; + unsigned long tin; + + if (JAR_FREAD(fp, inbuf, chunk) != chunk) { + /* incomplete read */ + JAR_FCLOSE(out); + status = JAR_ERR_CORRUPT; + break; + } + at += chunk; + if (at == length) { + /* add an extra dummy byte at the end */ + inbuf[chunk++] = 0xDD; + } + zs.next_in = (Bytef *)inbuf; + zs.avail_in = chunk; + zs.avail_out = OCHUNK; + tin = zs.total_in; + while ((zs.total_in - tin < chunk) || (zs.avail_out == 0)) { + unsigned long prev_total = zs.total_out; + unsigned long ochunk; + + zs.next_out = (Bytef *)outbuf; + zs.avail_out = OCHUNK; + status = inflate(&zs, Z_NO_FLUSH); + if (status != Z_OK && status != Z_STREAM_END) { + /* error during decompression */ + JAR_FCLOSE(out); + status = JAR_ERR_CORRUPT; + break; + } + ochunk = zs.total_out - prev_total; + if (JAR_FWRITE(out, outbuf, ochunk) < (long)ochunk) { + /* most likely a disk full error */ + status = JAR_ERR_DISK; + break; + } + if (status == Z_STREAM_END) + break; + } + if (status != Z_OK) { + break; + } + } + JAR_FCLOSE(out); + status2 = inflateEnd(&zs); + if (status == Z_OK) { + status = status2; + } + } else { + /* error opening output file */ + status = JAR_ERR_DISK; + } +loser: + if (inbuf) { + PORT_Free(inbuf); + } + if (outbuf) { + PORT_Free(outbuf); + } + return status; +} + +/* + * j a r _ i n f l a t e _ m e m o r y + * + * Call zlib to inflate the given memory chunk. It is re-XP_ALLOC'd, + * and thus appears to operate inplace to the caller. + * + */ +static int +jar_inflate_memory(unsigned int method, long *length, long expected_out_len, + char **data) +{ + char *inbuf = *data; + char *outbuf = (char *)PORT_ZAlloc(expected_out_len); + long insz = *length; + int status; + z_stream zs; + + if (outbuf == NULL) + return JAR_ERR_MEMORY; + + PORT_Memset(&zs, 0, sizeof zs); + status = inflateInit2(&zs, -MAX_WBITS); + if (status < 0) { + /* error initializing zlib stream */ + PORT_Free(outbuf); + return JAR_ERR_GENERAL; + } + + zs.next_in = (Bytef *)inbuf; + zs.next_out = (Bytef *)outbuf; + zs.avail_in = insz; + zs.avail_out = expected_out_len; + + status = inflate(&zs, Z_FINISH); + if (status != Z_OK && status != Z_STREAM_END) { + /* error during deflation */ + PORT_Free(outbuf); + return JAR_ERR_GENERAL; + } + + status = inflateEnd(&zs); + if (status != Z_OK) { + /* error during deflation */ + PORT_Free(outbuf); + return JAR_ERR_GENERAL; + } + PORT_Free(*data); + *data = outbuf; + *length = zs.total_out; + return 0; +} + +/* + * v e r i f y _ e x t r a c t + * + * Validate signature on the freshly extracted file. + * + */ +static int +jar_verify_extract(JAR *jar, char *path, char *physical_path) +{ + int status; + JAR_Digest dig; + + PORT_Memset(&dig, 0, sizeof dig); + status = JAR_digest_file(physical_path, &dig); + if (!status) + status = JAR_verify_digest(jar, path, &dig); + return status; +} + +/* + * g e t _ p h y s i c a l + * + * Let's get physical. + * Obtains the offset and length of this file in the jar file. + * + */ +static JAR_Physical * +jar_get_physical(JAR *jar, char *pathname) +{ + ZZLink *link; + ZZList *list = jar->phy; + + if (ZZ_ListEmpty(list)) + return NULL; + + for (link = ZZ_ListHead(list); + !ZZ_ListIterDone(list, link); + link = link->next) { + JAR_Item *it = link->thing; + + if (it->type == jarTypePhy && + it->pathname && !PORT_Strcmp(it->pathname, pathname)) { + JAR_Physical *phy = (JAR_Physical *)it->data; + return phy; + } + } + return NULL; +} + +/* + * j a r _ e x t r a c t _ m a n i f e s t s + * + * Extract the manifest files and parse them, + * from an open archive file whose contents are known. + * + */ +static int +jar_extract_manifests(JAR *jar, jarArch format, JAR_FILE fp) +{ + int status, signatures; + + if (format != jarArchZip && format != jarArchTar) + return JAR_ERR_CORRUPT; + + if ((status = jar_extract_mf(jar, format, fp, "mf")) < 0) + return status; + if (!status) + return JAR_ERR_ORDER; + if ((status = jar_extract_mf(jar, format, fp, "sf")) < 0) + return status; + if (!status) + return JAR_ERR_ORDER; + if ((status = jar_extract_mf(jar, format, fp, "rsa")) < 0) + return status; + signatures = status; + if ((status = jar_extract_mf(jar, format, fp, "dsa")) < 0) + return status; + if (!(signatures += status)) + return JAR_ERR_SIG; + return 0; +} + +/* + * j a r _ e x t r a c t _ m f + * + * Extracts manifest files based on an extension, which + * should be .MF, .SF, .RSA, etc. Order of the files is now no + * longer important when zipping jar files. + * + */ +static int +jar_extract_mf(JAR *jar, jarArch format, JAR_FILE fp, char *ext) +{ + ZZLink *link; + ZZList *list = jar->phy; + int ret = 0; + + if (ZZ_ListEmpty(list)) + return JAR_ERR_PNF; + + for (link = ZZ_ListHead(list); + ret >= 0 && !ZZ_ListIterDone(list, link); + link = link->next) { + JAR_Item *it = link->thing; + + if (it->type == jarTypePhy && + !PORT_Strncmp(it->pathname, "META-INF", 8)) { + JAR_Physical *phy = (JAR_Physical *)it->data; + char *fn = it->pathname + 8; + char *e; + char *manifest; + long length; + int num, status; + + if (PORT_Strlen(it->pathname) < 8) + continue; + + if (*fn == '/' || *fn == '\\') + fn++; + if (*fn == 0) { + /* just a directory entry */ + continue; + } + + /* skip to extension */ + for (e = fn; *e && *e != '.'; e++) + /* yip */; + + /* and skip dot */ + if (*e == '.') + e++; + if (PORT_Strcasecmp(ext, e)) { + /* not the right extension */ + continue; + } + if (phy->length == 0 || phy->length > 0xFFFF) { + /* manifest files cannot be zero length or too big! */ + /* the 0xFFFF limit is per J2SE SDK */ + return JAR_ERR_CORRUPT; + } + + /* Read in the manifest and parse it */ + /* Raw inflate in zlib 1.1.4 needs an extra dummy byte at the end */ + manifest = (char *)PORT_ZAlloc(phy->length + 1); + if (!manifest) + return JAR_ERR_MEMORY; + + JAR_FSEEK(fp, phy->offset, (PRSeekWhence)0); + num = JAR_FREAD(fp, manifest, phy->length); + if (num != phy->length) { + /* corrupt archive file */ + PORT_Free(manifest); + return JAR_ERR_CORRUPT; + } + + if (phy->compression == 8) { + length = phy->length; + /* add an extra dummy byte at the end */ + manifest[length++] = 0xDD; + status = jar_inflate_memory((unsigned int)phy->compression, + &length, + phy->uncompressed_length, + &manifest); + if (status < 0) { + PORT_Free(manifest); + return status; + } + } else if (phy->compression) { + /* unsupported compression method */ + PORT_Free(manifest); + return JAR_ERR_CORRUPT; + } else + length = phy->length; + + status = JAR_parse_manifest(jar, manifest, length, + it->pathname, "url"); + PORT_Free(manifest); + if (status < 0) + ret = status; + else + ++ret; + } else if (it->type == jarTypePhy) { + /* ordinary file */ + } + } + return ret; +} + +/* + * j a r _ g e n _ i n d e x + * + * Generate an index for the various types of + * known archive files. Right now .ZIP and .TAR + * + */ +static int +jar_gen_index(JAR *jar, jarArch format, JAR_FILE fp) +{ + int result = JAR_ERR_CORRUPT; + + JAR_FSEEK(fp, 0, (PRSeekWhence)0); + switch (format) { + case jarArchZip: + result = jar_listzip(jar, fp); + break; + + case jarArchTar: + result = jar_listtar(jar, fp); + break; + + case jarArchGuess: + case jarArchNone: + return JAR_ERR_GENERAL; + } + JAR_FSEEK(fp, 0, (PRSeekWhence)0); + return result; +} + +/* + * j a r _ l i s t z i p + * + * List the physical contents of a Phil Katz + * style .ZIP file into the JAR linked list. + * + */ +static int +jar_listzip(JAR *jar, JAR_FILE fp) +{ + ZZLink *ent; + JAR_Item *it = NULL; + JAR_Physical *phy = NULL; + struct ZipLocal *Local = PORT_ZNew(struct ZipLocal); + struct ZipCentral *Central = PORT_ZNew(struct ZipCentral); + struct ZipEnd *End = PORT_ZNew(struct ZipEnd); + + int err = 0; + long pos = 0L; + unsigned int compression; + unsigned int filename_len, extra_len; + + char filename[JAR_SIZE]; + char date[9], time[9]; + char sig[4]; + + if (!Local || !Central || !End) { + /* out of memory */ + err = JAR_ERR_MEMORY; + goto loser; + } + + while (1) { + PRUint32 sigVal; + JAR_FSEEK(fp, pos, (PRSeekWhence)0); + + if (JAR_FREAD(fp, sig, sizeof sig) != sizeof sig) { + /* zip file ends prematurely */ + err = JAR_ERR_CORRUPT; + goto loser; + } + + JAR_FSEEK(fp, pos, (PRSeekWhence)0); + sigVal = x86LongToUint32(sig); + if (sigVal == LSIG) { + JAR_FREAD(fp, Local, sizeof *Local); + + filename_len = x86ShortToUint32(Local->filename_len); + extra_len = x86ShortToUint32(Local->extrafield_len); + if (filename_len >= JAR_SIZE) { + /* corrupt zip file */ + err = JAR_ERR_CORRUPT; + goto loser; + } + + if (JAR_FREAD(fp, filename, filename_len) != filename_len) { + /* truncated archive file */ + err = JAR_ERR_CORRUPT; + goto loser; + } + filename[filename_len] = 0; + /* Add this to our jar chain */ + phy = PORT_ZNew(JAR_Physical); + if (phy == NULL) { + err = JAR_ERR_MEMORY; + goto loser; + } + + /* We will index any file that comes our way, but when it comes + to actually extraction, compression must be 0 or 8 */ + compression = x86ShortToUint32(Local->method); + phy->compression = (compression <= 255) ? compression : 222; + /* XXX 222 is bad magic. */ + + phy->offset = pos + (sizeof *Local) + filename_len + extra_len; + phy->length = x86LongToUint32(Local->size); + phy->uncompressed_length = x86LongToUint32(Local->orglen); + + dosdate(date, Local->date); + dostime(time, Local->time); + + it = PORT_ZNew(JAR_Item); + if (it == NULL) { + err = JAR_ERR_MEMORY; + goto loser; + } + + it->pathname = PORT_Strdup(filename); + it->type = jarTypePhy; + it->data = (unsigned char *)phy; + it->size = sizeof(JAR_Physical); + + ent = ZZ_NewLink(it); + if (ent == NULL) { + err = JAR_ERR_MEMORY; + goto loser; + } + + ZZ_AppendLink(jar->phy, ent); + pos = phy->offset + phy->length; + } else if (sigVal == CSIG) { +#if defined(XP_UNIX) + unsigned int attr = 0; +#endif + if (JAR_FREAD(fp, Central, sizeof *Central) != sizeof *Central) { + /* apparently truncated archive */ + err = JAR_ERR_CORRUPT; + goto loser; + } + +#if defined(XP_UNIX) + /* with unix we need to locate any bits from + the protection mask in the external attributes. */ + attr = Central->external_attributes[2]; /* magic */ + if (attr) { + /* we have to read the filename, again */ + filename_len = x86ShortToUint32(Central->filename_len); + if (filename_len >= JAR_SIZE) { + /* corrupt in central directory */ + err = JAR_ERR_CORRUPT; + goto loser; + } + + if (JAR_FREAD(fp, filename, filename_len) != filename_len) { + /* truncated in central directory */ + err = JAR_ERR_CORRUPT; + goto loser; + } + filename[filename_len] = 0; + + /* look up this name again */ + phy = jar_get_physical(jar, filename); + if (phy) { + /* always allow access by self */ + phy->mode = 0400 | attr; + } + } +#endif + pos += sizeof(struct ZipCentral) + + x86ShortToUint32(Central->filename_len) + + x86ShortToUint32(Central->commentfield_len) + + x86ShortToUint32(Central->extrafield_len); + } else if (sigVal == ESIG) { + if (JAR_FREAD(fp, End, sizeof *End) != sizeof *End) { + err = JAR_ERR_CORRUPT; + goto loser; + } + break; + } else { + /* garbage in archive */ + err = JAR_ERR_CORRUPT; + goto loser; + } + } + +loser: + if (Local) + PORT_Free(Local); + if (phy && it == NULL) + PORT_Free(phy); + if (Central) + PORT_Free(Central); + if (End) + PORT_Free(End); + return err; +} + +/* + * j a r _ l i s t t a r + * + * List the physical contents of a Unix + * .tar file into the JAR linked list. + * + */ +static int +jar_listtar(JAR *jar, JAR_FILE fp) +{ + char *s; + JAR_Physical *phy; + long pos = 0L; + long sz; + union TarEntry tarball; + + while (1) { + JAR_FSEEK(fp, pos, (PRSeekWhence)0); + + if (JAR_FREAD(fp, &tarball, sizeof tarball) < sizeof tarball) + break; + + if (!*tarball.val.filename) + break; + + sz = octalToLong(tarball.val.size); + + /* Tag the end of filename */ + s = tarball.val.filename; + while (*s && *s != ' ') + s++; + *s = 0; + + /* Add to our linked list */ + phy = PORT_ZNew(JAR_Physical); + if (phy == NULL) + return JAR_ERR_MEMORY; + + phy->compression = 0; + phy->offset = pos + sizeof tarball; + phy->length = sz; + + ADDITEM(jar->phy, jarTypePhy, tarball.val.filename, phy, + sizeof *phy); + + /* Advance to next file entry */ + sz = PR_ROUNDUP(sz, sizeof tarball); + pos += sz + sizeof tarball; + } + + return 0; +} + +/* + * d o s d a t e + * + * Not used right now, but keep it in here because + * it will be needed. + * + */ +static int +dosdate(char *date, const char *s) +{ + PRUint32 num = x86ShortToUint32(s); + + PR_snprintf(date, 9, "%02d-%02d-%02d", ((num >> 5) & 0x0F), (num & 0x1F), + ((num >> 9) + 80)); + return 0; +} + +/* + * d o s t i m e + * + * Not used right now, but keep it in here because + * it will be needed. + * + */ +static int +dostime(char *time, const char *s) +{ + PRUint32 num = x86ShortToUint32(s); + + PR_snprintf(time, 6, "%02d:%02d", ((num >> 11) & 0x1F), + ((num >> 5) & 0x3F)); + return 0; +} + +#ifndef NSS_X86_OR_X64 +/* + * Simulates an x86 (little endian, unaligned) ushort fetch from any address. + */ +static PRUint32 +x86ShortToUint32(const void *v) +{ + const unsigned char *ii = (const unsigned char *)v; + PRUint32 ret = (PRUint32)(ii[0]) | ((PRUint32)(ii[1]) << 8); + return ret; +} + +/* + * Simulates an x86 (little endian, unaligned) uint fetch from any address. + */ +static PRUint32 +x86LongToUint32(const void *v) +{ + const unsigned char *ll = (const unsigned char *)v; + PRUint32 ret; + + ret = ((((PRUint32)(ll[0])) << 0) | + (((PRUint32)(ll[1])) << 8) | + (((PRUint32)(ll[2])) << 16) | + (((PRUint32)(ll[3])) << 24)); + return ret; +} +#endif + +/* + * ASCII octal to binary long. + * Used for integer encoding inside tar files. + * + */ +static long +octalToLong(const char *s) +{ + long num = 0L; + + while (*s == ' ') + s++; + while (*s >= '0' && *s <= '7') { + num <<= 3; + num += *s++ - '0'; + } + return num; +} + +/* + * g u e s s _ j a r + * + * Try to guess what kind of JAR file this is. + * Maybe tar, maybe zip. Look in the file for magic + * or at its filename. + * + */ +static int +jar_guess_jar(const char *filename, JAR_FILE fp) +{ + PRInt32 len = PORT_Strlen(filename); + const char *ext = filename + len - 4; /* 4 for ".tar" */ + + if (len >= 4 && !PL_strcasecmp(ext, ".tar")) + return jarArchTar; + return jarArchZip; +} diff --git a/security/nss/lib/jar/jarfile.h b/security/nss/lib/jar/jarfile.h new file mode 100644 index 0000000000..52d4f1fc77 --- /dev/null +++ b/security/nss/lib/jar/jarfile.h @@ -0,0 +1,76 @@ +/* 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/. */ + +/* + * JARFILE.H + * + * Certain constants and structures for the archive format. + * + */ + +/* ZIP */ +struct ZipLocal { /* 30 bytes */ + char signature[4]; + char word[2]; + char bitflag[2]; + char method[2]; + char time[2]; + char date[2]; + char crc32[4]; + char size[4]; + char orglen[4]; + char filename_len[2]; + char extrafield_len[2]; +}; + +struct ZipCentral { /* 46 bytes */ + char signature[4]; + char version_made_by[2]; + char version[2]; + char bitflag[2]; + char method[2]; + char time[2]; + char date[2]; + char crc32[4]; + char size[4]; + char orglen[4]; + char filename_len[2]; + char extrafield_len[2]; + char commentfield_len[2]; + char diskstart_number[2]; + char internal_attributes[2]; + char external_attributes[4]; + char localhdr_offset[4]; +}; + +struct ZipEnd { /* 22 bytes */ + char signature[4]; + char disk_nr[2]; + char start_central_dir[2]; + char total_entries_disk[2]; + char total_entries_archive[2]; + char central_dir_size[4]; + char offset_central_dir[4]; + char commentfield_len[2]; +}; + +#define LSIG 0x04034B50l +#define CSIG 0x02014B50l +#define ESIG 0x06054B50l + +/* TAR */ +union TarEntry { /* 512 bytes */ + struct header { /* 257 bytes */ + char filename[100]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char time[12]; + char checksum[8]; + char linkflag; + char linkname[100]; + } val; + char buffer[512]; +}; diff --git a/security/nss/lib/jar/jarint.c b/security/nss/lib/jar/jarint.c new file mode 100644 index 0000000000..b0ef7fb210 --- /dev/null +++ b/security/nss/lib/jar/jarint.c @@ -0,0 +1,52 @@ +/* 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/. */ + +/* + * Internal libjar routines. + */ + +#include "jar.h" +#include "jarint.h" + +/*----------------------------------------------------------------------- + * JAR_FOPEN_to_PR_Open + * Translate JAR_FOPEN arguments to PR_Open arguments + */ +PRFileDesc* +JAR_FOPEN_to_PR_Open(const char* name, const char* mode) +{ + + PRIntn prflags = 0, prmode = 0; + + /* Get read/write flags */ + if (strchr(mode, 'r') && !strchr(mode, '+')) { + prflags |= PR_RDONLY; + } else if ((strchr(mode, 'w') || strchr(mode, 'a')) && + !strchr(mode, '+')) { + prflags |= PR_WRONLY; + } else { + prflags |= PR_RDWR; + } + + /* Create a new file? */ + if (strchr(mode, 'w') || strchr(mode, 'a')) { + prflags |= PR_CREATE_FILE; + } + + /* Append? */ + if (strchr(mode, 'a')) { + prflags |= PR_APPEND; + } + + /* Truncate? */ + if (strchr(mode, 'w')) { + prflags |= PR_TRUNCATE; + } + + /* We can't do umask because it isn't XP. Choose some default + mode for created files */ + prmode = 0755; + + return PR_Open(name, prflags, prmode); +} diff --git a/security/nss/lib/jar/jarint.h b/security/nss/lib/jar/jarint.h new file mode 100644 index 0000000000..0f40f931f6 --- /dev/null +++ b/security/nss/lib/jar/jarint.h @@ -0,0 +1,54 @@ +/* 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 internal routines */ + +#include "nspr.h" +#include "keyhi.h" +#include "base64.h" + +extern CERTCertDBHandle *JAR_open_database(void); + +extern int JAR_close_database(CERTCertDBHandle *certdb); + +extern int jar_close_key_database(void *keydb); + +extern void *jar_open_key_database(void); + +extern JAR_Signer *JAR_new_signer(void); + +extern void JAR_destroy_signer(JAR_Signer *signer); + +extern JAR_Signer *jar_get_signer(JAR *jar, char *basename); + +extern int +jar_append(ZZList *list, int type, char *pathname, void *data, size_t size); + +/* Translate fopen mode arg to PR_Open flags and mode */ +PRFileDesc * +JAR_FOPEN_to_PR_Open(const char *name, const char *mode); + +#define ADDITEM(list, type, pathname, data, size) \ + { \ + int err = jar_append(list, type, pathname, data, size); \ + if (err < 0) \ + return err; \ + } + +/* Here is some ugliness in the event it is necessary to link + with NSPR 1.0 libraries, which do not include an FSEEK. It is + difficult to fudge an FSEEK into 1.0 so we use stdio. */ + +/* nspr 2.0 suite */ +#define JAR_FILE PRFileDesc * +#define JAR_FOPEN(fn, mode) JAR_FOPEN_to_PR_Open(fn, mode) +#define JAR_FCLOSE PR_Close +#define JAR_FSEEK PR_Seek +#define JAR_FREAD PR_Read +#define JAR_FWRITE PR_Write + +int +jar_create_pk7(CERTCertDBHandle *certdb, void *keydb, + CERTCertificate *cert, char *password, JAR_FILE infp, + JAR_FILE outfp); diff --git a/security/nss/lib/jar/jarnav.c b/security/nss/lib/jar/jarnav.c new file mode 100644 index 0000000000..8b72a52d83 --- /dev/null +++ b/security/nss/lib/jar/jarnav.c @@ -0,0 +1,63 @@ +/* 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/. */ + +/* + * JARNAV.C + * + * JAR stuff needed for client only. + * + */ + +#include "jar.h" +#include "jarint.h" + +/* from proto.h */ +extern MWContext *FE_GetInitContext(void); + +/* To return an MWContext for Java */ +static MWContext *(*jar_fn_FindSomeContext)(void) = NULL; + +/* To fabricate an MWContext for FE_GetPassword */ +static MWContext *(*jar_fn_GetInitContext)(void) = NULL; + +/* + * J A R _ i n i t + * + * Initialize the JAR functions. + * + */ + +void +JAR_init(void) +{ + JAR_init_callbacks(XP_GetString, NULL, NULL); +} + +/* + * J A R _ s e t _ c o n t e x t + * + * Set the jar window context for use by PKCS11, since + * it may be needed to prompt the user for a password. + * + */ +int +JAR_set_context(JAR *jar, MWContext *mw) +{ + if (mw) { + jar->mw = mw; + } else { + /* jar->mw = XP_FindSomeContext(); */ + jar->mw = NULL; + /* + * We can't find a context because we're in startup state and none + * exist yet. go get an FE_InitContext that only works at + * initialization time. + */ + /* Turn on the mac when we get the FE_ function */ + if (jar->mw == NULL) { + jar->mw = jar_fn_GetInitContext(); + } + } + return 0; +} diff --git a/security/nss/lib/jar/jarsign.c b/security/nss/lib/jar/jarsign.c new file mode 100644 index 0000000000..601a7fd044 --- /dev/null +++ b/security/nss/lib/jar/jarsign.c @@ -0,0 +1,250 @@ +/* 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/. */ + +/* + * JARSIGN + * + * Routines used in signing archives. + */ + +#include "jar.h" +#include "jarint.h" +#include "secpkcs7.h" +#include "pk11func.h" +#include "sechash.h" + +/* from libevent.h */ +typedef void (*ETVoidPtrFunc)(void *data); + +/* key database wrapper */ +/* static SECKEYKeyDBHandle *jar_open_key_database (void); */ +/* CHUNQ is our bite size */ + +#define CHUNQ 64000 +#define FILECHUNQ 32768 + +/* + * J A R _ c a l c u l a t e _ d i g e s t + * + * Quick calculation of a digest for + * the specified block of memory. Will calculate + * for all supported algorithms, now MD5. + * + * This version supports huge pointers for WIN16. + * + */ +JAR_Digest *PR_CALLBACK +JAR_calculate_digest(void *data, long length) +{ + PK11Context *md5 = 0; + PK11Context *sha1 = 0; + JAR_Digest *dig = PORT_ZNew(JAR_Digest); + long chunq; + unsigned int md5_length, sha1_length; + + if (dig == NULL) { + /* out of memory allocating digest */ + return NULL; + } + + md5 = PK11_CreateDigestContext(SEC_OID_MD5); + if (md5 == NULL) { + PORT_ZFree(dig, sizeof(JAR_Digest)); + return NULL; + } + sha1 = PK11_CreateDigestContext(SEC_OID_SHA1); + if (sha1 == NULL) { + PK11_DestroyContext(md5, PR_TRUE); + /* added due to bug Bug 1250214 - prevent the 2nd memory leak */ + PORT_ZFree(dig, sizeof(JAR_Digest)); + return NULL; + } + + if (length >= 0) { + PK11_DigestBegin(md5); + PK11_DigestBegin(sha1); + + do { + chunq = length; + + PK11_DigestOp(md5, (unsigned char *)data, chunq); + PK11_DigestOp(sha1, (unsigned char *)data, chunq); + length -= chunq; + data = ((char *)data + chunq); + } while (length > 0); + + PK11_DigestFinal(md5, dig->md5, &md5_length, MD5_LENGTH); + PK11_DigestFinal(sha1, dig->sha1, &sha1_length, SHA1_LENGTH); + + PK11_DestroyContext(md5, PR_TRUE); + PK11_DestroyContext(sha1, PR_TRUE); + } + return dig; +} + +/* + * J A R _ d i g e s t _ f i l e + * + * Calculates the MD5 and SHA1 digests for a file + * present on disk, and returns these in JAR_Digest struct. + * + */ +int +JAR_digest_file(char *filename, JAR_Digest *dig) +{ + JAR_FILE fp; + PK11Context *md5 = 0; + PK11Context *sha1 = 0; + unsigned char *buf = (unsigned char *)PORT_ZAlloc(FILECHUNQ); + int num; + unsigned int md5_length, sha1_length; + + if (buf == NULL) { + /* out of memory */ + return JAR_ERR_MEMORY; + } + + if ((fp = JAR_FOPEN(filename, "rb")) == 0) { + /* perror (filename); FIX XXX XXX XXX XXX XXX XXX */ + PORT_Free(buf); + return JAR_ERR_FNF; + } + + md5 = PK11_CreateDigestContext(SEC_OID_MD5); + sha1 = PK11_CreateDigestContext(SEC_OID_SHA1); + + if (md5 == NULL || sha1 == NULL) { + if (md5) { + PK11_DestroyContext(md5, PR_TRUE); + } + if (sha1) { + PK11_DestroyContext(sha1, PR_TRUE); + } + /* can't generate digest contexts */ + PORT_Free(buf); + JAR_FCLOSE(fp); + return JAR_ERR_GENERAL; + } + + PK11_DigestBegin(md5); + PK11_DigestBegin(sha1); + + while (1) { + if ((num = JAR_FREAD(fp, buf, FILECHUNQ)) == 0) + break; + + PK11_DigestOp(md5, buf, num); + PK11_DigestOp(sha1, buf, num); + } + + PK11_DigestFinal(md5, dig->md5, &md5_length, MD5_LENGTH); + PK11_DigestFinal(sha1, dig->sha1, &sha1_length, SHA1_LENGTH); + + PK11_DestroyContext(md5, PR_TRUE); + PK11_DestroyContext(sha1, PR_TRUE); + + PORT_Free(buf); + JAR_FCLOSE(fp); + + return 0; +} + +/* + * J A R _ o p e n _ k e y _ d a t a b a s e + * + */ + +void * +jar_open_key_database(void) +{ + return NULL; +} + +int +jar_close_key_database(void *keydb) +{ + /* We never do close it */ + return 0; +} + +/* + * j a r _ c r e a t e _ p k 7 + * + */ + +static void +jar_pk7_out(void *arg, const char *buf, unsigned long len) +{ + JAR_FWRITE((JAR_FILE)arg, buf, len); +} + +int +jar_create_pk7(CERTCertDBHandle *certdb, void *keydb, CERTCertificate *cert, + char *password, JAR_FILE infp, JAR_FILE outfp) +{ + SEC_PKCS7ContentInfo *cinfo; + const SECHashObject *hashObj; + void *mw = NULL; + void *hashcx; + unsigned int len; + int status = 0; + SECStatus rv; + SECItem digest; + unsigned char digestdata[32]; + unsigned char buffer[4096]; + + if (outfp == NULL || infp == NULL || cert == NULL) + return JAR_ERR_GENERAL; + + /* we sign with SHA */ + hashObj = HASH_GetHashObject(HASH_AlgSHA1); + + hashcx = (*hashObj->create)(); + if (hashcx == NULL) + return JAR_ERR_GENERAL; + + (*hashObj->begin)(hashcx); + while (1) { + int nb = JAR_FREAD(infp, buffer, sizeof buffer); + if (nb == 0) { /* eof */ + break; + } + (*hashObj->update)(hashcx, buffer, nb); + } + (*hashObj->end)(hashcx, digestdata, &len, 32); + (*hashObj->destroy)(hashcx, PR_TRUE); + + digest.data = digestdata; + digest.len = len; + + /* signtool must use any old context it can find since it's + calling from inside javaland. */ + PORT_SetError(0); + cinfo = SEC_PKCS7CreateSignedData(cert, certUsageObjectSigner, NULL, + SEC_OID_SHA1, &digest, NULL, mw); + if (cinfo == NULL) + return JAR_ERR_PK7; + + rv = SEC_PKCS7IncludeCertChain(cinfo, NULL); + if (rv != SECSuccess) { + status = PORT_GetError(); + SEC_PKCS7DestroyContentInfo(cinfo); + return status; + } + + /* Having this here forces signtool to always include signing time. */ + rv = SEC_PKCS7AddSigningTime(cinfo); + /* don't check error */ + PORT_SetError(0); + + /* if calling from mozilla thread*/ + rv = SEC_PKCS7Encode(cinfo, jar_pk7_out, outfp, NULL, NULL, mw); + if (rv != SECSuccess) + status = PORT_GetError(); + SEC_PKCS7DestroyContentInfo(cinfo); + if (rv != SECSuccess) { + return ((status < 0) ? status : JAR_ERR_GENERAL); + } + return 0; +} diff --git a/security/nss/lib/jar/jarver.c b/security/nss/lib/jar/jarver.c new file mode 100644 index 0000000000..38d73c21c0 --- /dev/null +++ b/security/nss/lib/jar/jarver.c @@ -0,0 +1,1167 @@ +/* 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/. */ + +/* + * JARVER + * + * Jarnature Parsing & Verification + */ + +#include "nssrenam.h" +#include "jar.h" +#include "jarint.h" +#include "certdb.h" +#include "certt.h" +#include "secpkcs7.h" +#include "secder.h" + +#define SZ 512 + +static int +jar_validate_pkcs7(JAR *jar, JAR_Signer *signer, char *data, long length); + +static void +jar_catch_bytes(void *arg, const char *buf, unsigned long len); + +static int +jar_gather_signers(JAR *jar, JAR_Signer *signer, SEC_PKCS7ContentInfo *cinfo); + +static char * +jar_eat_line(int lines, int eating, char *data, long *len); + +static JAR_Digest * +jar_digest_section(char *manifest, long length); + +static JAR_Digest *jar_get_mf_digest(JAR *jar, char *path); + +static int +jar_parse_digital_signature(char *raw_manifest, JAR_Signer *signer, + long length, JAR *jar); + +static int +jar_add_cert(JAR *jar, JAR_Signer *signer, int type, CERTCertificate *cert); + +static char *jar_basename(const char *path); + +static int +jar_signal(int status, JAR *jar, const char *metafile, char *pathname); + +#ifdef DEBUG +static int jar_insanity_check(char *data, long length); +#endif + +int +jar_parse_mf(JAR *jar, char *raw_manifest, long length, + const char *path, const char *url); + +int +jar_parse_sf(JAR *jar, char *raw_manifest, long length, + const char *path, const char *url); + +int +jar_parse_sig(JAR *jar, const char *path, char *raw_manifest, + long length); + +int +jar_parse_any(JAR *jar, int type, JAR_Signer *signer, + char *raw_manifest, long length, const char *path, + const char *url); + +static int +jar_internal_digest(JAR *jar, const char *path, char *x_name, JAR_Digest *dig); + +/* + * J A R _ p a r s e _ m a n i f e s t + * + * Pass manifest files to this function. They are + * decoded and placed into internal representations. + * + * Accepts both signature and manifest files. Use + * the same "jar" for both. + * + */ +int +JAR_parse_manifest(JAR *jar, char *raw_manifest, long length, + const char *path, const char *url) +{ + int filename_free = 0; + + /* fill in the path, if supplied. This is the location + of the jar file on disk, if known */ + + if (jar->filename == NULL && path) { + jar->filename = PORT_Strdup(path); + if (jar->filename == NULL) + return JAR_ERR_MEMORY; + filename_free = 1; + } + + /* fill in the URL, if supplied. This is the place + from which the jar file was retrieved. */ + + if (jar->url == NULL && url) { + jar->url = PORT_Strdup(url); + if (jar->url == NULL) { + if (filename_free) { + PORT_Free(jar->filename); + } + return JAR_ERR_MEMORY; + } + } + + /* Determine what kind of file this is from the META-INF + directory. It could be MF, SF, or a binary RSA/DSA file */ + + if (!PORT_Strncasecmp(raw_manifest, "Manifest-Version:", 17)) { + return jar_parse_mf(jar, raw_manifest, length, path, url); + } else if (!PORT_Strncasecmp(raw_manifest, "Signature-Version:", 18)) { + return jar_parse_sf(jar, raw_manifest, length, path, url); + } else { + /* This is probably a binary signature */ + return jar_parse_sig(jar, path, raw_manifest, length); + } +} + +/* + * j a r _ p a r s e _ s i g + * + * Pass some manner of RSA or DSA digital signature + * on, after checking to see if it comes at an appropriate state. + * + */ +int +jar_parse_sig(JAR *jar, const char *path, char *raw_manifest, + long length) +{ + JAR_Signer *signer; + int status = JAR_ERR_ORDER; + + if (length <= 128) { + /* signature is way too small */ + return JAR_ERR_SIG; + } + + /* make sure that MF and SF have already been processed */ + + if (jar->globalmeta == NULL) + return JAR_ERR_ORDER; + + /* Determine whether or not this RSA file has + has an associated SF file */ + + if (path) { + char *owner; + owner = jar_basename(path); + + if (owner == NULL) + return JAR_ERR_MEMORY; + + signer = jar_get_signer(jar, owner); + PORT_Free(owner); + } else + signer = jar_get_signer(jar, "*"); + + if (signer == NULL) + return JAR_ERR_ORDER; + + /* Do not pass a huge pointer to this function, + since the underlying security code is unaware. We will + never pass >64k through here. */ + + if (length > 64000) { + /* this digital signature is way too big */ + return JAR_ERR_SIG; + } + + /* don't expense unneeded calloc overhead on non-win16 */ + status = jar_parse_digital_signature(raw_manifest, signer, length, jar); + + return status; +} + +/* + * j a r _ p a r s e _ m f + * + * Parse the META-INF/manifest.mf file, whose + * information applies to all signers. + * + */ +int +jar_parse_mf(JAR *jar, char *raw_manifest, long length, + const char *path, const char *url) +{ + if (jar->globalmeta) { + /* refuse a second manifest file, if passed for some reason */ + return JAR_ERR_ORDER; + } + + /* remember a digest for the global section */ + jar->globalmeta = jar_digest_section(raw_manifest, length); + if (jar->globalmeta == NULL) + return JAR_ERR_MEMORY; + return jar_parse_any(jar, jarTypeMF, NULL, raw_manifest, length, + path, url); +} + +/* + * j a r _ p a r s e _ s f + * + * Parse META-INF/xxx.sf, a digitally signed file + * pointing to a subset of MF sections. + * + */ +int +jar_parse_sf(JAR *jar, char *raw_manifest, long length, + const char *path, const char *url) +{ + JAR_Signer *signer = NULL; + int status = JAR_ERR_MEMORY; + + if (jar->globalmeta == NULL) { + /* It is a requirement that the MF file be passed before the SF file */ + return JAR_ERR_ORDER; + } + + signer = JAR_new_signer(); + if (signer == NULL) + goto loser; + + if (path) { + signer->owner = jar_basename(path); + if (signer->owner == NULL) + goto loser; + } + + /* check for priors. When someone doctors a jar file + to contain identical path entries, prevent the second + one from affecting JAR functions */ + if (jar_get_signer(jar, signer->owner)) { + /* someone is trying to spoof us */ + status = JAR_ERR_ORDER; + goto loser; + } + + /* remember its digest */ + signer->digest = JAR_calculate_digest(raw_manifest, length); + if (signer->digest == NULL) + goto loser; + + /* Add this signer to the jar */ + ADDITEM(jar->signers, jarTypeOwner, signer->owner, signer, + sizeof(JAR_Signer)); + + return jar_parse_any(jar, jarTypeSF, signer, raw_manifest, length, + path, url); + +loser: + if (signer) + JAR_destroy_signer(signer); + return status; +} + +/* + * j a r _ p a r s e _ a n y + * + * Parse a MF or SF manifest file. + * + */ +int +jar_parse_any(JAR *jar, int type, JAR_Signer *signer, + char *raw_manifest, long length, const char *path, + const char *url) +{ + int status; + long raw_len; + JAR_Digest *dig, *mfdig = NULL; + char line[SZ]; + char x_name[SZ], x_md5[SZ], x_sha[SZ]; + char *x_info; + char *sf_md5 = NULL, *sf_sha1 = NULL; + + *x_name = 0; + *x_md5 = 0; + *x_sha = 0; + + PORT_Assert(length > 0); + raw_len = length; + +#ifdef DEBUG + if ((status = jar_insanity_check(raw_manifest, raw_len)) < 0) + return status; +#endif + + /* null terminate the first line */ + raw_manifest = jar_eat_line(0, PR_TRUE, raw_manifest, &raw_len); + + /* skip over the preliminary section */ + /* This is one section at the top of the file with global metainfo */ + while (raw_len > 0) { + JAR_Metainfo *met; + + raw_manifest = jar_eat_line(1, PR_TRUE, raw_manifest, &raw_len); + if (raw_len <= 0 || !*raw_manifest) + break; + + met = PORT_ZNew(JAR_Metainfo); + if (met == NULL) + return JAR_ERR_MEMORY; + + /* Parse out the header & info */ + if (PORT_Strlen(raw_manifest) >= SZ) { + /* almost certainly nonsense */ + PORT_Free(met); + continue; + } + + PORT_Strcpy(line, raw_manifest); + x_info = line; + + while (*x_info && *x_info != ' ' && *x_info != '\t' && *x_info != ':') + x_info++; + + if (*x_info) + *x_info++ = 0; + + while (*x_info == ' ' || *x_info == '\t') + x_info++; + + /* metainfo (name, value) pair is now (line, x_info) */ + met->header = PORT_Strdup(line); + met->info = PORT_Strdup(x_info); + + if (type == jarTypeMF) { + ADDITEM(jar->metainfo, jarTypeMeta, + /* pathname */ NULL, met, sizeof(JAR_Metainfo)); + } + + /* For SF files, this metadata may be the digests + of the MF file, still in the "met" structure. */ + + if (type == jarTypeSF) { + if (!PORT_Strcasecmp(line, "MD5-Digest")) { + sf_md5 = (char *)met->info; + } else if (!PORT_Strcasecmp(line, "SHA1-Digest") || + !PORT_Strcasecmp(line, "SHA-Digest")) { + sf_sha1 = (char *)met->info; + } else { + PORT_Free(met->info); + met->info = NULL; + } + } + + if (type != jarTypeMF) { + PORT_Free(met->header); + if ((type != jarTypeSF || !jar->globalmeta) && met->info) { + PORT_Free(met->info); + } + PORT_Free(met); + } + } + + if (type == jarTypeSF && jar->globalmeta) { + /* this is a SF file which may contain a digest of the manifest.mf's + global metainfo. */ + + int match = 0; + JAR_Digest *glob = jar->globalmeta; + + if (sf_md5) { + unsigned int md5_length; + unsigned char *md5_digest; + + md5_digest = ATOB_AsciiToData(sf_md5, &md5_length); + PORT_Assert(md5_length == MD5_LENGTH); + PORT_Free(sf_md5); + + if (md5_length != MD5_LENGTH) + return JAR_ERR_CORRUPT; + + match = PORT_Memcmp(md5_digest, glob->md5, MD5_LENGTH); + PORT_Free(md5_digest); + } + + if (sf_sha1 && match == 0) { + unsigned int sha1_length; + unsigned char *sha1_digest; + + sha1_digest = ATOB_AsciiToData(sf_sha1, &sha1_length); + PORT_Assert(sha1_length == SHA1_LENGTH); + PORT_Free(sf_sha1); + + if (sha1_length != SHA1_LENGTH) + return JAR_ERR_CORRUPT; + + match = PORT_Memcmp(sha1_digest, glob->sha1, SHA1_LENGTH); + PORT_Free(sha1_digest); + } + + if (match != 0) { + /* global digest doesn't match, SF file therefore invalid */ + jar->valid = JAR_ERR_METADATA; + return JAR_ERR_METADATA; + } + } + + /* done with top section of global data */ + while (raw_len > 0) { + *x_md5 = 0; + *x_sha = 0; + *x_name = 0; + + /* If this is a manifest file, attempt to get a digest of the following + section, without damaging it. This digest will be saved later. */ + + if (type == jarTypeMF) { + char *sec; + long sec_len = raw_len; + + if (!*raw_manifest || *raw_manifest == '\n') { + /* skip the blank line */ + sec = jar_eat_line(1, PR_FALSE, raw_manifest, &sec_len); + } else + sec = raw_manifest; + + if (sec_len > 0 && !PORT_Strncasecmp(sec, "Name:", 5)) { + if (type == jarTypeMF) + mfdig = jar_digest_section(sec, sec_len); + else + mfdig = NULL; + } + } + + while (raw_len > 0) { + raw_manifest = jar_eat_line(1, PR_TRUE, raw_manifest, &raw_len); + if (raw_len <= 0 || !*raw_manifest) + break; /* blank line, done with this entry */ + + if (PORT_Strlen(raw_manifest) >= SZ) { + /* almost certainly nonsense */ + continue; + } + + /* Parse out the name/value pair */ + PORT_Strcpy(line, raw_manifest); + x_info = line; + + while (*x_info && *x_info != ' ' && *x_info != '\t' && + *x_info != ':') + x_info++; + + if (*x_info) + *x_info++ = 0; + + while (*x_info == ' ' || *x_info == '\t') + x_info++; + + if (!PORT_Strcasecmp(line, "Name")) + PORT_Strcpy(x_name, x_info); + else if (!PORT_Strcasecmp(line, "MD5-Digest")) + PORT_Strcpy(x_md5, x_info); + else if (!PORT_Strcasecmp(line, "SHA1-Digest") || + !PORT_Strcasecmp(line, "SHA-Digest")) + PORT_Strcpy(x_sha, x_info); + + /* Algorithm list is meta info we don't care about; keeping it out + of metadata saves significant space for large jar files */ + else if (!PORT_Strcasecmp(line, "Digest-Algorithms") || + !PORT_Strcasecmp(line, "Hash-Algorithms")) + continue; + + /* Meta info is only collected for the manifest.mf file, + since the JAR_get_metainfo call does not support identity */ + else if (type == jarTypeMF) { + JAR_Metainfo *met; + + /* this is meta-data */ + met = PORT_ZNew(JAR_Metainfo); + if (met == NULL) + return JAR_ERR_MEMORY; + + /* metainfo (name, value) pair is now (line, x_info) */ + if ((met->header = PORT_Strdup(line)) == NULL) { + PORT_Free(met); + return JAR_ERR_MEMORY; + } + + if ((met->info = PORT_Strdup(x_info)) == NULL) { + PORT_Free(met->header); + PORT_Free(met); + return JAR_ERR_MEMORY; + } + + ADDITEM(jar->metainfo, jarTypeMeta, + x_name, met, sizeof(JAR_Metainfo)); + } + } + + if (!*x_name) { + /* Whatever that was, it wasn't an entry, because we didn't get a + name. We don't really have anything, so don't record this. */ + continue; + } + + dig = PORT_ZNew(JAR_Digest); + if (dig == NULL) + return JAR_ERR_MEMORY; + + if (*x_md5) { + unsigned int binary_length; + unsigned char *binary_digest; + + binary_digest = ATOB_AsciiToData(x_md5, &binary_length); + PORT_Assert(binary_length == MD5_LENGTH); + if (binary_length != MD5_LENGTH) { + PORT_Free(dig); + return JAR_ERR_CORRUPT; + } + memcpy(dig->md5, binary_digest, MD5_LENGTH); + dig->md5_status = jarHashPresent; + PORT_Free(binary_digest); + } + + if (*x_sha) { + unsigned int binary_length; + unsigned char *binary_digest; + + binary_digest = ATOB_AsciiToData(x_sha, &binary_length); + PORT_Assert(binary_length == SHA1_LENGTH); + if (binary_length != SHA1_LENGTH) { + PORT_Free(dig); + return JAR_ERR_CORRUPT; + } + memcpy(dig->sha1, binary_digest, SHA1_LENGTH); + dig->sha1_status = jarHashPresent; + PORT_Free(binary_digest); + } + + PORT_Assert(type == jarTypeMF || type == jarTypeSF); + if (type == jarTypeMF) { + ADDITEM(jar->hashes, jarTypeMF, x_name, dig, sizeof(JAR_Digest)); + } else if (type == jarTypeSF) { + ADDITEM(signer->sf, jarTypeSF, x_name, dig, sizeof(JAR_Digest)); + } else { + PORT_Free(dig); + return JAR_ERR_ORDER; + } + + /* we're placing these calculated digests of manifest.mf + sections in a list where they can subsequently be forgotten */ + if (type == jarTypeMF && mfdig) { + ADDITEM(jar->manifest, jarTypeSect, + x_name, mfdig, sizeof(JAR_Digest)); + mfdig = NULL; + } + + /* Retrieve our saved SHA1 digest from saved copy and check digests. + This is just comparing the digest of the MF section as indicated in + the SF file with the one we remembered from parsing the MF file */ + + if (type == jarTypeSF) { + if ((status = jar_internal_digest(jar, path, x_name, dig)) < 0) + return status; + } + } + + return 0; +} + +static int +jar_internal_digest(JAR *jar, const char *path, char *x_name, JAR_Digest *dig) +{ + int cv; + int status; + + JAR_Digest *savdig; + + savdig = jar_get_mf_digest(jar, x_name); + if (savdig == NULL) { + /* no .mf digest for this pathname */ + status = jar_signal(JAR_ERR_ENTRY, jar, path, x_name); + if (status < 0) + return 0; /* was continue; */ + return status; + } + + /* check for md5 consistency */ + if (dig->md5_status) { + cv = PORT_Memcmp(savdig->md5, dig->md5, MD5_LENGTH); + /* md5 hash of .mf file is not what expected */ + if (cv) { + status = jar_signal(JAR_ERR_HASH, jar, path, x_name); + + /* bad hash, man */ + dig->md5_status = jarHashBad; + savdig->md5_status = jarHashBad; + + if (status < 0) + return 0; /* was continue; */ + return status; + } + } + + /* check for sha1 consistency */ + if (dig->sha1_status) { + cv = PORT_Memcmp(savdig->sha1, dig->sha1, SHA1_LENGTH); + /* sha1 hash of .mf file is not what expected */ + if (cv) { + status = jar_signal(JAR_ERR_HASH, jar, path, x_name); + + /* bad hash, man */ + dig->sha1_status = jarHashBad; + savdig->sha1_status = jarHashBad; + + if (status < 0) + return 0; /* was continue; */ + return status; + } + } + return 0; +} + +#ifdef DEBUG +/* + * j a r _ i n s a n i t y _ c h e c k + * + * Check for illegal characters (or possibly so) + * in the manifest files, to detect potential memory + * corruption by our neighbors. Debug only, since + * not I18N safe. + * + */ +static int +jar_insanity_check(char *data, long length) +{ + int c; + long off; + + for (off = 0; off < length; off++) { + c = data[off]; + if (c == '\n' || c == '\r' || (c >= ' ' && c <= 128)) + continue; + return JAR_ERR_CORRUPT; + } + return 0; +} +#endif + +/* + * j a r _ p a r s e _ d i g i t a l _ s i g n a t u r e + * + * Parse an RSA or DSA (or perhaps other) digital signature. + * Right now everything is PKCS7. + * + */ +static int +jar_parse_digital_signature(char *raw_manifest, JAR_Signer *signer, + long length, JAR *jar) +{ + return jar_validate_pkcs7(jar, signer, raw_manifest, length); +} + +/* + * j a r _ a d d _ c e r t + * + * Add information for the given certificate + * (or whatever) to the JAR linked list. A pointer + * is passed for some relevant reference, say + * for example the original certificate. + * + */ +static int +jar_add_cert(JAR *jar, JAR_Signer *signer, int type, CERTCertificate *cert) +{ + JAR_Cert *fing; + unsigned char *keyData; + + if (cert == NULL) + return JAR_ERR_ORDER; + + fing = PORT_ZNew(JAR_Cert); + if (fing == NULL) + goto loser; + + fing->cert = CERT_DupCertificate(cert); + + /* get the certkey */ + fing->length = cert->derIssuer.len + 2 + cert->serialNumber.len; + fing->key = keyData = (unsigned char *)PORT_ZAlloc(fing->length); + if (fing->key == NULL) + goto loser; + keyData[0] = ((cert->derIssuer.len) >> 8) & 0xff; + keyData[1] = ((cert->derIssuer.len) & 0xff); + PORT_Memcpy(&keyData[2], cert->derIssuer.data, cert->derIssuer.len); + PORT_Memcpy(&keyData[2 + cert->derIssuer.len], cert->serialNumber.data, + cert->serialNumber.len); + + ADDITEM(signer->certs, type, NULL, fing, sizeof(JAR_Cert)); + return 0; + +loser: + if (fing) { + if (fing->cert) + CERT_DestroyCertificate(fing->cert); + PORT_Free(fing); + } + return JAR_ERR_MEMORY; +} + +/* + * e a t _ l i n e + * + * Reads and/or modifies input buffer "data" of length "*len". + * This function does zero, one or two of the following tasks: + * 1) if "lines" is non-zero, it reads and discards that many lines from + * the input. NUL characters are treated as end-of-line characters, + * not as end-of-input characters. The input is NOT NUL terminated. + * Note: presently, all callers pass either 0 or 1 for lines. + * 2) After skipping the specified number of input lines, if "eating" is + * non-zero, it finds the end of the next line of input and replaces + * the end of line character(s) with a NUL character. + * This function modifies the input buffer, containing the file, in place. + * This function handles PC, Mac, and Unix style text files. + * On entry, *len contains the maximum number of characters that this + * function should ever examine, starting with the character in *data. + * On return, *len is reduced by the number of characters skipped by the + * first task, if any; + * If lines is zero and eating is false, this function returns + * the value in the data argument, but otherwise does nothing. + */ +static char * +jar_eat_line(int lines, int eating, char *data, long *len) +{ + char *start = data; + long maxLen = *len; + + if (maxLen <= 0) + return start; + +#define GO_ON ((data - start) < maxLen) + + /* Eat the requisite number of lines, if any; + prior to terminating the current line with a 0. */ + for (/* yip */; lines > 0; lines--) { + while (GO_ON && *data && *data != '\r' && *data != '\n') + data++; + + /* Eat any leading CR */ + if (GO_ON && *data == '\r') + data++; + + /* After the CR, ok to eat one LF */ + if (GO_ON && *data == '\n') + data++; + + /* If there are NULs, this function probably put them there */ + while (GO_ON && !*data) + data++; + } + maxLen -= data - start; /* we have this many characters left. */ + *len = maxLen; + start = data; /* now start again here. */ + if (maxLen > 0 && eating) { + /* Terminate this line with a 0 */ + while (GO_ON && *data && *data != '\n' && *data != '\r') + data++; + + /* If not past the end, we are allowed to eat one CR */ + if (GO_ON && *data == '\r') + *data++ = 0; + + /* After the CR (if any), if not past the end, ok to eat one LF */ + if (GO_ON && *data == '\n') + *data++ = 0; + } + return start; +} +#undef GO_ON + +/* + * j a r _ d i g e s t _ s e c t i o n + * + * Return the digests of the next section of the manifest file. + * Does not damage the manifest file, unlike parse_manifest. + * + */ +static JAR_Digest * +jar_digest_section(char *manifest, long length) +{ + long global_len; + char *global_end; + + global_end = manifest; + global_len = length; + + while (global_len > 0) { + global_end = jar_eat_line(1, PR_FALSE, global_end, &global_len); + if (global_len > 0 && (*global_end == 0 || *global_end == '\n')) + break; + } + return JAR_calculate_digest(manifest, global_end - manifest); +} + +/* + * J A R _ v e r i f y _ d i g e s t + * + * Verifies that a precalculated digest matches the + * expected value in the manifest. + * + */ +int PR_CALLBACK +JAR_verify_digest(JAR *jar, const char *name, JAR_Digest *dig) +{ + JAR_Item *it; + JAR_Digest *shindig; + ZZLink *link; + ZZList *list = jar->hashes; + int result1 = 0; + int result2 = 0; + + if (jar->valid < 0) { + /* signature not valid */ + return JAR_ERR_SIG; + } + if (ZZ_ListEmpty(list)) { + /* empty list */ + return JAR_ERR_PNF; + } + + for (link = ZZ_ListHead(list); + !ZZ_ListIterDone(list, link); + link = link->next) { + it = link->thing; + if (it->type == jarTypeMF && + it->pathname && !PORT_Strcmp(it->pathname, name)) { + shindig = (JAR_Digest *)it->data; + if (shindig->md5_status) { + if (shindig->md5_status == jarHashBad) + return JAR_ERR_HASH; + result1 = memcmp(dig->md5, shindig->md5, MD5_LENGTH); + } + if (shindig->sha1_status) { + if (shindig->sha1_status == jarHashBad) + return JAR_ERR_HASH; + result2 = memcmp(dig->sha1, shindig->sha1, SHA1_LENGTH); + } + return (result1 == 0 && result2 == 0) ? 0 : JAR_ERR_HASH; + } + } + return JAR_ERR_PNF; +} + +/* + * J A R _ f e t c h _ c e r t + * + * Given an opaque identifier of a certificate, + * return the full certificate. + * + * The new function, which retrieves by key. + * + */ +CERTCertificate * +JAR_fetch_cert(long length, void *key) +{ + CERTIssuerAndSN issuerSN; + CERTCertificate *cert = NULL; + CERTCertDBHandle *certdb; + + certdb = JAR_open_database(); + if (certdb) { + unsigned char *keyData = (unsigned char *)key; + issuerSN.derIssuer.len = (keyData[0] << 8) + keyData[0]; + issuerSN.derIssuer.data = &keyData[2]; + issuerSN.serialNumber.len = length - (2 + issuerSN.derIssuer.len); + issuerSN.serialNumber.data = &keyData[2 + issuerSN.derIssuer.len]; + cert = CERT_FindCertByIssuerAndSN(certdb, &issuerSN); + JAR_close_database(certdb); + } + return cert; +} + +/* + * j a r _ g e t _ m f _ d i g e s t + * + * Retrieve a corresponding saved digest over a section + * of the main manifest file. + * + */ +static JAR_Digest * +jar_get_mf_digest(JAR *jar, char *pathname) +{ + JAR_Item *it; + JAR_Digest *dig; + ZZLink *link; + ZZList *list = jar->manifest; + + if (ZZ_ListEmpty(list)) + return NULL; + + for (link = ZZ_ListHead(list); + !ZZ_ListIterDone(list, link); + link = link->next) { + it = link->thing; + if (it->type == jarTypeSect && + it->pathname && !PORT_Strcmp(it->pathname, pathname)) { + dig = (JAR_Digest *)it->data; + return dig; + } + } + return NULL; +} + +/* + * j a r _ b a s e n a m e + * + * Return the basename -- leading components of path stripped off, + * extension ripped off -- of a path. + * + */ +static char * +jar_basename(const char *path) +{ + char *pith, *e, *basename, *ext; + + if (path == NULL) + return PORT_Strdup(""); + + pith = PORT_Strdup(path); + basename = pith; + while (1) { + for (e = basename; *e && *e != '/' && *e != '\\'; e++) + /* yip */; + if (*e) + basename = ++e; + else + break; + } + + if ((ext = PORT_Strrchr(basename, '.')) != NULL) + *ext = 0; + + /* We already have the space allocated */ + PORT_Strcpy(pith, basename); + return pith; +} + +/* + * + + + + + + + + + + + + + + + + * + * CRYPTO ROUTINES FOR JAR + * + * The following functions are the cryptographic + * interface to PKCS7 for Jarnatures. + * + * + + + + + + + + + + + + + + + + * + */ + +/* + * j a r _ c a t c h _ b y t e s + * + * In the event signatures contain enveloped data, it will show up here. + * But note that the lib/pkcs7 routines aren't ready for it. + * + */ +static void +jar_catch_bytes(void *arg, const char *buf, unsigned long len) +{ + /* Actually this should never be called, since there is + presumably no data in the signature itself. */ +} + +/* + * j a r _ v a l i d a t e _ p k c s 7 + * + * Validate (and decode, if necessary) a binary pkcs7 + * signature in DER format. + * + */ +static int +jar_validate_pkcs7(JAR *jar, JAR_Signer *signer, char *data, long length) +{ + + SEC_PKCS7ContentInfo *cinfo = NULL; + SEC_PKCS7DecoderContext *dcx; + PRBool goodSig; + int status = 0; + SECItem detdig; + + PORT_Assert(jar != NULL && signer != NULL); + + if (jar == NULL || signer == NULL) + return JAR_ERR_ORDER; + + signer->valid = JAR_ERR_SIG; + + /* We need a context if we can get one */ + dcx = SEC_PKCS7DecoderStart(jar_catch_bytes, NULL /*cb_arg*/, + NULL /*getpassword*/, jar->mw, + NULL, NULL, NULL); + if (dcx == NULL) { + /* strange pkcs7 failure */ + return JAR_ERR_PK7; + } + + SEC_PKCS7DecoderUpdate(dcx, data, length); + cinfo = SEC_PKCS7DecoderFinish(dcx); + if (cinfo == NULL) { + /* strange pkcs7 failure */ + return JAR_ERR_PK7; + } + if (SEC_PKCS7ContentIsEncrypted(cinfo)) { + /* content was encrypted, fail */ + return JAR_ERR_PK7; + } + if (SEC_PKCS7ContentIsSigned(cinfo) == PR_FALSE) { + /* content was not signed, fail */ + return JAR_ERR_PK7; + } + + PORT_SetError(0); + + /* use SHA1 only */ + detdig.len = SHA1_LENGTH; + detdig.data = signer->digest->sha1; + goodSig = SEC_PKCS7VerifyDetachedSignature(cinfo, + certUsageObjectSigner, + &detdig, HASH_AlgSHA1, + PR_FALSE); + jar_gather_signers(jar, signer, cinfo); + if (goodSig == PR_TRUE) { + /* signature is valid */ + signer->valid = 0; + } else { + status = PORT_GetError(); + PORT_Assert(status < 0); + if (status >= 0) + status = JAR_ERR_SIG; + jar->valid = status; + signer->valid = status; + } + jar->pkcs7 = PR_TRUE; + signer->pkcs7 = PR_TRUE; + SEC_PKCS7DestroyContentInfo(cinfo); + return status; +} + +/* + * j a r _ g a t h e r _ s i g n e r s + * + * Add the single signer of this signature to the + * certificate linked list. + * + */ +static int +jar_gather_signers(JAR *jar, JAR_Signer *signer, SEC_PKCS7ContentInfo *cinfo) +{ + int result; + CERTCertificate *cert; + CERTCertDBHandle *certdb; + SEC_PKCS7SignedData *sdp = cinfo->content.signedData; + SEC_PKCS7SignerInfo **pksigners, *pksigner; + + if (sdp == NULL) + return JAR_ERR_PK7; + + pksigners = sdp->signerInfos; + /* permit exactly one signer */ + if (pksigners == NULL || pksigners[0] == NULL || pksigners[1] != NULL) + return JAR_ERR_PK7; + + pksigner = *pksigners; + cert = pksigner->cert; + + if (cert == NULL) + return JAR_ERR_PK7; + + certdb = JAR_open_database(); + if (certdb == NULL) + return JAR_ERR_GENERAL; + + result = jar_add_cert(jar, signer, jarTypeSign, cert); + JAR_close_database(certdb); + return result; +} + +/* + * j a r _ o p e n _ d a t a b a s e + * + * Open the certificate database, + * for use by JAR functions. + * + */ +CERTCertDBHandle * +JAR_open_database(void) +{ + return CERT_GetDefaultCertDB(); +} + +/* + * j a r _ c l o s e _ d a t a b a s e + * + * Close the certificate database. + * For use by JAR functions. + * + */ +int +JAR_close_database(CERTCertDBHandle *certdb) +{ + return 0; +} + +/* + * j a r _ s i g n a l + * + * Nonfatal errors come here to callback Java. + * + */ +static int +jar_signal(int status, JAR *jar, const char *metafile, char *pathname) +{ + char *errstring = JAR_get_error(status); + if (jar->signal) { + (*jar->signal)(status, jar, metafile, pathname, errstring); + return 0; + } + return status; +} + +/* + * j a r _ a p p e n d + * + * Tack on an element to one of a JAR's linked + * lists, with rudimentary error handling. + * + */ +int +jar_append(ZZList *list, int type, char *pathname, void *data, size_t size) +{ + JAR_Item *it = PORT_ZNew(JAR_Item); + ZZLink *entity; + + if (it == NULL) + goto loser; + + if (pathname) { + it->pathname = PORT_Strdup(pathname); + if (it->pathname == NULL) + goto loser; + } + + it->type = (jarType)type; + it->data = (unsigned char *)data; + it->size = size; + entity = ZZ_NewLink(it); + if (entity) { + ZZ_AppendLink(list, entity); + return 0; + } + +loser: + if (it) { + if (it->pathname) + PORT_Free(it->pathname); + PORT_Free(it); + } + return JAR_ERR_MEMORY; +} diff --git a/security/nss/lib/jar/jzconf.h b/security/nss/lib/jar/jzconf.h new file mode 100644 index 0000000000..7687eb3a59 --- /dev/null +++ b/security/nss/lib/jar/jzconf.h @@ -0,0 +1,189 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-1996 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ +/* This file was modified since it was taken from the zlib distribution */ + +#ifndef _ZCONF_H +#define _ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +#define deflateInit_ z_deflateInit_ +#define deflate z_deflate +#define deflateEnd z_deflateEnd +#define inflateInit_ z_inflateInit_ +#define inflate z_inflate +#define inflateEnd z_inflateEnd +#define deflateInit2_ z_deflateInit2_ +#define deflateSetDictionary z_deflateSetDictionary +#define deflateCopy z_deflateCopy +#define deflateReset z_deflateReset +#define deflateParams z_deflateParams +#define inflateInit2_ z_inflateInit2_ +#define inflateSetDictionary z_inflateSetDictionary +#define inflateSync z_inflateSync +#define inflateReset z_inflateReset +#define compress z_compress +#define uncompress z_uncompress +#define adler32 z_adler32 +#define crc32 z_crc32 +#define get_crc_table z_get_crc_table + +#define Byte z_Byte +#define uInt z_uInt +#define uLong z_uLong +#define Bytef z_Bytef +#define charf z_charf +#define intf z_intf +#define uIntf z_uIntf +#define uLongf z_uLongf +#define voidpf z_voidpf +#define voidp z_voidp +#endif + +#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) +#define WIN32 +#endif +#if defined(__GNUC__) || defined(WIN32) || defined(__386__) || defined(i386) +#ifndef __32BIT__ +#define __32BIT__ +#endif +#endif +#if defined(__MSDOS__) && !defined(MSDOS) +#define MSDOS +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#if defined(MSDOS) && !defined(__32BIT__) +#define MAXSEG_64K +#endif +#ifdef MSDOS +#define UNALIGNED_OK +#endif + +#if (defined(MSDOS) || defined(_WINDOWS) || defined(WIN32) || defined(XP_OS2)) && !defined(STDC) +#define STDC +#endif +#if (defined(__STDC__) || defined(__cplusplus)) && !defined(STDC) +#define STDC +#endif + +#ifndef STDC +#ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +#define const +#endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__) || defined(applec) || defined(THINK_C) || defined(__SC__) +#define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +#ifdef MAXSEG_64K +#define MAX_MEM_LEVEL 8 +#else +#define MAX_MEM_LEVEL 9 +#endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2 */ +#ifndef MAX_WBITS +#define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + 1 << (windowBits+2) + 1 << (memLevel+9) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + +/* Type declarations */ + +#ifndef OF /* function prototypes */ +#ifdef STDC +#define OF(args) args +#else +#define OF(args) () +#endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(__32BIT__) +/* MSC small or medium model */ +#define SMALL_MEDIUM +#ifdef _MSC_VER +#define FAR __far +#else +#define FAR far +#endif +#endif +#if defined(__BORLANDC__) && (defined(__SMALL__) || defined(__MEDIUM__)) +#ifndef __32BIT__ +#define SMALL_MEDIUM +#define FAR __far +#endif +#endif +#ifndef FAR +#define FAR +#endif + +typedef unsigned char Byte; /* 8 bits */ +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#if defined(__BORLANDC__) && defined(SMALL_MEDIUM) +/* Borland C/C++ ignores FAR inside typedef */ +#define Bytef Byte FAR +#else +typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC +typedef void FAR *voidpf; +typedef void *voidp; +#else +typedef Byte FAR *voidpf; +typedef Byte *voidp; +#endif + +#ifdef MOZILLA_CLIENT +#include "prtypes.h" +#else +/* Compile with -DZLIB_DLL for Windows DLL support */ +#if (defined(_WINDOWS) || defined(WINDOWS)) && defined(ZLIB_DLL) +#include <windows.h> +#define EXPORT WINAPI +#else +#define EXPORT +#endif + +#define PR_PUBLIC_API(type) type + +#endif /* MOZILLA_CLIENT */ + +#endif /* _ZCONF_H */ diff --git a/security/nss/lib/jar/jzlib.h b/security/nss/lib/jar/jzlib.h new file mode 100644 index 0000000000..92d3d5b21e --- /dev/null +++ b/security/nss/lib/jar/jzlib.h @@ -0,0 +1,916 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.0.4, Jul 24th, 1996. + + Copyright (C) 1995-1996 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + gzip@prep.ai.mit.edu madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ +/* This file was modified since it was taken from the zlib distribution */ + +#ifndef _ZLIB_H +#define _ZLIB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef MOZILLA_CLIENT +#include "jzconf.h" +#else +#include "zconf.h" +#endif + +#define ZLIB_VERSION "1.0.4" + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms may be added later and will have the same + stream interface. + + For compression the application must provide the output buffer and + may optionally provide the input buffer for optimization. For decompression, + the application must provide the input buffer and may optionally provide + the output buffer for optimization. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The library does not install any signal handler. It is recommended to + add at least a handler for SIGSEGV when decompressing; the library checks + the consistency of the input data whenever possible but may go nuts + for some forms of corrupted input. +*/ + +typedef voidpf(*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void(*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: ascii or binary */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + +/* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +/* Allowed flush values; see deflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 +/* Possible values of the data_type field */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + +/* basic functions */ + +#ifdef MOZILLA_CLIENT +PR_EXTERN(const char *) +zlibVersion(void); +#else +extern const char *EXPORT zlibVersion OF((void)); +#endif +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +extern int EXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + +#ifdef MOZILLA_CLIENT +PR_EXTERN(int) +deflate(z_streamp strm, int flush); +#else +extern int EXPORT deflate OF((z_streamp strm, int flush)); +#endif +/* + Performs one or both of the following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + If the parameter flush is set to Z_PARTIAL_FLUSH, the current compression + block is terminated and flushed to the output buffer so that the + decompressor can get all input data available so far. For method 9, a future + variant on method 8, the current block will be flushed but not terminated. + Z_SYNC_FLUSH has the same effect as partial flush except that the compressed + output is byte aligned (the compressor can clear its internal bit buffer) + and the current block is always terminated; this can be useful if the + compressor has to be restarted from scratch after an interruption (in which + case the internal state of the compressor may be lost). + If flush is set to Z_FULL_FLUSH, the compression block is terminated, a + special marker is output and the compression dictionary is discarded; this + is useful to allow the decompressor to synchronize if one compressed block + has been damaged (see inflateSync below). Flushing degrades compression and + so should be used only when necessary. Using Z_FULL_FLUSH too often can + seriously degrade the compression. If deflate returns with avail_out == 0, + this function must be called again with the same value of the flush + parameter and more output space (updated avail_out), until the flush is + complete (deflate returns with non-zero avail_out). + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + 0.1% larger than avail_in plus 12 bytes. If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() may update data_type if it can make a good guess about + the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible. +*/ + +#ifdef MOZILLA_CLIENT +PR_EXTERN(int) +deflateEnd(z_streamp strm); +#else +extern int EXPORT deflateEnd OF((z_streamp strm)); +#endif +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + +/* +extern int EXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, inflateInit updates them to use default + allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_VERSION_ERROR if the zlib library version is incompatible + with the version assumed by the caller. msg is set to null if there is no + error message. inflateInit does not perform any decompression: this will be + done by inflate(). +*/ + +#ifdef MOZILLA_CLIENT +PR_EXTERN(int) +inflate(z_streamp strm, int flush); +#else +extern int EXPORT inflate OF((z_streamp strm, int flush)); +#endif +/* + Performs one or both of the following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + If the parameter flush is set to Z_PARTIAL_FLUSH, inflate flushes as much + output as possible to the output buffer. The flushing behavior of inflate is + not specified for values of the flush parameter other than Z_PARTIAL_FLUSH + and Z_FINISH, but the current implementation actually flushes as much output + as possible anyway. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster routine + may be used for the single inflate() call. + + inflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if the end of the + compressed data has been reached and all uncompressed output has been + produced, Z_NEED_DICT if a preset dictionary is needed at this point (see + inflateSetDictionary below), Z_DATA_ERROR if the input data was corrupted, + Z_STREAM_ERROR if the stream structure was inconsistent (for example if + next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in + the output buffer when Z_FINISH is used. In the Z_DATA_ERROR case, the + application may then call inflateSync to look for a good compression block. + In the Z_NEED_DICT case, strm->adler is set to the Adler32 value of the + dictionary chosen by the compressor. +*/ + +#ifdef MOZILLA_CLIENT +PR_EXTERN(int) +inflateEnd(z_streamp strm); +#else +extern int EXPORT inflateEnd OF((z_streamp strm)); +#endif +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + +/* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +extern int EXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. (Method 9 will allow a 64K history buffer and + partial block flushes.) + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library (the value 16 will be allowed for method 9). Larger + values of this parameter result in better compression at the expense of + memory usage. The default value is 15 if deflateInit is used instead. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match). Filtered data consists mostly of small values with a + somewhat random distribution. In this case, the compression algorithm is + tuned to compress them better. The effect of Z_FILTERED is to force more + Huffman coding and less string matching; it is somewhat intermediate + between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects + the compression ratio but not the correctness of the compressed output even + if it is not set appropriately. + + If next_in is not null, the library will use this buffer to hold also + some history information; the buffer must either hold the entire input + data, or have at least 1<<(windowBits+1) bytes and be writable. If next_in + is null, the library will allocate its own history buffer (and leave next_in + null). next_out need not be provided here but must be provided by the + application for the next call of deflate(). + + If the history buffer is provided by the application, next_in must + must never be changed by the application since the compressor maintains + information inside this buffer from call to call; the application + must provide more input only by increasing avail_in. next_in is always + reset by the library in this case. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was + not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as + an invalid method). msg is set to null if there is no error message. + deflateInit2 does not perform any compression: this will be done by + deflate(). +*/ + +#ifdef MOZILLA_CLIENT +PR_EXTERN(int) +deflateSetDictionary(z_streamp strm, + const Bytef *dictionary, + uInt dictLength); +#else +extern int EXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +#endif +/* + Initializes the compression dictionary (history buffer) from the given + byte sequence without producing any compressed output. This function must + be called immediately after deflateInit or deflateInit2, before any call + of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and + can be predicted with good accuracy; the data can then be compressed better + than with the default empty dictionary. In this version of the library, + only the last 32K bytes of the dictionary are used. + Upon return of this function, strm->adler is set to the Adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The Adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state + is inconsistent (for example if deflate has already been called for this + stream). deflateSetDictionary does not perform any compression: this will + be done by deflate(). +*/ + +#ifdef MOZILLA_CLIENT +PR_EXTERN(int) +deflateCopy(z_streamp dest, z_streamp source); +#else +extern int EXPORT deflateCopy OF((z_streamp dest, z_streamp source)); +#endif +/* + Sets the destination stream as a complete copy of the source stream. If + the source stream is using an application-supplied history buffer, a new + buffer is allocated for the destination stream. The compressed output + buffer is always application-supplied. It's the responsibility of the + application to provide the correct values of next_out and avail_out for the + next call of deflate. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +#ifdef MOZILLA_CLIENT +PR_EXTERN(int) +deflateReset(z_streamp strm); +#else +extern int EXPORT deflateReset OF((z_streamp strm)); +#endif +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +#ifdef MOZILLA_CLIENT +PR_EXTERN(int) +deflateParams(z_streamp strm, int level, int strategy); +#else +extern int EXPORT deflateParams OF((z_streamp strm, int level, int strategy)); +#endif +/* + Dynamically update the compression level and compression strategy. + This can be used to switch between compression and straight copy of + the input data, or to switch to a different kind of input data requiring + a different strategy. If the compression level is changed, the input + available so far is compressed with the old level (and may be flushed); + the new level will take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +/* +extern int EXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with more compression options. The + fields next_out, zalloc, zfree and opaque must be initialized before by + the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library (the value 16 will be allowed soon). The + default value is 15 if inflateInit is used instead. If a compressed stream + with a larger window size is given as input, inflate() will return with + the error code Z_DATA_ERROR instead of trying to allocate a larger window. + + If next_out is not null, the library will use this buffer for the history + buffer; the buffer must either be large enough to hold the entire output + data, or have at least 1<<windowBits bytes. If next_out is null, the + library will allocate its own buffer (and leave next_out null). next_in + need not be provided here but must be provided by the application for the + next call of inflate(). + + If the history buffer is provided by the application, next_out must + never be changed by the application since the decompressor maintains + history information inside this buffer from call to call; the application + can only reset next_out to the beginning of the history buffer when + avail_out is zero and all output has been consumed. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was + not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as + windowBits < 8). msg is set to null if there is no error message. + inflateInit2 does not perform any decompression: this will be done by + inflate(). +*/ + +#ifdef MOZILLA_CLIENT +PR_EXTERN(int) +inflateSetDictionary(z_streamp strm, + const Bytef *dictionary, + uInt dictLength); +#else +extern int EXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +#endif +/* + Initializes the decompression dictionary (history buffer) from the given + uncompressed byte sequence. This function must be called immediately after + a call of inflate if this call returned Z_NEED_DICT. The dictionary chosen + by the compressor can be determined from the Adler32 value returned by this + call of inflate. The compressor and decompressor must use exactly the same + dictionary (see deflateSetDictionary). + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect Adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +#ifdef MOZILLA_CLIENT +PR_EXTERN(int) +inflateSync(z_streamp strm); +#else +extern int EXPORT inflateSync OF((z_streamp strm)); +#endif +/* + Skips invalid compressed data until the special marker (see deflate() + above) can be found, or until all available input is skipped. No output + is provided. + + inflateSync returns Z_OK if the special marker has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no marker has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +#ifdef MOZILLA_CLIENT +PR_EXTERN(int) +inflateReset(z_streamp strm); +#else +extern int EXPORT inflateReset OF((z_streamp strm)); +#endif +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +/* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level, window size, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +#ifdef MOZILLA_CLIENT +PR_EXTERN(int) +compress(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen); +#else +extern int EXPORT compress OF((Bytef * dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +#endif +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least 0.1% larger than + sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +#ifdef MOZILLA_CLIENT +PR_EXTERN(int) +uncompress(Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen); +#else +extern int EXPORT uncompress OF((Bytef * dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +#endif +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ + +typedef voidp gzFile; + +#ifdef MOZILLA_CLIENT +PR_EXTERN(gzFile) +gzopen(const char *path, const char *mode); +#else +extern gzFile EXPORT gzopen OF((const char *path, const char *mode)); +#endif +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9"). gzopen can be used to read a file which is not in gzip format; + in this case gzread will directly read from the file without decompression. + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). +*/ + +#ifdef MOZILLA_CLIENT +PR_EXTERN(gzFile) +gzdopen(int fd, const char *mode); +#else +extern gzFile EXPORT gzdopen OF((int fd, const char *mode)); +#endif +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +#ifdef MOZILLA_CLIENT +PR_EXTERN(int) +gzread(gzFile file, voidp buf, unsigned len); +#else +extern int EXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +#endif +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +#ifdef MOZILLA_CLIENT +PR_EXTERN(int) +gzwrite(gzFile file, const voidp buf, unsigned len); +#else +extern int EXPORT gzwrite OF((gzFile file, const voidp buf, unsigned len)); +#endif +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +#ifdef MOZILLA_CLIENT +PR_EXTERN(int) +gzflush(gzFile file, int flush); +#else +extern int EXPORT gzflush OF((gzFile file, int flush)); +#endif +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +#ifdef MOZILLA_CLIENT +PR_EXTERN(int) +gzclose(gzFile file); +#else +extern int EXPORT gzclose OF((gzFile file)); +#endif +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +#ifdef MOZILLA_CLIENT +PR_EXTERN(const char *) +gzerror(gzFile file, int *errnum); +#else +extern const char *EXPORT gzerror OF((gzFile file, int *errnum)); +#endif +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + +/* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +#ifdef MOZILLA_CLIENT +PR_EXTERN(uLong) +adler32(uLong adler, const Bytef *buf, uInt len); +#else +extern uLong EXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +#endif + +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +#ifdef MOZILLA_CLIENT +PR_EXTERN(uLong) +crc32(uLong crc, const Bytef *buf, uInt len); +#else +extern uLong EXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +#endif +/* + Update a running crc with the bytes buf[0..len-1] and return the updated + crc. If buf is NULL, this function returns the required initial value + for the crc. Pre- and post-conditioning (one's complement) is performed + within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +/* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +#ifdef MOZILLA_CLIENT +PR_EXTERN(int) +deflateInit(z_streamp strm, int level, const char *version, + int stream_size); +PR_EXTERN(int) +inflateInit_(z_streamp strm, const char *version, + int stream_size); +PR_EXTERN(int) +deflateInit2_(z_streamp strm, int level, int method, + int windowBits, int memLevel, int strategy, + const char *version, int stream_size); +PR_EXTERN(int) +inflateInit2_(z_streamp strm, int windowBits, + const char *version, int stream_size); +#else +extern int EXPORT deflateInit_ OF((z_streamp strm, int level, const char *version, + int stream_size)); +extern int EXPORT inflateInit_ OF((z_streamp strm, const char *version, + int stream_size)); +extern int EXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, int strategy, + const char *version, int stream_size)); +extern int EXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +#endif /* MOZILLA_CLIENT */ + +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm), (level), (method), (windowBits), (memLevel), \ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) + +#if !defined(_Z_UTIL_H) && !defined(NO_DUMMY_DECL) +struct internal_state { + int dummy; +}; /* hack for buggy compilers */ +#endif + +uLongf *get_crc_table OF((void)); /* can be used by asm versions of crc32() */ + +#ifdef __cplusplus +} +#endif + +#endif /* _ZLIB_H */ diff --git a/security/nss/lib/jar/manifest.mn b/security/nss/lib/jar/manifest.mn new file mode 100644 index 0000000000..bcfff578dc --- /dev/null +++ b/security/nss/lib/jar/manifest.mn @@ -0,0 +1,26 @@ +# 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/. + +MODULE = nss + +LIBRARY_NAME = jar +SHARED_LIBRARY = $(NULL) + +CORE_DEPTH = ../.. + +CSRCS = \ + jarver.c \ + jarsign.c \ + jar.c \ + jar-ds.c \ + jarfile.c \ + jarint.c \ + $(NULL) + +EXPORTS = jar.h jar-ds.h jarfile.h + +DEFINES = -DMOZILLA_CLIENT=1 + +# This part of the code, including all sub-dirs, can be optimized for size +export ALLOW_OPT_CODE_SIZE = 1 |