1 /*
2  * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */

25
26 package java.util.jar;
27
28 import java.io.*;
29 import java.net.URL;
30 import java.util.*;
31 import java.security.*;
32 import java.security.cert.CertificateException;
33 import java.util.zip.ZipEntry;
34
35 import jdk.internal.util.jar.JarIndex;
36 import sun.security.util.ManifestDigester;
37 import sun.security.util.ManifestEntryVerifier;
38 import sun.security.util.SignatureFileVerifier;
39 import sun.security.util.Debug;
40
41 /**
42  *
43  * @author      Roland Schemers
44  */

45 class JarVerifier {
46
47     /* Are we debugging ? */
48     static final Debug debug = Debug.getInstance("jar");
49
50     /* a table mapping names to code signers, for jar entries that have
51        had their actual hashes verified */

52     private Hashtable<String, CodeSigner[]> verifiedSigners;
53
54     /* a table mapping names to code signers, for jar entries that have
55        passed the .SF/.DSA/.EC -> MANIFEST check */

56     private Hashtable<String, CodeSigner[]> sigFileSigners;
57
58     /* a hash table to hold .SF bytes */
59     private Hashtable<String, byte[]> sigFileData;
60
61     /** "queue" of pending PKCS7 blocks that we couldn't parse
62      *  until we parsed the .SF file */

63     private ArrayList<SignatureFileVerifier> pendingBlocks;
64
65     /* cache of CodeSigner objects */
66     private ArrayList<CodeSigner[]> signerCache;
67
68     /* Are we parsing a block? */
69     private boolean parsingBlockOrSF = false;
70
71     /* Are we done parsing META-INF entries? */
72     private boolean parsingMeta = true;
73
74     /* Are there are files to verify? */
75     private boolean anyToVerify = true;
76
77     /* The output stream to use when keeping track of files we are interested
78        in */

79     private ByteArrayOutputStream baos;
80
81     /** The ManifestDigester object */
82     private volatile ManifestDigester manDig;
83
84     /** the bytes for the manDig object */
85     byte manifestRawBytes[] = null;
86
87     /** controls eager signature validation */
88     boolean eagerValidation;
89
90     /** makes code source singleton instances unique to us */
91     private Object csdomain = new Object();
92
93     /** collect -DIGEST-MANIFEST values for blacklist */
94     private List<Object> manifestDigests;
95
96     public JarVerifier(byte rawBytes[]) {
97         manifestRawBytes = rawBytes;
98         sigFileSigners = new Hashtable<>();
99         verifiedSigners = new Hashtable<>();
100         sigFileData = new Hashtable<>(11);
101         pendingBlocks = new ArrayList<>();
102         baos = new ByteArrayOutputStream();
103         manifestDigests = new ArrayList<>();
104     }
105
106     /**
107      * This method scans to see which entry we're parsing and
108      * keeps various state information depending on what type of
109      * file is being parsed.
110      */

111     public void beginEntry(JarEntry je, ManifestEntryVerifier mev)
112         throws IOException
113     {
114         if (je == null)
115             return;
116
117         if (debug != null) {
118             debug.println("beginEntry "+je.getName());
119         }
120
121         String name = je.getName();
122
123         /*
124          * Assumptions:
125          * 1. The manifest should be the first entry in the META-INF directory.
126          * 2. The .SF/.DSA/.EC files follow the manifest, before any normal entries
127          * 3. Any of the following will throw a SecurityException:
128          *    a. digest mismatch between a manifest section and
129          *       the SF section.
130          *    b. digest mismatch between the actual jar entry and the manifest
131          */

132
133         if (parsingMeta) {
134             String uname = name.toUpperCase(Locale.ENGLISH);
135             if ((uname.startsWith("META-INF/") ||
136                  uname.startsWith("/META-INF/"))) {
137
138                 if (je.isDirectory()) {
139                     mev.setEntry(null, je);
140                     return;
141                 }
142
143                 if (uname.equals(JarFile.MANIFEST_NAME) ||
144                         uname.equals(JarIndex.INDEX_NAME)) {
145                     return;
146                 }
147
148                 if (SignatureFileVerifier.isBlockOrSF(uname)) {
149                     /* We parse only DSA, RSA or EC PKCS7 blocks. */
150                     parsingBlockOrSF = true;
151                     baos.reset();
152                     mev.setEntry(null, je);
153                     return;
154                 }
155
156                 // If a META-INF entry is not MF or block or SF, they should
157                 // be normal entries. According to 2 above, no more block or
158                 // SF will appear. Let's doneWithMeta.
159             }
160         }
161
162         if (parsingMeta) {
163             doneWithMeta();
164         }
165
166         if (je.isDirectory()) {
167             mev.setEntry(null, je);
168             return;
169         }
170
171         // be liberal in what you accept. If the name starts with ./, remove
172         // it as we internally canonicalize it with out the ./.
173         if (name.startsWith("./"))
174             name = name.substring(2);
175
176         // be liberal in what you accept. If the name starts with /, remove
177         // it as we internally canonicalize it with out the /.
178         if (name.startsWith("/"))
179             name = name.substring(1);
180
181         // only set the jev object for entries that have a signature
182         // (either verified or not)
183         if (!name.equals(JarFile.MANIFEST_NAME)) {
184             if (sigFileSigners.get(name) != null ||
185                     verifiedSigners.get(name) != null) {
186                 mev.setEntry(name, je);
187                 return;
188             }
189         }
190
191         // don't compute the digest for this entry
192         mev.setEntry(null, je);
193
194         return;
195     }
196
197     /**
198      * update a single byte.
199      */

200
201     public void update(int b, ManifestEntryVerifier mev)
202         throws IOException
203     {
204         if (b != -1) {
205             if (parsingBlockOrSF) {
206                 baos.write(b);
207             } else {
208                 mev.update((byte)b);
209             }
210         } else {
211             processEntry(mev);
212         }
213     }
214
215     /**
216      * update an array of bytes.
217      */

218
219     public void update(int n, byte[] b, int off, int len,
220                        ManifestEntryVerifier mev)
221         throws IOException
222     {
223         if (n != -1) {
224             if (parsingBlockOrSF) {
225                 baos.write(b, off, n);
226             } else {
227                 mev.update(b, off, n);
228             }
229         } else {
230             processEntry(mev);
231         }
232     }
233
234     /**
235      * called when we reach the end of entry in one of the read() methods.
236      */

237     private void processEntry(ManifestEntryVerifier mev)
238         throws IOException
239     {
240         if (!parsingBlockOrSF) {
241             JarEntry je = mev.getEntry();
242             if ((je != null) && (je.signers == null)) {
243                 je.signers = mev.verify(verifiedSigners, sigFileSigners);
244                 je.certs = mapSignersToCertArray(je.signers);
245             }
246         } else {
247
248             try {
249                 parsingBlockOrSF = false;
250
251                 if (debug != null) {
252                     debug.println("processEntry: processing block");
253                 }
254
255                 String uname = mev.getEntry().getName()
256                                              .toUpperCase(Locale.ENGLISH);
257
258                 if (uname.endsWith(".SF")) {
259                     String key = uname.substring(0, uname.length()-3);
260                     byte bytes[] = baos.toByteArray();
261                     // add to sigFileData in case future blocks need it
262                     sigFileData.put(key, bytes);
263                     // check pending blocks, we can now process
264                     // anyone waiting for this .SF file
265                     for (SignatureFileVerifier sfv : pendingBlocks) {
266                         if (sfv.needSignatureFile(key)) {
267                             if (debug != null) {
268                                 debug.println(
269                                  "processEntry: processing pending block");
270                             }
271
272                             sfv.setSignatureFile(bytes);
273                             sfv.process(sigFileSigners, manifestDigests);
274                         }
275                     }
276                     return;
277                 }
278
279                 // now we are parsing a signature block file
280
281                 String key = uname.substring(0, uname.lastIndexOf('.'));
282
283                 if (signerCache == null)
284                     signerCache = new ArrayList<>();
285
286                 if (manDig == null) {
287                     synchronized(manifestRawBytes) {
288                         if (manDig == null) {
289                             manDig = new ManifestDigester(manifestRawBytes);
290                             manifestRawBytes = null;
291                         }
292                     }
293                 }
294
295                 SignatureFileVerifier sfv =
296                   new SignatureFileVerifier(signerCache,
297                                             manDig, uname, baos.toByteArray());
298
299                 if (sfv.needSignatureFileBytes()) {
300                     // see if we have already parsed an external .SF file
301                     byte[] bytes = sigFileData.get(key);
302
303                     if (bytes == null) {
304                         // put this block on queue for later processing
305                         // since we don't have the .SF bytes yet
306                         // (uname, block);
307                         if (debug != null) {
308                             debug.println("adding pending block");
309                         }
310                         pendingBlocks.add(sfv);
311                         return;
312                     } else {
313                         sfv.setSignatureFile(bytes);
314                     }
315                 }
316                 sfv.process(sigFileSigners, manifestDigests);
317
318             } catch (IOException | CertificateException |
319                     NoSuchAlgorithmException | SignatureException e) {
320                 if (debug != null) debug.println("processEntry caught: "+e);
321                 // ignore and treat as unsigned
322             }
323         }
324     }
325
326     /**
327      * Return an array of java.security.cert.Certificate objects for
328      * the given file in the jar.
329      * @deprecated
330      */

331     @Deprecated
332     public java.security.cert.Certificate[] getCerts(String name)
333     {
334         return mapSignersToCertArray(getCodeSigners(name));
335     }
336
337     public java.security.cert.Certificate[] getCerts(JarFile jar, JarEntry entry)
338     {
339         return mapSignersToCertArray(getCodeSigners(jar, entry));
340     }
341
342     /**
343      * return an array of CodeSigner objects for
344      * the given file in the jar. this array is not cloned.
345      *
346      */

347     public CodeSigner[] getCodeSigners(String name)
348     {
349         return verifiedSigners.get(name);
350     }
351
352     public CodeSigner[] getCodeSigners(JarFile jar, JarEntry entry)
353     {
354         String name = entry.getName();
355         if (eagerValidation && sigFileSigners.get(name) != null) {
356             /*
357              * Force a read of the entry data to generate the
358              * verification hash.
359              */

360             try {
361                 InputStream s = jar.getInputStream(entry);
362                 byte[] buffer = new byte[1024];
363                 int n = buffer.length;
364                 while (n != -1) {
365                     n = s.read(buffer, 0, buffer.length);
366                 }
367                 s.close();
368             } catch (IOException e) {
369             }
370         }
371         return getCodeSigners(name);
372     }
373
374     /*
375      * Convert an array of signers into an array of concatenated certificate
376      * arrays.
377      */

378     private static java.security.cert.Certificate[] mapSignersToCertArray(
379         CodeSigner[] signers) {
380
381         if (signers != null) {
382             ArrayList<java.security.cert.Certificate> certChains = new ArrayList<>();
383             for (CodeSigner signer : signers) {
384                 certChains.addAll(
385                     signer.getSignerCertPath().getCertificates());
386             }
387
388             // Convert into a Certificate[]
389             return certChains.toArray(
390                     new java.security.cert.Certificate[certChains.size()]);
391         }
392         return null;
393     }
394
395     /**
396      * returns true if there no files to verify.
397      * should only be called after all the META-INF entries
398      * have been processed.
399      */

400     boolean nothingToVerify()
401     {
402         return (anyToVerify == false);
403     }
404
405     /**
406      * called to let us know we have processed all the
407      * META-INF entries, and if we re-read one of them, don't
408      * re-process it. Also gets rid of any data structures
409      * we needed when parsing META-INF entries.
410      */

411     void doneWithMeta()
412     {
413         parsingMeta = false;
414         anyToVerify = !sigFileSigners.isEmpty();
415         baos = null;
416         sigFileData = null;
417         pendingBlocks = null;
418         signerCache = null;
419         manDig = null;
420         // MANIFEST.MF is always treated as signed and verified,
421         // move its signers from sigFileSigners to verifiedSigners.
422         if (sigFileSigners.containsKey(JarFile.MANIFEST_NAME)) {
423             CodeSigner[] codeSigners = sigFileSigners.remove(JarFile.MANIFEST_NAME);
424             verifiedSigners.put(JarFile.MANIFEST_NAME, codeSigners);
425         }
426     }
427
428     static class VerifierStream extends java.io.InputStream {
429
430         private InputStream is;
431         private JarVerifier jv;
432         private ManifestEntryVerifier mev;
433         private long numLeft;
434
435         VerifierStream(Manifest man,
436                        JarEntry je,
437                        InputStream is,
438                        JarVerifier jv) throws IOException
439         {
440             this.is = is;
441             this.jv = jv;
442             this.mev = new ManifestEntryVerifier(man);
443             this.jv.beginEntry(je, mev);
444             this.numLeft = je.getSize();
445             if (this.numLeft == 0)
446                 this.jv.update(-1, this.mev);
447         }
448
449         public int read() throws IOException
450         {
451             if (numLeft > 0) {
452                 int b = is.read();
453                 jv.update(b, mev);
454                 numLeft--;
455                 if (numLeft == 0)
456                     jv.update(-1, mev);
457                 return b;
458             } else {
459                 return -1;
460             }
461         }
462
463         public int read(byte b[], int off, int len) throws IOException {
464             if ((numLeft > 0) && (numLeft < len)) {
465                 len = (int)numLeft;
466             }
467
468             if (numLeft > 0) {
469                 int n = is.read(b, off, len);
470                 jv.update(n, b, off, len, mev);
471                 numLeft -= n;
472                 if (numLeft == 0)
473                     jv.update(-1, b, off, len, mev);
474                 return n;
475             } else {
476                 return -1;
477             }
478         }
479
480         public void close()
481             throws IOException
482         {
483             if (is != null)
484                 is.close();
485             is = null;
486             mev = null;
487             jv = null;
488         }
489
490         public int available() throws IOException {
491             return is.available();
492         }
493
494     }
495
496     // Extended JavaUtilJarAccess CodeSource API Support
497
498     private Map<URL, Map<CodeSigner[], CodeSource>> urlToCodeSourceMap = new HashMap<>();
499     private Map<CodeSigner[], CodeSource> signerToCodeSource = new HashMap<>();
500     private URL lastURL;
501     private Map<CodeSigner[], CodeSource> lastURLMap;
502
503     /*
504      * Create a unique mapping from codeSigner cache entries to CodeSource.
505      * In theory, multiple URLs origins could map to a single locally cached
506      * and shared JAR file although in practice there will be a single URL in use.
507      */

508     private synchronized CodeSource mapSignersToCodeSource(URL url, CodeSigner[] signers) {
509         Map<CodeSigner[], CodeSource> map;
510         if (url == lastURL) {
511             map = lastURLMap;
512         } else {
513             map = urlToCodeSourceMap.get(url);
514             if (map == null) {
515                 map = new HashMap<>();
516                 urlToCodeSourceMap.put(url, map);
517             }
518             lastURLMap = map;
519             lastURL = url;
520         }
521         CodeSource cs = map.get(signers);
522         if (cs == null) {
523             cs = new VerifierCodeSource(csdomain, url, signers);
524             signerToCodeSource.put(signers, cs);
525         }
526         return cs;
527     }
528
529     private CodeSource[] mapSignersToCodeSources(URL url, List<CodeSigner[]> signers, boolean unsigned) {
530         List<CodeSource> sources = new ArrayList<>();
531
532         for (CodeSigner[] signer : signers) {
533             sources.add(mapSignersToCodeSource(url, signer));
534         }
535         if (unsigned) {
536             sources.add(mapSignersToCodeSource(url, null));
537         }
538         return sources.toArray(new CodeSource[sources.size()]);
539     }
540     private CodeSigner[] emptySigner = new CodeSigner[0];
541
542     /*
543      * Match CodeSource to a CodeSigner[] in the signer cache.
544      */

545     private CodeSigner[] findMatchingSigners(CodeSource cs) {
546         if (cs instanceof VerifierCodeSource) {
547             VerifierCodeSource vcs = (VerifierCodeSource) cs;
548             if (vcs.isSameDomain(csdomain)) {
549                 return ((VerifierCodeSource) cs).getPrivateSigners();
550             }
551         }
552
553         /*
554          * In practice signers should always be optimized above
555          * but this handles a CodeSource of any type, just in case.
556          */

557         CodeSource[] sources = mapSignersToCodeSources(cs.getLocation(), getJarCodeSigners(), true);
558         List<CodeSource> sourceList = new ArrayList<>();
559         for (CodeSource source : sources) {
560             sourceList.add(source);
561         }
562         int j = sourceList.indexOf(cs);
563         if (j != -1) {
564             CodeSigner[] match;
565             match = ((VerifierCodeSource) sourceList.get(j)).getPrivateSigners();
566             if (match == null) {
567                 match = emptySigner;
568             }
569             return match;
570         }
571         return null;
572     }
573
574     /*
575      * Instances of this class hold uncopied references to internal
576      * signing data that can be compared by object reference identity.
577      */

578     private static class VerifierCodeSource extends CodeSource {
579         private static final long serialVersionUID = -9047366145967768825L;
580
581         URL vlocation;
582         CodeSigner[] vsigners;
583         java.security.cert.Certificate[] vcerts;
584         Object csdomain;
585
586         VerifierCodeSource(Object csdomain, URL location, CodeSigner[] signers) {
587             super(location, signers);
588             this.csdomain = csdomain;
589             vlocation = location;
590             vsigners = signers; // from signerCache
591         }
592
593         VerifierCodeSource(Object csdomain, URL location, java.security.cert.Certificate[] certs) {
594             super(location, certs);
595             this.csdomain = csdomain;
596             vlocation = location;
597             vcerts = certs; // from signerCache
598         }
599
600         /*
601          * All VerifierCodeSource instances are constructed based on
602          * singleton signerCache or signerCacheCert entries for each unique signer.
603          * No CodeSigner<->Certificate[] conversion is required.
604          * We use these assumptions to optimize equality comparisons.
605          */

606         public boolean equals(Object obj) {
607             if (obj == this) {
608                 return true;
609             }
610             if (obj instanceof VerifierCodeSource) {
611                 VerifierCodeSource that = (VerifierCodeSource) obj;
612
613                 /*
614                  * Only compare against other per-signer singletons constructed
615                  * on behalf of the same JarFile instance. Otherwise, compare
616                  * things the slower way.
617                  */

618                 if (isSameDomain(that.csdomain)) {
619                     if (that.vsigners != this.vsigners
620                             || that.vcerts != this.vcerts) {
621                         return false;
622                     }
623                     if (that.vlocation != null) {
624                         return that.vlocation.equals(this.vlocation);
625                     } else if (this.vlocation != null) {
626                         return this.vlocation.equals(that.vlocation);
627                     } else { // both null
628                         return true;
629                     }
630                 }
631             }
632             return super.equals(obj);
633         }
634
635         boolean isSameDomain(Object csdomain) {
636             return this.csdomain == csdomain;
637         }
638
639         private CodeSigner[] getPrivateSigners() {
640             return vsigners;
641         }
642
643         private java.security.cert.Certificate[] getPrivateCertificates() {
644             return vcerts;
645         }
646     }
647     private Map<String, CodeSigner[]> signerMap;
648
649     private synchronized Map<String, CodeSigner[]> signerMap() {
650         if (signerMap == null) {
651             /*
652              * Snapshot signer state so it doesn't change on us. We care
653              * only about the asserted signatures. Verification of
654              * signature validity happens via the JarEntry apis.
655              */

656             signerMap = new HashMap<>(verifiedSigners.size() + sigFileSigners.size());
657             signerMap.putAll(verifiedSigners);
658             signerMap.putAll(sigFileSigners);
659         }
660         return signerMap;
661     }
662
663     public synchronized Enumeration<String> entryNames(JarFile jar, final CodeSource[] cs) {
664         final Map<String, CodeSigner[]> map = signerMap();
665         final Iterator<Map.Entry<String, CodeSigner[]>> itor = map.entrySet().iterator();
666         boolean matchUnsigned = false;
667
668         /*
669          * Grab a single copy of the CodeSigner arrays. Check
670          * to see if we can optimize CodeSigner equality test.
671          */

672         List<CodeSigner[]> req = new ArrayList<>(cs.length);
673         for (CodeSource c : cs) {
674             CodeSigner[] match = findMatchingSigners(c);
675             if (match != null) {
676                 if (match.length > 0) {
677                     req.add(match);
678                 } else {
679                     matchUnsigned = true;
680                 }
681             } else {
682                 matchUnsigned = true;
683             }
684         }
685
686         final List<CodeSigner[]> signersReq = req;
687         final Enumeration<String> enum2 = matchUnsigned ? unsignedEntryNames(jar) : Collections.emptyEnumeration();
688
689         return new Enumeration<>() {
690
691             String name;
692
693             public boolean hasMoreElements() {
694                 if (name != null) {
695                     return true;
696                 }
697
698                 while (itor.hasNext()) {
699                     Map.Entry<String, CodeSigner[]> e = itor.next();
700                     if (signersReq.contains(e.getValue())) {
701                         name = e.getKey();
702                         return true;
703                     }
704                 }
705                 while (enum2.hasMoreElements()) {
706                     name = enum2.nextElement();
707                     return true;
708                 }
709                 return false;
710             }
711
712             public String nextElement() {
713                 if (hasMoreElements()) {
714                     String value = name;
715                     name = null;
716                     return value;
717                 }
718                 throw new NoSuchElementException();
719             }
720         };
721     }
722
723     /*
724      * Like entries() but screens out internal JAR mechanism entries
725      * and includes signed entries with no ZIP data.
726      */

727     public Enumeration<JarEntry> entries2(final JarFile jar, Enumeration<JarEntry> e) {
728         final Map<String, CodeSigner[]> map = new HashMap<>();
729         map.putAll(signerMap());
730         final Enumeration<JarEntry> enum_ = e;
731         return new Enumeration<>() {
732
733             Enumeration<String> signers = null;
734             JarEntry entry;
735
736             public boolean hasMoreElements() {
737                 if (entry != null) {
738                     return true;
739                 }
740                 while (enum_.hasMoreElements()) {
741                     JarEntry je = enum_.nextElement();
742                     if (JarVerifier.isSigningRelated(je.getName())) {
743                         continue;
744                     }
745                     entry = jar.newEntry(je);
746                     return true;
747                 }
748                 if (signers == null) {
749                     signers = Collections.enumeration(map.keySet());
750                 }
751                 while (signers.hasMoreElements()) {
752                     String name = signers.nextElement();
753                     entry = jar.newEntry(name);
754                     return true;
755                 }
756
757                 // Any map entries left?
758                 return false;
759             }
760
761             public JarEntry nextElement() {
762                 if (hasMoreElements()) {
763                     JarEntry je = entry;
764                     map.remove(je.getName());
765                     entry = null;
766                     return je;
767                 }
768                 throw new NoSuchElementException();
769             }
770         };
771     }
772
773     // true if file is part of the signature mechanism itself
774     static boolean isSigningRelated(String name) {
775         return SignatureFileVerifier.isSigningRelated(name);
776     }
777
778     private Enumeration<String> unsignedEntryNames(JarFile jar) {
779         final Map<String, CodeSigner[]> map = signerMap();
780         final Enumeration<JarEntry> entries = jar.entries();
781         return new Enumeration<>() {
782
783             String name;
784
785             /*
786              * Grab entries from ZIP directory but screen out
787              * metadata.
788              */

789             public boolean hasMoreElements() {
790                 if (name != null) {
791                     return true;
792                 }
793                 while (entries.hasMoreElements()) {
794                     String value;
795                     ZipEntry e = entries.nextElement();
796                     value = e.getName();
797                     if (e.isDirectory() || isSigningRelated(value)) {
798                         continue;
799                     }
800                     if (map.get(value) == null) {
801                         name = value;
802                         return true;
803                     }
804                 }
805                 return false;
806             }
807
808             public String nextElement() {
809                 if (hasMoreElements()) {
810                     String value = name;
811                     name = null;
812                     return value;
813                 }
814                 throw new NoSuchElementException();
815             }
816         };
817     }
818     private List<CodeSigner[]> jarCodeSigners;
819
820     private synchronized List<CodeSigner[]> getJarCodeSigners() {
821         CodeSigner[] signers;
822         if (jarCodeSigners == null) {
823             HashSet<CodeSigner[]> set = new HashSet<>();
824             set.addAll(signerMap().values());
825             jarCodeSigners = new ArrayList<>();
826             jarCodeSigners.addAll(set);
827         }
828         return jarCodeSigners;
829     }
830
831     public synchronized CodeSource[] getCodeSources(JarFile jar, URL url) {
832         boolean hasUnsigned = unsignedEntryNames(jar).hasMoreElements();
833
834         return mapSignersToCodeSources(url, getJarCodeSigners(), hasUnsigned);
835     }
836
837     public CodeSource getCodeSource(URL url, String name) {
838         CodeSigner[] signers;
839
840         signers = signerMap().get(name);
841         return mapSignersToCodeSource(url, signers);
842     }
843
844     public CodeSource getCodeSource(URL url, JarFile jar, JarEntry je) {
845         CodeSigner[] signers;
846
847         return mapSignersToCodeSource(url, getCodeSigners(jar, je));
848     }
849
850     public void setEagerValidation(boolean eager) {
851         eagerValidation = eager;
852     }
853
854     public synchronized List<Object> getManifestDigests() {
855         return Collections.unmodifiableList(manifestDigests);
856     }
857
858     static CodeSource getUnsignedCS(URL url) {
859         return new VerifierCodeSource(null, url, (java.security.cert.Certificate[]) null);
860     }
861
862     /**
863      * Returns whether the name is trusted. Used by
864      * {@link Manifest#getTrustedAttributes(String)}.
865      */

866     boolean isTrustedManifestEntry(String name) {
867         // How many signers? MANIFEST.MF is always verified
868         CodeSigner[] forMan = verifiedSigners.get(JarFile.MANIFEST_NAME);
869         if (forMan == null) {
870             return true;
871         }
872         // Check sigFileSigners first, because we are mainly dealing with
873         // non-file entries which will stay in sigFileSigners forever.
874         CodeSigner[] forName = sigFileSigners.get(name);
875         if (forName == null) {
876             forName = verifiedSigners.get(name);
877         }
878         // Returns trusted if all signers sign the entry
879         return forName != null && forName.length == forMan.length;
880     }
881 }
882