Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
5 /*
6 * JARVER
7 *
8 * Jarnature Parsing & Verification
9 */
11 #include "nssrenam.h"
12 #include "jar.h"
13 #include "jarint.h"
14 #include "certdb.h"
15 #include "certt.h"
16 #include "secpkcs7.h"
17 #include "secder.h"
19 #define SZ 512
21 static int
22 jar_validate_pkcs7(JAR *jar, JAR_Signer *signer, char *data, long length);
24 static void
25 jar_catch_bytes(void *arg, const char *buf, unsigned long len);
27 static int
28 jar_gather_signers(JAR *jar, JAR_Signer *signer, SEC_PKCS7ContentInfo *cinfo);
30 static char *
31 jar_eat_line(int lines, int eating, char *data, long *len);
33 static JAR_Digest *
34 jar_digest_section(char *manifest, long length);
36 static JAR_Digest *jar_get_mf_digest(JAR *jar, char *path);
38 static int
39 jar_parse_digital_signature(char *raw_manifest, JAR_Signer *signer,
40 long length, JAR *jar);
42 static int
43 jar_add_cert(JAR *jar, JAR_Signer *signer, int type, CERTCertificate *cert);
45 static char *jar_basename(const char *path);
47 static int
48 jar_signal(int status, JAR *jar, const char *metafile, char *pathname);
50 #ifdef DEBUG
51 static int jar_insanity_check(char *data, long length);
52 #endif
54 int
55 jar_parse_mf(JAR *jar, char *raw_manifest, long length,
56 const char *path, const char *url);
58 int
59 jar_parse_sf(JAR *jar, char *raw_manifest, long length,
60 const char *path, const char *url);
62 int
63 jar_parse_sig(JAR *jar, const char *path, char *raw_manifest,
64 long length);
66 int
67 jar_parse_any(JAR *jar, int type, JAR_Signer *signer,
68 char *raw_manifest, long length, const char *path,
69 const char *url);
71 static int
72 jar_internal_digest(JAR *jar, const char *path, char *x_name, JAR_Digest *dig);
74 /*
75 * J A R _ p a r s e _ m a n i f e s t
76 *
77 * Pass manifest files to this function. They are
78 * decoded and placed into internal representations.
79 *
80 * Accepts both signature and manifest files. Use
81 * the same "jar" for both.
82 *
83 */
84 int
85 JAR_parse_manifest(JAR *jar, char *raw_manifest, long length,
86 const char *path, const char *url)
87 {
88 int filename_free = 0;
90 /* fill in the path, if supplied. This is the location
91 of the jar file on disk, if known */
93 if (jar->filename == NULL && path) {
94 jar->filename = PORT_Strdup(path);
95 if (jar->filename == NULL)
96 return JAR_ERR_MEMORY;
97 filename_free = 1;
98 }
100 /* fill in the URL, if supplied. This is the place
101 from which the jar file was retrieved. */
103 if (jar->url == NULL && url) {
104 jar->url = PORT_Strdup(url);
105 if (jar->url == NULL) {
106 if (filename_free) {
107 PORT_Free(jar->filename);
108 }
109 return JAR_ERR_MEMORY;
110 }
111 }
113 /* Determine what kind of file this is from the META-INF
114 directory. It could be MF, SF, or a binary RSA/DSA file */
116 if (!PORT_Strncasecmp (raw_manifest, "Manifest-Version:", 17)) {
117 return jar_parse_mf(jar, raw_manifest, length, path, url);
118 }
119 else if (!PORT_Strncasecmp (raw_manifest, "Signature-Version:", 18))
120 {
121 return jar_parse_sf(jar, raw_manifest, length, path, url);
122 } else {
123 /* This is probably a binary signature */
124 return jar_parse_sig(jar, path, raw_manifest, length);
125 }
126 }
128 /*
129 * j a r _ p a r s e _ s i g
130 *
131 * Pass some manner of RSA or DSA digital signature
132 * on, after checking to see if it comes at an appropriate state.
133 *
134 */
135 int
136 jar_parse_sig(JAR *jar, const char *path, char *raw_manifest,
137 long length)
138 {
139 JAR_Signer *signer;
140 int status = JAR_ERR_ORDER;
142 if (length <= 128) {
143 /* signature is way too small */
144 return JAR_ERR_SIG;
145 }
147 /* make sure that MF and SF have already been processed */
149 if (jar->globalmeta == NULL)
150 return JAR_ERR_ORDER;
152 /* Determine whether or not this RSA file has
153 has an associated SF file */
155 if (path) {
156 char *owner;
157 owner = jar_basename(path);
159 if (owner == NULL)
160 return JAR_ERR_MEMORY;
162 signer = jar_get_signer(jar, owner);
163 PORT_Free(owner);
164 } else
165 signer = jar_get_signer(jar, "*");
167 if (signer == NULL)
168 return JAR_ERR_ORDER;
171 /* Do not pass a huge pointer to this function,
172 since the underlying security code is unaware. We will
173 never pass >64k through here. */
175 if (length > 64000) {
176 /* this digital signature is way too big */
177 return JAR_ERR_SIG;
178 }
180 /* don't expense unneeded calloc overhead on non-win16 */
181 status = jar_parse_digital_signature(raw_manifest, signer, length, jar);
183 return status;
184 }
186 /*
187 * j a r _ p a r s e _ m f
188 *
189 * Parse the META-INF/manifest.mf file, whose
190 * information applies to all signers.
191 *
192 */
193 int
194 jar_parse_mf(JAR *jar, char *raw_manifest, long length,
195 const char *path, const char *url)
196 {
197 if (jar->globalmeta) {
198 /* refuse a second manifest file, if passed for some reason */
199 return JAR_ERR_ORDER;
200 }
202 /* remember a digest for the global section */
203 jar->globalmeta = jar_digest_section(raw_manifest, length);
204 if (jar->globalmeta == NULL)
205 return JAR_ERR_MEMORY;
206 return jar_parse_any(jar, jarTypeMF, NULL, raw_manifest, length,
207 path, url);
208 }
210 /*
211 * j a r _ p a r s e _ s f
212 *
213 * Parse META-INF/xxx.sf, a digitally signed file
214 * pointing to a subset of MF sections.
215 *
216 */
217 int
218 jar_parse_sf(JAR *jar, char *raw_manifest, long length,
219 const char *path, const char *url)
220 {
221 JAR_Signer *signer = NULL;
222 int status = JAR_ERR_MEMORY;
224 if (jar->globalmeta == NULL) {
225 /* It is a requirement that the MF file be passed before the SF file */
226 return JAR_ERR_ORDER;
227 }
229 signer = JAR_new_signer();
230 if (signer == NULL)
231 goto loser;
233 if (path) {
234 signer->owner = jar_basename(path);
235 if (signer->owner == NULL)
236 goto loser;
237 }
239 /* check for priors. When someone doctors a jar file
240 to contain identical path entries, prevent the second
241 one from affecting JAR functions */
242 if (jar_get_signer(jar, signer->owner)) {
243 /* someone is trying to spoof us */
244 status = JAR_ERR_ORDER;
245 goto loser;
246 }
248 /* remember its digest */
249 signer->digest = JAR_calculate_digest (raw_manifest, length);
250 if (signer->digest == NULL)
251 goto loser;
253 /* Add this signer to the jar */
254 ADDITEM(jar->signers, jarTypeOwner, signer->owner, signer,
255 sizeof (JAR_Signer));
257 return jar_parse_any(jar, jarTypeSF, signer, raw_manifest, length,
258 path, url);
260 loser:
261 if (signer)
262 JAR_destroy_signer (signer);
263 return status;
264 }
266 /*
267 * j a r _ p a r s e _ a n y
268 *
269 * Parse a MF or SF manifest file.
270 *
271 */
272 int
273 jar_parse_any(JAR *jar, int type, JAR_Signer *signer,
274 char *raw_manifest, long length, const char *path,
275 const char *url)
276 {
277 int status;
278 long raw_len;
279 JAR_Digest *dig, *mfdig = NULL;
280 char line [SZ];
281 char x_name [SZ], x_md5 [SZ], x_sha [SZ];
282 char *x_info;
283 char *sf_md5 = NULL, *sf_sha1 = NULL;
285 *x_name = 0;
286 *x_md5 = 0;
287 *x_sha = 0;
289 PORT_Assert( length > 0 );
290 raw_len = length;
292 #ifdef DEBUG
293 if ((status = jar_insanity_check(raw_manifest, raw_len)) < 0)
294 return status;
295 #endif
297 /* null terminate the first line */
298 raw_manifest = jar_eat_line(0, PR_TRUE, raw_manifest, &raw_len);
300 /* skip over the preliminary section */
301 /* This is one section at the top of the file with global metainfo */
302 while (raw_len > 0) {
303 JAR_Metainfo *met;
305 raw_manifest = jar_eat_line(1, PR_TRUE, raw_manifest, &raw_len);
306 if (raw_len <= 0 || !*raw_manifest)
307 break;
309 met = PORT_ZNew(JAR_Metainfo);
310 if (met == NULL)
311 return JAR_ERR_MEMORY;
313 /* Parse out the header & info */
314 if (PORT_Strlen (raw_manifest) >= SZ) {
315 /* almost certainly nonsense */
316 PORT_Free(met);
317 continue;
318 }
320 PORT_Strcpy (line, raw_manifest);
321 x_info = line;
323 while (*x_info && *x_info != ' ' && *x_info != '\t' && *x_info != ':')
324 x_info++;
326 if (*x_info)
327 *x_info++ = 0;
329 while (*x_info == ' ' || *x_info == '\t')
330 x_info++;
332 /* metainfo (name, value) pair is now (line, x_info) */
333 met->header = PORT_Strdup(line);
334 met->info = PORT_Strdup(x_info);
336 if (type == jarTypeMF) {
337 ADDITEM (jar->metainfo, jarTypeMeta,
338 /* pathname */ NULL, met, sizeof (JAR_Metainfo));
339 }
341 /* For SF files, this metadata may be the digests
342 of the MF file, still in the "met" structure. */
344 if (type == jarTypeSF) {
345 if (!PORT_Strcasecmp(line, "MD5-Digest"))
346 sf_md5 = (char *) met->info;
348 if (!PORT_Strcasecmp(line, "SHA1-Digest") ||
349 !PORT_Strcasecmp(line, "SHA-Digest"))
350 sf_sha1 = (char *) met->info;
351 }
353 if (type != jarTypeMF) {
354 PORT_Free(met->header);
355 if (type != jarTypeSF) {
356 PORT_Free(met->info);
357 }
358 PORT_Free(met);
359 }
360 }
362 if (type == jarTypeSF && jar->globalmeta) {
363 /* this is a SF file which may contain a digest of the manifest.mf's
364 global metainfo. */
366 int match = 0;
367 JAR_Digest *glob = jar->globalmeta;
369 if (sf_md5) {
370 unsigned int md5_length;
371 unsigned char *md5_digest;
373 md5_digest = ATOB_AsciiToData (sf_md5, &md5_length);
374 PORT_Assert( md5_length == MD5_LENGTH );
376 if (md5_length != MD5_LENGTH)
377 return JAR_ERR_CORRUPT;
379 match = PORT_Memcmp(md5_digest, glob->md5, MD5_LENGTH);
380 }
382 if (sf_sha1 && match == 0) {
383 unsigned int sha1_length;
384 unsigned char *sha1_digest;
386 sha1_digest = ATOB_AsciiToData (sf_sha1, &sha1_length);
387 PORT_Assert( sha1_length == SHA1_LENGTH );
389 if (sha1_length != SHA1_LENGTH)
390 return JAR_ERR_CORRUPT;
392 match = PORT_Memcmp(sha1_digest, glob->sha1, SHA1_LENGTH);
393 }
395 if (match != 0) {
396 /* global digest doesn't match, SF file therefore invalid */
397 jar->valid = JAR_ERR_METADATA;
398 return JAR_ERR_METADATA;
399 }
400 }
402 /* done with top section of global data */
403 while (raw_len > 0) {
404 *x_md5 = 0;
405 *x_sha = 0;
406 *x_name = 0;
408 /* If this is a manifest file, attempt to get a digest of the following
409 section, without damaging it. This digest will be saved later. */
411 if (type == jarTypeMF) {
412 char *sec;
413 long sec_len = raw_len;
415 if (!*raw_manifest || *raw_manifest == '\n') {
416 /* skip the blank line */
417 sec = jar_eat_line(1, PR_FALSE, raw_manifest, &sec_len);
418 } else
419 sec = raw_manifest;
421 if (sec_len > 0 && !PORT_Strncasecmp(sec, "Name:", 5)) {
422 if (type == jarTypeMF)
423 mfdig = jar_digest_section(sec, sec_len);
424 else
425 mfdig = NULL;
426 }
427 }
430 while (raw_len > 0) {
431 raw_manifest = jar_eat_line(1, PR_TRUE, raw_manifest, &raw_len);
432 if (raw_len <= 0 || !*raw_manifest)
433 break; /* blank line, done with this entry */
435 if (PORT_Strlen(raw_manifest) >= SZ) {
436 /* almost certainly nonsense */
437 continue;
438 }
440 /* Parse out the name/value pair */
441 PORT_Strcpy(line, raw_manifest);
442 x_info = line;
444 while (*x_info && *x_info != ' ' && *x_info != '\t' &&
445 *x_info != ':')
446 x_info++;
448 if (*x_info)
449 *x_info++ = 0;
451 while (*x_info == ' ' || *x_info == '\t')
452 x_info++;
454 if (!PORT_Strcasecmp(line, "Name"))
455 PORT_Strcpy(x_name, x_info);
456 else if (!PORT_Strcasecmp(line, "MD5-Digest"))
457 PORT_Strcpy(x_md5, x_info);
458 else if (!PORT_Strcasecmp(line, "SHA1-Digest")
459 || !PORT_Strcasecmp(line, "SHA-Digest"))
460 PORT_Strcpy(x_sha, x_info);
462 /* Algorithm list is meta info we don't care about; keeping it out
463 of metadata saves significant space for large jar files */
464 else if (!PORT_Strcasecmp(line, "Digest-Algorithms")
465 || !PORT_Strcasecmp(line, "Hash-Algorithms"))
466 continue;
468 /* Meta info is only collected for the manifest.mf file,
469 since the JAR_get_metainfo call does not support identity */
470 else if (type == jarTypeMF) {
471 JAR_Metainfo *met;
473 /* this is meta-data */
474 met = PORT_ZNew(JAR_Metainfo);
475 if (met == NULL)
476 return JAR_ERR_MEMORY;
478 /* metainfo (name, value) pair is now (line, x_info) */
479 if ((met->header = PORT_Strdup(line)) == NULL) {
480 PORT_Free(met);
481 return JAR_ERR_MEMORY;
482 }
484 if ((met->info = PORT_Strdup(x_info)) == NULL) {
485 PORT_Free(met->header);
486 PORT_Free(met);
487 return JAR_ERR_MEMORY;
488 }
490 ADDITEM (jar->metainfo, jarTypeMeta,
491 x_name, met, sizeof (JAR_Metainfo));
492 }
493 }
495 if (!*x_name) {
496 /* Whatever that was, it wasn't an entry, because we didn't get a
497 name. We don't really have anything, so don't record this. */
498 continue;
499 }
501 dig = PORT_ZNew(JAR_Digest);
502 if (dig == NULL)
503 return JAR_ERR_MEMORY;
505 if (*x_md5) {
506 unsigned int binary_length;
507 unsigned char *binary_digest;
509 binary_digest = ATOB_AsciiToData (x_md5, &binary_length);
510 PORT_Assert( binary_length == MD5_LENGTH );
511 if (binary_length != MD5_LENGTH) {
512 PORT_Free(dig);
513 return JAR_ERR_CORRUPT;
514 }
515 memcpy (dig->md5, binary_digest, MD5_LENGTH);
516 dig->md5_status = jarHashPresent;
517 }
519 if (*x_sha ) {
520 unsigned int binary_length;
521 unsigned char *binary_digest;
523 binary_digest = ATOB_AsciiToData (x_sha, &binary_length);
524 PORT_Assert( binary_length == SHA1_LENGTH );
525 if (binary_length != SHA1_LENGTH) {
526 PORT_Free(dig);
527 return JAR_ERR_CORRUPT;
528 }
529 memcpy (dig->sha1, binary_digest, SHA1_LENGTH);
530 dig->sha1_status = jarHashPresent;
531 }
533 PORT_Assert( type == jarTypeMF || type == jarTypeSF );
534 if (type == jarTypeMF) {
535 ADDITEM (jar->hashes, jarTypeMF, x_name, dig, sizeof (JAR_Digest));
536 } else if (type == jarTypeSF) {
537 ADDITEM (signer->sf, jarTypeSF, x_name, dig, sizeof (JAR_Digest));
538 } else {
539 PORT_Free(dig);
540 return JAR_ERR_ORDER;
541 }
543 /* we're placing these calculated digests of manifest.mf
544 sections in a list where they can subsequently be forgotten */
545 if (type == jarTypeMF && mfdig) {
546 ADDITEM (jar->manifest, jarTypeSect,
547 x_name, mfdig, sizeof (JAR_Digest));
548 mfdig = NULL;
549 }
551 /* Retrieve our saved SHA1 digest from saved copy and check digests.
552 This is just comparing the digest of the MF section as indicated in
553 the SF file with the one we remembered from parsing the MF file */
555 if (type == jarTypeSF) {
556 if ((status = jar_internal_digest(jar, path, x_name, dig)) < 0)
557 return status;
558 }
559 }
561 return 0;
562 }
564 static int
565 jar_internal_digest(JAR *jar, const char *path, char *x_name, JAR_Digest *dig)
566 {
567 int cv;
568 int status;
570 JAR_Digest *savdig;
572 savdig = jar_get_mf_digest(jar, x_name);
573 if (savdig == NULL) {
574 /* no .mf digest for this pathname */
575 status = jar_signal(JAR_ERR_ENTRY, jar, path, x_name);
576 if (status < 0)
577 return 0; /* was continue; */
578 return status;
579 }
581 /* check for md5 consistency */
582 if (dig->md5_status) {
583 cv = PORT_Memcmp(savdig->md5, dig->md5, MD5_LENGTH);
584 /* md5 hash of .mf file is not what expected */
585 if (cv) {
586 status = jar_signal(JAR_ERR_HASH, jar, path, x_name);
588 /* bad hash, man */
589 dig->md5_status = jarHashBad;
590 savdig->md5_status = jarHashBad;
592 if (status < 0)
593 return 0; /* was continue; */
594 return status;
595 }
596 }
598 /* check for sha1 consistency */
599 if (dig->sha1_status) {
600 cv = PORT_Memcmp(savdig->sha1, dig->sha1, SHA1_LENGTH);
601 /* sha1 hash of .mf file is not what expected */
602 if (cv) {
603 status = jar_signal(JAR_ERR_HASH, jar, path, x_name);
605 /* bad hash, man */
606 dig->sha1_status = jarHashBad;
607 savdig->sha1_status = jarHashBad;
609 if (status < 0)
610 return 0; /* was continue; */
611 return status;
612 }
613 }
614 return 0;
615 }
617 #ifdef DEBUG
618 /*
619 * j a r _ i n s a n i t y _ c h e c k
620 *
621 * Check for illegal characters (or possibly so)
622 * in the manifest files, to detect potential memory
623 * corruption by our neighbors. Debug only, since
624 * not I18N safe.
625 *
626 */
627 static int
628 jar_insanity_check(char *data, long length)
629 {
630 int c;
631 long off;
633 for (off = 0; off < length; off++) {
634 c = data [off];
635 if (c == '\n' || c == '\r' || (c >= ' ' && c <= 128))
636 continue;
637 return JAR_ERR_CORRUPT;
638 }
639 return 0;
640 }
641 #endif
643 /*
644 * j a r _ p a r s e _ d i g i t a l _ s i g n a t u r e
645 *
646 * Parse an RSA or DSA (or perhaps other) digital signature.
647 * Right now everything is PKCS7.
648 *
649 */
650 static int
651 jar_parse_digital_signature(char *raw_manifest, JAR_Signer *signer,
652 long length, JAR *jar)
653 {
654 return jar_validate_pkcs7 (jar, signer, raw_manifest, length);
655 }
657 /*
658 * j a r _ a d d _ c e r t
659 *
660 * Add information for the given certificate
661 * (or whatever) to the JAR linked list. A pointer
662 * is passed for some relevant reference, say
663 * for example the original certificate.
664 *
665 */
666 static int
667 jar_add_cert(JAR *jar, JAR_Signer *signer, int type, CERTCertificate *cert)
668 {
669 JAR_Cert *fing;
670 unsigned char *keyData;
672 if (cert == NULL)
673 return JAR_ERR_ORDER;
675 fing = PORT_ZNew(JAR_Cert);
676 if (fing == NULL)
677 goto loser;
679 fing->cert = CERT_DupCertificate (cert);
681 /* get the certkey */
682 fing->length = cert->derIssuer.len + 2 + cert->serialNumber.len;
683 fing->key = keyData = (unsigned char *) PORT_ZAlloc(fing->length);
684 if (fing->key == NULL)
685 goto loser;
686 keyData[0] = ((cert->derIssuer.len) >> 8) & 0xff;
687 keyData[1] = ((cert->derIssuer.len) & 0xff);
688 PORT_Memcpy(&keyData[2], cert->derIssuer.data, cert->derIssuer.len);
689 PORT_Memcpy(&keyData[2+cert->derIssuer.len], cert->serialNumber.data,
690 cert->serialNumber.len);
692 ADDITEM (signer->certs, type, NULL, fing, sizeof (JAR_Cert));
693 return 0;
695 loser:
696 if (fing) {
697 if (fing->cert)
698 CERT_DestroyCertificate (fing->cert);
699 PORT_Free(fing);
700 }
701 return JAR_ERR_MEMORY;
702 }
704 /*
705 * e a t _ l i n e
706 *
707 * Reads and/or modifies input buffer "data" of length "*len".
708 * This function does zero, one or two of the following tasks:
709 * 1) if "lines" is non-zero, it reads and discards that many lines from
710 * the input. NUL characters are treated as end-of-line characters,
711 * not as end-of-input characters. The input is NOT NUL terminated.
712 * Note: presently, all callers pass either 0 or 1 for lines.
713 * 2) After skipping the specified number of input lines, if "eating" is
714 * non-zero, it finds the end of the next line of input and replaces
715 * the end of line character(s) with a NUL character.
716 * This function modifies the input buffer, containing the file, in place.
717 * This function handles PC, Mac, and Unix style text files.
718 * On entry, *len contains the maximum number of characters that this
719 * function should ever examine, starting with the character in *data.
720 * On return, *len is reduced by the number of characters skipped by the
721 * first task, if any;
722 * If lines is zero and eating is false, this function returns
723 * the value in the data argument, but otherwise does nothing.
724 */
725 static char *
726 jar_eat_line(int lines, int eating, char *data, long *len)
727 {
728 char *start = data;
729 long maxLen = *len;
731 if (maxLen <= 0)
732 return start;
734 #define GO_ON ((data - start) < maxLen)
736 /* Eat the requisite number of lines, if any;
737 prior to terminating the current line with a 0. */
738 for (/* yip */ ; lines > 0; lines--) {
739 while (GO_ON && *data && *data != '\r' && *data != '\n')
740 data++;
742 /* Eat any leading CR */
743 if (GO_ON && *data == '\r')
744 data++;
746 /* After the CR, ok to eat one LF */
747 if (GO_ON && *data == '\n')
748 data++;
750 /* If there are NULs, this function probably put them there */
751 while (GO_ON && !*data)
752 data++;
753 }
754 maxLen -= data - start; /* we have this many characters left. */
755 *len = maxLen;
756 start = data; /* now start again here. */
757 if (maxLen > 0 && eating) {
758 /* Terminate this line with a 0 */
759 while (GO_ON && *data && *data != '\n' && *data != '\r')
760 data++;
762 /* If not past the end, we are allowed to eat one CR */
763 if (GO_ON && *data == '\r')
764 *data++ = 0;
766 /* After the CR (if any), if not past the end, ok to eat one LF */
767 if (GO_ON && *data == '\n')
768 *data++ = 0;
769 }
770 return start;
771 }
772 #undef GO_ON
774 /*
775 * j a r _ d i g e s t _ s e c t i o n
776 *
777 * Return the digests of the next section of the manifest file.
778 * Does not damage the manifest file, unlike parse_manifest.
779 *
780 */
781 static JAR_Digest *
782 jar_digest_section(char *manifest, long length)
783 {
784 long global_len;
785 char *global_end;
787 global_end = manifest;
788 global_len = length;
790 while (global_len > 0) {
791 global_end = jar_eat_line(1, PR_FALSE, global_end, &global_len);
792 if (global_len > 0 && (*global_end == 0 || *global_end == '\n'))
793 break;
794 }
795 return JAR_calculate_digest (manifest, global_end - manifest);
796 }
798 /*
799 * J A R _ v e r i f y _ d i g e s t
800 *
801 * Verifies that a precalculated digest matches the
802 * expected value in the manifest.
803 *
804 */
805 int PR_CALLBACK
806 JAR_verify_digest(JAR *jar, const char *name, JAR_Digest *dig)
807 {
808 JAR_Item *it;
809 JAR_Digest *shindig;
810 ZZLink *link;
811 ZZList *list = jar->hashes;
812 int result1 = 0;
813 int result2 = 0;
816 if (jar->valid < 0) {
817 /* signature not valid */
818 return JAR_ERR_SIG;
819 }
820 if (ZZ_ListEmpty (list)) {
821 /* empty list */
822 return JAR_ERR_PNF;
823 }
825 for (link = ZZ_ListHead (list);
826 !ZZ_ListIterDone (list, link);
827 link = link->next) {
828 it = link->thing;
829 if (it->type == jarTypeMF
830 && it->pathname && !PORT_Strcmp(it->pathname, name)) {
831 shindig = (JAR_Digest *) it->data;
832 if (shindig->md5_status) {
833 if (shindig->md5_status == jarHashBad)
834 return JAR_ERR_HASH;
835 result1 = memcmp (dig->md5, shindig->md5, MD5_LENGTH);
836 }
837 if (shindig->sha1_status) {
838 if (shindig->sha1_status == jarHashBad)
839 return JAR_ERR_HASH;
840 result2 = memcmp (dig->sha1, shindig->sha1, SHA1_LENGTH);
841 }
842 return (result1 == 0 && result2 == 0) ? 0 : JAR_ERR_HASH;
843 }
844 }
845 return JAR_ERR_PNF;
846 }
854 /*
855 * J A R _ f e t c h _ c e r t
856 *
857 * Given an opaque identifier of a certificate,
858 * return the full certificate.
859 *
860 * The new function, which retrieves by key.
861 *
862 */
863 CERTCertificate *
864 JAR_fetch_cert(long length, void *key)
865 {
866 CERTIssuerAndSN issuerSN;
867 CERTCertificate *cert = NULL;
868 CERTCertDBHandle *certdb;
870 certdb = JAR_open_database();
871 if (certdb) {
872 unsigned char *keyData = (unsigned char *)key;
873 issuerSN.derIssuer.len = (keyData[0] << 8) + keyData[0];
874 issuerSN.derIssuer.data = &keyData[2];
875 issuerSN.serialNumber.len = length - (2 + issuerSN.derIssuer.len);
876 issuerSN.serialNumber.data = &keyData[2+issuerSN.derIssuer.len];
877 cert = CERT_FindCertByIssuerAndSN (certdb, &issuerSN);
878 JAR_close_database (certdb);
879 }
880 return cert;
881 }
883 /*
884 * j a r _ g e t _ m f _ d i g e s t
885 *
886 * Retrieve a corresponding saved digest over a section
887 * of the main manifest file.
888 *
889 */
890 static JAR_Digest *
891 jar_get_mf_digest(JAR *jar, char *pathname)
892 {
893 JAR_Item *it;
894 JAR_Digest *dig;
895 ZZLink *link;
896 ZZList *list = jar->manifest;
898 if (ZZ_ListEmpty (list))
899 return NULL;
901 for (link = ZZ_ListHead (list);
902 !ZZ_ListIterDone (list, link);
903 link = link->next) {
904 it = link->thing;
905 if (it->type == jarTypeSect
906 && it->pathname && !PORT_Strcmp(it->pathname, pathname)) {
907 dig = (JAR_Digest *) it->data;
908 return dig;
909 }
910 }
911 return NULL;
912 }
914 /*
915 * j a r _ b a s e n a m e
916 *
917 * Return the basename -- leading components of path stripped off,
918 * extension ripped off -- of a path.
919 *
920 */
921 static char *
922 jar_basename(const char *path)
923 {
924 char *pith, *e, *basename, *ext;
926 if (path == NULL)
927 return PORT_Strdup("");
929 pith = PORT_Strdup(path);
930 basename = pith;
931 while (1) {
932 for (e = basename; *e && *e != '/' && *e != '\\'; e++)
933 /* yip */ ;
934 if (*e)
935 basename = ++e;
936 else
937 break;
938 }
940 if ((ext = PORT_Strrchr(basename, '.')) != NULL)
941 *ext = 0;
943 /* We already have the space allocated */
944 PORT_Strcpy(pith, basename);
945 return pith;
946 }
948 /*
949 * + + + + + + + + + + + + + + +
950 *
951 * CRYPTO ROUTINES FOR JAR
952 *
953 * The following functions are the cryptographic
954 * interface to PKCS7 for Jarnatures.
955 *
956 * + + + + + + + + + + + + + + +
957 *
958 */
960 /*
961 * j a r _ c a t c h _ b y t e s
962 *
963 * In the event signatures contain enveloped data, it will show up here.
964 * But note that the lib/pkcs7 routines aren't ready for it.
965 *
966 */
967 static void
968 jar_catch_bytes(void *arg, const char *buf, unsigned long len)
969 {
970 /* Actually this should never be called, since there is
971 presumably no data in the signature itself. */
972 }
974 /*
975 * j a r _ v a l i d a t e _ p k c s 7
976 *
977 * Validate (and decode, if necessary) a binary pkcs7
978 * signature in DER format.
979 *
980 */
981 static int
982 jar_validate_pkcs7(JAR *jar, JAR_Signer *signer, char *data, long length)
983 {
985 SEC_PKCS7ContentInfo *cinfo = NULL;
986 SEC_PKCS7DecoderContext *dcx;
987 PRBool goodSig;
988 int status = 0;
989 SECItem detdig;
991 PORT_Assert( jar != NULL && signer != NULL );
993 if (jar == NULL || signer == NULL)
994 return JAR_ERR_ORDER;
996 signer->valid = JAR_ERR_SIG;
998 /* We need a context if we can get one */
999 dcx = SEC_PKCS7DecoderStart(jar_catch_bytes, NULL /*cb_arg*/,
1000 NULL /*getpassword*/, jar->mw,
1001 NULL, NULL, NULL);
1002 if (dcx == NULL) {
1003 /* strange pkcs7 failure */
1004 return JAR_ERR_PK7;
1005 }
1007 SEC_PKCS7DecoderUpdate (dcx, data, length);
1008 cinfo = SEC_PKCS7DecoderFinish (dcx);
1009 if (cinfo == NULL) {
1010 /* strange pkcs7 failure */
1011 return JAR_ERR_PK7;
1012 }
1013 if (SEC_PKCS7ContentIsEncrypted (cinfo)) {
1014 /* content was encrypted, fail */
1015 return JAR_ERR_PK7;
1016 }
1017 if (SEC_PKCS7ContentIsSigned (cinfo) == PR_FALSE) {
1018 /* content was not signed, fail */
1019 return JAR_ERR_PK7;
1020 }
1022 PORT_SetError(0);
1024 /* use SHA1 only */
1025 detdig.len = SHA1_LENGTH;
1026 detdig.data = signer->digest->sha1;
1027 goodSig = SEC_PKCS7VerifyDetachedSignature(cinfo,
1028 certUsageObjectSigner,
1029 &detdig, HASH_AlgSHA1,
1030 PR_FALSE);
1031 jar_gather_signers(jar, signer, cinfo);
1032 if (goodSig == PR_TRUE) {
1033 /* signature is valid */
1034 signer->valid = 0;
1035 } else {
1036 status = PORT_GetError();
1037 PORT_Assert( status < 0 );
1038 if (status >= 0)
1039 status = JAR_ERR_SIG;
1040 jar->valid = status;
1041 signer->valid = status;
1042 }
1043 jar->pkcs7 = PR_TRUE;
1044 signer->pkcs7 = PR_TRUE;
1045 SEC_PKCS7DestroyContentInfo(cinfo);
1046 return status;
1047 }
1049 /*
1050 * j a r _ g a t h e r _ s i g n e r s
1051 *
1052 * Add the single signer of this signature to the
1053 * certificate linked list.
1054 *
1055 */
1056 static int
1057 jar_gather_signers(JAR *jar, JAR_Signer *signer, SEC_PKCS7ContentInfo *cinfo)
1058 {
1059 int result;
1060 CERTCertificate *cert;
1061 CERTCertDBHandle *certdb;
1062 SEC_PKCS7SignedData *sdp = cinfo->content.signedData;
1063 SEC_PKCS7SignerInfo **pksigners, *pksigner;
1065 if (sdp == NULL)
1066 return JAR_ERR_PK7;
1068 pksigners = sdp->signerInfos;
1069 /* permit exactly one signer */
1070 if (pksigners == NULL || pksigners [0] == NULL || pksigners [1] != NULL)
1071 return JAR_ERR_PK7;
1073 pksigner = *pksigners;
1074 cert = pksigner->cert;
1076 if (cert == NULL)
1077 return JAR_ERR_PK7;
1079 certdb = JAR_open_database();
1080 if (certdb == NULL)
1081 return JAR_ERR_GENERAL;
1083 result = jar_add_cert(jar, signer, jarTypeSign, cert);
1084 JAR_close_database (certdb);
1085 return result;
1086 }
1088 /*
1089 * j a r _ o p e n _ d a t a b a s e
1090 *
1091 * Open the certificate database,
1092 * for use by JAR functions.
1093 *
1094 */
1095 CERTCertDBHandle *
1096 JAR_open_database(void)
1097 {
1098 return CERT_GetDefaultCertDB();
1099 }
1101 /*
1102 * j a r _ c l o s e _ d a t a b a s e
1103 *
1104 * Close the certificate database.
1105 * For use by JAR functions.
1106 *
1107 */
1108 int
1109 JAR_close_database(CERTCertDBHandle *certdb)
1110 {
1111 return 0;
1112 }
1115 /*
1116 * j a r _ s i g n a l
1117 *
1118 * Nonfatal errors come here to callback Java.
1119 *
1120 */
1121 static int
1122 jar_signal(int status, JAR *jar, const char *metafile, char *pathname)
1123 {
1124 char *errstring = JAR_get_error (status);
1125 if (jar->signal) {
1126 (*jar->signal) (status, jar, metafile, pathname, errstring);
1127 return 0;
1128 }
1129 return status;
1130 }
1132 /*
1133 * j a r _ a p p e n d
1134 *
1135 * Tack on an element to one of a JAR's linked
1136 * lists, with rudimentary error handling.
1137 *
1138 */
1139 int
1140 jar_append(ZZList *list, int type, char *pathname, void *data, size_t size)
1141 {
1142 JAR_Item *it = PORT_ZNew(JAR_Item);
1143 ZZLink *entity;
1145 if (it == NULL)
1146 goto loser;
1148 if (pathname) {
1149 it->pathname = PORT_Strdup(pathname);
1150 if (it->pathname == NULL)
1151 goto loser;
1152 }
1154 it->type = (jarType)type;
1155 it->data = (unsigned char *) data;
1156 it->size = size;
1157 entity = ZZ_NewLink (it);
1158 if (entity) {
1159 ZZ_AppendLink (list, entity);
1160 return 0;
1161 }
1163 loser:
1164 if (it) {
1165 if (it->pathname)
1166 PORT_Free(it->pathname);
1167 PORT_Free(it);
1168 }
1169 return JAR_ERR_MEMORY;
1170 }