Back to index

lightning-sunbird  0.9+nobinonly
jarsign.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  *  JARSIGN
00039  *
00040  *  Routines used in signing archives.
00041  */
00042 
00043 
00044 #define USE_MOZ_THREAD
00045 
00046 #include "jar.h"
00047 #include "jarint.h"
00048 
00049 #ifdef USE_MOZ_THREAD
00050 #include "jarevil.h"
00051 #endif
00052 
00053 #include "pk11func.h"
00054 #include "sechash.h"
00055 
00056 /* from libevent.h */
00057 typedef void (*ETVoidPtrFunc) (void * data);
00058 
00059 #ifdef MOZILLA_CLIENT_OLD
00060 
00061 extern void ET_moz_CallFunction (ETVoidPtrFunc fn, void *data);
00062 
00063 /* from proto.h */
00064 /* extern MWContext *XP_FindSomeContext(void); */
00065 extern void *XP_FindSomeContext(void);
00066 
00067 #endif
00068 
00069 /* key database wrapper */
00070 
00071 /* static SECKEYKeyDBHandle *jar_open_key_database (void); */
00072 
00073 /* CHUNQ is our bite size */
00074 
00075 #define CHUNQ 64000
00076 #define FILECHUNQ 32768
00077 
00078 /*
00079  *  J A R _ c a l c u l a t e _ d i g e s t 
00080  *
00081  *  Quick calculation of a digest for
00082  *  the specified block of memory. Will calculate
00083  *  for all supported algorithms, now MD5.
00084  *
00085  *  This version supports huge pointers for WIN16.
00086  * 
00087  */
00088 
00089 JAR_Digest * PR_CALLBACK JAR_calculate_digest (void ZHUGEP *data, long length)
00090   {
00091   long chunq;
00092   JAR_Digest *dig;
00093 
00094   unsigned int md5_length, sha1_length;
00095 
00096   PK11Context *md5  = 0;
00097   PK11Context *sha1 = 0;
00098 
00099   dig = (JAR_Digest *) PORT_ZAlloc (sizeof (JAR_Digest));
00100 
00101   if (dig == NULL) 
00102     {
00103     /* out of memory allocating digest */
00104     return NULL;
00105     }
00106 
00107 #if defined(XP_WIN16)
00108   PORT_Assert ( !IsBadHugeReadPtr(data, length) );
00109 #endif
00110 
00111   md5  = PK11_CreateDigestContext (SEC_OID_MD5);
00112   sha1 = PK11_CreateDigestContext (SEC_OID_SHA1);
00113 
00114   if (length >= 0) 
00115     {
00116     PK11_DigestBegin (md5);
00117     PK11_DigestBegin (sha1);
00118 
00119     do {
00120        chunq = length;
00121 
00122 #ifdef XP_WIN16
00123        if (length > CHUNQ) chunq = CHUNQ;
00124 
00125        /*
00126         *  If the block of data crosses one or more segment 
00127         *  boundaries then only pass the chunk of data in the 
00128         *  first segment.
00129         * 
00130         *  This allows the data to be treated as FAR by the
00131         *  PK11_DigestOp(...) routine.
00132         *
00133         */
00134 
00135        if (OFFSETOF(data) + chunq >= 0x10000) 
00136          chunq = 0x10000 - OFFSETOF(data);
00137 #endif
00138 
00139        PK11_DigestOp (md5,  (unsigned char*)data, chunq);
00140        PK11_DigestOp (sha1, (unsigned char*)data, chunq);
00141 
00142        length -= chunq;
00143        data = ((char ZHUGEP *) data + chunq);
00144        } 
00145     while (length > 0);
00146 
00147     PK11_DigestFinal (md5,  dig->md5,  &md5_length,  MD5_LENGTH);
00148     PK11_DigestFinal (sha1, dig->sha1, &sha1_length, SHA1_LENGTH);
00149 
00150     PK11_DestroyContext (md5,  PR_TRUE);
00151     PK11_DestroyContext (sha1, PR_TRUE);
00152     }
00153 
00154   return dig;
00155   }
00156 
00157 /*
00158  *  J A R _ d i g e s t _ f i l e
00159  *
00160  *  Calculates the MD5 and SHA1 digests for a file 
00161  *  present on disk, and returns these in JAR_Digest struct.
00162  *
00163  */
00164 
00165 int JAR_digest_file (char *filename, JAR_Digest *dig)
00166     {
00167     JAR_FILE fp;
00168 
00169     int num;
00170     unsigned char *buf;
00171 
00172     PK11Context *md5 = 0;
00173     PK11Context *sha1 = 0;
00174 
00175     unsigned int md5_length, sha1_length;
00176 
00177     buf = (unsigned char *) PORT_ZAlloc (FILECHUNQ);
00178     if (buf == NULL)
00179       {
00180       /* out of memory */
00181       return JAR_ERR_MEMORY;
00182       }
00183  
00184     if ((fp = JAR_FOPEN (filename, "rb")) == 0)
00185       {
00186       /* perror (filename); FIX XXX XXX XXX XXX XXX XXX */
00187       PORT_Free (buf);
00188       return JAR_ERR_FNF;
00189       }
00190 
00191     md5 = PK11_CreateDigestContext (SEC_OID_MD5);
00192     sha1 = PK11_CreateDigestContext (SEC_OID_SHA1);
00193 
00194     if (md5 == NULL || sha1 == NULL) 
00195       {
00196       /* can't generate digest contexts */
00197       PORT_Free (buf);
00198       JAR_FCLOSE (fp);
00199       return JAR_ERR_GENERAL;
00200       }
00201 
00202     PK11_DigestBegin (md5);
00203     PK11_DigestBegin (sha1);
00204 
00205     while (1)
00206       {
00207       if ((num = JAR_FREAD (fp, buf, FILECHUNQ)) == 0)
00208         break;
00209 
00210       PK11_DigestOp (md5, buf, num);
00211       PK11_DigestOp (sha1, buf, num);
00212       }
00213 
00214     PK11_DigestFinal (md5, dig->md5, &md5_length, MD5_LENGTH);
00215     PK11_DigestFinal (sha1, dig->sha1, &sha1_length, SHA1_LENGTH);
00216 
00217     PK11_DestroyContext (md5, PR_TRUE);
00218     PK11_DestroyContext (sha1, PR_TRUE);
00219 
00220     PORT_Free (buf);
00221     JAR_FCLOSE (fp);
00222 
00223     return 0;
00224     }
00225 
00226 /*
00227  *  J A R _ o p e n _ k e y _ d a t a b a s e
00228  *
00229  */
00230 
00231 void* jar_open_key_database (void)
00232   {
00233     return NULL;
00234   }
00235 
00236 int jar_close_key_database (void *keydb)
00237   {
00238   /* We never do close it */
00239   return 0;
00240   }
00241 
00242 
00243 /*
00244  *  j a r _ c r e a t e _ p k 7
00245  *
00246  */
00247 
00248 static void jar_pk7_out (void *arg, const char *buf, unsigned long len)
00249   {
00250   JAR_FWRITE ((JAR_FILE) arg, buf, len);
00251   }
00252 
00253 int jar_create_pk7 
00254    (CERTCertDBHandle *certdb, void *keydb, 
00255        CERTCertificate *cert, char *password, JAR_FILE infp, JAR_FILE outfp)
00256   {
00257   int nb;
00258   unsigned char buffer [4096], digestdata[32];
00259   const SECHashObject *hashObj;
00260   void *hashcx;
00261   unsigned int len;
00262 
00263   int status = 0;
00264   char *errstring;
00265 
00266   SECItem digest;
00267   SEC_PKCS7ContentInfo *cinfo;
00268   SECStatus rv;
00269 
00270   void /*MWContext*/ *mw;
00271 
00272   if (outfp == NULL || infp == NULL || cert == NULL)
00273     return JAR_ERR_GENERAL;
00274 
00275   /* we sign with SHA */
00276   hashObj = HASH_GetHashObject(HASH_AlgSHA1);
00277 
00278   hashcx = (* hashObj->create)();
00279   if (hashcx == NULL)
00280     return JAR_ERR_GENERAL;
00281 
00282   (* hashObj->begin)(hashcx);
00283 
00284   while (1)
00285     {
00286     /* nspr2.0 doesn't support feof 
00287        if (feof (infp)) break; */
00288 
00289     nb = JAR_FREAD (infp, buffer, sizeof (buffer));
00290     if (nb == 0) 
00291       {
00292 #if 0
00293       if (ferror(infp)) 
00294         {
00295         /* PORT_SetError(SEC_ERROR_IO); */ /* FIX */
00296        (* hashObj->destroy) (hashcx, PR_TRUE);
00297        return JAR_ERR_GENERAL;
00298         }
00299 #endif
00300       /* eof */
00301       break;
00302       }
00303     (* hashObj->update) (hashcx, buffer, nb);
00304     }
00305 
00306   (* hashObj->end) (hashcx, digestdata, &len, 32);
00307   (* hashObj->destroy) (hashcx, PR_TRUE);
00308 
00309   digest.data = digestdata;
00310   digest.len = len;
00311 
00312   /* signtool must use any old context it can find since it's
00313      calling from inside javaland. */
00314 
00315 #ifdef MOZILLA_CLIENT_OLD
00316   mw = XP_FindSomeContext();
00317 #else
00318   mw = NULL;
00319 #endif
00320 
00321   PORT_SetError (0);
00322 
00323   cinfo = SEC_PKCS7CreateSignedData 
00324              (cert, certUsageObjectSigner, NULL, 
00325                 SEC_OID_SHA1, &digest, NULL, (void *) mw);
00326 
00327   if (cinfo == NULL)
00328     return JAR_ERR_PK7;
00329 
00330   rv = SEC_PKCS7IncludeCertChain (cinfo, NULL);
00331   if (rv != SECSuccess) 
00332     {
00333     status = PORT_GetError();
00334     SEC_PKCS7DestroyContentInfo (cinfo);
00335     return status;
00336     }
00337 
00338   /* Having this here forces signtool to always include
00339      signing time. */
00340 
00341   rv = SEC_PKCS7AddSigningTime (cinfo);
00342   if (rv != SECSuccess)
00343     {
00344     /* don't check error */
00345     }
00346 
00347   PORT_SetError (0);
00348 
00349 #ifdef USE_MOZ_THREAD
00350   /* if calling from mozilla */
00351   rv = jar_moz_encode
00352              (cinfo, jar_pk7_out, outfp, 
00353                  NULL,  /* pwfn */ NULL,  /* pwarg */ (void *) mw);
00354 #else
00355   /* if calling from mozilla thread*/
00356   rv = SEC_PKCS7Encode 
00357              (cinfo, jar_pk7_out, outfp, 
00358                  NULL,  /* pwfn */ NULL,  /* pwarg */ (void *) mw):
00359 #endif
00360 
00361   if (rv != SECSuccess)
00362     status = PORT_GetError();
00363 
00364   SEC_PKCS7DestroyContentInfo (cinfo);
00365 
00366   if (rv != SECSuccess)
00367     {
00368     errstring = JAR_get_error (status);
00369     /*XP_TRACE (("Jar signing failed (reason %d = %s)", status, errstring));*/
00370     return status < 0 ? status : JAR_ERR_GENERAL;
00371     }
00372 
00373   return 0;
00374   }