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