security/nss/lib/jar/jarver.c

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:a873ac4a3549
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 /*
6 * JARVER
7 *
8 * Jarnature Parsing & Verification
9 */
10
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"
18
19 #define SZ 512
20
21 static int
22 jar_validate_pkcs7(JAR *jar, JAR_Signer *signer, char *data, long length);
23
24 static void
25 jar_catch_bytes(void *arg, const char *buf, unsigned long len);
26
27 static int
28 jar_gather_signers(JAR *jar, JAR_Signer *signer, SEC_PKCS7ContentInfo *cinfo);
29
30 static char *
31 jar_eat_line(int lines, int eating, char *data, long *len);
32
33 static JAR_Digest *
34 jar_digest_section(char *manifest, long length);
35
36 static JAR_Digest *jar_get_mf_digest(JAR *jar, char *path);
37
38 static int
39 jar_parse_digital_signature(char *raw_manifest, JAR_Signer *signer,
40 long length, JAR *jar);
41
42 static int
43 jar_add_cert(JAR *jar, JAR_Signer *signer, int type, CERTCertificate *cert);
44
45 static char *jar_basename(const char *path);
46
47 static int
48 jar_signal(int status, JAR *jar, const char *metafile, char *pathname);
49
50 #ifdef DEBUG
51 static int jar_insanity_check(char *data, long length);
52 #endif
53
54 int
55 jar_parse_mf(JAR *jar, char *raw_manifest, long length,
56 const char *path, const char *url);
57
58 int
59 jar_parse_sf(JAR *jar, char *raw_manifest, long length,
60 const char *path, const char *url);
61
62 int
63 jar_parse_sig(JAR *jar, const char *path, char *raw_manifest,
64 long length);
65
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);
70
71 static int
72 jar_internal_digest(JAR *jar, const char *path, char *x_name, JAR_Digest *dig);
73
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;
89
90 /* fill in the path, if supplied. This is the location
91 of the jar file on disk, if known */
92
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 }
99
100 /* fill in the URL, if supplied. This is the place
101 from which the jar file was retrieved. */
102
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 }
112
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 */
115
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 }
127
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;
141
142 if (length <= 128) {
143 /* signature is way too small */
144 return JAR_ERR_SIG;
145 }
146
147 /* make sure that MF and SF have already been processed */
148
149 if (jar->globalmeta == NULL)
150 return JAR_ERR_ORDER;
151
152 /* Determine whether or not this RSA file has
153 has an associated SF file */
154
155 if (path) {
156 char *owner;
157 owner = jar_basename(path);
158
159 if (owner == NULL)
160 return JAR_ERR_MEMORY;
161
162 signer = jar_get_signer(jar, owner);
163 PORT_Free(owner);
164 } else
165 signer = jar_get_signer(jar, "*");
166
167 if (signer == NULL)
168 return JAR_ERR_ORDER;
169
170
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. */
174
175 if (length > 64000) {
176 /* this digital signature is way too big */
177 return JAR_ERR_SIG;
178 }
179
180 /* don't expense unneeded calloc overhead on non-win16 */
181 status = jar_parse_digital_signature(raw_manifest, signer, length, jar);
182
183 return status;
184 }
185
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 }
201
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 }
209
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;
223
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 }
228
229 signer = JAR_new_signer();
230 if (signer == NULL)
231 goto loser;
232
233 if (path) {
234 signer->owner = jar_basename(path);
235 if (signer->owner == NULL)
236 goto loser;
237 }
238
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 }
247
248 /* remember its digest */
249 signer->digest = JAR_calculate_digest (raw_manifest, length);
250 if (signer->digest == NULL)
251 goto loser;
252
253 /* Add this signer to the jar */
254 ADDITEM(jar->signers, jarTypeOwner, signer->owner, signer,
255 sizeof (JAR_Signer));
256
257 return jar_parse_any(jar, jarTypeSF, signer, raw_manifest, length,
258 path, url);
259
260 loser:
261 if (signer)
262 JAR_destroy_signer (signer);
263 return status;
264 }
265
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;
284
285 *x_name = 0;
286 *x_md5 = 0;
287 *x_sha = 0;
288
289 PORT_Assert( length > 0 );
290 raw_len = length;
291
292 #ifdef DEBUG
293 if ((status = jar_insanity_check(raw_manifest, raw_len)) < 0)
294 return status;
295 #endif
296
297 /* null terminate the first line */
298 raw_manifest = jar_eat_line(0, PR_TRUE, raw_manifest, &raw_len);
299
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;
304
305 raw_manifest = jar_eat_line(1, PR_TRUE, raw_manifest, &raw_len);
306 if (raw_len <= 0 || !*raw_manifest)
307 break;
308
309 met = PORT_ZNew(JAR_Metainfo);
310 if (met == NULL)
311 return JAR_ERR_MEMORY;
312
313 /* Parse out the header & info */
314 if (PORT_Strlen (raw_manifest) >= SZ) {
315 /* almost certainly nonsense */
316 PORT_Free(met);
317 continue;
318 }
319
320 PORT_Strcpy (line, raw_manifest);
321 x_info = line;
322
323 while (*x_info && *x_info != ' ' && *x_info != '\t' && *x_info != ':')
324 x_info++;
325
326 if (*x_info)
327 *x_info++ = 0;
328
329 while (*x_info == ' ' || *x_info == '\t')
330 x_info++;
331
332 /* metainfo (name, value) pair is now (line, x_info) */
333 met->header = PORT_Strdup(line);
334 met->info = PORT_Strdup(x_info);
335
336 if (type == jarTypeMF) {
337 ADDITEM (jar->metainfo, jarTypeMeta,
338 /* pathname */ NULL, met, sizeof (JAR_Metainfo));
339 }
340
341 /* For SF files, this metadata may be the digests
342 of the MF file, still in the "met" structure. */
343
344 if (type == jarTypeSF) {
345 if (!PORT_Strcasecmp(line, "MD5-Digest"))
346 sf_md5 = (char *) met->info;
347
348 if (!PORT_Strcasecmp(line, "SHA1-Digest") ||
349 !PORT_Strcasecmp(line, "SHA-Digest"))
350 sf_sha1 = (char *) met->info;
351 }
352
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 }
361
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. */
365
366 int match = 0;
367 JAR_Digest *glob = jar->globalmeta;
368
369 if (sf_md5) {
370 unsigned int md5_length;
371 unsigned char *md5_digest;
372
373 md5_digest = ATOB_AsciiToData (sf_md5, &md5_length);
374 PORT_Assert( md5_length == MD5_LENGTH );
375
376 if (md5_length != MD5_LENGTH)
377 return JAR_ERR_CORRUPT;
378
379 match = PORT_Memcmp(md5_digest, glob->md5, MD5_LENGTH);
380 }
381
382 if (sf_sha1 && match == 0) {
383 unsigned int sha1_length;
384 unsigned char *sha1_digest;
385
386 sha1_digest = ATOB_AsciiToData (sf_sha1, &sha1_length);
387 PORT_Assert( sha1_length == SHA1_LENGTH );
388
389 if (sha1_length != SHA1_LENGTH)
390 return JAR_ERR_CORRUPT;
391
392 match = PORT_Memcmp(sha1_digest, glob->sha1, SHA1_LENGTH);
393 }
394
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 }
401
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;
407
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. */
410
411 if (type == jarTypeMF) {
412 char *sec;
413 long sec_len = raw_len;
414
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;
420
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 }
428
429
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 */
434
435 if (PORT_Strlen(raw_manifest) >= SZ) {
436 /* almost certainly nonsense */
437 continue;
438 }
439
440 /* Parse out the name/value pair */
441 PORT_Strcpy(line, raw_manifest);
442 x_info = line;
443
444 while (*x_info && *x_info != ' ' && *x_info != '\t' &&
445 *x_info != ':')
446 x_info++;
447
448 if (*x_info)
449 *x_info++ = 0;
450
451 while (*x_info == ' ' || *x_info == '\t')
452 x_info++;
453
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);
461
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;
467
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;
472
473 /* this is meta-data */
474 met = PORT_ZNew(JAR_Metainfo);
475 if (met == NULL)
476 return JAR_ERR_MEMORY;
477
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 }
483
484 if ((met->info = PORT_Strdup(x_info)) == NULL) {
485 PORT_Free(met->header);
486 PORT_Free(met);
487 return JAR_ERR_MEMORY;
488 }
489
490 ADDITEM (jar->metainfo, jarTypeMeta,
491 x_name, met, sizeof (JAR_Metainfo));
492 }
493 }
494
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 }
500
501 dig = PORT_ZNew(JAR_Digest);
502 if (dig == NULL)
503 return JAR_ERR_MEMORY;
504
505 if (*x_md5) {
506 unsigned int binary_length;
507 unsigned char *binary_digest;
508
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 }
518
519 if (*x_sha ) {
520 unsigned int binary_length;
521 unsigned char *binary_digest;
522
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 }
532
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 }
542
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 }
550
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 */
554
555 if (type == jarTypeSF) {
556 if ((status = jar_internal_digest(jar, path, x_name, dig)) < 0)
557 return status;
558 }
559 }
560
561 return 0;
562 }
563
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;
569
570 JAR_Digest *savdig;
571
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 }
580
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);
587
588 /* bad hash, man */
589 dig->md5_status = jarHashBad;
590 savdig->md5_status = jarHashBad;
591
592 if (status < 0)
593 return 0; /* was continue; */
594 return status;
595 }
596 }
597
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);
604
605 /* bad hash, man */
606 dig->sha1_status = jarHashBad;
607 savdig->sha1_status = jarHashBad;
608
609 if (status < 0)
610 return 0; /* was continue; */
611 return status;
612 }
613 }
614 return 0;
615 }
616
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;
632
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
642
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 }
656
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;
671
672 if (cert == NULL)
673 return JAR_ERR_ORDER;
674
675 fing = PORT_ZNew(JAR_Cert);
676 if (fing == NULL)
677 goto loser;
678
679 fing->cert = CERT_DupCertificate (cert);
680
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);
691
692 ADDITEM (signer->certs, type, NULL, fing, sizeof (JAR_Cert));
693 return 0;
694
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 }
703
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;
730
731 if (maxLen <= 0)
732 return start;
733
734 #define GO_ON ((data - start) < maxLen)
735
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++;
741
742 /* Eat any leading CR */
743 if (GO_ON && *data == '\r')
744 data++;
745
746 /* After the CR, ok to eat one LF */
747 if (GO_ON && *data == '\n')
748 data++;
749
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++;
761
762 /* If not past the end, we are allowed to eat one CR */
763 if (GO_ON && *data == '\r')
764 *data++ = 0;
765
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
773
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;
786
787 global_end = manifest;
788 global_len = length;
789
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 }
797
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;
814
815
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 }
824
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 }
847
848
849
850
851
852
853
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;
869
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 }
882
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;
897
898 if (ZZ_ListEmpty (list))
899 return NULL;
900
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 }
913
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;
925
926 if (path == NULL)
927 return PORT_Strdup("");
928
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 }
939
940 if ((ext = PORT_Strrchr(basename, '.')) != NULL)
941 *ext = 0;
942
943 /* We already have the space allocated */
944 PORT_Strcpy(pith, basename);
945 return pith;
946 }
947
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 */
959
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 }
973
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 {
984
985 SEC_PKCS7ContentInfo *cinfo = NULL;
986 SEC_PKCS7DecoderContext *dcx;
987 PRBool goodSig;
988 int status = 0;
989 SECItem detdig;
990
991 PORT_Assert( jar != NULL && signer != NULL );
992
993 if (jar == NULL || signer == NULL)
994 return JAR_ERR_ORDER;
995
996 signer->valid = JAR_ERR_SIG;
997
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 }
1006
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 }
1021
1022 PORT_SetError(0);
1023
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 }
1048
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;
1064
1065 if (sdp == NULL)
1066 return JAR_ERR_PK7;
1067
1068 pksigners = sdp->signerInfos;
1069 /* permit exactly one signer */
1070 if (pksigners == NULL || pksigners [0] == NULL || pksigners [1] != NULL)
1071 return JAR_ERR_PK7;
1072
1073 pksigner = *pksigners;
1074 cert = pksigner->cert;
1075
1076 if (cert == NULL)
1077 return JAR_ERR_PK7;
1078
1079 certdb = JAR_open_database();
1080 if (certdb == NULL)
1081 return JAR_ERR_GENERAL;
1082
1083 result = jar_add_cert(jar, signer, jarTypeSign, cert);
1084 JAR_close_database (certdb);
1085 return result;
1086 }
1087
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 }
1100
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 }
1113
1114
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 }
1131
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;
1144
1145 if (it == NULL)
1146 goto loser;
1147
1148 if (pathname) {
1149 it->pathname = PORT_Strdup(pathname);
1150 if (it->pathname == NULL)
1151 goto loser;
1152 }
1153
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 }
1162
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 }

mercurial