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 #define xp_HUGE_MEMCPY PORT_Memcpy
56 #define xp_HUGE_STRCPY PORT_Strcpy
57 #define xp_HUGE_STRLEN PORT_Strlen
58 #define xp_HUGE_STRNCASECMP PORT_Strncasecmp
61 #define CERTDB_USER (1<<6)
65 static int jar_validate_pkcs7
66 (JAR
*jar
, JAR_Signer
*signer
, char *data
, long length
);
68 static void jar_catch_bytes
69 (void *arg
, const char *buf
, unsigned long len
);
71 static int jar_gather_signers
72 (JAR
*jar
, JAR_Signer
*signer
, SEC_PKCS7ContentInfo
*cinfo
);
74 static char ZHUGEP
*jar_eat_line
75 (int lines
, int eating
, char ZHUGEP
*data
, long *len
);
77 static JAR_Digest
*jar_digest_section
78 (char ZHUGEP
*manifest
, long length
);
80 static JAR_Digest
*jar_get_mf_digest (JAR
*jar
, char *path
);
82 static int jar_parse_digital_signature
83 (char *raw_manifest
, JAR_Signer
*signer
, long length
, JAR
*jar
);
85 static int jar_add_cert
86 (JAR
*jar
, JAR_Signer
*signer
, int type
, CERTCertificate
*cert
);
88 static CERTCertificate
*jar_get_certificate
89 (JAR
*jar
, long keylen
, void *key
, int *result
);
91 static char *jar_cert_element (char *name
, char *tag
, int occ
);
93 static char *jar_choose_nickname (CERTCertificate
*cert
);
95 static char *jar_basename (const char *path
);
98 (int status
, JAR
*jar
, const char *metafile
, char *pathname
);
101 static int jar_insanity_check (char ZHUGEP
*data
, long length
);
105 (JAR
*jar
, char ZHUGEP
*raw_manifest
,
106 long length
, const char *path
, const char *url
);
109 (JAR
*jar
, char ZHUGEP
*raw_manifest
,
110 long length
, const char *path
, const char *url
);
113 (JAR
*jar
, const char *path
, char ZHUGEP
*raw_manifest
, long length
);
116 (JAR
*jar
, int type
, JAR_Signer
*signer
, char ZHUGEP
*raw_manifest
,
117 long length
, const char *path
, const char *url
);
119 static int jar_internal_digest
120 (JAR
*jar
, const char *path
, char *x_name
, JAR_Digest
*dig
);
123 * J A R _ p a r s e _ m a n i f e s t
125 * Pass manifest files to this function. They are
126 * decoded and placed into internal representations.
128 * Accepts both signature and manifest files. Use
129 * the same "jar" for both.
133 int JAR_parse_manifest
134 (JAR
*jar
, char ZHUGEP
*raw_manifest
,
135 long length
, const char *path
, const char *url
)
137 int filename_free
= 0;
139 /* fill in the path, if supplied. This is a the location
140 of the jar file on disk, if known */
142 if (jar
->filename
== NULL
&& path
)
144 jar
->filename
= PORT_Strdup (path
);
145 if (jar
->filename
== NULL
)
146 return JAR_ERR_MEMORY
;
150 /* fill in the URL, if supplied. This is the place
151 from which the jar file was retrieved. */
153 if (jar
->url
== NULL
&& url
)
155 jar
->url
= PORT_Strdup (url
);
156 if (jar
->url
== NULL
)
160 PORT_Free (jar
->filename
);
162 return JAR_ERR_MEMORY
;
166 /* Determine what kind of file this is from the META-INF
167 directory. It could be MF, SF, or a binary RSA/DSA file */
169 if (!xp_HUGE_STRNCASECMP (raw_manifest
, "Manifest-Version:", 17))
171 return jar_parse_mf (jar
, raw_manifest
, length
, path
, url
);
173 else if (!xp_HUGE_STRNCASECMP (raw_manifest
, "Signature-Version:", 18))
175 return jar_parse_sf (jar
, raw_manifest
, length
, path
, url
);
179 /* This is probably a binary signature */
180 return jar_parse_sig (jar
, path
, raw_manifest
, length
);
185 * j a r _ p a r s e _ s i g
187 * Pass some manner of RSA or DSA digital signature
188 * on, after checking to see if it comes at an appropriate state.
193 (JAR
*jar
, const char *path
, char ZHUGEP
*raw_manifest
, long length
)
196 int status
= JAR_ERR_ORDER
;
200 /* signature is way too small */
204 /* make sure that MF and SF have already been processed */
206 if (jar
->globalmeta
== NULL
)
207 return JAR_ERR_ORDER
;
209 /* Determine whether or not this RSA file has
210 has an associated SF file */
215 owner
= jar_basename (path
);
218 return JAR_ERR_MEMORY
;
220 signer
= jar_get_signer (jar
, owner
);
225 signer
= jar_get_signer (jar
, "*");
228 return JAR_ERR_ORDER
;
231 /* Do not pass a huge pointer to this function,
232 since the underlying security code is unaware. We will
233 never pass >64k through here. */
237 /* this digital signature is way too big */
241 /* don't expense unneeded calloc overhead on non-win16 */
242 status
= jar_parse_digital_signature
243 (raw_manifest
, signer
, length
, jar
);
249 * j a r _ p a r s e _ m f
251 * Parse the META-INF/manifest.mf file, whose
252 * information applies to all signers.
257 (JAR
*jar
, char ZHUGEP
*raw_manifest
,
258 long length
, const char *path
, const char *url
)
262 /* refuse a second manifest file, if passed for some reason */
263 return JAR_ERR_ORDER
;
267 /* remember a digest for the global section */
269 jar
->globalmeta
= jar_digest_section (raw_manifest
, length
);
271 if (jar
->globalmeta
== NULL
)
272 return JAR_ERR_MEMORY
;
276 (jar
, jarTypeMF
, NULL
, raw_manifest
, length
, path
, url
);
280 * j a r _ p a r s e _ s f
282 * Parse META-INF/xxx.sf, a digitally signed file
283 * pointing to a subset of MF sections.
288 (JAR
*jar
, char ZHUGEP
*raw_manifest
,
289 long length
, const char *path
, const char *url
)
291 JAR_Signer
*signer
= NULL
;
292 int status
= JAR_ERR_MEMORY
;
294 if (jar
->globalmeta
== NULL
)
296 /* It is a requirement that the MF file be passed before the SF file */
297 return JAR_ERR_ORDER
;
300 signer
= JAR_new_signer();
307 signer
->owner
= jar_basename (path
);
308 if (signer
->owner
== NULL
)
313 /* check for priors. When someone doctors a jar file
314 to contain identical path entries, prevent the second
315 one from affecting JAR functions */
317 if (jar_get_signer (jar
, signer
->owner
))
319 /* someone is trying to spoof us */
320 status
= JAR_ERR_ORDER
;
325 /* remember its digest */
327 signer
->digest
= JAR_calculate_digest (raw_manifest
, length
);
329 if (signer
->digest
== NULL
)
332 /* Add this signer to the jar */
334 ADDITEM (jar
->signers
, jarTypeOwner
,
335 signer
->owner
, signer
, sizeof (JAR_Signer
));
339 (jar
, jarTypeSF
, signer
, raw_manifest
, length
, path
, url
);
344 JAR_destroy_signer (signer
);
350 * j a r _ p a r s e _ a n y
352 * Parse a MF or SF manifest file.
357 (JAR
*jar
, int type
, JAR_Signer
*signer
, char ZHUGEP
*raw_manifest
,
358 long length
, const char *path
, const char *url
)
364 JAR_Digest
*dig
, *mfdig
= NULL
;
367 char x_name
[SZ
], x_md5
[SZ
], x_sha
[SZ
];
371 char *sf_md5
= NULL
, *sf_sha1
= NULL
;
377 PORT_Assert( length
> 0 );
381 if ((status
= jar_insanity_check (raw_manifest
, raw_len
)) < 0)
386 /* null terminate the first line */
387 raw_manifest
= jar_eat_line (0, PR_TRUE
, raw_manifest
, &raw_len
);
390 /* skip over the preliminary section */
391 /* This is one section at the top of the file with global metainfo */
397 raw_manifest
= jar_eat_line (1, PR_TRUE
, raw_manifest
, &raw_len
);
398 if (!*raw_manifest
) break;
400 met
= (JAR_Metainfo
*)PORT_ZAlloc (sizeof (JAR_Metainfo
));
402 return JAR_ERR_MEMORY
;
404 /* Parse out the header & info */
406 if (xp_HUGE_STRLEN (raw_manifest
) >= SZ
)
408 /* almost certainly nonsense */
413 xp_HUGE_STRCPY (line
, raw_manifest
);
416 while (*x_info
&& *x_info
!= ' ' && *x_info
!= '\t' && *x_info
!= ':')
419 if (*x_info
) *x_info
++ = 0;
421 while (*x_info
== ' ' || *x_info
== '\t')
424 /* metainfo (name, value) pair is now (line, x_info) */
426 met
->header
= PORT_Strdup (line
);
427 met
->info
= PORT_Strdup (x_info
);
429 if (type
== jarTypeMF
)
431 ADDITEM (jar
->metainfo
, jarTypeMeta
,
432 /* pathname */ NULL
, met
, sizeof (JAR_Metainfo
));
435 /* For SF files, this metadata may be the digests
436 of the MF file, still in the "met" structure. */
438 if (type
== jarTypeSF
)
440 if (!PORT_Strcasecmp (line
, "MD5-Digest"))
441 sf_md5
= (char *) met
->info
;
443 if (!PORT_Strcasecmp (line
, "SHA1-Digest") || !PORT_Strcasecmp (line
, "SHA-Digest"))
444 sf_sha1
= (char *) met
->info
;
447 if (type
!= jarTypeMF
)
449 PORT_Free (met
->header
);
450 if (type
!= jarTypeSF
)
452 PORT_Free (met
->info
);
458 if (type
== jarTypeSF
&& jar
->globalmeta
)
460 /* this is a SF file which may contain a digest of the manifest.mf's
464 JAR_Digest
*glob
= jar
->globalmeta
;
468 unsigned int md5_length
;
469 unsigned char *md5_digest
;
471 md5_digest
= ATOB_AsciiToData (sf_md5
, &md5_length
);
472 PORT_Assert( md5_length
== MD5_LENGTH
);
474 if (md5_length
!= MD5_LENGTH
)
475 return JAR_ERR_CORRUPT
;
477 match
= PORT_Memcmp (md5_digest
, glob
->md5
, MD5_LENGTH
);
480 if (sf_sha1
&& match
== 0)
482 unsigned int sha1_length
;
483 unsigned char *sha1_digest
;
485 sha1_digest
= ATOB_AsciiToData (sf_sha1
, &sha1_length
);
486 PORT_Assert( sha1_length
== SHA1_LENGTH
);
488 if (sha1_length
!= SHA1_LENGTH
)
489 return JAR_ERR_CORRUPT
;
491 match
= PORT_Memcmp (sha1_digest
, glob
->sha1
, SHA1_LENGTH
);
496 /* global digest doesn't match, SF file therefore invalid */
497 jar
->valid
= JAR_ERR_METADATA
;
498 return JAR_ERR_METADATA
;
502 /* done with top section of global data */
512 /* If this is a manifest file, attempt to get a digest of the following section,
513 without damaging it. This digest will be saved later. */
515 if (type
== jarTypeMF
)
518 long sec_len
= raw_len
;
520 if (!*raw_manifest
|| *raw_manifest
== '\n')
522 /* skip the blank line */
523 sec
= jar_eat_line (1, PR_FALSE
, raw_manifest
, &sec_len
);
528 if (!xp_HUGE_STRNCASECMP (sec
, "Name:", 5))
530 if (type
== jarTypeMF
)
531 mfdig
= jar_digest_section (sec
, sec_len
);
540 raw_manifest
= jar_eat_line (1, PR_TRUE
, raw_manifest
, &raw_len
);
541 if (!*raw_manifest
) break; /* blank line, done with this entry */
543 if (xp_HUGE_STRLEN (raw_manifest
) >= SZ
)
545 /* almost certainly nonsense */
550 /* Parse out the name/value pair */
552 xp_HUGE_STRCPY (line
, raw_manifest
);
555 while (*x_info
&& *x_info
!= ' ' && *x_info
!= '\t' && *x_info
!= ':')
558 if (*x_info
) *x_info
++ = 0;
560 while (*x_info
== ' ' || *x_info
== '\t')
564 if (!PORT_Strcasecmp (line
, "Name"))
565 PORT_Strcpy (x_name
, x_info
);
567 else if (!PORT_Strcasecmp (line
, "MD5-Digest"))
568 PORT_Strcpy (x_md5
, x_info
);
570 else if (!PORT_Strcasecmp (line
, "SHA1-Digest")
571 || !PORT_Strcasecmp (line
, "SHA-Digest"))
573 PORT_Strcpy (x_sha
, x_info
);
576 /* Algorithm list is meta info we don't care about; keeping it out
577 of metadata saves significant space for large jar files */
579 else if (!PORT_Strcasecmp (line
, "Digest-Algorithms")
580 || !PORT_Strcasecmp (line
, "Hash-Algorithms"))
585 /* Meta info is only collected for the manifest.mf file,
586 since the JAR_get_metainfo call does not support identity */
588 else if (type
== jarTypeMF
)
592 /* this is meta-data */
594 met
= (JAR_Metainfo
*)PORT_ZAlloc (sizeof (JAR_Metainfo
));
597 return JAR_ERR_MEMORY
;
599 /* metainfo (name, value) pair is now (line, x_info) */
601 if ((met
->header
= PORT_Strdup (line
)) == NULL
)
604 return JAR_ERR_MEMORY
;
607 if ((met
->info
= PORT_Strdup (x_info
)) == NULL
)
609 PORT_Free (met
->header
);
611 return JAR_ERR_MEMORY
;
614 ADDITEM (jar
->metainfo
, jarTypeMeta
,
615 x_name
, met
, sizeof (JAR_Metainfo
));
619 if(!x_name
|| !*x_name
) {
620 /* Whatever that was, it wasn't an entry, because we didn't get a name.
621 * We don't really have anything, so don't record this. */
625 dig
= (JAR_Digest
*)PORT_ZAlloc (sizeof (JAR_Digest
));
627 return JAR_ERR_MEMORY
;
631 unsigned int binary_length
;
632 unsigned char *binary_digest
;
634 binary_digest
= ATOB_AsciiToData (x_md5
, &binary_length
);
635 PORT_Assert( binary_length
== MD5_LENGTH
);
637 if (binary_length
!= MD5_LENGTH
)
640 return JAR_ERR_CORRUPT
;
643 memcpy (dig
->md5
, binary_digest
, MD5_LENGTH
);
644 dig
->md5_status
= jarHashPresent
;
649 unsigned int binary_length
;
650 unsigned char *binary_digest
;
652 binary_digest
= ATOB_AsciiToData (x_sha
, &binary_length
);
653 PORT_Assert( binary_length
== SHA1_LENGTH
);
655 if (binary_length
!= SHA1_LENGTH
)
658 return JAR_ERR_CORRUPT
;
661 memcpy (dig
->sha1
, binary_digest
, SHA1_LENGTH
);
662 dig
->sha1_status
= jarHashPresent
;
665 PORT_Assert( type
== jarTypeMF
|| type
== jarTypeSF
);
668 if (type
== jarTypeMF
)
670 ADDITEM (jar
->hashes
, jarTypeMF
, x_name
, dig
, sizeof (JAR_Digest
));
672 else if (type
== jarTypeSF
)
674 ADDITEM (signer
->sf
, jarTypeSF
, x_name
, dig
, sizeof (JAR_Digest
));
679 return JAR_ERR_ORDER
;
682 /* we're placing these calculated digests of manifest.mf
683 sections in a list where they can subsequently be forgotten */
685 if (type
== jarTypeMF
&& mfdig
)
687 ADDITEM (jar
->manifest
, jarTypeSect
,
688 x_name
, mfdig
, sizeof (JAR_Digest
));
694 /* Retrieve our saved SHA1 digest from saved copy and check digests.
695 This is just comparing the digest of the MF section as indicated in
696 the SF file with the one we remembered from parsing the MF file */
698 if (type
== jarTypeSF
)
700 if ((status
= jar_internal_digest (jar
, path
, x_name
, dig
)) < 0)
708 static int jar_internal_digest
709 (JAR
*jar
, const char *path
, char *x_name
, JAR_Digest
*dig
)
716 savdig
= jar_get_mf_digest (jar
, x_name
);
720 /* no .mf digest for this pathname */
721 status
= jar_signal (JAR_ERR_ENTRY
, jar
, path
, x_name
);
723 return 0; /* was continue; */
728 /* check for md5 consistency */
731 cv
= PORT_Memcmp (savdig
->md5
, dig
->md5
, MD5_LENGTH
);
732 /* md5 hash of .mf file is not what expected */
735 status
= jar_signal (JAR_ERR_HASH
, jar
, path
, x_name
);
739 dig
->md5_status
= jarHashBad
;
740 savdig
->md5_status
= jarHashBad
;
743 return 0; /* was continue; */
749 /* check for sha1 consistency */
750 if (dig
->sha1_status
)
752 cv
= PORT_Memcmp (savdig
->sha1
, dig
->sha1
, SHA1_LENGTH
);
753 /* sha1 hash of .mf file is not what expected */
756 status
= jar_signal (JAR_ERR_HASH
, jar
, path
, x_name
);
760 dig
->sha1_status
= jarHashBad
;
761 savdig
->sha1_status
= jarHashBad
;
764 return 0; /* was continue; */
774 * j a r _ i n s a n i t y _ c h e c k
776 * Check for illegal characters (or possibly so)
777 * in the manifest files, to detect potential memory
778 * corruption by our neighbors. Debug only, since
783 static int jar_insanity_check (char ZHUGEP
*data
, long length
)
788 for (off
= 0; off
< length
; off
++)
792 if (c
== '\n' || c
== '\r' || (c
>= ' ' && c
<= 128))
795 return JAR_ERR_CORRUPT
;
803 * j a r _ p a r s e _ d i g i t a l _ s i g n a t u r e
805 * Parse an RSA or DSA (or perhaps other) digital signature.
806 * Right now everything is PKCS7.
810 static int jar_parse_digital_signature
811 (char *raw_manifest
, JAR_Signer
*signer
, long length
, JAR
*jar
)
813 return jar_validate_pkcs7 (jar
, signer
, raw_manifest
, length
);
817 * j a r _ a d d _ c e r t
819 * Add information for the given certificate
820 * (or whatever) to the JAR linked list. A pointer
821 * is passed for some relevant reference, say
822 * for example the original certificate.
826 static int jar_add_cert
827 (JAR
*jar
, JAR_Signer
*signer
, int type
, CERTCertificate
*cert
)
830 unsigned char *keyData
;
833 return JAR_ERR_ORDER
;
835 fing
= (JAR_Cert
*)PORT_ZAlloc (sizeof (JAR_Cert
));
840 fing
->cert
= CERT_DupCertificate (cert
);
842 /* get the certkey */
844 fing
->length
= cert
->derIssuer
.len
+ 2 + cert
->serialNumber
.len
;
846 keyData
= (unsigned char *) PORT_ZAlloc (fing
->length
);
849 if (fing
->key
== NULL
)
851 keyData
[0] = ((cert
->derIssuer
.len
) >> 8) & 0xff;
852 keyData
[1] = ((cert
->derIssuer
.len
) & 0xff);
853 PORT_Memcpy (&keyData
[2], cert
->derIssuer
.data
, cert
->derIssuer
.len
);
854 PORT_Memcpy (&keyData
[2+cert
->derIssuer
.len
], cert
->serialNumber
.data
,
855 cert
->serialNumber
.len
);
857 ADDITEM (signer
->certs
, type
,
858 /* pathname */ NULL
, fing
, sizeof (JAR_Cert
));
867 CERT_DestroyCertificate (fing
->cert
);
872 return JAR_ERR_MEMORY
;
878 * Consume an ascii line from the top of a file kept
879 * in memory. This destroys the file in place. This function
880 * handles PC, Mac, and Unix style text files.
884 static char ZHUGEP
*jar_eat_line
885 (int lines
, int eating
, char ZHUGEP
*data
, long *len
)
890 if (!*len
) return ret
;
892 /* Eat the requisite number of lines, if any;
893 prior to terminating the current line with a 0. */
895 for (/* yip */ ; lines
; lines
--)
897 while (*data
&& *data
!= '\n')
900 /* After the CR, ok to eat one LF */
905 /* If there are zeros, we put them there */
907 while (*data
== 0 && data
- ret
< *len
)
916 /* Terminate this line with a 0 */
918 while (*data
&& *data
!= '\n' && *data
!= '\r')
921 /* In any case we are allowed to eat CR */
926 /* After the CR, ok to eat one LF */
936 * j a r _ d i g e s t _ s e c t i o n
938 * Return the digests of the next section of the manifest file.
939 * Does not damage the manifest file, unlike parse_manifest.
943 static JAR_Digest
*jar_digest_section
944 (char ZHUGEP
*manifest
, long length
)
947 char ZHUGEP
*global_end
;
949 global_end
= manifest
;
954 global_end
= jar_eat_line (1, PR_FALSE
, global_end
, &global_len
);
955 if (*global_end
== 0 || *global_end
== '\n')
959 return JAR_calculate_digest (manifest
, global_end
- manifest
);
963 * J A R _ v e r i f y _ d i g e s t
965 * Verifies that a precalculated digest matches the
966 * expected value in the manifest.
970 int PR_CALLBACK JAR_verify_digest
971 (JAR
*jar
, const char *name
, JAR_Digest
*dig
)
980 int result1
, result2
;
984 result1
= result2
= 0;
988 /* signature not valid */
992 if (ZZ_ListEmpty (list
))
998 for (link
= ZZ_ListHead (list
);
999 !ZZ_ListIterDone (list
, link
);
1003 if (it
->type
== jarTypeMF
1004 && it
->pathname
&& !PORT_Strcmp (it
->pathname
, name
))
1006 shindig
= (JAR_Digest
*) it
->data
;
1008 if (shindig
->md5_status
)
1010 if (shindig
->md5_status
== jarHashBad
)
1011 return JAR_ERR_HASH
;
1013 result1
= memcmp (dig
->md5
, shindig
->md5
, MD5_LENGTH
);
1016 if (shindig
->sha1_status
)
1018 if (shindig
->sha1_status
== jarHashBad
)
1019 return JAR_ERR_HASH
;
1021 result2
= memcmp (dig
->sha1
, shindig
->sha1
, SHA1_LENGTH
);
1024 return (result1
== 0 && result2
== 0) ? 0 : JAR_ERR_HASH
;
1032 * J A R _ c e r t _ a t t r i b u t e
1034 * Return the named certificate attribute from the
1035 * certificate specified by the given key.
1039 int PR_CALLBACK JAR_cert_attribute
1040 (JAR
*jar
, jarCert attrib
, long keylen
, void *key
,
1041 void **result
, unsigned long *length
)
1046 CERTCertificate
*cert
;
1048 CERTCertDBHandle
*certdb
;
1055 if (attrib
== 0 || key
== 0)
1056 return JAR_ERR_GENERAL
;
1058 if (attrib
== jarCertJavaHack
)
1060 cert
= (CERTCertificate
*) NULL
;
1061 certdb
= JAR_open_database();
1065 cert
= CERT_FindCertByNickname (certdb
, key
);
1069 *length
= cert
->certKey
.len
;
1071 *result
= (void *) PORT_ZAlloc (*length
);
1074 PORT_Memcpy (*result
, cert
->certKey
.data
, *length
);
1077 JAR_close_database (certdb
);
1078 return JAR_ERR_MEMORY
;
1081 JAR_close_database (certdb
);
1084 return cert
? 0 : JAR_ERR_GENERAL
;
1087 if (jar
&& jar
->pkcs7
== 0)
1088 return JAR_ERR_GENERAL
;
1090 cert
= jar_get_certificate (jar
, keylen
, key
, &status
);
1092 if (cert
== NULL
|| status
< 0)
1093 return JAR_ERR_GENERAL
;
1095 #define SEP " <br> "
1096 #define SEPLEN (PORT_Strlen(SEP))
1100 case jarCertCompany
:
1102 ret
= cert
->subjectName
;
1104 /* This is pretty ugly looking but only used
1105 here for this one purpose. */
1111 char *cer_ou1
, *cer_ou2
, *cer_ou3
;
1112 char *cer_cn
, *cer_e
, *cer_o
, *cer_l
;
1114 cer_cn
= CERT_GetCommonName (&cert
->subject
);
1115 cer_e
= CERT_GetCertEmailAddress (&cert
->subject
);
1116 cer_ou3
= jar_cert_element (ret
, "OU=", 3);
1117 cer_ou2
= jar_cert_element (ret
, "OU=", 2);
1118 cer_ou1
= jar_cert_element (ret
, "OU=", 1);
1119 cer_o
= CERT_GetOrgName (&cert
->subject
);
1120 cer_l
= CERT_GetCountryName (&cert
->subject
);
1122 if (cer_cn
) retlen
+= SEPLEN
+ PORT_Strlen (cer_cn
);
1123 if (cer_e
) retlen
+= SEPLEN
+ PORT_Strlen (cer_e
);
1124 if (cer_ou1
) retlen
+= SEPLEN
+ PORT_Strlen (cer_ou1
);
1125 if (cer_ou2
) retlen
+= SEPLEN
+ PORT_Strlen (cer_ou2
);
1126 if (cer_ou3
) retlen
+= SEPLEN
+ PORT_Strlen (cer_ou3
);
1127 if (cer_o
) retlen
+= SEPLEN
+ PORT_Strlen (cer_o
);
1128 if (cer_l
) retlen
+= SEPLEN
+ PORT_Strlen (cer_l
);
1130 ret
= (char *) PORT_ZAlloc (1 + retlen
);
1132 if (cer_cn
) { PORT_Strcpy (ret
, cer_cn
); PORT_Strcat (ret
, SEP
); }
1133 if (cer_e
) { PORT_Strcat (ret
, cer_e
); PORT_Strcat (ret
, SEP
); }
1134 if (cer_ou1
) { PORT_Strcat (ret
, cer_ou1
); PORT_Strcat (ret
, SEP
); }
1135 if (cer_ou2
) { PORT_Strcat (ret
, cer_ou2
); PORT_Strcat (ret
, SEP
); }
1136 if (cer_ou3
) { PORT_Strcat (ret
, cer_ou3
); PORT_Strcat (ret
, SEP
); }
1137 if (cer_o
) { PORT_Strcat (ret
, cer_o
); PORT_Strcat (ret
, SEP
); }
1138 if (cer_l
) PORT_Strcat (ret
, cer_l
);
1140 /* return here to avoid unsightly memory leak */
1143 *length
= PORT_Strlen (ret
);
1151 ret
= cert
->issuerName
;
1157 char *cer_ou1
, *cer_ou2
, *cer_ou3
;
1158 char *cer_cn
, *cer_e
, *cer_o
, *cer_l
;
1160 /* This is pretty ugly looking but only used
1161 here for this one purpose. */
1163 cer_cn
= CERT_GetCommonName (&cert
->issuer
);
1164 cer_e
= CERT_GetCertEmailAddress (&cert
->issuer
);
1165 cer_ou3
= jar_cert_element (ret
, "OU=", 3);
1166 cer_ou2
= jar_cert_element (ret
, "OU=", 2);
1167 cer_ou1
= jar_cert_element (ret
, "OU=", 1);
1168 cer_o
= CERT_GetOrgName (&cert
->issuer
);
1169 cer_l
= CERT_GetCountryName (&cert
->issuer
);
1171 if (cer_cn
) retlen
+= SEPLEN
+ PORT_Strlen (cer_cn
);
1172 if (cer_e
) retlen
+= SEPLEN
+ PORT_Strlen (cer_e
);
1173 if (cer_ou1
) retlen
+= SEPLEN
+ PORT_Strlen (cer_ou1
);
1174 if (cer_ou2
) retlen
+= SEPLEN
+ PORT_Strlen (cer_ou2
);
1175 if (cer_ou3
) retlen
+= SEPLEN
+ PORT_Strlen (cer_ou3
);
1176 if (cer_o
) retlen
+= SEPLEN
+ PORT_Strlen (cer_o
);
1177 if (cer_l
) retlen
+= SEPLEN
+ PORT_Strlen (cer_l
);
1179 ret
= (char *) PORT_ZAlloc (1 + retlen
);
1181 if (cer_cn
) { PORT_Strcpy (ret
, cer_cn
); PORT_Strcat (ret
, SEP
); }
1182 if (cer_e
) { PORT_Strcat (ret
, cer_e
); PORT_Strcat (ret
, SEP
); }
1183 if (cer_ou1
) { PORT_Strcat (ret
, cer_ou1
); PORT_Strcat (ret
, SEP
); }
1184 if (cer_ou2
) { PORT_Strcat (ret
, cer_ou2
); PORT_Strcat (ret
, SEP
); }
1185 if (cer_ou3
) { PORT_Strcat (ret
, cer_ou3
); PORT_Strcat (ret
, SEP
); }
1186 if (cer_o
) { PORT_Strcat (ret
, cer_o
); PORT_Strcat (ret
, SEP
); }
1187 if (cer_l
) PORT_Strcat (ret
, cer_l
);
1189 /* return here to avoid unsightly memory leak */
1192 *length
= PORT_Strlen (ret
);
1201 ret
= CERT_Hexify (&cert
->serialNumber
, 1);
1204 case jarCertExpires
:
1206 ret
= DER_UTCDayToAscii (&cert
->validity
.notAfter
);
1209 case jarCertNickname
:
1211 ret
= jar_choose_nickname (cert
);
1216 dig
= JAR_calculate_digest
1217 ((char *) cert
->derCert
.data
, cert
->derCert
.len
);
1221 hexme
.len
= sizeof (dig
->md5
);
1222 hexme
.data
= dig
->md5
;
1223 ret
= CERT_Hexify (&hexme
, 1);
1229 return JAR_ERR_GENERAL
;
1232 *result
= ret
? PORT_Strdup (ret
) : NULL
;
1233 *length
= ret
? PORT_Strlen (ret
) : 0;
1239 * j a r _ c e r t _ e l e m e n t
1241 * Retrieve an element from an x400ish ascii
1242 * designator, in a hackish sort of way. The right
1243 * thing would probably be to sort AVATags.
1247 static char *jar_cert_element (char *name
, char *tag
, int occ
)
1256 if (PORT_Strstr (name
, tag
))
1258 name
= PORT_Strstr (name
, tag
) + PORT_Strlen (tag
);
1263 name
= PORT_Strstr (name
, "=");
1264 if (name
== NULL
) return NULL
;
1269 if (!found
) return NULL
;
1271 /* must mangle only the copy */
1272 name
= PORT_Strdup (name
);
1274 /* advance to next equal */
1275 for (s
= name
; *s
&& *s
!= '='; s
++)
1278 /* back up to previous comma */
1279 while (s
> name
&& *s
!= ',') s
--;
1281 /* zap the whitespace and return */
1289 * j a r _ c h o o s e _ n i c k n a m e
1291 * Attempt to determine a suitable nickname for
1292 * a certificate with a computer-generated "tmpcertxxx"
1293 * nickname. It needs to be something a user can
1294 * understand, so try a few things.
1298 static char *jar_choose_nickname (CERTCertificate
*cert
)
1306 /* is the existing name ok */
1308 if (cert
->nickname
&& PORT_Strncmp (cert
->nickname
, "tmpcert", 7))
1309 return PORT_Strdup (cert
->nickname
);
1311 /* we have an ugly name here people */
1314 cert_cn
= CERT_GetCommonName (&cert
->subject
);
1318 /* check for duplicate nickname */
1320 if (CERT_FindCertByNickname (CERT_GetDefaultCertDB(), cert_cn
) == NULL
)
1323 /* Try the CN plus O */
1324 cert_o
= CERT_GetOrgName (&cert
->subject
);
1326 cn_o_length
= PORT_Strlen (cert_cn
) + 3 + PORT_Strlen (cert_o
) + 20;
1327 cert_cn_o
= (char*)PORT_ZAlloc (cn_o_length
);
1329 PR_snprintf (cert_cn_o
, cn_o_length
,
1330 "%s's %s Certificate", cert_cn
, cert_o
);
1332 if (CERT_FindCertByNickname (CERT_GetDefaultCertDB(), cert_cn_o
) == NULL
)
1334 PORT_Free (cert_cn_o
);
1337 PORT_Free (cert_cn_o
);
1340 /* If all that failed, use the ugly nickname */
1341 return cert
->nickname
? PORT_Strdup (cert
->nickname
) : NULL
;
1345 * J A R _ c e r t _ h t m l
1347 * Return an HTML representation of the certificate
1348 * designated by the given fingerprint, in specified style.
1350 * JAR is optional, but supply it if you can in order
1356 (JAR
*jar
, int style
, long keylen
, void *key
, int *result
)
1358 CERTCertificate
*cert
;
1365 cert
= jar_get_certificate (jar
, keylen
, key
, result
);
1367 if (cert
== NULL
|| *result
< 0)
1376 * J A R _ s t a s h _ c e r t
1378 * Stash the certificate pointed to by this
1379 * fingerprint, in persistent storage somewhere.
1383 extern int PR_CALLBACK JAR_stash_cert
1384 (JAR
*jar
, long keylen
, void *key
)
1389 CERTCertTrust trust
;
1391 CERTCertDBHandle
*certdb
;
1392 CERTCertificate
*cert
, *newcert
;
1394 cert
= jar_get_certificate (jar
, keylen
, key
, &result
);
1400 return JAR_ERR_GENERAL
;
1402 if ((certdb
= JAR_open_database()) == NULL
)
1403 return JAR_ERR_GENERAL
;
1405 /* Attempt to give a name to the newish certificate */
1406 nickname
= jar_choose_nickname (cert
);
1408 newcert
= CERT_FindCertByNickname (certdb
, nickname
);
1410 if (newcert
&& newcert
->isperm
)
1412 /* already in permanant database */
1413 JAR_close_database (certdb
);
1417 if (newcert
) cert
= newcert
;
1419 /* FIX, since FindCert returns a bogus dbhandle
1422 cert
->dbhandle
= certdb
;
1424 if (nickname
!= NULL
)
1426 PORT_Memset ((void *) &trust
, 0, sizeof(trust
));
1428 if (CERT_AddTempCertToPerm (cert
, nickname
, &trust
) != SECSuccess
)
1430 /* XXX might want to call PORT_GetError here */
1431 result
= JAR_ERR_GENERAL
;
1435 JAR_close_database (certdb
);
1441 * J A R _ f e t c h _ c e r t
1443 * Given an opaque identifier of a certificate,
1444 * return the full certificate.
1446 * The new function, which retrieves by key.
1450 void *JAR_fetch_cert (long length
, void *key
)
1452 CERTIssuerAndSN issuerSN
;
1453 CERTCertificate
*cert
= NULL
;
1455 CERTCertDBHandle
*certdb
;
1457 certdb
= JAR_open_database();
1461 unsigned char *keyData
= (unsigned char *)key
;
1462 issuerSN
.derIssuer
.len
= (keyData
[0] << 8) + keyData
[0];
1463 issuerSN
.derIssuer
.data
= &keyData
[2];
1464 issuerSN
.serialNumber
.len
= length
- (2 + issuerSN
.derIssuer
.len
);
1465 issuerSN
.serialNumber
.data
= &keyData
[2+issuerSN
.derIssuer
.len
];
1467 cert
= CERT_FindCertByIssuerAndSN (certdb
, &issuerSN
);
1469 JAR_close_database (certdb
);
1472 return (void *) cert
;
1476 * j a r _ g e t _ m f _ d i g e s t
1478 * Retrieve a corresponding saved digest over a section
1479 * of the main manifest file.
1483 static JAR_Digest
*jar_get_mf_digest (JAR
*jar
, char *pathname
)
1492 list
= jar
->manifest
;
1494 if (ZZ_ListEmpty (list
))
1497 for (link
= ZZ_ListHead (list
);
1498 !ZZ_ListIterDone (list
, link
);
1502 if (it
->type
== jarTypeSect
1503 && it
->pathname
&& !PORT_Strcmp (it
->pathname
, pathname
))
1505 dig
= (JAR_Digest
*) it
->data
;
1514 * j a r _ b a s e n a m e
1516 * Return the basename -- leading components of path stripped off,
1517 * extension ripped off -- of a path.
1521 static char *jar_basename (const char *path
)
1523 char *pith
, *e
, *basename
, *ext
;
1526 return PORT_Strdup ("");
1528 pith
= PORT_Strdup (path
);
1534 for (e
= basename
; *e
&& *e
!= '/' && *e
!= '\\'; e
++)
1542 if ((ext
= PORT_Strrchr (basename
, '.')) != NULL
)
1545 /* We already have the space allocated */
1546 PORT_Strcpy (pith
, basename
);
1552 * + + + + + + + + + + + + + + +
1554 * CRYPTO ROUTINES FOR JAR
1556 * The following functions are the cryptographic
1557 * interface to PKCS7 for Jarnatures.
1559 * + + + + + + + + + + + + + + +
1564 * j a r _ c a t c h _ b y t e s
1566 * In the event signatures contain enveloped data, it will show up here.
1567 * But note that the lib/pkcs7 routines aren't ready for it.
1571 static void jar_catch_bytes
1572 (void *arg
, const char *buf
, unsigned long len
)
1574 /* Actually this should never be called, since there is
1575 presumably no data in the signature itself. */
1579 * j a r _ v a l i d a t e _ p k c s 7
1581 * Validate (and decode, if necessary) a binary pkcs7
1582 * signature in DER format.
1586 static int jar_validate_pkcs7
1587 (JAR
*jar
, JAR_Signer
*signer
, char *data
, long length
)
1590 SEC_PKCS7ContentInfo
*cinfo
= NULL
;
1591 SEC_PKCS7DecoderContext
*dcx
;
1596 PORT_Assert( jar
!= NULL
&& signer
!= NULL
);
1598 if (jar
== NULL
|| signer
== NULL
)
1599 return JAR_ERR_ORDER
;
1601 signer
->valid
= JAR_ERR_SIG
;
1603 /* We need a context if we can get one */
1605 #ifdef MOZILLA_CLIENT_OLD
1606 if (jar
->mw
== NULL
) {
1607 JAR_set_context (jar
, NULL
);
1612 dcx
= SEC_PKCS7DecoderStart
1613 (jar_catch_bytes
, NULL
/*cb_arg*/, NULL
/*getpassword*/, jar
->mw
,
1618 /* strange pkcs7 failure */
1622 SEC_PKCS7DecoderUpdate (dcx
, data
, length
);
1623 cinfo
= SEC_PKCS7DecoderFinish (dcx
);
1627 /* strange pkcs7 failure */
1631 if (SEC_PKCS7ContentIsEncrypted (cinfo
))
1633 /* content was encrypted, fail */
1637 if (SEC_PKCS7ContentIsSigned (cinfo
) == PR_FALSE
)
1639 /* content was not signed, fail */
1647 detdig
.len
= SHA1_LENGTH
;
1648 detdig
.data
= signer
->digest
->sha1
;
1650 goodSig
= SEC_PKCS7VerifyDetachedSignature(cinfo
, certUsageObjectSigner
,
1651 &detdig
, HASH_AlgSHA1
, PR_FALSE
);
1652 jar_gather_signers (jar
, signer
, cinfo
);
1653 if (goodSig
== PR_TRUE
)
1655 /* signature is valid */
1660 status
= PORT_GetError();
1662 PORT_Assert( status
< 0 );
1663 if (status
>= 0) status
= JAR_ERR_SIG
;
1665 jar
->valid
= status
;
1666 signer
->valid
= status
;
1669 jar
->pkcs7
= PR_TRUE
;
1670 signer
->pkcs7
= PR_TRUE
;
1672 SEC_PKCS7DestroyContentInfo (cinfo
);
1678 * j a r _ g a t h e r _ s i g n e r s
1680 * Add the single signer of this signature to the
1681 * certificate linked list.
1685 static int jar_gather_signers
1686 (JAR
*jar
, JAR_Signer
*signer
, SEC_PKCS7ContentInfo
*cinfo
)
1690 CERTCertificate
*cert
;
1691 CERTCertDBHandle
*certdb
;
1693 SEC_PKCS7SignedData
*sdp
;
1694 SEC_PKCS7SignerInfo
**pksigners
, *pksigner
;
1696 sdp
= cinfo
->content
.signedData
;
1701 pksigners
= sdp
->signerInfos
;
1703 /* permit exactly one signer */
1705 if (pksigners
== NULL
|| pksigners
[0] == NULL
|| pksigners
[1] != NULL
)
1708 pksigner
= *pksigners
;
1709 cert
= pksigner
->cert
;
1714 certdb
= JAR_open_database();
1717 return JAR_ERR_GENERAL
;
1719 result
= jar_add_cert (jar
, signer
, jarTypeSign
, cert
);
1721 JAR_close_database (certdb
);
1727 * j a r _ o p e n _ d a t a b a s e
1729 * Open the certificate database,
1730 * for use by JAR functions.
1734 CERTCertDBHandle
*JAR_open_database (void)
1736 CERTCertDBHandle
*certdb
;
1738 certdb
= CERT_GetDefaultCertDB();
1744 * j a r _ c l o s e _ d a t a b a s e
1746 * Close the certificate database.
1747 * For use by JAR functions.
1751 int JAR_close_database (CERTCertDBHandle
*certdb
)
1757 * j a r _ g e t _ c e r t i f i c a t e
1759 * Return the certificate referenced
1760 * by a given fingerprint, or NULL if not found.
1761 * Error code is returned in result.
1765 static CERTCertificate
*jar_get_certificate
1766 (JAR
*jar
, long keylen
, void *key
, int *result
)
1771 JAR_Cert
*fing
= NULL
;
1778 cert
= JAR_fetch_cert (keylen
, key
);
1779 *result
= (cert
== NULL
) ? JAR_ERR_GENERAL
: 0;
1780 return (CERTCertificate
*) cert
;
1783 ctx
= JAR_find (jar
, NULL
, jarTypeSign
);
1785 while (JAR_find_next (ctx
, &it
) >= 0)
1787 fing
= (JAR_Cert
*) it
->data
;
1789 if (keylen
!= fing
->length
)
1792 PORT_Assert( keylen
< 0xFFFF );
1793 if (!PORT_Memcmp (fing
->key
, key
, keylen
))
1804 *result
= JAR_ERR_GENERAL
;
1808 PORT_Assert(fing
!= NULL
);
1814 * j a r _ s i g n a l
1816 * Nonfatal errors come here to callback Java.
1820 static int jar_signal
1821 (int status
, JAR
*jar
, const char *metafile
, char *pathname
)
1825 errstring
= JAR_get_error (status
);
1829 (*jar
->signal
) (status
, jar
, metafile
, pathname
, errstring
);
1837 * j a r _ a p p e n d
1839 * Tack on an element to one of a JAR's linked
1840 * lists, with rudimentary error handling.
1844 int jar_append (ZZList
*list
, int type
,
1845 char *pathname
, void *data
, size_t size
)
1850 it
= (JAR_Item
*)PORT_ZAlloc (sizeof (JAR_Item
));
1857 it
->pathname
= PORT_Strdup (pathname
);
1858 if (it
->pathname
== NULL
)
1862 it
->type
= (jarType
)type
;
1863 it
->data
= (unsigned char *) data
;
1866 entity
= ZZ_NewLink (it
);
1870 ZZ_AppendLink (list
, entity
);
1878 if (it
->pathname
) PORT_Free (it
->pathname
);
1882 return JAR_ERR_MEMORY
;