Back to index

lightning-sunbird  0.9+nobinonly
jarver.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 /*
00038  *  JARVER
00039  *
00040  *  Jarnature Parsing & Verification
00041  */
00042 
00043 #define USE_MOZ_THREAD
00044 
00045 #include "jar.h"
00046 #include "jarint.h"
00047 
00048 #ifdef USE_MOZ_THREAD
00049 #include "jarevil.h"
00050 #endif
00051 /*#include "cdbhdl.h" */
00052 #include "secder.h"
00053 
00054 /* to use huge pointers in win16 */
00055 
00056 #if !defined(XP_WIN16)
00057 #define xp_HUGE_MEMCPY PORT_Memcpy
00058 #define xp_HUGE_STRCPY PORT_Strcpy
00059 #define xp_HUGE_STRLEN PORT_Strlen
00060 #define xp_HUGE_STRNCASECMP PORT_Strncasecmp
00061 #else
00062 #define xp_HUGE_MEMCPY hmemcpy
00063 int xp_HUGE_STRNCASECMP (char ZHUGEP *buf, char *key, int len);
00064 size_t xp_HUGE_STRLEN (char ZHUGEP *s);
00065 char *xp_HUGE_STRCPY (char *to, char ZHUGEP *from);
00066 #endif
00067 
00068 /* from certdb.h */
00069 #define CERTDB_USER (1<<6)
00070 
00071 #define SZ 512
00072 
00073 static int jar_validate_pkcs7 
00074      (JAR *jar, JAR_Signer *signer, char *data, long length);
00075 
00076 static void jar_catch_bytes
00077      (void *arg, const char *buf, unsigned long len);
00078 
00079 static int jar_gather_signers
00080      (JAR *jar, JAR_Signer *signer, SEC_PKCS7ContentInfo *cinfo);
00081 
00082 static char ZHUGEP *jar_eat_line 
00083      (int lines, int eating, char ZHUGEP *data, long *len);
00084 
00085 static JAR_Digest *jar_digest_section 
00086      (char ZHUGEP *manifest, long length);
00087 
00088 static JAR_Digest *jar_get_mf_digest (JAR *jar, char *path);
00089 
00090 static int jar_parse_digital_signature 
00091      (char *raw_manifest, JAR_Signer *signer, long length, JAR *jar);
00092 
00093 static int jar_add_cert
00094      (JAR *jar, JAR_Signer *signer, int type, CERTCertificate *cert);
00095 
00096 static CERTCertificate *jar_get_certificate
00097             (JAR *jar, long keylen, void *key, int *result);
00098 
00099 static char *jar_cert_element (char *name, char *tag, int occ);
00100 
00101 static char *jar_choose_nickname (CERTCertificate *cert);
00102 
00103 static char *jar_basename (const char *path);
00104 
00105 static int jar_signal 
00106      (int status, JAR *jar, const char *metafile, char *pathname);
00107 
00108 #ifdef DEBUG
00109 static int jar_insanity_check (char ZHUGEP *data, long length);
00110 #endif
00111 
00112 int jar_parse_mf
00113     (JAR *jar, char ZHUGEP *raw_manifest, 
00114         long length, const char *path, const char *url);
00115 
00116 int jar_parse_sf
00117     (JAR *jar, char ZHUGEP *raw_manifest, 
00118         long length, const char *path, const char *url);
00119 
00120 int jar_parse_sig
00121     (JAR *jar, const char *path, char ZHUGEP *raw_manifest, long length);
00122 
00123 int jar_parse_any
00124     (JAR *jar, int type, JAR_Signer *signer, char ZHUGEP *raw_manifest, 
00125         long length, const char *path, const char *url);
00126 
00127 static int jar_internal_digest 
00128      (JAR *jar, const char *path, char *x_name, JAR_Digest *dig);
00129 
00130 /*
00131  *  J A R _ p a r s e _ m a n i f e s t
00132  * 
00133  *  Pass manifest files to this function. They are
00134  *  decoded and placed into internal representations.
00135  * 
00136  *  Accepts both signature and manifest files. Use
00137  *  the same "jar" for both. 
00138  *
00139  */
00140 
00141 int JAR_parse_manifest 
00142     (JAR *jar, char ZHUGEP *raw_manifest, 
00143         long length, const char *path, const char *url)
00144   {
00145 
00146 #if defined(XP_WIN16)
00147     PORT_Assert( !IsBadHugeReadPtr(raw_manifest, length) );
00148 #endif
00149 
00150   /* fill in the path, if supplied. This is a the location
00151      of the jar file on disk, if known */
00152 
00153   if (jar->filename == NULL && path)
00154     {
00155     jar->filename = PORT_Strdup (path);
00156     if (jar->filename == NULL)
00157       return JAR_ERR_MEMORY;
00158     }
00159 
00160   /* fill in the URL, if supplied. This is the place
00161      from which the jar file was retrieved. */
00162 
00163   if (jar->url == NULL && url)
00164     {
00165     jar->url = PORT_Strdup (url);
00166     if (jar->url == NULL)
00167       return JAR_ERR_MEMORY;
00168     }
00169 
00170   /* Determine what kind of file this is from the META-INF 
00171      directory. It could be MF, SF, or a binary RSA/DSA file */
00172 
00173   if (!xp_HUGE_STRNCASECMP (raw_manifest, "Manifest-Version:", 17))
00174     {
00175     return jar_parse_mf (jar, raw_manifest, length, path, url);
00176     }
00177   else if (!xp_HUGE_STRNCASECMP (raw_manifest, "Signature-Version:", 18))
00178     {
00179     return jar_parse_sf (jar, raw_manifest, length, path, url);
00180     }
00181   else
00182     {
00183     /* This is probably a binary signature */
00184     return jar_parse_sig (jar, path, raw_manifest, length);
00185     }
00186   }
00187 
00188 /*
00189  *  j a r _ p a r s e _ s i g
00190  *
00191  *  Pass some manner of RSA or DSA digital signature
00192  *  on, after checking to see if it comes at an appropriate state.
00193  *
00194  */
00195  
00196 int jar_parse_sig
00197     (JAR *jar, const char *path, char ZHUGEP *raw_manifest, long length)
00198   {
00199   JAR_Signer *signer;
00200   int status = JAR_ERR_ORDER;
00201 
00202   if (length <= 128) 
00203     {
00204     /* signature is way too small */
00205     return JAR_ERR_SIG;
00206     }
00207 
00208   /* make sure that MF and SF have already been processed */
00209 
00210   if (jar->globalmeta == NULL)
00211     return JAR_ERR_ORDER;
00212 
00213 #if 0
00214   /* XXX Turn this on to disable multiple signers */
00215   if (jar->digest == NULL)
00216     return JAR_ERR_ORDER;
00217 #endif
00218 
00219   /* Determine whether or not this RSA file has
00220      has an associated SF file */
00221 
00222   if (path)
00223     {
00224     char *owner;
00225     owner = jar_basename (path);
00226 
00227     if (owner == NULL)
00228       return JAR_ERR_MEMORY;
00229 
00230     signer = jar_get_signer (jar, owner);
00231 
00232     PORT_Free (owner);
00233     }
00234   else
00235     signer = jar_get_signer (jar, "*");
00236 
00237   if (signer == NULL)
00238     return JAR_ERR_ORDER;
00239 
00240 
00241   /* Do not pass a huge pointer to this function,
00242      since the underlying security code is unaware. We will
00243      never pass >64k through here. */
00244 
00245   if (length > 64000)
00246     {
00247     /* this digital signature is way too big */
00248     return JAR_ERR_SIG;
00249     }
00250 
00251 #ifdef XP_WIN16
00252   /*
00253    * For Win16, copy the portion of the raw_buffer containing the digital 
00254    * signature into another buffer...  This insures that the data will
00255    * NOT cross a segment boundary.  Therefore, 
00256    * jar_parse_digital_signature(...) does NOT need to deal with HUGE 
00257    * pointers...
00258    */
00259 
00260     {
00261     unsigned char *manifest_copy;
00262 
00263     manifest_copy = (unsigned char *) PORT_ZAlloc (length);
00264     if (manifest_copy)
00265       {
00266       xp_HUGE_MEMCPY (manifest_copy, raw_manifest, length);
00267 
00268       status = jar_parse_digital_signature 
00269                   (manifest_copy, signer, length, jar);
00270 
00271       PORT_Free (manifest_copy);
00272       }
00273     else
00274       {
00275       /* out of memory */
00276       return JAR_ERR_MEMORY;
00277       }
00278     }
00279 #else
00280   /* don't expense unneeded calloc overhead on non-win16 */
00281   status = jar_parse_digital_signature 
00282                 (raw_manifest, signer, length, jar);
00283 #endif
00284 
00285   return status;
00286   }
00287 
00288 /*
00289  *  j a r _ p a r s e _ m f
00290  *
00291  *  Parse the META-INF/manifest.mf file, whose
00292  *  information applies to all signers.
00293  *
00294  */
00295 
00296 int jar_parse_mf
00297     (JAR *jar, char ZHUGEP *raw_manifest, 
00298         long length, const char *path, const char *url)
00299   {
00300   if (jar->globalmeta)
00301     {
00302     /* refuse a second manifest file, if passed for some reason */
00303     return JAR_ERR_ORDER;
00304     }
00305 
00306 
00307   /* remember a digest for the global section */
00308 
00309   jar->globalmeta = jar_digest_section (raw_manifest, length);
00310 
00311   if (jar->globalmeta == NULL)
00312     return JAR_ERR_MEMORY;
00313 
00314 
00315   return jar_parse_any 
00316     (jar, jarTypeMF, NULL, raw_manifest, length, path, url);
00317   }
00318 
00319 /*
00320  *  j a r _ p a r s e _ s f
00321  *
00322  *  Parse META-INF/xxx.sf, a digitally signed file
00323  *  pointing to a subset of MF sections. 
00324  *
00325  */
00326 
00327 int jar_parse_sf
00328     (JAR *jar, char ZHUGEP *raw_manifest, 
00329         long length, const char *path, const char *url)
00330   {
00331   JAR_Signer *signer = NULL;
00332   int status = JAR_ERR_MEMORY;
00333 
00334   if (jar->globalmeta == NULL)
00335     {
00336     /* It is a requirement that the MF file be passed before the SF file */
00337     return JAR_ERR_ORDER;
00338     }
00339 
00340   signer = JAR_new_signer();
00341 
00342   if (signer == NULL)
00343     goto loser;
00344 
00345   if (path)
00346     {
00347     signer->owner = jar_basename (path);
00348     if (signer->owner == NULL)
00349       goto loser;
00350     }
00351 
00352 
00353   /* check for priors. When someone doctors a jar file
00354      to contain identical path entries, prevent the second
00355      one from affecting JAR functions */
00356 
00357   if (jar_get_signer (jar, signer->owner))
00358     {
00359     /* someone is trying to spoof us */
00360     status = JAR_ERR_ORDER;
00361     goto loser;
00362     }
00363 
00364 
00365   /* remember its digest */
00366 
00367   signer->digest = JAR_calculate_digest (raw_manifest, length);
00368 
00369   if (signer->digest == NULL)
00370     goto loser;
00371 
00372   /* Add this signer to the jar */
00373 
00374   ADDITEM (jar->signers, jarTypeOwner, 
00375      signer->owner, signer, sizeof (JAR_Signer));
00376 
00377 
00378   return jar_parse_any 
00379     (jar, jarTypeSF, signer, raw_manifest, length, path, url);
00380 
00381 loser:
00382 
00383   if (signer)
00384     JAR_destroy_signer (signer);
00385 
00386   return status;
00387   }
00388 
00389 /* 
00390  *  j a r _ p a r s e _ a n y
00391  *
00392  *  Parse a MF or SF manifest file. 
00393  *
00394  */
00395  
00396 int jar_parse_any
00397     (JAR *jar, int type, JAR_Signer *signer, char ZHUGEP *raw_manifest, 
00398         long length, const char *path, const char *url)
00399   {
00400   int status;
00401 
00402   long raw_len;
00403 
00404   JAR_Digest *dig, *mfdig = NULL;
00405 
00406   char line [SZ];
00407   char x_name [SZ], x_md5 [SZ], x_sha [SZ];
00408 
00409   char *x_info;
00410 
00411   char *sf_md5 = NULL, *sf_sha1 = NULL;
00412 
00413   *x_name = 0;
00414   *x_md5 = 0;
00415   *x_sha = 0;
00416 
00417   PORT_Assert( length > 0 );
00418   raw_len = length;
00419 
00420 #ifdef DEBUG
00421   if ((status = jar_insanity_check (raw_manifest, raw_len)) < 0)
00422     return status;
00423 #endif
00424 
00425 
00426   /* null terminate the first line */
00427   raw_manifest = jar_eat_line (0, PR_TRUE, raw_manifest, &raw_len);
00428 
00429 
00430   /* skip over the preliminary section */
00431   /* This is one section at the top of the file with global metainfo */
00432 
00433   while (raw_len)
00434     {
00435     JAR_Metainfo *met;
00436 
00437     raw_manifest = jar_eat_line (1, PR_TRUE, raw_manifest, &raw_len);
00438     if (!*raw_manifest) break;
00439 
00440     met = (JAR_Metainfo*)PORT_ZAlloc (sizeof (JAR_Metainfo));
00441     if (met == NULL)
00442       return JAR_ERR_MEMORY;
00443 
00444     /* Parse out the header & info */
00445 
00446     if (xp_HUGE_STRLEN (raw_manifest) >= SZ)
00447       {
00448       /* almost certainly nonsense */
00449       continue;
00450       }
00451 
00452     xp_HUGE_STRCPY (line, raw_manifest);
00453     x_info = line;
00454 
00455     while (*x_info && *x_info != ' ' && *x_info != '\t' && *x_info != ':')
00456       x_info++;
00457 
00458     if (*x_info) *x_info++ = 0;
00459 
00460     while (*x_info == ' ' || *x_info == '\t')
00461       x_info++;
00462 
00463     /* metainfo (name, value) pair is now (line, x_info) */
00464 
00465     met->header = PORT_Strdup (line);
00466     met->info = PORT_Strdup (x_info);
00467 
00468     if (type == jarTypeMF)
00469       {
00470       ADDITEM (jar->metainfo, jarTypeMeta, 
00471          /* pathname */ NULL, met, sizeof (JAR_Metainfo));
00472       }
00473 
00474     /* For SF files, this metadata may be the digests
00475        of the MF file, still in the "met" structure. */
00476 
00477     if (type == jarTypeSF)
00478       {
00479       if (!PORT_Strcasecmp (line, "MD5-Digest"))
00480         sf_md5 = (char *) met->info;
00481 
00482       if (!PORT_Strcasecmp (line, "SHA1-Digest") || !PORT_Strcasecmp (line, "SHA-Digest"))
00483         sf_sha1 = (char *) met->info;
00484       }
00485     }
00486 
00487   if (type == jarTypeSF && jar->globalmeta)
00488     {
00489     /* this is a SF file which may contain a digest of the manifest.mf's 
00490        global metainfo. */
00491 
00492     int match = 0;
00493     JAR_Digest *glob = jar->globalmeta;
00494 
00495     if (sf_md5)
00496       {
00497       unsigned int md5_length;
00498       unsigned char *md5_digest;
00499 
00500       md5_digest = ATOB_AsciiToData (sf_md5, &md5_length);
00501       PORT_Assert( md5_length == MD5_LENGTH );
00502 
00503       if (md5_length != MD5_LENGTH)
00504         return JAR_ERR_CORRUPT;
00505 
00506       match = PORT_Memcmp (md5_digest, glob->md5, MD5_LENGTH);
00507       }
00508 
00509     if (sf_sha1 && match == 0)
00510       {
00511       unsigned int sha1_length;
00512       unsigned char *sha1_digest;
00513 
00514       sha1_digest = ATOB_AsciiToData (sf_sha1, &sha1_length);
00515       PORT_Assert( sha1_length == SHA1_LENGTH );
00516 
00517       if (sha1_length != SHA1_LENGTH)
00518         return JAR_ERR_CORRUPT;
00519 
00520       match = PORT_Memcmp (sha1_digest, glob->sha1, SHA1_LENGTH);
00521       }
00522 
00523     if (match != 0)
00524       {
00525       /* global digest doesn't match, SF file therefore invalid */
00526       jar->valid = JAR_ERR_METADATA;
00527       return JAR_ERR_METADATA;
00528       }
00529     }
00530 
00531   /* done with top section of global data */
00532 
00533 
00534   while (raw_len)
00535     {
00536     *x_md5 = 0;
00537     *x_sha = 0;
00538     *x_name = 0;
00539 
00540 
00541     /* If this is a manifest file, attempt to get a digest of the following section, 
00542        without damaging it. This digest will be saved later. */
00543 
00544     if (type == jarTypeMF)
00545       {
00546       char ZHUGEP *sec;
00547       long sec_len = raw_len;
00548 
00549       if (!*raw_manifest || *raw_manifest == '\n')
00550         {     
00551         /* skip the blank line */ 
00552         sec = jar_eat_line (1, PR_FALSE, raw_manifest, &sec_len);
00553         }
00554       else
00555         sec = raw_manifest;
00556 
00557       if (!xp_HUGE_STRNCASECMP (sec, "Name:", 5))
00558         {
00559         if (type == jarTypeMF)
00560           mfdig = jar_digest_section (sec, sec_len);
00561         else
00562           mfdig = NULL;
00563         }
00564       }
00565 
00566 
00567     while (raw_len)
00568       {
00569       raw_manifest = jar_eat_line (1, PR_TRUE, raw_manifest, &raw_len);
00570       if (!*raw_manifest) break; /* blank line, done with this entry */
00571 
00572       if (xp_HUGE_STRLEN (raw_manifest) >= SZ)
00573         {
00574         /* almost certainly nonsense */
00575         continue;
00576         }
00577 
00578 
00579       /* Parse out the name/value pair */
00580 
00581       xp_HUGE_STRCPY (line, raw_manifest);
00582       x_info = line;
00583 
00584       while (*x_info && *x_info != ' ' && *x_info != '\t' && *x_info != ':')
00585         x_info++;
00586 
00587       if (*x_info) *x_info++ = 0;
00588 
00589       while (*x_info == ' ' || *x_info == '\t') 
00590         x_info++;
00591 
00592 
00593       if (!PORT_Strcasecmp (line, "Name"))
00594         PORT_Strcpy (x_name, x_info);
00595 
00596       else if (!PORT_Strcasecmp (line, "MD5-Digest"))
00597         PORT_Strcpy (x_md5, x_info);
00598 
00599       else if (!PORT_Strcasecmp (line, "SHA1-Digest") 
00600                   || !PORT_Strcasecmp (line, "SHA-Digest"))
00601         {
00602         PORT_Strcpy (x_sha, x_info);
00603         }
00604 
00605       /* Algorithm list is meta info we don't care about; keeping it out
00606          of metadata saves significant space for large jar files */
00607 
00608       else if (!PORT_Strcasecmp (line, "Digest-Algorithms")
00609                     || !PORT_Strcasecmp (line, "Hash-Algorithms"))
00610         {
00611         continue;
00612         }
00613 
00614       /* Meta info is only collected for the manifest.mf file,
00615          since the JAR_get_metainfo call does not support identity */
00616 
00617       else if (type == jarTypeMF)
00618         {
00619         JAR_Metainfo *met;
00620 
00621         /* this is meta-data */
00622 
00623         met = (JAR_Metainfo*)PORT_ZAlloc (sizeof (JAR_Metainfo));
00624 
00625         if (met == NULL)
00626           return JAR_ERR_MEMORY;
00627 
00628         /* metainfo (name, value) pair is now (line, x_info) */
00629 
00630         if ((met->header = PORT_Strdup (line)) == NULL)
00631           return JAR_ERR_MEMORY;
00632 
00633         if ((met->info = PORT_Strdup (x_info)) == NULL)
00634           return JAR_ERR_MEMORY;
00635 
00636         ADDITEM (jar->metainfo, jarTypeMeta, 
00637            x_name, met, sizeof (JAR_Metainfo));
00638         }
00639       }
00640 
00641        if(!x_name || !*x_name) {
00642               /* Whatever that was, it wasn't an entry, because we didn't get a name.
00643                * We don't really have anything, so don't record this. */
00644               continue;
00645        }
00646 
00647     dig = (JAR_Digest*)PORT_ZAlloc (sizeof (JAR_Digest));
00648     if (dig == NULL)
00649       return JAR_ERR_MEMORY;
00650 
00651     if (*x_md5 ) 
00652       {
00653       unsigned int binary_length;
00654       unsigned char *binary_digest;
00655 
00656       binary_digest = ATOB_AsciiToData (x_md5, &binary_length);
00657       PORT_Assert( binary_length == MD5_LENGTH );
00658 
00659       if (binary_length != MD5_LENGTH)
00660         return JAR_ERR_CORRUPT;
00661 
00662       memcpy (dig->md5, binary_digest, MD5_LENGTH);
00663       dig->md5_status = jarHashPresent;
00664       }
00665 
00666     if (*x_sha ) 
00667       {
00668       unsigned int binary_length;
00669       unsigned char *binary_digest;
00670 
00671       binary_digest = ATOB_AsciiToData (x_sha, &binary_length);
00672       PORT_Assert( binary_length == SHA1_LENGTH );
00673 
00674       if (binary_length != SHA1_LENGTH)
00675         return JAR_ERR_CORRUPT;
00676 
00677       memcpy (dig->sha1, binary_digest, SHA1_LENGTH);
00678       dig->sha1_status = jarHashPresent;
00679       }
00680 
00681     PORT_Assert( type == jarTypeMF || type == jarTypeSF );
00682 
00683 
00684     if (type == jarTypeMF)
00685       {
00686       ADDITEM (jar->hashes, jarTypeMF, x_name, dig, sizeof (JAR_Digest));
00687       }
00688     else if (type == jarTypeSF)
00689       {
00690       ADDITEM (signer->sf, jarTypeSF, x_name, dig, sizeof (JAR_Digest));
00691       }
00692     else
00693       return JAR_ERR_ORDER;
00694 
00695     /* we're placing these calculated digests of manifest.mf 
00696        sections in a list where they can subsequently be forgotten */
00697 
00698     if (type == jarTypeMF && mfdig)
00699       {
00700       ADDITEM (jar->manifest, jarTypeSect, 
00701          x_name, mfdig, sizeof (JAR_Digest));
00702 
00703       mfdig = NULL;
00704       }
00705 
00706 
00707     /* Retrieve our saved SHA1 digest from saved copy and check digests.
00708        This is just comparing the digest of the MF section as indicated in
00709        the SF file with the one we remembered from parsing the MF file */
00710 
00711     if (type == jarTypeSF)
00712       {
00713       if ((status = jar_internal_digest (jar, path, x_name, dig)) < 0)
00714         return status;
00715       }
00716     }
00717 
00718   return 0;
00719   }
00720 
00721 static int jar_internal_digest 
00722      (JAR *jar, const char *path, char *x_name, JAR_Digest *dig)
00723   {
00724   int cv;
00725   int status;
00726 
00727   JAR_Digest *savdig;
00728 
00729   savdig = jar_get_mf_digest (jar, x_name);
00730 
00731   if (savdig == NULL)
00732     {
00733     /* no .mf digest for this pathname */
00734     status = jar_signal (JAR_ERR_ENTRY, jar, path, x_name);
00735     if (status < 0) 
00736       return 0; /* was continue; */
00737     else 
00738       return status;
00739     }
00740 
00741   /* check for md5 consistency */
00742   if (dig->md5_status)
00743     {
00744     cv = PORT_Memcmp (savdig->md5, dig->md5, MD5_LENGTH);
00745     /* md5 hash of .mf file is not what expected */
00746     if (cv) 
00747       {
00748       status = jar_signal (JAR_ERR_HASH, jar, path, x_name);
00749 
00750       /* bad hash, man */
00751 
00752       dig->md5_status = jarHashBad;
00753       savdig->md5_status = jarHashBad;
00754 
00755       if (status < 0) 
00756         return 0; /* was continue; */
00757       else 
00758         return status;
00759       }
00760     }
00761 
00762   /* check for sha1 consistency */
00763   if (dig->sha1_status)
00764     {
00765     cv = PORT_Memcmp (savdig->sha1, dig->sha1, SHA1_LENGTH);
00766     /* sha1 hash of .mf file is not what expected */
00767     if (cv) 
00768       {
00769       status = jar_signal (JAR_ERR_HASH, jar, path, x_name);
00770 
00771       /* bad hash, man */
00772 
00773       dig->sha1_status = jarHashBad;
00774       savdig->sha1_status = jarHashBad;
00775 
00776       if (status < 0)
00777         return 0; /* was continue; */
00778       else
00779         return status;
00780       }
00781     }
00782        return 0;
00783   }
00784 
00785 #ifdef DEBUG
00786 /*
00787  *  j a r _ i n s a n i t y _ c h e c k
00788  *
00789  *  Check for illegal characters (or possibly so)
00790  *  in the manifest files, to detect potential memory
00791  *  corruption by our neighbors. Debug only, since
00792  *  not I18N safe.
00793  * 
00794  */
00795 
00796 static int jar_insanity_check (char ZHUGEP *data, long length)
00797   {
00798   int c;
00799   long off;
00800 
00801   for (off = 0; off < length; off++)
00802     {
00803     c = data [off];
00804 
00805     if (c == '\n' || c == '\r' || (c >= ' ' && c <= 128))
00806       continue;
00807 
00808     return JAR_ERR_CORRUPT;
00809     }
00810 
00811   return 0;
00812   }
00813 #endif
00814 
00815 /*
00816  *  j a r _ p a r s e _ d i g i t a l _ s i g n a t u r e
00817  *
00818  *  Parse an RSA or DSA (or perhaps other) digital signature.
00819  *  Right now everything is PKCS7.
00820  *
00821  */
00822 
00823 static int jar_parse_digital_signature 
00824      (char *raw_manifest, JAR_Signer *signer, long length, JAR *jar)
00825   {
00826 #if defined(XP_WIN16)
00827   PORT_Assert( LOWORD(raw_manifest) + length < 0xFFFF );
00828 #endif
00829   return jar_validate_pkcs7 (jar, signer, raw_manifest, length);
00830   }
00831 
00832 /*
00833  *  j a r _ a d d _ c e r t
00834  * 
00835  *  Add information for the given certificate
00836  *  (or whatever) to the JAR linked list. A pointer
00837  *  is passed for some relevant reference, say
00838  *  for example the original certificate.
00839  *
00840  */
00841 
00842 static int jar_add_cert
00843      (JAR *jar, JAR_Signer *signer, int type, CERTCertificate *cert)
00844   {
00845   JAR_Cert *fing;
00846   unsigned char *keyData;
00847 
00848   if (cert == NULL)
00849     return JAR_ERR_ORDER;
00850 
00851   fing = (JAR_Cert*)PORT_ZAlloc (sizeof (JAR_Cert));
00852 
00853   if (fing == NULL)
00854     goto loser;
00855 
00856 #ifdef USE_MOZ_THREAD
00857   fing->cert = jar_moz_dup (cert);
00858 #else
00859   fing->cert = CERT_DupCertificate (cert);
00860 #endif
00861 
00862   /* get the certkey */
00863 
00864   fing->length = cert->derIssuer.len + 2 + cert->serialNumber.len;
00865 
00866   keyData = (unsigned char *) PORT_ZAlloc (fing->length);
00867   fing->key = keyData;
00868 
00869   if (fing->key == NULL)
00870     goto loser;
00871   keyData[0] = ((cert->derIssuer.len) >> 8) & 0xff;
00872   keyData[1] = ((cert->derIssuer.len) & 0xff);
00873   PORT_Memcpy (&keyData[2], cert->derIssuer.data, cert->derIssuer.len);
00874   PORT_Memcpy (&keyData[2+cert->derIssuer.len], cert->serialNumber.data,
00875                                            cert->serialNumber.len);
00876 
00877   ADDITEM (signer->certs, type, 
00878     /* pathname */ NULL, fing, sizeof (JAR_Cert));
00879 
00880   return 0;
00881 
00882 loser:
00883 
00884   if (fing)
00885     {
00886     if (fing->cert) 
00887       CERT_DestroyCertificate (fing->cert);
00888 
00889     PORT_Free (fing);
00890     }
00891 
00892   return JAR_ERR_MEMORY;
00893   }
00894 
00895 /*
00896  *  e a t _ l i n e 
00897  *
00898  *  Consume an ascii line from the top of a file kept
00899  *  in memory. This destroys the file in place. This function
00900  *  handles PC, Mac, and Unix style text files.
00901  *
00902  */
00903 
00904 static char ZHUGEP *jar_eat_line 
00905     (int lines, int eating, char ZHUGEP *data, long *len)
00906   {
00907   char ZHUGEP *ret;
00908 
00909   ret = data;
00910   if (!*len) return ret;
00911 
00912   /* Eat the requisite number of lines, if any; 
00913      prior to terminating the current line with a 0. */
00914 
00915   for (/* yip */ ; lines; lines--)
00916     {
00917     while (*data && *data != '\n')
00918       data++;
00919 
00920     /* After the CR, ok to eat one LF */
00921 
00922     if (*data == '\n')
00923       data++;
00924 
00925     /* If there are zeros, we put them there */
00926 
00927     while (*data == 0 && data - ret < *len)
00928       data++;
00929     }
00930 
00931   *len -= data - ret;
00932   ret = data;
00933 
00934   if (eating)
00935     {
00936     /* Terminate this line with a 0 */ 
00937 
00938     while (*data && *data != '\n' && *data != '\r')
00939       data++;
00940 
00941     /* In any case we are allowed to eat CR */
00942 
00943     if (*data == '\r')
00944       *data++ = 0;
00945 
00946     /* After the CR, ok to eat one LF */
00947 
00948     if (*data == '\n')
00949       *data++ = 0;
00950     }
00951 
00952   return ret;
00953   }
00954 
00955 /*
00956  *  j a r _ d i g e s t _ s e c t i o n
00957  *
00958  *  Return the digests of the next section of the manifest file.
00959  *  Does not damage the manifest file, unlike parse_manifest.
00960  * 
00961  */
00962 
00963 static JAR_Digest *jar_digest_section 
00964     (char ZHUGEP *manifest, long length)
00965   {
00966   long global_len;
00967   char ZHUGEP *global_end;
00968 
00969   global_end = manifest;
00970   global_len = length;
00971 
00972   while (global_len)
00973     {
00974     global_end = jar_eat_line (1, PR_FALSE, global_end, &global_len);
00975     if (*global_end == 0 || *global_end == '\n')
00976       break;
00977     }
00978 
00979   return JAR_calculate_digest (manifest, global_end - manifest);
00980   }
00981 
00982 /*
00983  *  J A R _ v e r i f y _ d i g e s t
00984  *
00985  *  Verifies that a precalculated digest matches the
00986  *  expected value in the manifest.
00987  *
00988  */
00989 
00990 int PR_CALLBACK JAR_verify_digest
00991     (JAR *jar, const char *name, JAR_Digest *dig)
00992   {
00993   JAR_Item *it;
00994 
00995   JAR_Digest *shindig;
00996 
00997   ZZLink *link;
00998   ZZList *list;
00999 
01000   int result1, result2;
01001 
01002   list = jar->hashes;
01003 
01004   result1 = result2 = 0;
01005 
01006   if (jar->valid < 0)
01007     {
01008     /* signature not valid */
01009     return JAR_ERR_SIG;
01010     }
01011 
01012   if (ZZ_ListEmpty (list))
01013     {
01014     /* empty list */
01015     return JAR_ERR_PNF;
01016     }
01017 
01018   for (link = ZZ_ListHead (list); 
01019        !ZZ_ListIterDone (list, link); 
01020        link = link->next)
01021     {
01022     it = link->thing;
01023     if (it->type == jarTypeMF 
01024            && it->pathname && !PORT_Strcmp (it->pathname, name))
01025       {
01026       shindig = (JAR_Digest *) it->data;
01027 
01028       if (shindig->md5_status)
01029         {
01030         if (shindig->md5_status == jarHashBad)
01031           return JAR_ERR_HASH;
01032         else
01033           result1 = memcmp (dig->md5, shindig->md5, MD5_LENGTH);
01034         }
01035 
01036       if (shindig->sha1_status)
01037         {
01038         if (shindig->sha1_status == jarHashBad)
01039           return JAR_ERR_HASH;
01040         else
01041           result2 = memcmp (dig->sha1, shindig->sha1, SHA1_LENGTH);
01042         }
01043 
01044       return (result1 == 0 && result2 == 0) ? 0 : JAR_ERR_HASH;
01045       }
01046     }
01047 
01048   return JAR_ERR_PNF;
01049   }
01050 
01051 /* 
01052  *  J A R _ c e r t _ a t t r i b u t e
01053  *
01054  *  Return the named certificate attribute from the
01055  *  certificate specified by the given key.
01056  *
01057  */
01058 
01059 int PR_CALLBACK JAR_cert_attribute
01060     (JAR *jar, jarCert attrib, long keylen, void *key, 
01061         void **result, unsigned long *length)
01062   {
01063   int status = 0;
01064   char *ret = NULL;
01065 
01066   CERTCertificate *cert;
01067 
01068   CERTCertDBHandle *certdb;
01069 
01070   JAR_Digest *dig;
01071   SECItem hexme;
01072 
01073   *length = 0;
01074 
01075   if (attrib == 0 || key == 0)
01076     return JAR_ERR_GENERAL;
01077 
01078   if (attrib == jarCertJavaHack)
01079     {
01080     cert = (CERTCertificate *) NULL;
01081     certdb = JAR_open_database();
01082 
01083     if (certdb)
01084       {
01085 #ifdef USE_MOZ_THREAD
01086       cert = jar_moz_nickname (certdb, (char*)key);
01087 #else
01088       cert = CERT_FindCertByNickname (certdb, key);
01089 #endif
01090 
01091       if (cert)
01092         {
01093         *length = cert->certKey.len;
01094 
01095         *result = (void *) PORT_ZAlloc (*length);
01096 
01097         if (*result)
01098           PORT_Memcpy (*result, cert->certKey.data, *length);
01099         else
01100           return JAR_ERR_MEMORY;
01101         }
01102       JAR_close_database (certdb);
01103       }
01104 
01105     return cert ? 0 : JAR_ERR_GENERAL;
01106     }
01107 
01108   if (jar && jar->pkcs7 == 0)
01109     return JAR_ERR_GENERAL;
01110 
01111   cert = jar_get_certificate (jar, keylen, key, &status);
01112 
01113   if (cert == NULL || status < 0)
01114     return JAR_ERR_GENERAL;
01115 
01116 #define SEP " <br> "
01117 #define SEPLEN (PORT_Strlen(SEP))
01118 
01119   switch (attrib)
01120     {
01121     case jarCertCompany:
01122 
01123       ret = cert->subjectName;
01124 
01125       /* This is pretty ugly looking but only used
01126          here for this one purpose. */
01127 
01128       if (ret)
01129         {
01130         int retlen = 0;
01131 
01132         char *cer_ou1, *cer_ou2, *cer_ou3;
01133        char *cer_cn, *cer_e, *cer_o, *cer_l;
01134 
01135        cer_cn  = CERT_GetCommonName (&cert->subject);
01136         cer_e   = CERT_GetCertEmailAddress (&cert->subject);
01137         cer_ou3 = jar_cert_element (ret, "OU=", 3);
01138         cer_ou2 = jar_cert_element (ret, "OU=", 2);
01139         cer_ou1 = jar_cert_element (ret, "OU=", 1);
01140         cer_o   = CERT_GetOrgName (&cert->subject);
01141         cer_l   = CERT_GetCountryName (&cert->subject);
01142 
01143         if (cer_cn)  retlen += SEPLEN + PORT_Strlen (cer_cn);
01144         if (cer_e)   retlen += SEPLEN + PORT_Strlen (cer_e);
01145         if (cer_ou1) retlen += SEPLEN + PORT_Strlen (cer_ou1);
01146         if (cer_ou2) retlen += SEPLEN + PORT_Strlen (cer_ou2);
01147         if (cer_ou3) retlen += SEPLEN + PORT_Strlen (cer_ou3);
01148         if (cer_o)   retlen += SEPLEN + PORT_Strlen (cer_o);
01149         if (cer_l)   retlen += SEPLEN + PORT_Strlen (cer_l);
01150 
01151         ret = (char *) PORT_ZAlloc (1 + retlen);
01152 
01153         if (cer_cn)  { PORT_Strcpy (ret, cer_cn);  PORT_Strcat (ret, SEP); }
01154         if (cer_e)   { PORT_Strcat (ret, cer_e);   PORT_Strcat (ret, SEP); }
01155         if (cer_ou1) { PORT_Strcat (ret, cer_ou1); PORT_Strcat (ret, SEP); }
01156         if (cer_ou2) { PORT_Strcat (ret, cer_ou2); PORT_Strcat (ret, SEP); }
01157         if (cer_ou3) { PORT_Strcat (ret, cer_ou3); PORT_Strcat (ret, SEP); }
01158         if (cer_o)   { PORT_Strcat (ret, cer_o);   PORT_Strcat (ret, SEP); }
01159         if (cer_l)     PORT_Strcat (ret, cer_l);
01160 
01161        /* return here to avoid unsightly memory leak */
01162 
01163         *result = ret;
01164         *length = PORT_Strlen (ret);
01165 
01166         return 0;
01167         }
01168       break;
01169 
01170     case jarCertCA:
01171 
01172       ret = cert->issuerName;
01173 
01174       if (ret)
01175         {
01176         int retlen = 0;
01177 
01178         char *cer_ou1, *cer_ou2, *cer_ou3;
01179        char *cer_cn, *cer_e, *cer_o, *cer_l;
01180 
01181         /* This is pretty ugly looking but only used
01182            here for this one purpose. */
01183 
01184        cer_cn  = CERT_GetCommonName (&cert->issuer);
01185         cer_e   = CERT_GetCertEmailAddress (&cert->issuer);
01186         cer_ou3 = jar_cert_element (ret, "OU=", 3);
01187         cer_ou2 = jar_cert_element (ret, "OU=", 2);
01188         cer_ou1 = jar_cert_element (ret, "OU=", 1);
01189         cer_o   = CERT_GetOrgName (&cert->issuer);
01190         cer_l   = CERT_GetCountryName (&cert->issuer);
01191 
01192         if (cer_cn)  retlen += SEPLEN + PORT_Strlen (cer_cn);
01193         if (cer_e)   retlen += SEPLEN + PORT_Strlen (cer_e);
01194         if (cer_ou1) retlen += SEPLEN + PORT_Strlen (cer_ou1);
01195         if (cer_ou2) retlen += SEPLEN + PORT_Strlen (cer_ou2);
01196         if (cer_ou3) retlen += SEPLEN + PORT_Strlen (cer_ou3);
01197         if (cer_o)   retlen += SEPLEN + PORT_Strlen (cer_o);
01198         if (cer_l)   retlen += SEPLEN + PORT_Strlen (cer_l);
01199 
01200         ret = (char *) PORT_ZAlloc (1 + retlen);
01201 
01202         if (cer_cn)  { PORT_Strcpy (ret, cer_cn);  PORT_Strcat (ret, SEP); }
01203         if (cer_e)   { PORT_Strcat (ret, cer_e);   PORT_Strcat (ret, SEP); }
01204         if (cer_ou1) { PORT_Strcat (ret, cer_ou1); PORT_Strcat (ret, SEP); }
01205         if (cer_ou2) { PORT_Strcat (ret, cer_ou2); PORT_Strcat (ret, SEP); }
01206         if (cer_ou3) { PORT_Strcat (ret, cer_ou3); PORT_Strcat (ret, SEP); }
01207         if (cer_o)   { PORT_Strcat (ret, cer_o);   PORT_Strcat (ret, SEP); }
01208         if (cer_l)     PORT_Strcat (ret, cer_l);
01209 
01210        /* return here to avoid unsightly memory leak */
01211 
01212         *result = ret;
01213         *length = PORT_Strlen (ret);
01214 
01215         return 0;
01216         }
01217 
01218       break;
01219 
01220     case jarCertSerial:
01221 
01222       ret = CERT_Hexify (&cert->serialNumber, 1);
01223       break;
01224 
01225     case jarCertExpires:
01226 
01227       ret = DER_UTCDayToAscii (&cert->validity.notAfter);
01228       break;
01229 
01230     case jarCertNickname:
01231 
01232       ret = jar_choose_nickname (cert);
01233       break;
01234 
01235     case jarCertFinger:
01236 
01237       dig = JAR_calculate_digest 
01238          ((char *) cert->derCert.data, cert->derCert.len);
01239 
01240       if (dig)
01241         {
01242         hexme.len = sizeof (dig->md5);
01243         hexme.data = dig->md5;
01244         ret = CERT_Hexify (&hexme, 1);
01245         }
01246       break;
01247 
01248     default:
01249 
01250       return JAR_ERR_GENERAL;
01251     }
01252 
01253   *result = ret ? PORT_Strdup (ret) : NULL;
01254   *length = ret ? PORT_Strlen (ret) : 0;
01255 
01256   return 0;
01257   }
01258 
01259 /* 
01260  *  j a r  _ c e r t _ e l e m e n t
01261  *
01262  *  Retrieve an element from an x400ish ascii
01263  *  designator, in a hackish sort of way. The right
01264  *  thing would probably be to sort AVATags.
01265  *
01266  */
01267 
01268 static char *jar_cert_element (char *name, char *tag, int occ)
01269   {
01270   if (name && tag)
01271     {
01272     char *s;
01273     int found = 0;
01274 
01275     while (occ--)
01276       {
01277       if (PORT_Strstr (name, tag))
01278         {
01279         name = PORT_Strstr (name, tag) + PORT_Strlen (tag);
01280         found = 1;
01281         }
01282       else
01283         {
01284         name = PORT_Strstr (name, "=");
01285         if (name == NULL) return NULL;
01286         found = 0;
01287         }
01288       }
01289 
01290     if (!found) return NULL;
01291 
01292     /* must mangle only the copy */
01293     name = PORT_Strdup (name);
01294 
01295     /* advance to next equal */
01296     for (s = name; *s && *s != '='; s++)
01297       /* yip */ ;
01298 
01299     /* back up to previous comma */
01300     while (s > name && *s != ',') s--;
01301 
01302     /* zap the whitespace and return */
01303     *s = 0;
01304     }
01305 
01306   return name;
01307   }
01308 
01309 /* 
01310  *  j a r _ c h o o s e _ n i c k n a m e
01311  *
01312  *  Attempt to determine a suitable nickname for
01313  *  a certificate with a computer-generated "tmpcertxxx" 
01314  *  nickname. It needs to be something a user can
01315  *  understand, so try a few things.
01316  *
01317  */
01318 
01319 static char *jar_choose_nickname (CERTCertificate *cert)
01320   {
01321   char *cert_cn;
01322   char *cert_o;
01323   char *cert_cn_o;
01324 
01325   int cn_o_length;
01326 
01327   /* is the existing name ok */
01328 
01329   if (cert->nickname && PORT_Strncmp (cert->nickname, "tmpcert", 7))
01330     return PORT_Strdup (cert->nickname);
01331 
01332   /* we have an ugly name here people */
01333 
01334   /* Try the CN */
01335   cert_cn = CERT_GetCommonName (&cert->subject);
01336 
01337   if (cert_cn)
01338     {
01339     /* check for duplicate nickname */
01340 
01341 #ifdef USE_MOZ_THREAD
01342     if (jar_moz_nickname (CERT_GetDefaultCertDB(), cert_cn) == NULL)
01343 #else
01344     if (CERT_FindCertByNickname (CERT_GetDefaultCertDB(), cert_cn) == NULL)
01345 #endif
01346       return cert_cn;
01347 
01348     /* Try the CN plus O */
01349     cert_o = CERT_GetOrgName (&cert->subject);
01350 
01351     cn_o_length = PORT_Strlen (cert_cn) + 3 + PORT_Strlen (cert_o) + 20;
01352     cert_cn_o = (char*)PORT_ZAlloc (cn_o_length);
01353 
01354     PR_snprintf (cert_cn_o, cn_o_length, 
01355            "%s's %s Certificate", cert_cn, cert_o);
01356 
01357 #ifdef USE_MOZ_THREAD
01358     if (jar_moz_nickname (CERT_GetDefaultCertDB(), cert_cn_o) == NULL)
01359 #else
01360     if (CERT_FindCertByNickname (CERT_GetDefaultCertDB(), cert_cn_o) == NULL)
01361 #endif
01362       return cert_cn;
01363     }
01364 
01365   /* If all that failed, use the ugly nickname */
01366   return cert->nickname ? PORT_Strdup (cert->nickname) : NULL;
01367   }
01368 
01369 /*
01370  *  J A R _ c e r t _ h t m l 
01371  *
01372  *  Return an HTML representation of the certificate
01373  *  designated by the given fingerprint, in specified style.
01374  *
01375  *  JAR is optional, but supply it if you can in order
01376  *  to optimize.
01377  *
01378  */
01379 
01380 char *JAR_cert_html
01381     (JAR *jar, int style, long keylen, void *key, int *result)
01382   {
01383 #ifdef notdef
01384   char *html;
01385 #endif
01386   CERTCertificate *cert;
01387 
01388   *result = -1;
01389 
01390   if (style != 0)
01391     return NULL;
01392 
01393   cert = jar_get_certificate (jar, keylen, key, result);
01394 
01395   if (cert == NULL || *result < 0)
01396     return NULL;
01397 
01398   *result = -1;
01399 
01400    return NULL;
01401 
01402 #ifdef notdef
01403   html = CERT_HTMLCertInfo (cert, /* show images */ PR_TRUE,
01404               /*show issuer*/PR_TRUE);
01405 
01406   if (html == NULL)
01407     *result = -1;
01408 
01409   return html;
01410 #endif
01411   }
01412 
01413 /*
01414  *  J A R _ s t a s h _ c e r t
01415  *
01416  *  Stash the certificate pointed to by this
01417  *  fingerprint, in persistent storage somewhere.
01418  *
01419  */
01420 
01421 extern int PR_CALLBACK JAR_stash_cert
01422     (JAR *jar, long keylen, void *key)
01423   {
01424   int result = 0;
01425 
01426   char *nickname;
01427   CERTCertTrust trust;
01428 
01429   CERTCertDBHandle *certdb;
01430   CERTCertificate *cert, *newcert;
01431 
01432   cert = jar_get_certificate (jar, keylen, key, &result);
01433 
01434   if (result < 0)
01435     return result;
01436 
01437   if (cert == NULL)
01438     return JAR_ERR_GENERAL;
01439 
01440   if ((certdb = JAR_open_database()) == NULL)
01441     return JAR_ERR_GENERAL;
01442 
01443   /* Attempt to give a name to the newish certificate */
01444   nickname = jar_choose_nickname (cert);
01445 
01446 #ifdef USE_MOZ_THREAD
01447   newcert = jar_moz_nickname (certdb, nickname);
01448 #else
01449   newcert = CERT_FindCertByNickname (certdb, nickname);
01450 #endif
01451 
01452   if (newcert && newcert->isperm) 
01453     {
01454     /* already in permanant database */
01455     return 0;
01456     }
01457 
01458   if (newcert) cert = newcert;
01459 
01460   /* FIX, since FindCert returns a bogus dbhandle
01461      set it ourselves */
01462 
01463   cert->dbhandle = certdb;
01464 
01465 #if 0
01466   nickname = cert->subjectName;
01467   if (nickname)
01468     {
01469     /* Not checking for a conflict here. But this should
01470        be a new cert or it would have been found earlier. */
01471 
01472     nickname = jar_cert_element (nickname, "CN=", 1);
01473 
01474     if (SEC_CertNicknameConflict (nickname, cert->dbhandle))
01475       {
01476       /* conflict */
01477       nickname = PORT_Realloc (&nickname, PORT_Strlen (nickname) + 3);
01478 
01479       /* Beyond one copy, there are probably serious problems 
01480          so we will stop at two rather than counting.. */
01481 
01482       PORT_Strcat (nickname, " #2");
01483       }
01484     }
01485 #endif
01486 
01487   if (nickname != NULL)
01488     {
01489     PORT_Memset ((void *) &trust, 0, sizeof(trust));
01490 
01491 #ifdef USE_MOZ_THREAD
01492     if (jar_moz_perm (cert, nickname, &trust) != SECSuccess) 
01493 #else
01494     if (CERT_AddTempCertToPerm (cert, nickname, &trust) != SECSuccess) 
01495 #endif
01496       {
01497       /* XXX might want to call PORT_GetError here */
01498       result = JAR_ERR_GENERAL;
01499       }
01500     }
01501 
01502   JAR_close_database (certdb);
01503 
01504   return result;
01505   }
01506 
01507 /*
01508  *  J A R _ f e t c h _ c e r t
01509  *
01510  *  Given an opaque identifier of a certificate, 
01511  *  return the full certificate.
01512  *
01513  * The new function, which retrieves by key.
01514  *
01515  */
01516 
01517 void *JAR_fetch_cert (long length, void *key)
01518   {
01519   CERTIssuerAndSN issuerSN;
01520   CERTCertificate *cert = NULL;
01521 
01522   CERTCertDBHandle *certdb;
01523 
01524   certdb = JAR_open_database();
01525 
01526   if (certdb)
01527     {
01528     unsigned char *keyData = (unsigned char *)key;
01529     issuerSN.derIssuer.len = (keyData[0] << 8) + keyData[0];
01530     issuerSN.derIssuer.data = &keyData[2];
01531     issuerSN.serialNumber.len = length - (2 + issuerSN.derIssuer.len);
01532     issuerSN.serialNumber.data = &keyData[2+issuerSN.derIssuer.len];
01533 
01534 #ifdef USE_MOZ_THREAD
01535     cert = jar_moz_certkey (certdb, &issuerSN);
01536 #else
01537     cert = CERT_FindCertByIssuerAndSN (certdb, &issuerSN);
01538 #endif
01539 
01540     JAR_close_database (certdb);
01541     }
01542 
01543   return (void *) cert;
01544   }
01545 
01546 /*
01547  *  j a r _ g e t _ m f _ d i g e s t
01548  *
01549  *  Retrieve a corresponding saved digest over a section
01550  *  of the main manifest file. 
01551  *
01552  */
01553 
01554 static JAR_Digest *jar_get_mf_digest (JAR *jar, char *pathname)
01555   {
01556   JAR_Item *it;
01557 
01558   JAR_Digest *dig;
01559 
01560   ZZLink *link;
01561   ZZList *list;
01562 
01563   list = jar->manifest;
01564 
01565   if (ZZ_ListEmpty (list))
01566     return NULL;
01567 
01568   for (link = ZZ_ListHead (list);
01569        !ZZ_ListIterDone (list, link);
01570        link = link->next)
01571     {
01572     it = link->thing;
01573     if (it->type == jarTypeSect 
01574           && it->pathname && !PORT_Strcmp (it->pathname, pathname))
01575       {
01576       dig = (JAR_Digest *) it->data;
01577       return dig;
01578       }
01579     }
01580 
01581   return NULL;
01582   }
01583 
01584 /*
01585  *  j a r _ b a s e n a m e
01586  *
01587  *  Return the basename -- leading components of path stripped off,
01588  *  extension ripped off -- of a path.
01589  *
01590  */
01591 
01592 static char *jar_basename (const char *path)
01593   {
01594   char *pith, *e, *basename, *ext;
01595 
01596   if (path == NULL)
01597     return PORT_Strdup ("");
01598 
01599   pith = PORT_Strdup (path);
01600 
01601   basename = pith;
01602 
01603   while (1)
01604     {
01605     for (e = basename; *e && *e != '/' && *e != '\\'; e++)
01606       /* yip */ ;
01607     if (*e) 
01608       basename = ++e; 
01609     else
01610       break;
01611     }
01612 
01613   if ((ext = PORT_Strrchr (basename, '.')) != NULL)
01614     *ext = 0;
01615 
01616   /* We already have the space allocated */
01617   PORT_Strcpy (pith, basename);
01618 
01619   return pith;
01620   }
01621 
01622 /*
01623  *  + + + + + + + + + + + + + + +
01624  *
01625  *  CRYPTO ROUTINES FOR JAR
01626  *
01627  *  The following functions are the cryptographic 
01628  *  interface to PKCS7 for Jarnatures.
01629  *
01630  *  + + + + + + + + + + + + + + +
01631  *
01632  */
01633 
01634 /*
01635  *  j a r _ c a t c h _ b y t e s
01636  *
01637  *  In the event signatures contain enveloped data, it will show up here.
01638  *  But note that the lib/pkcs7 routines aren't ready for it.
01639  *
01640  */
01641 
01642 static void jar_catch_bytes
01643      (void *arg, const char *buf, unsigned long len)
01644   {
01645   /* Actually this should never be called, since there is
01646      presumably no data in the signature itself. */
01647   }
01648 
01649 /*
01650  *  j a r _ v a l i d a t e _ p k c s 7
01651  *
01652  *  Validate (and decode, if necessary) a binary pkcs7
01653  *  signature in DER format.
01654  *
01655  */
01656 
01657 static int jar_validate_pkcs7 
01658      (JAR *jar, JAR_Signer *signer, char *data, long length)
01659   {
01660   SECItem detdig;
01661 
01662   SEC_PKCS7ContentInfo *cinfo = NULL;
01663   SEC_PKCS7DecoderContext *dcx;
01664 
01665   int status = 0;
01666   char *errstring = NULL;
01667 
01668   PORT_Assert( jar != NULL && signer != NULL );
01669 
01670   if (jar == NULL || signer == NULL)
01671     return JAR_ERR_ORDER;
01672 
01673   signer->valid = JAR_ERR_SIG;
01674 
01675   /* We need a context if we can get one */
01676 
01677 #ifdef MOZILLA_CLIENT_OLD
01678   if (jar->mw == NULL) {
01679     JAR_set_context (jar, NULL);
01680   }
01681 #endif
01682 
01683 
01684   dcx = SEC_PKCS7DecoderStart
01685            (jar_catch_bytes, NULL /*cb_arg*/, NULL /*getpassword*/, jar->mw,
01686             NULL, NULL, NULL);
01687 
01688   if (dcx == NULL) 
01689     {
01690     /* strange pkcs7 failure */
01691     return JAR_ERR_PK7;
01692     }
01693 
01694   SEC_PKCS7DecoderUpdate (dcx, data, length);
01695   cinfo = SEC_PKCS7DecoderFinish (dcx);
01696 
01697   if (cinfo == NULL)
01698     {
01699     /* strange pkcs7 failure */
01700     return JAR_ERR_PK7;
01701     }
01702 
01703   if (SEC_PKCS7ContentIsEncrypted (cinfo))
01704     {
01705     /* content was encrypted, fail */
01706     return JAR_ERR_PK7;
01707     }
01708 
01709   if (SEC_PKCS7ContentIsSigned (cinfo) == PR_FALSE)
01710     {
01711     /* content was not signed, fail */
01712     return JAR_ERR_PK7;
01713     }
01714 
01715   PORT_SetError (0);
01716 
01717   /* use SHA1 only */
01718 
01719   detdig.len = SHA1_LENGTH;
01720   detdig.data = signer->digest->sha1;
01721 
01722 #ifdef USE_MOZ_THREAD
01723   if (jar_moz_verify
01724         (cinfo, certUsageObjectSigner, &detdig, HASH_AlgSHA1, PR_FALSE)==
01725               SECSuccess)
01726 #else
01727   if (SEC_PKCS7VerifyDetachedSignature 
01728         (cinfo, certUsageObjectSigner, &detdig, HASH_AlgSHA1, PR_FALSE)==
01729               PR_TRUE)
01730 #endif
01731     {
01732     /* signature is valid */
01733     signer->valid = 0;
01734     jar_gather_signers (jar, signer, cinfo);
01735     }
01736   else
01737     {
01738     status = PORT_GetError();
01739 
01740     PORT_Assert( status < 0 );
01741     if (status >= 0) status = JAR_ERR_SIG;
01742 
01743     jar->valid = status;
01744     signer->valid = status;
01745 
01746     errstring = JAR_get_error (status);
01747     /*XP_TRACE(("JAR signature invalid (reason %d = %s)", status, errstring));*/
01748     }
01749 
01750   jar->pkcs7 = PR_TRUE;
01751   signer->pkcs7 = PR_TRUE;
01752 
01753   SEC_PKCS7DestroyContentInfo (cinfo);
01754 
01755   return status;
01756   }
01757 
01758 /*
01759  *  j a r _ g a t h e r _ s i g n e r s
01760  *
01761  *  Add the single signer of this signature to the
01762  *  certificate linked list.
01763  *
01764  */
01765 
01766 static int jar_gather_signers
01767      (JAR *jar, JAR_Signer *signer, SEC_PKCS7ContentInfo *cinfo)
01768   {
01769   int result;
01770 
01771   CERTCertificate *cert;
01772   CERTCertDBHandle *certdb;
01773 
01774   SEC_PKCS7SignedData *sdp;
01775   SEC_PKCS7SignerInfo **pksigners, *pksigner;
01776 
01777   sdp = cinfo->content.signedData;
01778 
01779   if (sdp == NULL)
01780     return JAR_ERR_PK7;
01781 
01782   pksigners = sdp->signerInfos;
01783 
01784   /* permit exactly one signer */
01785 
01786   if (pksigners == NULL || pksigners [0] == NULL || pksigners [1] != NULL)
01787     return JAR_ERR_PK7;
01788 
01789   pksigner = *pksigners;
01790   cert = pksigner->cert;
01791 
01792   if (cert == NULL)
01793     return JAR_ERR_PK7;
01794 
01795   certdb = JAR_open_database();
01796 
01797   if (certdb == NULL)
01798     return JAR_ERR_GENERAL;
01799 
01800   result = jar_add_cert (jar, signer, jarTypeSign, cert);
01801 
01802   JAR_close_database (certdb);
01803 
01804   return result;
01805   }
01806 
01807 /*
01808  *  j a r _ o p e n _ d a t a b a s e
01809  *
01810  *  Open the certificate database,
01811  *  for use by JAR functions.
01812  *
01813  */
01814 
01815 CERTCertDBHandle *JAR_open_database (void)
01816   {
01817   CERTCertDBHandle *certdb;
01818 
01819   certdb = CERT_GetDefaultCertDB();
01820 
01821   return certdb;
01822   }
01823 
01824 /*
01825  *  j a r _ c l o s e _ d a t a b a s e
01826  *
01827  *  Close the certificate database.
01828  *  For use by JAR functions.
01829  *
01830  */
01831 
01832 int JAR_close_database (CERTCertDBHandle *certdb)
01833   {
01834 #ifdef notdef
01835   CERTCertDBHandle *defaultdb;
01836 
01837   /* This really just retrieves the handle, nothing more */
01838   defaultdb = CERT_GetDefaultCertDB();
01839 
01840   /* If there is no default db, it means we opened 
01841      the permanent database for some reason */
01842 
01843   if (defaultdb == NULL && certdb != NULL)
01844     CERT_ClosePermCertDB (certdb);
01845 #endif
01846 
01847   return 0;
01848   }
01849 
01850 /*
01851  *  j a r _ g e t _ c e r t i f i c a t e
01852  *
01853  *  Return the certificate referenced
01854  *  by a given fingerprint, or NULL if not found.
01855  *  Error code is returned in result.
01856  *
01857  */
01858 
01859 static CERTCertificate *jar_get_certificate
01860       (JAR *jar, long keylen, void *key, int *result)
01861   {
01862   int found = 0;
01863 
01864   JAR_Item *it;
01865   JAR_Cert *fing = NULL;
01866 
01867   JAR_Context *ctx;
01868 
01869   if (jar == NULL) 
01870     {
01871     void *cert;
01872     cert = JAR_fetch_cert (keylen, key);
01873     *result = (cert == NULL) ? JAR_ERR_GENERAL : 0;
01874     return (CERTCertificate *) cert;
01875     }
01876 
01877   ctx = JAR_find (jar, NULL, jarTypeSign);
01878 
01879   while (JAR_find_next (ctx, &it) >= 0)
01880     {
01881     fing = (JAR_Cert *) it->data;
01882 
01883     if (keylen != fing->length)
01884       continue;
01885 
01886     PORT_Assert( keylen < 0xFFFF );
01887     if (!PORT_Memcmp (fing->key, key, keylen))
01888       {
01889       found = 1;
01890       break;
01891       }
01892     }
01893 
01894   JAR_find_end (ctx);
01895 
01896   if (found == 0)
01897     {
01898     *result = JAR_ERR_GENERAL;
01899     return NULL;
01900     }
01901 
01902   PORT_Assert(fing != NULL);
01903   *result = 0;
01904   return fing->cert;
01905   }
01906 
01907 /*
01908  *  j a r _ s i g n a l
01909  *
01910  *  Nonfatal errors come here to callback Java.
01911  *  
01912  */
01913 
01914 static int jar_signal 
01915      (int status, JAR *jar, const char *metafile, char *pathname)
01916   {
01917   char *errstring;
01918 
01919   errstring = JAR_get_error (status);
01920 
01921   if (jar->signal)
01922     {
01923     (*jar->signal) (status, jar, metafile, pathname, errstring);
01924     return 0;
01925     }
01926 
01927   return status;
01928   }
01929 
01930 /*
01931  *  j a r _ a p p e n d
01932  *
01933  *  Tack on an element to one of a JAR's linked
01934  *  lists, with rudimentary error handling.
01935  *
01936  */
01937 
01938 int jar_append (ZZList *list, int type, 
01939         char *pathname, void *data, size_t size)
01940   {
01941   JAR_Item *it;
01942   ZZLink *entity;
01943 
01944   it = (JAR_Item*)PORT_ZAlloc (sizeof (JAR_Item));
01945 
01946   if (it == NULL)
01947     goto loser;
01948 
01949   if (pathname)
01950     {
01951     it->pathname = PORT_Strdup (pathname);
01952     if (it->pathname == NULL)
01953       goto loser;
01954     }
01955 
01956   it->type = (jarType)type;
01957   it->data = (unsigned char *) data;
01958   it->size = size;
01959 
01960   entity = ZZ_NewLink (it);
01961 
01962   if (entity)
01963     {
01964     ZZ_AppendLink (list, entity);
01965     return 0;
01966     }
01967 
01968 loser:
01969 
01970   if (it)
01971     {
01972     if (it->pathname) PORT_Free (it->pathname);
01973     PORT_Free (it);
01974     }
01975 
01976   return JAR_ERR_MEMORY;
01977   }
01978 
01979 /* 
01980  *  W I N 1 6   s t u f f
01981  *
01982  *  These functions possibly belong in xp_mem.c, they operate 
01983  *  on huge string pointers for win16.
01984  *
01985  */
01986 
01987 #if defined(XP_WIN16)
01988 int xp_HUGE_STRNCASECMP (char ZHUGEP *buf, char *key, int len)
01989   {
01990   while (len--) 
01991     {
01992     char c1, c2;
01993 
01994     c1 = *buf++;
01995     c2 = *key++;
01996 
01997     if (c1 >= 'a' && c1 <= 'z') c1 -= ('a' - 'A');
01998     if (c2 >= 'a' && c2 <= 'z') c2 -= ('a' - 'A');
01999 
02000     if (c1 != c2) 
02001       return (c1 < c2) ? -1 : 1;
02002     }
02003   return 0;
02004   }
02005 
02006 size_t xp_HUGE_STRLEN (char ZHUGEP *s)
02007   {
02008   size_t len = 0L;
02009   while (*s++) len++;
02010   return len;
02011   }
02012 
02013 char *xp_HUGE_STRCPY (char *to, char ZHUGEP *from)
02014   {
02015   char *ret = to;
02016 
02017   while (*from)
02018     *to++ = *from++;
02019   *to = 0;
02020 
02021   return ret;
02022   }
02023 #endif