1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
40 * Jarnature Parsing & Verification
50 /*#include "cdbhdl.h" */
53 /* to use huge pointers in win16 */
55 #if !defined(XP_WIN16)
56 #define xp_HUGE_MEMCPY PORT_Memcpy
57 #define xp_HUGE_STRCPY PORT_Strcpy
58 #define xp_HUGE_STRLEN PORT_Strlen
59 #define xp_HUGE_STRNCASECMP PORT_Strncasecmp
61 #define xp_HUGE_MEMCPY hmemcpy
62 int xp_HUGE_STRNCASECMP (char ZHUGEP
*buf
, char *key
, int len
);
63 size_t xp_HUGE_STRLEN (char ZHUGEP
*s
);
64 char *xp_HUGE_STRCPY (char *to
, char ZHUGEP
*from
);
68 #define CERTDB_USER (1<<6)
72 static int jar_validate_pkcs7
73 (JAR
*jar
, JAR_Signer
*signer
, char *data
, long length
);
75 static void jar_catch_bytes
76 (void *arg
, const char *buf
, unsigned long len
);
78 static int jar_gather_signers
79 (JAR
*jar
, JAR_Signer
*signer
, SEC_PKCS7ContentInfo
*cinfo
);
81 static char ZHUGEP
*jar_eat_line
82 (int lines
, int eating
, char ZHUGEP
*data
, long *len
);
84 static JAR_Digest
*jar_digest_section
85 (char ZHUGEP
*manifest
, long length
);
87 static JAR_Digest
*jar_get_mf_digest (JAR
*jar
, char *path
);
89 static int jar_parse_digital_signature
90 (char *raw_manifest
, JAR_Signer
*signer
, long length
, JAR
*jar
);
92 static int jar_add_cert
93 (JAR
*jar
, JAR_Signer
*signer
, int type
, CERTCertificate
*cert
);
95 static CERTCertificate
*jar_get_certificate
96 (JAR
*jar
, long keylen
, void *key
, int *result
);
98 static char *jar_cert_element (char *name
, char *tag
, int occ
);
100 static char *jar_choose_nickname (CERTCertificate
*cert
);
102 static char *jar_basename (const char *path
);
104 static int jar_signal
105 (int status
, JAR
*jar
, const char *metafile
, char *pathname
);
108 static int jar_insanity_check (char ZHUGEP
*data
, long length
);
112 (JAR
*jar
, char ZHUGEP
*raw_manifest
,
113 long length
, const char *path
, const char *url
);
116 (JAR
*jar
, char ZHUGEP
*raw_manifest
,
117 long length
, const char *path
, const char *url
);
120 (JAR
*jar
, const char *path
, char ZHUGEP
*raw_manifest
, long length
);
123 (JAR
*jar
, int type
, JAR_Signer
*signer
, char ZHUGEP
*raw_manifest
,
124 long length
, const char *path
, const char *url
);
126 static int jar_internal_digest
127 (JAR
*jar
, const char *path
, char *x_name
, JAR_Digest
*dig
);
130 * J A R _ p a r s e _ m a n i f e s t
132 * Pass manifest files to this function. They are
133 * decoded and placed into internal representations.
135 * Accepts both signature and manifest files. Use
136 * the same "jar" for both.
140 int JAR_parse_manifest
141 (JAR
*jar
, char ZHUGEP
*raw_manifest
,
142 long length
, const char *path
, const char *url
)
144 int filename_free
= 0;
146 #if defined(XP_WIN16)
147 PORT_Assert( !IsBadHugeReadPtr(raw_manifest
, length
) );
150 /* fill in the path, if supplied. This is a the location
151 of the jar file on disk, if known */
153 if (jar
->filename
== NULL
&& path
)
155 jar
->filename
= PORT_Strdup (path
);
156 if (jar
->filename
== NULL
)
157 return JAR_ERR_MEMORY
;
161 /* fill in the URL, if supplied. This is the place
162 from which the jar file was retrieved. */
164 if (jar
->url
== NULL
&& url
)
166 jar
->url
= PORT_Strdup (url
);
167 if (jar
->url
== NULL
)
171 PORT_Free (jar
->filename
);
173 return JAR_ERR_MEMORY
;
177 /* Determine what kind of file this is from the META-INF
178 directory. It could be MF, SF, or a binary RSA/DSA file */
180 if (!xp_HUGE_STRNCASECMP (raw_manifest
, "Manifest-Version:", 17))
182 return jar_parse_mf (jar
, raw_manifest
, length
, path
, url
);
184 else if (!xp_HUGE_STRNCASECMP (raw_manifest
, "Signature-Version:", 18))
186 return jar_parse_sf (jar
, raw_manifest
, length
, path
, url
);
190 /* This is probably a binary signature */
191 return jar_parse_sig (jar
, path
, raw_manifest
, length
);
196 * j a r _ p a r s e _ s i g
198 * Pass some manner of RSA or DSA digital signature
199 * on, after checking to see if it comes at an appropriate state.
204 (JAR
*jar
, const char *path
, char ZHUGEP
*raw_manifest
, long length
)
207 int status
= JAR_ERR_ORDER
;
211 /* signature is way too small */
215 /* make sure that MF and SF have already been processed */
217 if (jar
->globalmeta
== NULL
)
218 return JAR_ERR_ORDER
;
220 /* Determine whether or not this RSA file has
221 has an associated SF file */
226 owner
= jar_basename (path
);
229 return JAR_ERR_MEMORY
;
231 signer
= jar_get_signer (jar
, owner
);
236 signer
= jar_get_signer (jar
, "*");
239 return JAR_ERR_ORDER
;
242 /* Do not pass a huge pointer to this function,
243 since the underlying security code is unaware. We will
244 never pass >64k through here. */
248 /* this digital signature is way too big */
254 * For Win16, copy the portion of the raw_buffer containing the digital
255 * signature into another buffer... This insures that the data will
256 * NOT cross a segment boundary. Therefore,
257 * jar_parse_digital_signature(...) does NOT need to deal with HUGE
262 unsigned char *manifest_copy
;
264 manifest_copy
= (unsigned char *) PORT_ZAlloc (length
);
267 xp_HUGE_MEMCPY (manifest_copy
, raw_manifest
, length
);
269 status
= jar_parse_digital_signature
270 (manifest_copy
, signer
, length
, jar
);
272 PORT_Free (manifest_copy
);
277 return JAR_ERR_MEMORY
;
281 /* don't expense unneeded calloc overhead on non-win16 */
282 status
= jar_parse_digital_signature
283 (raw_manifest
, signer
, length
, jar
);
290 * j a r _ p a r s e _ m f
292 * Parse the META-INF/manifest.mf file, whose
293 * information applies to all signers.
298 (JAR
*jar
, char ZHUGEP
*raw_manifest
,
299 long length
, const char *path
, const char *url
)
303 /* refuse a second manifest file, if passed for some reason */
304 return JAR_ERR_ORDER
;
308 /* remember a digest for the global section */
310 jar
->globalmeta
= jar_digest_section (raw_manifest
, length
);
312 if (jar
->globalmeta
== NULL
)
313 return JAR_ERR_MEMORY
;
317 (jar
, jarTypeMF
, NULL
, raw_manifest
, length
, path
, url
);
321 * j a r _ p a r s e _ s f
323 * Parse META-INF/xxx.sf, a digitally signed file
324 * pointing to a subset of MF sections.
329 (JAR
*jar
, char ZHUGEP
*raw_manifest
,
330 long length
, const char *path
, const char *url
)
332 JAR_Signer
*signer
= NULL
;
333 int status
= JAR_ERR_MEMORY
;
335 if (jar
->globalmeta
== NULL
)
337 /* It is a requirement that the MF file be passed before the SF file */
338 return JAR_ERR_ORDER
;
341 signer
= JAR_new_signer();
348 signer
->owner
= jar_basename (path
);
349 if (signer
->owner
== NULL
)
354 /* check for priors. When someone doctors a jar file
355 to contain identical path entries, prevent the second
356 one from affecting JAR functions */
358 if (jar_get_signer (jar
, signer
->owner
))
360 /* someone is trying to spoof us */
361 status
= JAR_ERR_ORDER
;
366 /* remember its digest */
368 signer
->digest
= JAR_calculate_digest (raw_manifest
, length
);
370 if (signer
->digest
== NULL
)
373 /* Add this signer to the jar */
375 ADDITEM (jar
->signers
, jarTypeOwner
,
376 signer
->owner
, signer
, sizeof (JAR_Signer
));
380 (jar
, jarTypeSF
, signer
, raw_manifest
, length
, path
, url
);
385 JAR_destroy_signer (signer
);
391 * j a r _ p a r s e _ a n y
393 * Parse a MF or SF manifest file.
398 (JAR
*jar
, int type
, JAR_Signer
*signer
, char ZHUGEP
*raw_manifest
,
399 long length
, const char *path
, const char *url
)
405 JAR_Digest
*dig
, *mfdig
= NULL
;
408 char x_name
[SZ
], x_md5
[SZ
], x_sha
[SZ
];
412 char *sf_md5
= NULL
, *sf_sha1
= NULL
;
418 PORT_Assert( length
> 0 );
422 if ((status
= jar_insanity_check (raw_manifest
, raw_len
)) < 0)
427 /* null terminate the first line */
428 raw_manifest
= jar_eat_line (0, PR_TRUE
, raw_manifest
, &raw_len
);
431 /* skip over the preliminary section */
432 /* This is one section at the top of the file with global metainfo */
438 raw_manifest
= jar_eat_line (1, PR_TRUE
, raw_manifest
, &raw_len
);
439 if (!*raw_manifest
) break;
441 met
= (JAR_Metainfo
*)PORT_ZAlloc (sizeof (JAR_Metainfo
));
443 return JAR_ERR_MEMORY
;
445 /* Parse out the header & info */
447 if (xp_HUGE_STRLEN (raw_manifest
) >= SZ
)
449 /* almost certainly nonsense */
454 xp_HUGE_STRCPY (line
, raw_manifest
);
457 while (*x_info
&& *x_info
!= ' ' && *x_info
!= '\t' && *x_info
!= ':')
460 if (*x_info
) *x_info
++ = 0;
462 while (*x_info
== ' ' || *x_info
== '\t')
465 /* metainfo (name, value) pair is now (line, x_info) */
467 met
->header
= PORT_Strdup (line
);
468 met
->info
= PORT_Strdup (x_info
);
470 if (type
== jarTypeMF
)
472 ADDITEM (jar
->metainfo
, jarTypeMeta
,
473 /* pathname */ NULL
, met
, sizeof (JAR_Metainfo
));
476 /* For SF files, this metadata may be the digests
477 of the MF file, still in the "met" structure. */
479 if (type
== jarTypeSF
)
481 if (!PORT_Strcasecmp (line
, "MD5-Digest"))
482 sf_md5
= (char *) met
->info
;
484 if (!PORT_Strcasecmp (line
, "SHA1-Digest") || !PORT_Strcasecmp (line
, "SHA-Digest"))
485 sf_sha1
= (char *) met
->info
;
488 if (type
!= jarTypeMF
)
490 PORT_Free (met
->header
);
491 if (type
!= jarTypeSF
)
493 PORT_Free (met
->info
);
499 if (type
== jarTypeSF
&& jar
->globalmeta
)
501 /* this is a SF file which may contain a digest of the manifest.mf's
505 JAR_Digest
*glob
= jar
->globalmeta
;
509 unsigned int md5_length
;
510 unsigned char *md5_digest
;
512 md5_digest
= ATOB_AsciiToData (sf_md5
, &md5_length
);
513 PORT_Assert( md5_length
== MD5_LENGTH
);
515 if (md5_length
!= MD5_LENGTH
)
516 return JAR_ERR_CORRUPT
;
518 match
= PORT_Memcmp (md5_digest
, glob
->md5
, MD5_LENGTH
);
521 if (sf_sha1
&& match
== 0)
523 unsigned int sha1_length
;
524 unsigned char *sha1_digest
;
526 sha1_digest
= ATOB_AsciiToData (sf_sha1
, &sha1_length
);
527 PORT_Assert( sha1_length
== SHA1_LENGTH
);
529 if (sha1_length
!= SHA1_LENGTH
)
530 return JAR_ERR_CORRUPT
;
532 match
= PORT_Memcmp (sha1_digest
, glob
->sha1
, SHA1_LENGTH
);
537 /* global digest doesn't match, SF file therefore invalid */
538 jar
->valid
= JAR_ERR_METADATA
;
539 return JAR_ERR_METADATA
;
543 /* done with top section of global data */
553 /* If this is a manifest file, attempt to get a digest of the following section,
554 without damaging it. This digest will be saved later. */
556 if (type
== jarTypeMF
)
559 long sec_len
= raw_len
;
561 if (!*raw_manifest
|| *raw_manifest
== '\n')
563 /* skip the blank line */
564 sec
= jar_eat_line (1, PR_FALSE
, raw_manifest
, &sec_len
);
569 if (!xp_HUGE_STRNCASECMP (sec
, "Name:", 5))
571 if (type
== jarTypeMF
)
572 mfdig
= jar_digest_section (sec
, sec_len
);
581 raw_manifest
= jar_eat_line (1, PR_TRUE
, raw_manifest
, &raw_len
);
582 if (!*raw_manifest
) break; /* blank line, done with this entry */
584 if (xp_HUGE_STRLEN (raw_manifest
) >= SZ
)
586 /* almost certainly nonsense */
591 /* Parse out the name/value pair */
593 xp_HUGE_STRCPY (line
, raw_manifest
);
596 while (*x_info
&& *x_info
!= ' ' && *x_info
!= '\t' && *x_info
!= ':')
599 if (*x_info
) *x_info
++ = 0;
601 while (*x_info
== ' ' || *x_info
== '\t')
605 if (!PORT_Strcasecmp (line
, "Name"))
606 PORT_Strcpy (x_name
, x_info
);
608 else if (!PORT_Strcasecmp (line
, "MD5-Digest"))
609 PORT_Strcpy (x_md5
, x_info
);
611 else if (!PORT_Strcasecmp (line
, "SHA1-Digest")
612 || !PORT_Strcasecmp (line
, "SHA-Digest"))
614 PORT_Strcpy (x_sha
, x_info
);
617 /* Algorithm list is meta info we don't care about; keeping it out
618 of metadata saves significant space for large jar files */
620 else if (!PORT_Strcasecmp (line
, "Digest-Algorithms")
621 || !PORT_Strcasecmp (line
, "Hash-Algorithms"))
626 /* Meta info is only collected for the manifest.mf file,
627 since the JAR_get_metainfo call does not support identity */
629 else if (type
== jarTypeMF
)
633 /* this is meta-data */
635 met
= (JAR_Metainfo
*)PORT_ZAlloc (sizeof (JAR_Metainfo
));
638 return JAR_ERR_MEMORY
;
640 /* metainfo (name, value) pair is now (line, x_info) */
642 if ((met
->header
= PORT_Strdup (line
)) == NULL
)
645 return JAR_ERR_MEMORY
;
648 if ((met
->info
= PORT_Strdup (x_info
)) == NULL
)
650 PORT_Free (met
->header
);
652 return JAR_ERR_MEMORY
;
655 ADDITEM (jar
->metainfo
, jarTypeMeta
,
656 x_name
, met
, sizeof (JAR_Metainfo
));
660 if(!x_name
|| !*x_name
) {
661 /* Whatever that was, it wasn't an entry, because we didn't get a name.
662 * We don't really have anything, so don't record this. */
666 dig
= (JAR_Digest
*)PORT_ZAlloc (sizeof (JAR_Digest
));
668 return JAR_ERR_MEMORY
;
672 unsigned int binary_length
;
673 unsigned char *binary_digest
;
675 binary_digest
= ATOB_AsciiToData (x_md5
, &binary_length
);
676 PORT_Assert( binary_length
== MD5_LENGTH
);
678 if (binary_length
!= MD5_LENGTH
)
681 return JAR_ERR_CORRUPT
;
684 memcpy (dig
->md5
, binary_digest
, MD5_LENGTH
);
685 dig
->md5_status
= jarHashPresent
;
690 unsigned int binary_length
;
691 unsigned char *binary_digest
;
693 binary_digest
= ATOB_AsciiToData (x_sha
, &binary_length
);
694 PORT_Assert( binary_length
== SHA1_LENGTH
);
696 if (binary_length
!= SHA1_LENGTH
)
699 return JAR_ERR_CORRUPT
;
702 memcpy (dig
->sha1
, binary_digest
, SHA1_LENGTH
);
703 dig
->sha1_status
= jarHashPresent
;
706 PORT_Assert( type
== jarTypeMF
|| type
== jarTypeSF
);
709 if (type
== jarTypeMF
)
711 ADDITEM (jar
->hashes
, jarTypeMF
, x_name
, dig
, sizeof (JAR_Digest
));
713 else if (type
== jarTypeSF
)
715 ADDITEM (signer
->sf
, jarTypeSF
, x_name
, dig
, sizeof (JAR_Digest
));
720 return JAR_ERR_ORDER
;
723 /* we're placing these calculated digests of manifest.mf
724 sections in a list where they can subsequently be forgotten */
726 if (type
== jarTypeMF
&& mfdig
)
728 ADDITEM (jar
->manifest
, jarTypeSect
,
729 x_name
, mfdig
, sizeof (JAR_Digest
));
735 /* Retrieve our saved SHA1 digest from saved copy and check digests.
736 This is just comparing the digest of the MF section as indicated in
737 the SF file with the one we remembered from parsing the MF file */
739 if (type
== jarTypeSF
)
741 if ((status
= jar_internal_digest (jar
, path
, x_name
, dig
)) < 0)
749 static int jar_internal_digest
750 (JAR
*jar
, const char *path
, char *x_name
, JAR_Digest
*dig
)
757 savdig
= jar_get_mf_digest (jar
, x_name
);
761 /* no .mf digest for this pathname */
762 status
= jar_signal (JAR_ERR_ENTRY
, jar
, path
, x_name
);
764 return 0; /* was continue; */
769 /* check for md5 consistency */
772 cv
= PORT_Memcmp (savdig
->md5
, dig
->md5
, MD5_LENGTH
);
773 /* md5 hash of .mf file is not what expected */
776 status
= jar_signal (JAR_ERR_HASH
, jar
, path
, x_name
);
780 dig
->md5_status
= jarHashBad
;
781 savdig
->md5_status
= jarHashBad
;
784 return 0; /* was continue; */
790 /* check for sha1 consistency */
791 if (dig
->sha1_status
)
793 cv
= PORT_Memcmp (savdig
->sha1
, dig
->sha1
, SHA1_LENGTH
);
794 /* sha1 hash of .mf file is not what expected */
797 status
= jar_signal (JAR_ERR_HASH
, jar
, path
, x_name
);
801 dig
->sha1_status
= jarHashBad
;
802 savdig
->sha1_status
= jarHashBad
;
805 return 0; /* was continue; */
815 * j a r _ i n s a n i t y _ c h e c k
817 * Check for illegal characters (or possibly so)
818 * in the manifest files, to detect potential memory
819 * corruption by our neighbors. Debug only, since
824 static int jar_insanity_check (char ZHUGEP
*data
, long length
)
829 for (off
= 0; off
< length
; off
++)
833 if (c
== '\n' || c
== '\r' || (c
>= ' ' && c
<= 128))
836 return JAR_ERR_CORRUPT
;
844 * j a r _ p a r s e _ d i g i t a l _ s i g n a t u r e
846 * Parse an RSA or DSA (or perhaps other) digital signature.
847 * Right now everything is PKCS7.
851 static int jar_parse_digital_signature
852 (char *raw_manifest
, JAR_Signer
*signer
, long length
, JAR
*jar
)
854 #if defined(XP_WIN16)
855 PORT_Assert( LOWORD(raw_manifest
) + length
< 0xFFFF );
857 return jar_validate_pkcs7 (jar
, signer
, raw_manifest
, length
);
861 * j a r _ a d d _ c e r t
863 * Add information for the given certificate
864 * (or whatever) to the JAR linked list. A pointer
865 * is passed for some relevant reference, say
866 * for example the original certificate.
870 static int jar_add_cert
871 (JAR
*jar
, JAR_Signer
*signer
, int type
, CERTCertificate
*cert
)
874 unsigned char *keyData
;
877 return JAR_ERR_ORDER
;
879 fing
= (JAR_Cert
*)PORT_ZAlloc (sizeof (JAR_Cert
));
884 fing
->cert
= CERT_DupCertificate (cert
);
886 /* get the certkey */
888 fing
->length
= cert
->derIssuer
.len
+ 2 + cert
->serialNumber
.len
;
890 keyData
= (unsigned char *) PORT_ZAlloc (fing
->length
);
893 if (fing
->key
== NULL
)
895 keyData
[0] = ((cert
->derIssuer
.len
) >> 8) & 0xff;
896 keyData
[1] = ((cert
->derIssuer
.len
) & 0xff);
897 PORT_Memcpy (&keyData
[2], cert
->derIssuer
.data
, cert
->derIssuer
.len
);
898 PORT_Memcpy (&keyData
[2+cert
->derIssuer
.len
], cert
->serialNumber
.data
,
899 cert
->serialNumber
.len
);
901 ADDITEM (signer
->certs
, type
,
902 /* pathname */ NULL
, fing
, sizeof (JAR_Cert
));
911 CERT_DestroyCertificate (fing
->cert
);
916 return JAR_ERR_MEMORY
;
922 * Consume an ascii line from the top of a file kept
923 * in memory. This destroys the file in place. This function
924 * handles PC, Mac, and Unix style text files.
928 static char ZHUGEP
*jar_eat_line
929 (int lines
, int eating
, char ZHUGEP
*data
, long *len
)
934 if (!*len
) return ret
;
936 /* Eat the requisite number of lines, if any;
937 prior to terminating the current line with a 0. */
939 for (/* yip */ ; lines
; lines
--)
941 while (*data
&& *data
!= '\n')
944 /* After the CR, ok to eat one LF */
949 /* If there are zeros, we put them there */
951 while (*data
== 0 && data
- ret
< *len
)
960 /* Terminate this line with a 0 */
962 while (*data
&& *data
!= '\n' && *data
!= '\r')
965 /* In any case we are allowed to eat CR */
970 /* After the CR, ok to eat one LF */
980 * j a r _ d i g e s t _ s e c t i o n
982 * Return the digests of the next section of the manifest file.
983 * Does not damage the manifest file, unlike parse_manifest.
987 static JAR_Digest
*jar_digest_section
988 (char ZHUGEP
*manifest
, long length
)
991 char ZHUGEP
*global_end
;
993 global_end
= manifest
;
998 global_end
= jar_eat_line (1, PR_FALSE
, global_end
, &global_len
);
999 if (*global_end
== 0 || *global_end
== '\n')
1003 return JAR_calculate_digest (manifest
, global_end
- manifest
);
1007 * J A R _ v e r i f y _ d i g e s t
1009 * Verifies that a precalculated digest matches the
1010 * expected value in the manifest.
1014 int PR_CALLBACK JAR_verify_digest
1015 (JAR
*jar
, const char *name
, JAR_Digest
*dig
)
1019 JAR_Digest
*shindig
;
1024 int result1
, result2
;
1028 result1
= result2
= 0;
1032 /* signature not valid */
1036 if (ZZ_ListEmpty (list
))
1042 for (link
= ZZ_ListHead (list
);
1043 !ZZ_ListIterDone (list
, link
);
1047 if (it
->type
== jarTypeMF
1048 && it
->pathname
&& !PORT_Strcmp (it
->pathname
, name
))
1050 shindig
= (JAR_Digest
*) it
->data
;
1052 if (shindig
->md5_status
)
1054 if (shindig
->md5_status
== jarHashBad
)
1055 return JAR_ERR_HASH
;
1057 result1
= memcmp (dig
->md5
, shindig
->md5
, MD5_LENGTH
);
1060 if (shindig
->sha1_status
)
1062 if (shindig
->sha1_status
== jarHashBad
)
1063 return JAR_ERR_HASH
;
1065 result2
= memcmp (dig
->sha1
, shindig
->sha1
, SHA1_LENGTH
);
1068 return (result1
== 0 && result2
== 0) ? 0 : JAR_ERR_HASH
;
1076 * J A R _ c e r t _ a t t r i b u t e
1078 * Return the named certificate attribute from the
1079 * certificate specified by the given key.
1083 int PR_CALLBACK JAR_cert_attribute
1084 (JAR
*jar
, jarCert attrib
, long keylen
, void *key
,
1085 void **result
, unsigned long *length
)
1090 CERTCertificate
*cert
;
1092 CERTCertDBHandle
*certdb
;
1099 if (attrib
== 0 || key
== 0)
1100 return JAR_ERR_GENERAL
;
1102 if (attrib
== jarCertJavaHack
)
1104 cert
= (CERTCertificate
*) NULL
;
1105 certdb
= JAR_open_database();
1109 cert
= CERT_FindCertByNickname (certdb
, key
);
1113 *length
= cert
->certKey
.len
;
1115 *result
= (void *) PORT_ZAlloc (*length
);
1118 PORT_Memcpy (*result
, cert
->certKey
.data
, *length
);
1121 JAR_close_database (certdb
);
1122 return JAR_ERR_MEMORY
;
1125 JAR_close_database (certdb
);
1128 return cert
? 0 : JAR_ERR_GENERAL
;
1131 if (jar
&& jar
->pkcs7
== 0)
1132 return JAR_ERR_GENERAL
;
1134 cert
= jar_get_certificate (jar
, keylen
, key
, &status
);
1136 if (cert
== NULL
|| status
< 0)
1137 return JAR_ERR_GENERAL
;
1139 #define SEP " <br> "
1140 #define SEPLEN (PORT_Strlen(SEP))
1144 case jarCertCompany
:
1146 ret
= cert
->subjectName
;
1148 /* This is pretty ugly looking but only used
1149 here for this one purpose. */
1155 char *cer_ou1
, *cer_ou2
, *cer_ou3
;
1156 char *cer_cn
, *cer_e
, *cer_o
, *cer_l
;
1158 cer_cn
= CERT_GetCommonName (&cert
->subject
);
1159 cer_e
= CERT_GetCertEmailAddress (&cert
->subject
);
1160 cer_ou3
= jar_cert_element (ret
, "OU=", 3);
1161 cer_ou2
= jar_cert_element (ret
, "OU=", 2);
1162 cer_ou1
= jar_cert_element (ret
, "OU=", 1);
1163 cer_o
= CERT_GetOrgName (&cert
->subject
);
1164 cer_l
= CERT_GetCountryName (&cert
->subject
);
1166 if (cer_cn
) retlen
+= SEPLEN
+ PORT_Strlen (cer_cn
);
1167 if (cer_e
) retlen
+= SEPLEN
+ PORT_Strlen (cer_e
);
1168 if (cer_ou1
) retlen
+= SEPLEN
+ PORT_Strlen (cer_ou1
);
1169 if (cer_ou2
) retlen
+= SEPLEN
+ PORT_Strlen (cer_ou2
);
1170 if (cer_ou3
) retlen
+= SEPLEN
+ PORT_Strlen (cer_ou3
);
1171 if (cer_o
) retlen
+= SEPLEN
+ PORT_Strlen (cer_o
);
1172 if (cer_l
) retlen
+= SEPLEN
+ PORT_Strlen (cer_l
);
1174 ret
= (char *) PORT_ZAlloc (1 + retlen
);
1176 if (cer_cn
) { PORT_Strcpy (ret
, cer_cn
); PORT_Strcat (ret
, SEP
); }
1177 if (cer_e
) { PORT_Strcat (ret
, cer_e
); PORT_Strcat (ret
, SEP
); }
1178 if (cer_ou1
) { PORT_Strcat (ret
, cer_ou1
); PORT_Strcat (ret
, SEP
); }
1179 if (cer_ou2
) { PORT_Strcat (ret
, cer_ou2
); PORT_Strcat (ret
, SEP
); }
1180 if (cer_ou3
) { PORT_Strcat (ret
, cer_ou3
); PORT_Strcat (ret
, SEP
); }
1181 if (cer_o
) { PORT_Strcat (ret
, cer_o
); PORT_Strcat (ret
, SEP
); }
1182 if (cer_l
) PORT_Strcat (ret
, cer_l
);
1184 /* return here to avoid unsightly memory leak */
1187 *length
= PORT_Strlen (ret
);
1195 ret
= cert
->issuerName
;
1201 char *cer_ou1
, *cer_ou2
, *cer_ou3
;
1202 char *cer_cn
, *cer_e
, *cer_o
, *cer_l
;
1204 /* This is pretty ugly looking but only used
1205 here for this one purpose. */
1207 cer_cn
= CERT_GetCommonName (&cert
->issuer
);
1208 cer_e
= CERT_GetCertEmailAddress (&cert
->issuer
);
1209 cer_ou3
= jar_cert_element (ret
, "OU=", 3);
1210 cer_ou2
= jar_cert_element (ret
, "OU=", 2);
1211 cer_ou1
= jar_cert_element (ret
, "OU=", 1);
1212 cer_o
= CERT_GetOrgName (&cert
->issuer
);
1213 cer_l
= CERT_GetCountryName (&cert
->issuer
);
1215 if (cer_cn
) retlen
+= SEPLEN
+ PORT_Strlen (cer_cn
);
1216 if (cer_e
) retlen
+= SEPLEN
+ PORT_Strlen (cer_e
);
1217 if (cer_ou1
) retlen
+= SEPLEN
+ PORT_Strlen (cer_ou1
);
1218 if (cer_ou2
) retlen
+= SEPLEN
+ PORT_Strlen (cer_ou2
);
1219 if (cer_ou3
) retlen
+= SEPLEN
+ PORT_Strlen (cer_ou3
);
1220 if (cer_o
) retlen
+= SEPLEN
+ PORT_Strlen (cer_o
);
1221 if (cer_l
) retlen
+= SEPLEN
+ PORT_Strlen (cer_l
);
1223 ret
= (char *) PORT_ZAlloc (1 + retlen
);
1225 if (cer_cn
) { PORT_Strcpy (ret
, cer_cn
); PORT_Strcat (ret
, SEP
); }
1226 if (cer_e
) { PORT_Strcat (ret
, cer_e
); PORT_Strcat (ret
, SEP
); }
1227 if (cer_ou1
) { PORT_Strcat (ret
, cer_ou1
); PORT_Strcat (ret
, SEP
); }
1228 if (cer_ou2
) { PORT_Strcat (ret
, cer_ou2
); PORT_Strcat (ret
, SEP
); }
1229 if (cer_ou3
) { PORT_Strcat (ret
, cer_ou3
); PORT_Strcat (ret
, SEP
); }
1230 if (cer_o
) { PORT_Strcat (ret
, cer_o
); PORT_Strcat (ret
, SEP
); }
1231 if (cer_l
) PORT_Strcat (ret
, cer_l
);
1233 /* return here to avoid unsightly memory leak */
1236 *length
= PORT_Strlen (ret
);
1245 ret
= CERT_Hexify (&cert
->serialNumber
, 1);
1248 case jarCertExpires
:
1250 ret
= DER_UTCDayToAscii (&cert
->validity
.notAfter
);
1253 case jarCertNickname
:
1255 ret
= jar_choose_nickname (cert
);
1260 dig
= JAR_calculate_digest
1261 ((char *) cert
->derCert
.data
, cert
->derCert
.len
);
1265 hexme
.len
= sizeof (dig
->md5
);
1266 hexme
.data
= dig
->md5
;
1267 ret
= CERT_Hexify (&hexme
, 1);
1273 return JAR_ERR_GENERAL
;
1276 *result
= ret
? PORT_Strdup (ret
) : NULL
;
1277 *length
= ret
? PORT_Strlen (ret
) : 0;
1283 * j a r _ c e r t _ e l e m e n t
1285 * Retrieve an element from an x400ish ascii
1286 * designator, in a hackish sort of way. The right
1287 * thing would probably be to sort AVATags.
1291 static char *jar_cert_element (char *name
, char *tag
, int occ
)
1300 if (PORT_Strstr (name
, tag
))
1302 name
= PORT_Strstr (name
, tag
) + PORT_Strlen (tag
);
1307 name
= PORT_Strstr (name
, "=");
1308 if (name
== NULL
) return NULL
;
1313 if (!found
) return NULL
;
1315 /* must mangle only the copy */
1316 name
= PORT_Strdup (name
);
1318 /* advance to next equal */
1319 for (s
= name
; *s
&& *s
!= '='; s
++)
1322 /* back up to previous comma */
1323 while (s
> name
&& *s
!= ',') s
--;
1325 /* zap the whitespace and return */
1333 * j a r _ c h o o s e _ n i c k n a m e
1335 * Attempt to determine a suitable nickname for
1336 * a certificate with a computer-generated "tmpcertxxx"
1337 * nickname. It needs to be something a user can
1338 * understand, so try a few things.
1342 static char *jar_choose_nickname (CERTCertificate
*cert
)
1350 /* is the existing name ok */
1352 if (cert
->nickname
&& PORT_Strncmp (cert
->nickname
, "tmpcert", 7))
1353 return PORT_Strdup (cert
->nickname
);
1355 /* we have an ugly name here people */
1358 cert_cn
= CERT_GetCommonName (&cert
->subject
);
1362 /* check for duplicate nickname */
1364 if (CERT_FindCertByNickname (CERT_GetDefaultCertDB(), cert_cn
) == NULL
)
1367 /* Try the CN plus O */
1368 cert_o
= CERT_GetOrgName (&cert
->subject
);
1370 cn_o_length
= PORT_Strlen (cert_cn
) + 3 + PORT_Strlen (cert_o
) + 20;
1371 cert_cn_o
= (char*)PORT_ZAlloc (cn_o_length
);
1373 PR_snprintf (cert_cn_o
, cn_o_length
,
1374 "%s's %s Certificate", cert_cn
, cert_o
);
1376 if (CERT_FindCertByNickname (CERT_GetDefaultCertDB(), cert_cn_o
) == NULL
)
1378 PORT_Free (cert_cn_o
);
1381 PORT_Free (cert_cn_o
);
1384 /* If all that failed, use the ugly nickname */
1385 return cert
->nickname
? PORT_Strdup (cert
->nickname
) : NULL
;
1389 * J A R _ c e r t _ h t m l
1391 * Return an HTML representation of the certificate
1392 * designated by the given fingerprint, in specified style.
1394 * JAR is optional, but supply it if you can in order
1400 (JAR
*jar
, int style
, long keylen
, void *key
, int *result
)
1402 CERTCertificate
*cert
;
1409 cert
= jar_get_certificate (jar
, keylen
, key
, result
);
1411 if (cert
== NULL
|| *result
< 0)
1420 * J A R _ s t a s h _ c e r t
1422 * Stash the certificate pointed to by this
1423 * fingerprint, in persistent storage somewhere.
1427 extern int PR_CALLBACK JAR_stash_cert
1428 (JAR
*jar
, long keylen
, void *key
)
1433 CERTCertTrust trust
;
1435 CERTCertDBHandle
*certdb
;
1436 CERTCertificate
*cert
, *newcert
;
1438 cert
= jar_get_certificate (jar
, keylen
, key
, &result
);
1444 return JAR_ERR_GENERAL
;
1446 if ((certdb
= JAR_open_database()) == NULL
)
1447 return JAR_ERR_GENERAL
;
1449 /* Attempt to give a name to the newish certificate */
1450 nickname
= jar_choose_nickname (cert
);
1452 newcert
= CERT_FindCertByNickname (certdb
, nickname
);
1454 if (newcert
&& newcert
->isperm
)
1456 /* already in permanant database */
1457 JAR_close_database (certdb
);
1461 if (newcert
) cert
= newcert
;
1463 /* FIX, since FindCert returns a bogus dbhandle
1466 cert
->dbhandle
= certdb
;
1468 if (nickname
!= NULL
)
1470 PORT_Memset ((void *) &trust
, 0, sizeof(trust
));
1472 if (CERT_AddTempCertToPerm (cert
, nickname
, &trust
) != SECSuccess
)
1474 /* XXX might want to call PORT_GetError here */
1475 result
= JAR_ERR_GENERAL
;
1479 JAR_close_database (certdb
);
1485 * J A R _ f e t c h _ c e r t
1487 * Given an opaque identifier of a certificate,
1488 * return the full certificate.
1490 * The new function, which retrieves by key.
1494 void *JAR_fetch_cert (long length
, void *key
)
1496 CERTIssuerAndSN issuerSN
;
1497 CERTCertificate
*cert
= NULL
;
1499 CERTCertDBHandle
*certdb
;
1501 certdb
= JAR_open_database();
1505 unsigned char *keyData
= (unsigned char *)key
;
1506 issuerSN
.derIssuer
.len
= (keyData
[0] << 8) + keyData
[0];
1507 issuerSN
.derIssuer
.data
= &keyData
[2];
1508 issuerSN
.serialNumber
.len
= length
- (2 + issuerSN
.derIssuer
.len
);
1509 issuerSN
.serialNumber
.data
= &keyData
[2+issuerSN
.derIssuer
.len
];
1511 cert
= CERT_FindCertByIssuerAndSN (certdb
, &issuerSN
);
1513 JAR_close_database (certdb
);
1516 return (void *) cert
;
1520 * j a r _ g e t _ m f _ d i g e s t
1522 * Retrieve a corresponding saved digest over a section
1523 * of the main manifest file.
1527 static JAR_Digest
*jar_get_mf_digest (JAR
*jar
, char *pathname
)
1536 list
= jar
->manifest
;
1538 if (ZZ_ListEmpty (list
))
1541 for (link
= ZZ_ListHead (list
);
1542 !ZZ_ListIterDone (list
, link
);
1546 if (it
->type
== jarTypeSect
1547 && it
->pathname
&& !PORT_Strcmp (it
->pathname
, pathname
))
1549 dig
= (JAR_Digest
*) it
->data
;
1558 * j a r _ b a s e n a m e
1560 * Return the basename -- leading components of path stripped off,
1561 * extension ripped off -- of a path.
1565 static char *jar_basename (const char *path
)
1567 char *pith
, *e
, *basename
, *ext
;
1570 return PORT_Strdup ("");
1572 pith
= PORT_Strdup (path
);
1578 for (e
= basename
; *e
&& *e
!= '/' && *e
!= '\\'; e
++)
1586 if ((ext
= PORT_Strrchr (basename
, '.')) != NULL
)
1589 /* We already have the space allocated */
1590 PORT_Strcpy (pith
, basename
);
1596 * + + + + + + + + + + + + + + +
1598 * CRYPTO ROUTINES FOR JAR
1600 * The following functions are the cryptographic
1601 * interface to PKCS7 for Jarnatures.
1603 * + + + + + + + + + + + + + + +
1608 * j a r _ c a t c h _ b y t e s
1610 * In the event signatures contain enveloped data, it will show up here.
1611 * But note that the lib/pkcs7 routines aren't ready for it.
1615 static void jar_catch_bytes
1616 (void *arg
, const char *buf
, unsigned long len
)
1618 /* Actually this should never be called, since there is
1619 presumably no data in the signature itself. */
1623 * j a r _ v a l i d a t e _ p k c s 7
1625 * Validate (and decode, if necessary) a binary pkcs7
1626 * signature in DER format.
1630 static int jar_validate_pkcs7
1631 (JAR
*jar
, JAR_Signer
*signer
, char *data
, long length
)
1635 SEC_PKCS7ContentInfo
*cinfo
= NULL
;
1636 SEC_PKCS7DecoderContext
*dcx
;
1639 char *errstring
= NULL
;
1641 PORT_Assert( jar
!= NULL
&& signer
!= NULL
);
1643 if (jar
== NULL
|| signer
== NULL
)
1644 return JAR_ERR_ORDER
;
1646 signer
->valid
= JAR_ERR_SIG
;
1648 /* We need a context if we can get one */
1650 #ifdef MOZILLA_CLIENT_OLD
1651 if (jar
->mw
== NULL
) {
1652 JAR_set_context (jar
, NULL
);
1657 dcx
= SEC_PKCS7DecoderStart
1658 (jar_catch_bytes
, NULL
/*cb_arg*/, NULL
/*getpassword*/, jar
->mw
,
1663 /* strange pkcs7 failure */
1667 SEC_PKCS7DecoderUpdate (dcx
, data
, length
);
1668 cinfo
= SEC_PKCS7DecoderFinish (dcx
);
1672 /* strange pkcs7 failure */
1676 if (SEC_PKCS7ContentIsEncrypted (cinfo
))
1678 /* content was encrypted, fail */
1682 if (SEC_PKCS7ContentIsSigned (cinfo
) == PR_FALSE
)
1684 /* content was not signed, fail */
1692 detdig
.len
= SHA1_LENGTH
;
1693 detdig
.data
= signer
->digest
->sha1
;
1695 if (SEC_PKCS7VerifyDetachedSignature
1696 (cinfo
, certUsageObjectSigner
, &detdig
, HASH_AlgSHA1
, PR_FALSE
)==
1699 /* signature is valid */
1701 jar_gather_signers (jar
, signer
, cinfo
);
1705 status
= PORT_GetError();
1707 PORT_Assert( status
< 0 );
1708 if (status
>= 0) status
= JAR_ERR_SIG
;
1710 jar
->valid
= status
;
1711 signer
->valid
= status
;
1713 errstring
= JAR_get_error (status
);
1714 /*XP_TRACE(("JAR signature invalid (reason %d = %s)", status, errstring));*/
1717 jar
->pkcs7
= PR_TRUE
;
1718 signer
->pkcs7
= PR_TRUE
;
1720 SEC_PKCS7DestroyContentInfo (cinfo
);
1726 * j a r _ g a t h e r _ s i g n e r s
1728 * Add the single signer of this signature to the
1729 * certificate linked list.
1733 static int jar_gather_signers
1734 (JAR
*jar
, JAR_Signer
*signer
, SEC_PKCS7ContentInfo
*cinfo
)
1738 CERTCertificate
*cert
;
1739 CERTCertDBHandle
*certdb
;
1741 SEC_PKCS7SignedData
*sdp
;
1742 SEC_PKCS7SignerInfo
**pksigners
, *pksigner
;
1744 sdp
= cinfo
->content
.signedData
;
1749 pksigners
= sdp
->signerInfos
;
1751 /* permit exactly one signer */
1753 if (pksigners
== NULL
|| pksigners
[0] == NULL
|| pksigners
[1] != NULL
)
1756 pksigner
= *pksigners
;
1757 cert
= pksigner
->cert
;
1762 certdb
= JAR_open_database();
1765 return JAR_ERR_GENERAL
;
1767 result
= jar_add_cert (jar
, signer
, jarTypeSign
, cert
);
1769 JAR_close_database (certdb
);
1775 * j a r _ o p e n _ d a t a b a s e
1777 * Open the certificate database,
1778 * for use by JAR functions.
1782 CERTCertDBHandle
*JAR_open_database (void)
1784 CERTCertDBHandle
*certdb
;
1786 certdb
= CERT_GetDefaultCertDB();
1792 * j a r _ c l o s e _ d a t a b a s e
1794 * Close the certificate database.
1795 * For use by JAR functions.
1799 int JAR_close_database (CERTCertDBHandle
*certdb
)
1805 * j a r _ g e t _ c e r t i f i c a t e
1807 * Return the certificate referenced
1808 * by a given fingerprint, or NULL if not found.
1809 * Error code is returned in result.
1813 static CERTCertificate
*jar_get_certificate
1814 (JAR
*jar
, long keylen
, void *key
, int *result
)
1819 JAR_Cert
*fing
= NULL
;
1826 cert
= JAR_fetch_cert (keylen
, key
);
1827 *result
= (cert
== NULL
) ? JAR_ERR_GENERAL
: 0;
1828 return (CERTCertificate
*) cert
;
1831 ctx
= JAR_find (jar
, NULL
, jarTypeSign
);
1833 while (JAR_find_next (ctx
, &it
) >= 0)
1835 fing
= (JAR_Cert
*) it
->data
;
1837 if (keylen
!= fing
->length
)
1840 PORT_Assert( keylen
< 0xFFFF );
1841 if (!PORT_Memcmp (fing
->key
, key
, keylen
))
1852 *result
= JAR_ERR_GENERAL
;
1856 PORT_Assert(fing
!= NULL
);
1862 * j a r _ s i g n a l
1864 * Nonfatal errors come here to callback Java.
1868 static int jar_signal
1869 (int status
, JAR
*jar
, const char *metafile
, char *pathname
)
1873 errstring
= JAR_get_error (status
);
1877 (*jar
->signal
) (status
, jar
, metafile
, pathname
, errstring
);
1885 * j a r _ a p p e n d
1887 * Tack on an element to one of a JAR's linked
1888 * lists, with rudimentary error handling.
1892 int jar_append (ZZList
*list
, int type
,
1893 char *pathname
, void *data
, size_t size
)
1898 it
= (JAR_Item
*)PORT_ZAlloc (sizeof (JAR_Item
));
1905 it
->pathname
= PORT_Strdup (pathname
);
1906 if (it
->pathname
== NULL
)
1910 it
->type
= (jarType
)type
;
1911 it
->data
= (unsigned char *) data
;
1914 entity
= ZZ_NewLink (it
);
1918 ZZ_AppendLink (list
, entity
);
1926 if (it
->pathname
) PORT_Free (it
->pathname
);
1930 return JAR_ERR_MEMORY
;
1934 * W I N 1 6 s t u f f
1936 * These functions possibly belong in xp_mem.c, they operate
1937 * on huge string pointers for win16.
1941 #if defined(XP_WIN16)
1942 int xp_HUGE_STRNCASECMP (char ZHUGEP
*buf
, char *key
, int len
)
1951 if (c1
>= 'a' && c1
<= 'z') c1
-= ('a' - 'A');
1952 if (c2
>= 'a' && c2
<= 'z') c2
-= ('a' - 'A');
1955 return (c1
< c2
) ? -1 : 1;
1960 size_t xp_HUGE_STRLEN (char ZHUGEP
*s
)
1967 char *xp_HUGE_STRCPY (char *to
, char ZHUGEP
*from
)