1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #include "signtool.h"
6 #include "zip.h"
7 #include "prmem.h"
8 #include "blapi.h"
9 #include "sechash.h" /* for HASH_GetHashObject() */
10 
11 static int create_pk7(char *dir, char *keyName, int *keyType);
12 static int jar_find_key_type(CERTCertificate *cert);
13 static int manifesto(char *dirname, char *install_script, PRBool recurse);
14 static int manifesto_fn(char *relpath, char *basedir, char *reldir,
15                         char *filename, void *arg);
16 static int manifesto_xpi_fn(char *relpath, char *basedir, char *reldir,
17                             char *filename, void *arg);
18 static int sign_all_arc_fn(char *relpath, char *basedir, char *reldir,
19                            char *filename, void *arg);
20 static int add_meta(FILE *fp, char *name);
21 static int SignFile(FILE *outFile, FILE *inFile, CERTCertificate *cert);
22 static int generate_SF_file(char *manifile, char *who);
23 static int calculate_MD5_range(FILE *fp, long r1, long r2,
24                                JAR_Digest *dig);
25 static void SignOut(void *arg, const char *buf, unsigned long len);
26 
27 static char *metafile = NULL;
28 static int optimize = 0;
29 static FILE *mf;
30 static ZIPfile *zipfile = NULL;
31 
32 /*
33  *  S i g n A r c h i v e
34  *
35  *  Sign an individual archive tree. A directory
36  *  called META-INF is created underneath this.
37  *
38  */
39 int
SignArchive(char * tree,char * keyName,char * zip_file,int javascript,char * meta_file,char * install_script,int _optimize,PRBool recurse)40 SignArchive(char *tree, char *keyName, char *zip_file, int javascript,
41             char *meta_file, char *install_script, int _optimize, PRBool recurse)
42 {
43     int status;
44     char tempfn[FNSIZE], fullfn[FNSIZE];
45     int keyType = rsaKey;
46     int count;
47 
48     metafile = meta_file;
49     optimize = _optimize;
50 
51     /* To create XPI compatible Archive manifesto() must be run before
52      * the zipfile is opened. This is so the signed files are not added
53      * the archive before the crucial rsa/dsa file*/
54     if (xpi_arc) {
55         manifesto(tree, install_script, recurse);
56     }
57 
58     if (zip_file) {
59         zipfile = JzipOpen(zip_file, NULL /*no comment*/);
60     }
61 
62     /*Sign and add files to the archive normally with manifesto()*/
63     if (!xpi_arc) {
64         manifesto(tree, install_script, recurse);
65     }
66 
67     if (keyName) {
68         status = create_pk7(tree, keyName, &keyType);
69         if (status < 0) {
70             PR_fprintf(errorFD, "the tree \"%s\" was NOT SUCCESSFULLY SIGNED\n",
71                        tree);
72             errorCount++;
73             exit(ERRX);
74         }
75     }
76 
77     /* Add the rsa/dsa file as the first file in the archive. This is crucial
78      * for a XPInstall compatible archive */
79     if (xpi_arc) {
80         if (verbosity >= 0) {
81             PR_fprintf(outputFD, "%s \n", XPI_TEXT);
82         }
83 
84         /* rsa/dsa to zip */
85         count = snprintf(tempfn, sizeof(tempfn), "META-INF/%s.%s", base, (keyType == dsaKey ? "dsa" : "rsa"));
86         if (count >= sizeof(tempfn)) {
87             PR_fprintf(errorFD, "unable to write key metadata\n");
88             errorCount++;
89             exit(ERRX);
90         }
91         count = snprintf(fullfn, sizeof(fullfn), "%s/%s", tree, tempfn);
92         if (count >= sizeof(fullfn)) {
93             PR_fprintf(errorFD, "unable to write key metadata\n");
94             errorCount++;
95             exit(ERRX);
96         }
97         JzipAdd(fullfn, tempfn, zipfile, compression_level);
98 
99         /* Loop through all files & subdirectories, add to archive */
100         foreach (tree, "", manifesto_xpi_fn, recurse, PR_FALSE /*include dirs */,
101                  (void *)NULL)
102             ;
103     }
104     /* mf to zip */
105     strcpy(tempfn, "META-INF/manifest.mf");
106     count = snprintf(fullfn, sizeof(fullfn), "%s/%s", tree, tempfn);
107     if (count >= sizeof(fullfn)) {
108         PR_fprintf(errorFD, "unable to write manifest\n");
109         errorCount++;
110         exit(ERRX);
111     }
112     JzipAdd(fullfn, tempfn, zipfile, compression_level);
113 
114     /* sf to zip */
115     count = snprintf(tempfn, sizeof(tempfn), "META-INF/%s.sf", base);
116     if (count >= sizeof(tempfn)) {
117         PR_fprintf(errorFD, "unable to write sf metadata\n");
118         errorCount++;
119         exit(ERRX);
120     }
121     count = snprintf(fullfn, sizeof(fullfn), "%s/%s", tree, tempfn);
122     if (count >= sizeof(fullfn)) {
123         PR_fprintf(errorFD, "unable to write sf metadata\n");
124         errorCount++;
125         exit(ERRX);
126     }
127     JzipAdd(fullfn, tempfn, zipfile, compression_level);
128 
129     /* Add the rsa/dsa file to the zip archive normally */
130     if (!xpi_arc) {
131         /* rsa/dsa to zip */
132         count = snprintf(tempfn, sizeof(tempfn), "META-INF/%s.%s", base, (keyType == dsaKey ? "dsa" : "rsa"));
133         if (count >= sizeof(tempfn)) {
134             PR_fprintf(errorFD, "unable to write key metadata\n");
135             errorCount++;
136             exit(ERRX);
137         }
138         count = snprintf(fullfn, sizeof(fullfn), "%s/%s", tree, tempfn);
139         if (count >= sizeof(fullfn)) {
140             PR_fprintf(errorFD, "unable to write key metadata\n");
141             errorCount++;
142             exit(ERRX);
143         }
144         JzipAdd(fullfn, tempfn, zipfile, compression_level);
145     }
146 
147     JzipClose(zipfile);
148 
149     if (verbosity >= 0) {
150         if (javascript) {
151             PR_fprintf(outputFD, "jarfile \"%s\" signed successfully\n",
152                        zip_file);
153         } else {
154             PR_fprintf(outputFD, "tree \"%s\" signed successfully\n",
155                        tree);
156         }
157     }
158 
159     return 0;
160 }
161 
162 typedef struct {
163     char *keyName;
164     int javascript;
165     char *metafile;
166     char *install_script;
167     int optimize;
168 } SignArcInfo;
169 
170 /*
171  *  S i g n A l l A r c
172  *
173  *  Javascript may generate multiple .arc directories, one
174  *  for each jar archive needed. Sign them all.
175  *
176  */
177 int
SignAllArc(char * jartree,char * keyName,int javascript,char * metafilename,char * install_script,int optimize_level,PRBool recurse)178 SignAllArc(char *jartree, char *keyName, int javascript, char *metafilename,
179            char *install_script, int optimize_level, PRBool recurse)
180 {
181     SignArcInfo info;
182 
183     info.keyName = keyName;
184     info.javascript = javascript;
185     info.metafile = metafilename;
186     info.install_script = install_script;
187     info.optimize = optimize_level;
188 
189     return foreach (jartree, "", sign_all_arc_fn, recurse,
190                     PR_TRUE /*include dirs*/, (void *)&info);
191 }
192 
193 static int
sign_all_arc_fn(char * relpath,char * basedir,char * reldir,char * filename,void * arg)194 sign_all_arc_fn(char *relpath, char *basedir, char *reldir, char *filename,
195                 void *arg)
196 {
197     char *zipfilename = NULL;
198     char *arc = NULL, *archive = NULL;
199     int retval = 0;
200     SignArcInfo *infop = (SignArcInfo *)arg;
201 
202     /* Make sure there is one and only one ".arc" in the relative path,
203      * and that it is at the end of the path (don't sign .arcs within .arcs) */
204     if ((PL_strcaserstr(relpath, ".arc") == relpath + strlen(relpath) - 4) &&
205         (PL_strcasestr(relpath, ".arc") == relpath + strlen(relpath) - 4)) {
206 
207         if (!infop) {
208             PR_fprintf(errorFD, "%s: Internal failure\n", PROGRAM_NAME);
209             errorCount++;
210             retval = -1;
211             goto finish;
212         }
213         archive = PR_smprintf("%s/%s", basedir, relpath);
214 
215         zipfilename = PL_strdup(archive);
216         arc = PORT_Strrchr(zipfilename, '.');
217 
218         if (arc == NULL) {
219             PR_fprintf(errorFD, "%s: Internal failure\n", PROGRAM_NAME);
220             errorCount++;
221             retval = -1;
222             goto finish;
223         }
224 
225         PL_strcpy(arc, ".jar");
226 
227         if (verbosity >= 0) {
228             PR_fprintf(outputFD, "\nsigning: %s\n", zipfilename);
229         }
230         retval = SignArchive(archive, infop->keyName, zipfilename,
231                              infop->javascript, infop->metafile, infop->install_script,
232                              infop->optimize, PR_TRUE /* recurse */);
233     }
234 finish:
235     if (archive)
236         PR_Free(archive);
237     if (zipfilename)
238         PR_Free(zipfilename);
239 
240     return retval;
241 }
242 
243 /*********************************************************************
244  *
245  * c r e a t e _ p k 7
246  */
247 static int
create_pk7(char * dir,char * keyName,int * keyType)248 create_pk7(char *dir, char *keyName, int *keyType)
249 {
250     int status = 0;
251     char *file_ext;
252 
253     CERTCertificate *cert;
254     CERTCertDBHandle *db;
255 
256     FILE *in, *out;
257 
258     char sf_file[FNSIZE];
259     char pk7_file[FNSIZE];
260 
261     /* open cert database */
262     db = CERT_GetDefaultCertDB();
263 
264     if (db == NULL)
265         return -1;
266 
267     /* find cert */
268     /*cert = CERT_FindCertByNicknameOrEmailAddr(db, keyName);*/
269     cert = PK11_FindCertFromNickname(keyName, &pwdata);
270 
271     if (cert == NULL) {
272         SECU_PrintError(PROGRAM_NAME,
273                         "Cannot find the cert \"%s\"", keyName);
274         return -1;
275     }
276 
277     /* determine the key type, which sets the extension for pkcs7 object */
278 
279     *keyType = jar_find_key_type(cert);
280     file_ext = (*keyType == dsaKey) ? "dsa" : "rsa";
281 
282     sprintf(sf_file, "%s/META-INF/%s.sf", dir, base);
283     sprintf(pk7_file, "%s/META-INF/%s.%s", dir, base, file_ext);
284 
285     if ((in = fopen(sf_file, "rb")) == NULL) {
286         PR_fprintf(errorFD, "%s: Can't open %s for reading\n", PROGRAM_NAME,
287                    sf_file);
288         errorCount++;
289         exit(ERRX);
290     }
291 
292     if ((out = fopen(pk7_file, "wb")) == NULL) {
293         PR_fprintf(errorFD, "%s: Can't open %s for writing\n", PROGRAM_NAME,
294                    sf_file);
295         errorCount++;
296         exit(ERRX);
297     }
298 
299     status = SignFile(out, in, cert);
300 
301     CERT_DestroyCertificate(cert);
302     fclose(in);
303     fclose(out);
304 
305     if (status) {
306         PR_fprintf(errorFD, "%s: PROBLEM signing data (%s)\n",
307                    PROGRAM_NAME, SECU_Strerror(PORT_GetError()));
308         errorCount++;
309         return -1;
310     }
311 
312     return 0;
313 }
314 
315 /*
316  *  j a r _ f i n d _ k e y _ t y p e
317  *
318  *  Determine the key type for a given cert, which
319  * should be rsaKey or dsaKey. Any error return 0.
320  *
321  */
322 static int
jar_find_key_type(CERTCertificate * cert)323 jar_find_key_type(CERTCertificate *cert)
324 {
325     SECKEYPrivateKey *privk = NULL;
326     KeyType keyType;
327 
328     /* determine its type */
329     privk = PK11_FindKeyByAnyCert(cert, &pwdata);
330     if (privk == NULL) {
331         PR_fprintf(errorFD, "warning - can't find private key for this cert\n");
332         warningCount++;
333         return 0;
334     }
335 
336     keyType = privk->keyType;
337     SECKEY_DestroyPrivateKey(privk);
338     return keyType;
339 }
340 
341 /*
342  *  m a n i f e s t o
343  *
344  *  Run once for every subdirectory in which a
345  *  manifest is to be created -- usually exactly once.
346  *
347  */
348 static int
manifesto(char * dirname,char * install_script,PRBool recurse)349 manifesto(char *dirname, char *install_script, PRBool recurse)
350 {
351     char metadir[FNSIZE], sfname[FNSIZE];
352 
353     /* Create the META-INF directory to hold signing info */
354 
355     if (PR_Access(dirname, PR_ACCESS_READ_OK)) {
356         PR_fprintf(errorFD, "%s: unable to read your directory: %s\n",
357                    PROGRAM_NAME, dirname);
358         errorCount++;
359         perror(dirname);
360         exit(ERRX);
361     }
362 
363     if (PR_Access(dirname, PR_ACCESS_WRITE_OK)) {
364         PR_fprintf(errorFD, "%s: unable to write to your directory: %s\n",
365                    PROGRAM_NAME, dirname);
366         errorCount++;
367         perror(dirname);
368         exit(ERRX);
369     }
370 
371     sprintf(metadir, "%s/META-INF", dirname);
372 
373     strcpy(sfname, metadir);
374 
375     PR_MkDir(metadir, 0777);
376 
377     strcat(metadir, "/");
378     strcat(metadir, MANIFEST);
379 
380     if ((mf = fopen(metadir, "wb")) == NULL) {
381         perror(MANIFEST);
382         PR_fprintf(errorFD, "%s: Probably, the directory you are trying to"
383                             " sign has\n",
384                    PROGRAM_NAME);
385         PR_fprintf(errorFD, "%s: permissions problems or may not exist.\n",
386                    PROGRAM_NAME);
387         errorCount++;
388         exit(ERRX);
389     }
390 
391     if (verbosity >= 0) {
392         PR_fprintf(outputFD, "Generating %s file..\n", metadir);
393     }
394 
395     fprintf(mf, "Manifest-Version: 1.0\n");
396     fprintf(mf, "Created-By: %s\n", CREATOR);
397     fprintf(mf, "Comments: %s\n", BREAKAGE);
398 
399     if (scriptdir) {
400         fprintf(mf, "Comments: --\n");
401         fprintf(mf, "Comments: --\n");
402         fprintf(mf, "Comments: -- This archive signs Javascripts which may not necessarily\n");
403         fprintf(mf, "Comments: -- be included in the physical jar file.\n");
404         fprintf(mf, "Comments: --\n");
405         fprintf(mf, "Comments: --\n");
406     }
407 
408     if (install_script)
409         fprintf(mf, "Install-Script: %s\n", install_script);
410 
411     if (metafile)
412         add_meta(mf, "+");
413 
414     /* Loop through all files & subdirectories */
415     foreach (dirname, "", manifesto_fn, recurse, PR_FALSE /*include dirs */,
416              (void *)NULL)
417         ;
418 
419     fclose(mf);
420 
421     strcat(sfname, "/");
422     strcat(sfname, base);
423     strcat(sfname, ".sf");
424 
425     if (verbosity >= 0) {
426         PR_fprintf(outputFD, "Generating %s.sf file..\n", base);
427     }
428     generate_SF_file(metadir, sfname);
429 
430     return 0;
431 }
432 
433 /*
434  *  m a n i f e s t o _ x p i _ f n
435  *
436  *  Called by pointer from SignArchive(), once for
437  *  each file within the directory. This function
438  *  is only used for adding to XPI compatible archive
439  *
440  */
441 static int
manifesto_xpi_fn(char * relpath,char * basedir,char * reldir,char * filename,void * arg)442 manifesto_xpi_fn(char *relpath, char *basedir, char *reldir, char *filename, void *arg)
443 {
444     char fullname[FNSIZE];
445     int count;
446 
447     if (verbosity >= 0) {
448         PR_fprintf(outputFD, "--> %s\n", relpath);
449     }
450 
451     /* extension matching */
452     if (extensionsGiven) {
453         char *ext = PL_strrchr(relpath, '.');
454         if (!ext)
455             return 0;
456         if (!PL_HashTableLookup(extensions, ext))
457             return 0;
458     }
459     count = snprintf(fullname, sizeof(fullname), "%s/%s", basedir, relpath);
460     if (count >= sizeof(fullname)) {
461         return 1;
462     }
463     JzipAdd(fullname, relpath, zipfile, compression_level);
464 
465     return 0;
466 }
467 
468 /*
469  *  m a n i f e s t o _ f n
470  *
471  *  Called by pointer from manifesto(), once for
472  *  each file within the directory.
473  *
474  */
475 static int
manifesto_fn(char * relpath,char * basedir,char * reldir,char * filename,void * arg)476 manifesto_fn(char *relpath, char *basedir, char *reldir, char *filename, void *arg)
477 {
478     int use_js;
479     char *md5, *sha1;
480 
481     JAR_Digest dig;
482     char fullname[FNSIZE];
483 
484     if (verbosity >= 0) {
485         PR_fprintf(outputFD, "--> %s\n", relpath);
486     }
487 
488     /* extension matching */
489     if (extensionsGiven) {
490         char *ext = PL_strrchr(relpath, '.');
491         if (!ext)
492             return 0;
493         if (!PL_HashTableLookup(extensions, ext))
494             return 0;
495     }
496 
497     sprintf(fullname, "%s/%s", basedir, relpath);
498 
499     fprintf(mf, "\n");
500 
501     use_js = 0;
502 
503     if (scriptdir && !PORT_Strcmp(scriptdir, reldir))
504         use_js++;
505 
506     /* sign non-.js files inside .arc directories using the javascript magic */
507 
508     if ((PL_strcaserstr(filename, ".js") != filename + strlen(filename) - 3) &&
509         (PL_strcaserstr(reldir, ".arc") == reldir + strlen(filename) - 4))
510         use_js++;
511 
512     if (use_js) {
513         fprintf(mf, "Name: %s\n", filename);
514         fprintf(mf, "Magic: javascript\n");
515 
516         if (optimize == 0)
517             fprintf(mf, "javascript.id: %s\n", filename);
518 
519         if (metafile)
520             add_meta(mf, filename);
521     } else {
522         fprintf(mf, "Name: %s\n", relpath);
523         if (metafile)
524             add_meta(mf, relpath);
525     }
526 
527     JAR_digest_file(fullname, &dig);
528 
529     if (optimize == 0) {
530         fprintf(mf, "Digest-Algorithms: MD5 SHA1\n");
531 
532         md5 = BTOA_DataToAscii(dig.md5, MD5_LENGTH);
533         fprintf(mf, "MD5-Digest: %s\n", md5);
534         PORT_Free(md5);
535     }
536 
537     sha1 = BTOA_DataToAscii(dig.sha1, SHA1_LENGTH);
538     fprintf(mf, "SHA1-Digest: %s\n", sha1);
539     PORT_Free(sha1);
540 
541     if (!use_js) {
542         JzipAdd(fullname, relpath, zipfile, compression_level);
543     }
544 
545     return 0;
546 }
547 
548 /*
549  *  a d d _ m e t a
550  *
551  *  Parse the metainfo file, and add any details
552  *  necessary to the manifest file. In most cases you
553  *  should be using the -i option (ie, for SmartUpdate).
554  *
555  */
556 static int
add_meta(FILE * fp,char * name)557 add_meta(FILE *fp, char *name)
558 {
559     FILE *met;
560     char buf[BUFSIZ];
561 
562     int place;
563     char *pattern, *meta;
564 
565     int num = 0;
566 
567     if ((met = fopen(metafile, "r")) != NULL) {
568         while (fgets(buf, BUFSIZ, met)) {
569             char *s;
570 
571             for (s = buf; *s && *s != '\n' && *s != '\r'; s++)
572                 ;
573             *s = 0;
574 
575             if (*buf == 0)
576                 continue;
577 
578             pattern = buf;
579 
580             /* skip to whitespace */
581             for (s = buf; *s && *s != ' ' && *s != '\t'; s++)
582                 ;
583 
584             /* terminate pattern */
585             if (*s == ' ' || *s == '\t')
586                 *s++ = 0;
587 
588             /* eat through whitespace */
589             while (*s == ' ' || *s == '\t')
590                 s++;
591 
592             meta = s;
593 
594             /* this will eventually be regexp matching */
595 
596             place = 0;
597             if (!PORT_Strcmp(pattern, name))
598                 place = 1;
599 
600             if (place) {
601                 num++;
602                 if (verbosity >= 0) {
603                     PR_fprintf(outputFD, "[%s] %s\n", name, meta);
604                 }
605                 fprintf(fp, "%s\n", meta);
606             }
607         }
608         fclose(met);
609     } else {
610         PR_fprintf(errorFD, "%s: can't open metafile: %s\n", PROGRAM_NAME,
611                    metafile);
612         errorCount++;
613         exit(ERRX);
614     }
615 
616     return num;
617 }
618 
619 /**********************************************************************
620  *
621  * S i g n F i l e
622  */
623 static int
SignFile(FILE * outFile,FILE * inFile,CERTCertificate * cert)624 SignFile(FILE *outFile, FILE *inFile, CERTCertificate *cert)
625 {
626     int nb;
627     char ibuf[4096], digestdata[32];
628     const SECHashObject *hashObj;
629     void *hashcx;
630     unsigned int len;
631 
632     SECItem digest;
633     SEC_PKCS7ContentInfo *cinfo;
634     SECStatus rv;
635 
636     if (outFile == NULL || inFile == NULL || cert == NULL)
637         return -1;
638 
639     /* XXX probably want to extend interface to allow other hash algorithms */
640     hashObj = HASH_GetHashObject(HASH_AlgSHA1);
641 
642     hashcx = (*hashObj->create)();
643     if (hashcx == NULL)
644         return -1;
645 
646     (*hashObj->begin)(hashcx);
647 
648     for (;;) {
649         if (feof(inFile))
650             break;
651         nb = fread(ibuf, 1, sizeof(ibuf), inFile);
652         if (nb == 0) {
653             if (ferror(inFile)) {
654                 PORT_SetError(SEC_ERROR_IO);
655                 (*hashObj->destroy)(hashcx, PR_TRUE);
656                 return -1;
657             }
658             /* eof */
659             break;
660         }
661         (*hashObj->update)(hashcx, (unsigned char *)ibuf, nb);
662     }
663 
664     (*hashObj->end)(hashcx, (unsigned char *)digestdata, &len, 32);
665     (*hashObj->destroy)(hashcx, PR_TRUE);
666 
667     digest.data = (unsigned char *)digestdata;
668     digest.len = len;
669 
670     cinfo = SEC_PKCS7CreateSignedData(cert, certUsageObjectSigner, NULL,
671                                       SEC_OID_SHA1, &digest, NULL, NULL);
672 
673     if (cinfo == NULL)
674         return -1;
675 
676     rv = SEC_PKCS7IncludeCertChain(cinfo, NULL);
677     if (rv != SECSuccess) {
678         SEC_PKCS7DestroyContentInfo(cinfo);
679         return -1;
680     }
681 
682     if (no_time == 0) {
683         rv = SEC_PKCS7AddSigningTime(cinfo);
684         if (rv != SECSuccess) {
685             /* don't check error */
686         }
687     }
688 
689     rv = SEC_PKCS7Encode(cinfo, SignOut, outFile, NULL, NULL, &pwdata);
690 
691     SEC_PKCS7DestroyContentInfo(cinfo);
692 
693     if (rv != SECSuccess)
694         return -1;
695 
696     return 0;
697 }
698 
699 /*
700  *  g e n e r a t e _ S F _ f i l e
701  *
702  *  From the supplied manifest file, calculates
703  *  digests on the various sections, creating a .SF
704  *  file in the process.
705  *
706  */
707 static int
generate_SF_file(char * manifile,char * who)708 generate_SF_file(char *manifile, char *who)
709 {
710     FILE *sfFile;
711     FILE *mfFile;
712     long r1, r2, r3;
713     char whofile[FNSIZE];
714     char *buf, *name = NULL;
715     char *md5, *sha1;
716     JAR_Digest dig;
717     int line = 0;
718 
719     strcpy(whofile, who);
720 
721     if ((mfFile = fopen(manifile, "rb")) == NULL) {
722         perror(manifile);
723         exit(ERRX);
724     }
725 
726     if ((sfFile = fopen(whofile, "wb")) == NULL) {
727         perror(who);
728         exit(ERRX);
729     }
730 
731     buf = (char *)PORT_ZAlloc(BUFSIZ);
732 
733     if (buf)
734         name = (char *)PORT_ZAlloc(BUFSIZ);
735 
736     if (buf == NULL || name == NULL)
737         out_of_memory();
738 
739     fprintf(sfFile, "Signature-Version: 1.0\n");
740     fprintf(sfFile, "Created-By: %s\n", CREATOR);
741     fprintf(sfFile, "Comments: %s\n", BREAKAGE);
742 
743     if (fgets(buf, BUFSIZ, mfFile) == NULL) {
744         PR_fprintf(errorFD, "%s: empty manifest file!\n", PROGRAM_NAME);
745         errorCount++;
746         exit(ERRX);
747     }
748 
749     if (strncmp(buf, "Manifest-Version:", 17)) {
750         PR_fprintf(errorFD, "%s: not a manifest file!\n", PROGRAM_NAME);
751         errorCount++;
752         exit(ERRX);
753     }
754 
755     fseek(mfFile, 0L, SEEK_SET);
756 
757     /* Process blocks of headers, and calculate their hashen */
758 
759     while (1) {
760         /* Beginning range */
761         r1 = ftell(mfFile);
762 
763         if (fgets(name, BUFSIZ, mfFile) == NULL)
764             break;
765 
766         line++;
767 
768         if (r1 != 0 && strncmp(name, "Name:", 5)) {
769             PR_fprintf(errorFD,
770                        "warning: unexpected input in manifest file \"%s\" at line %d:\n",
771                        manifile, line);
772             PR_fprintf(errorFD, "%s\n", name);
773             warningCount++;
774         }
775 
776         r2 = r1;
777         while (fgets(buf, BUFSIZ, mfFile)) {
778             if (*buf == 0 || *buf == '\n' || *buf == '\r')
779                 break;
780 
781             line++;
782 
783             /* Ending range for hashing */
784             r2 = ftell(mfFile);
785         }
786 
787         r3 = ftell(mfFile);
788 
789         if (r1) {
790             fprintf(sfFile, "\n");
791             fprintf(sfFile, "%s", name);
792         }
793 
794         calculate_MD5_range(mfFile, r1, r2, &dig);
795 
796         if (optimize == 0) {
797             fprintf(sfFile, "Digest-Algorithms: MD5 SHA1\n");
798 
799             md5 = BTOA_DataToAscii(dig.md5, MD5_LENGTH);
800             fprintf(sfFile, "MD5-Digest: %s\n", md5);
801             PORT_Free(md5);
802         }
803 
804         sha1 = BTOA_DataToAscii(dig.sha1, SHA1_LENGTH);
805         fprintf(sfFile, "SHA1-Digest: %s\n", sha1);
806         PORT_Free(sha1);
807 
808         /* restore normalcy after changing offset position */
809         fseek(mfFile, r3, SEEK_SET);
810     }
811 
812     PORT_Free(buf);
813     PORT_Free(name);
814 
815     fclose(sfFile);
816     fclose(mfFile);
817 
818     return 0;
819 }
820 
821 /*
822  *  c a l c u l a t e _ M D 5 _ r a n g e
823  *
824  *  Calculate the MD5 digest on a range of bytes in
825  *  the specified fopen'd file. Returns base64.
826  *
827  */
828 static int
calculate_MD5_range(FILE * fp,long r1,long r2,JAR_Digest * dig)829 calculate_MD5_range(FILE *fp, long r1, long r2, JAR_Digest *dig)
830 {
831     int num;
832     int range;
833     unsigned char *buf;
834     SECStatus rv;
835 
836     range = r2 - r1;
837 
838     /* position to the beginning of range */
839     fseek(fp, r1, SEEK_SET);
840 
841     buf = (unsigned char *)PORT_ZAlloc(range);
842     if (buf == NULL)
843         out_of_memory();
844 
845     if ((num = fread(buf, 1, range, fp)) != range) {
846         PR_fprintf(errorFD, "%s: expected %d bytes, got %d\n", PROGRAM_NAME,
847                    range, num);
848         errorCount++;
849         exit(ERRX);
850     }
851 
852     rv = PK11_HashBuf(SEC_OID_MD5, dig->md5, buf, range);
853     if (rv == SECSuccess) {
854         rv = PK11_HashBuf(SEC_OID_SHA1, dig->sha1, buf, range);
855     }
856     if (rv != SECSuccess) {
857         PR_fprintf(errorFD, "%s: can't generate digest context\n",
858                    PROGRAM_NAME);
859         errorCount++;
860         exit(ERRX);
861     }
862 
863     PORT_Free(buf);
864 
865     return 0;
866 }
867 
868 static void
SignOut(void * arg,const char * buf,unsigned long len)869 SignOut(void *arg, const char *buf, unsigned long len)
870 {
871     fwrite(buf, len, 1, (FILE *)arg);
872 }
873