1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This code is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 2 only, as
8  * published by the Free Software Foundation.  Oracle designates this
9  * particular file as subject to the "Classpath" exception as provided
10  * by Oracle in the LICENSE file that accompanied this code.
11  *
12  * This code is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15  * version 2 for more details (a copy is included in the LICENSE file that
16  * accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License version
19  * 2 along with this work; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21  *
22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23  * or visit www.oracle.com if you need additional information or have any
24  * questions.
25  */
26 
27 package java.util.jar;
28 
29 import java.io.*;
30 import java.net.URL;
31 import java.util.*;
32 import java.security.*;
33 import java.security.cert.CertificateException;
34 import java.util.zip.ZipEntry;
35 
36 import sun.misc.JarIndex;
37 import sun.security.util.ManifestDigester;
38 import sun.security.util.ManifestEntryVerifier;
39 import sun.security.util.SignatureFileVerifier;
40 import sun.security.util.Debug;
41 
42 /**
43  *
44  * @author      Roland Schemers
45  */
46 class JarVerifier {
47 
48     /* Are we debugging ? */
49     static final Debug debug = Debug.getInstance("jar");
50 
51     /* a table mapping names to code signers, for jar entries that have
52        had their actual hashes verified */
53     private Hashtable<String, CodeSigner[]> verifiedSigners;
54 
55     /* a table mapping names to code signers, for jar entries that have
56        passed the .SF/.DSA/.EC -> MANIFEST check */
57     private Hashtable<String, CodeSigner[]> sigFileSigners;
58 
59     /* a hash table to hold .SF bytes */
60     private Hashtable<String, byte[]> sigFileData;
61 
62     /** "queue" of pending PKCS7 blocks that we couldn't parse
63      *  until we parsed the .SF file */
64     private ArrayList<SignatureFileVerifier> pendingBlocks;
65 
66     /* cache of CodeSigner objects */
67     private ArrayList<CodeSigner[]> signerCache;
68 
69     /* Are we parsing a block? */
70     private boolean parsingBlockOrSF = false;
71 
72     /* Are we done parsing META-INF entries? */
73     private boolean parsingMeta = true;
74 
75     /* Are there are files to verify? */
76     private boolean anyToVerify = true;
77 
78     /* The output stream to use when keeping track of files we are interested
79        in */
80     private ByteArrayOutputStream baos;
81 
82     /** The ManifestDigester object */
83     private volatile ManifestDigester manDig;
84 
85     /** the bytes for the manDig object */
86     byte manifestRawBytes[] = null;
87 
88     /** controls eager signature validation */
89     boolean eagerValidation;
90 
91     /** makes code source singleton instances unique to us */
92     private Object csdomain = new Object();
93 
94     /** collect -DIGEST-MANIFEST values for blacklist */
95     private List<Object> manifestDigests;
96 
JarVerifier(byte rawBytes[])97     public JarVerifier(byte rawBytes[]) {
98         manifestRawBytes = rawBytes;
99         sigFileSigners = new Hashtable<>();
100         verifiedSigners = new Hashtable<>();
101         sigFileData = new Hashtable<>(11);
102         pendingBlocks = new ArrayList<>();
103         baos = new ByteArrayOutputStream();
104         manifestDigests = new ArrayList<>();
105     }
106 
107     /**
108      * This method scans to see which entry we're parsing and
109      * keeps various state information depending on what type of
110      * file is being parsed.
111      */
beginEntry(JarEntry je, ManifestEntryVerifier mev)112     public void beginEntry(JarEntry je, ManifestEntryVerifier mev)
113         throws IOException
114     {
115         if (je == null)
116             return;
117 
118         if (debug != null) {
119             debug.println("beginEntry "+je.getName());
120         }
121 
122         String name = je.getName();
123 
124         /*
125          * Assumptions:
126          * 1. The manifest should be the first entry in the META-INF directory.
127          * 2. The .SF/.DSA/.EC files follow the manifest, before any normal entries
128          * 3. Any of the following will throw a SecurityException:
129          *    a. digest mismatch between a manifest section and
130          *       the SF section.
131          *    b. digest mismatch between the actual jar entry and the manifest
132          */
133 
134         if (parsingMeta) {
135             String uname = name.toUpperCase(Locale.ENGLISH);
136             if ((uname.startsWith("META-INF/") ||
137                  uname.startsWith("/META-INF/"))) {
138 
139                 if (je.isDirectory()) {
140                     mev.setEntry(null, je);
141                     return;
142                 }
143 
144                 if (uname.equals(JarFile.MANIFEST_NAME) ||
145                         uname.equals(JarIndex.INDEX_NAME)) {
146                     return;
147                 }
148 
149                 if (SignatureFileVerifier.isBlockOrSF(uname)) {
150                     /* We parse only DSA, RSA or EC PKCS7 blocks. */
151                     parsingBlockOrSF = true;
152                     baos.reset();
153                     mev.setEntry(null, je);
154                     return;
155                 }
156 
157                 // If a META-INF entry is not MF or block or SF, they should
158                 // be normal entries. According to 2 above, no more block or
159                 // SF will appear. Let's doneWithMeta.
160             }
161         }
162 
163         if (parsingMeta) {
164             doneWithMeta();
165         }
166 
167         if (je.isDirectory()) {
168             mev.setEntry(null, je);
169             return;
170         }
171 
172         // be liberal in what you accept. If the name starts with ./, remove
173         // it as we internally canonicalize it with out the ./.
174         if (name.startsWith("./"))
175             name = name.substring(2);
176 
177         // be liberal in what you accept. If the name starts with /, remove
178         // it as we internally canonicalize it with out the /.
179         if (name.startsWith("/"))
180             name = name.substring(1);
181 
182         // only set the jev object for entries that have a signature
183         // (either verified or not)
184         if (sigFileSigners.get(name) != null ||
185                 verifiedSigners.get(name) != null) {
186             mev.setEntry(name, je);
187             return;
188         }
189 
190         // don't compute the digest for this entry
191         mev.setEntry(null, je);
192 
193         return;
194     }
195 
196     /**
197      * update a single byte.
198      */
199 
update(int b, ManifestEntryVerifier mev)200     public void update(int b, ManifestEntryVerifier mev)
201         throws IOException
202     {
203         if (b != -1) {
204             if (parsingBlockOrSF) {
205                 baos.write(b);
206             } else {
207                 mev.update((byte)b);
208             }
209         } else {
210             processEntry(mev);
211         }
212     }
213 
214     /**
215      * update an array of bytes.
216      */
217 
update(int n, byte[] b, int off, int len, ManifestEntryVerifier mev)218     public void update(int n, byte[] b, int off, int len,
219                        ManifestEntryVerifier mev)
220         throws IOException
221     {
222         if (n != -1) {
223             if (parsingBlockOrSF) {
224                 baos.write(b, off, n);
225             } else {
226                 mev.update(b, off, n);
227             }
228         } else {
229             processEntry(mev);
230         }
231     }
232 
233     /**
234      * called when we reach the end of entry in one of the read() methods.
235      */
processEntry(ManifestEntryVerifier mev)236     private void processEntry(ManifestEntryVerifier mev)
237         throws IOException
238     {
239         if (!parsingBlockOrSF) {
240             JarEntry je = mev.getEntry();
241             if ((je != null) && (je.signers == null)) {
242                 je.signers = mev.verify(verifiedSigners, sigFileSigners);
243                 je.certs = mapSignersToCertArray(je.signers);
244             }
245         } else {
246 
247             try {
248                 parsingBlockOrSF = false;
249 
250                 if (debug != null) {
251                     debug.println("processEntry: processing block");
252                 }
253 
254                 String uname = mev.getEntry().getName()
255                                              .toUpperCase(Locale.ENGLISH);
256 
257                 if (uname.endsWith(".SF")) {
258                     String key = uname.substring(0, uname.length()-3);
259                     byte bytes[] = baos.toByteArray();
260                     // add to sigFileData in case future blocks need it
261                     sigFileData.put(key, bytes);
262                     // check pending blocks, we can now process
263                     // anyone waiting for this .SF file
264                     Iterator<SignatureFileVerifier> it = pendingBlocks.iterator();
265                     while (it.hasNext()) {
266                         SignatureFileVerifier sfv = it.next();
267                         if (sfv.needSignatureFile(key)) {
268                             if (debug != null) {
269                                 debug.println(
270                                  "processEntry: processing pending block");
271                             }
272 
273                             sfv.setSignatureFile(bytes);
274                             sfv.process(sigFileSigners, manifestDigests);
275                         }
276                     }
277                     return;
278                 }
279 
280                 // now we are parsing a signature block file
281 
282                 String key = uname.substring(0, uname.lastIndexOf("."));
283 
284                 if (signerCache == null)
285                     signerCache = new ArrayList<>();
286 
287                 if (manDig == null) {
288                     synchronized(manifestRawBytes) {
289                         if (manDig == null) {
290                             manDig = new ManifestDigester(manifestRawBytes);
291                             manifestRawBytes = null;
292                         }
293                     }
294                 }
295 
296                 SignatureFileVerifier sfv =
297                   new SignatureFileVerifier(signerCache,
298                                             manDig, uname, baos.toByteArray());
299 
300                 if (sfv.needSignatureFileBytes()) {
301                     // see if we have already parsed an external .SF file
302                     byte[] bytes = sigFileData.get(key);
303 
304                     if (bytes == null) {
305                         // put this block on queue for later processing
306                         // since we don't have the .SF bytes yet
307                         // (uname, block);
308                         if (debug != null) {
309                             debug.println("adding pending block");
310                         }
311                         pendingBlocks.add(sfv);
312                         return;
313                     } else {
314                         sfv.setSignatureFile(bytes);
315                     }
316                 }
317                 sfv.process(sigFileSigners, manifestDigests);
318 
319             } catch (IOException ioe) {
320                 // e.g. sun.security.pkcs.ParsingException
321                 if (debug != null) debug.println("processEntry caught: "+ioe);
322                 // ignore and treat as unsigned
323             } catch (SignatureException se) {
324                 if (debug != null) debug.println("processEntry caught: "+se);
325                 // ignore and treat as unsigned
326             } catch (NoSuchAlgorithmException nsae) {
327                 if (debug != null) debug.println("processEntry caught: "+nsae);
328                 // ignore and treat as unsigned
329             } catch (CertificateException ce) {
330                 if (debug != null) debug.println("processEntry caught: "+ce);
331                 // ignore and treat as unsigned
332             }
333         }
334     }
335 
336     // Android-changed: @deprecated tag needs a description. http://b/110781661
337     /**
338      * Return an array of java.security.cert.Certificate objects for
339      * the given file in the jar.
340      * @deprecated Deprecated.
341      */
342     @Deprecated
getCerts(String name)343     public java.security.cert.Certificate[] getCerts(String name)
344     {
345         return mapSignersToCertArray(getCodeSigners(name));
346     }
347 
getCerts(JarFile jar, JarEntry entry)348     public java.security.cert.Certificate[] getCerts(JarFile jar, JarEntry entry)
349     {
350         return mapSignersToCertArray(getCodeSigners(jar, entry));
351     }
352 
353     /**
354      * return an array of CodeSigner objects for
355      * the given file in the jar. this array is not cloned.
356      *
357      */
getCodeSigners(String name)358     public CodeSigner[] getCodeSigners(String name)
359     {
360         return verifiedSigners.get(name);
361     }
362 
getCodeSigners(JarFile jar, JarEntry entry)363     public CodeSigner[] getCodeSigners(JarFile jar, JarEntry entry)
364     {
365         String name = entry.getName();
366         if (eagerValidation && sigFileSigners.get(name) != null) {
367             /*
368              * Force a read of the entry data to generate the
369              * verification hash.
370              */
371             try {
372                 InputStream s = jar.getInputStream(entry);
373                 byte[] buffer = new byte[1024];
374                 int n = buffer.length;
375                 while (n != -1) {
376                     n = s.read(buffer, 0, buffer.length);
377                 }
378                 s.close();
379             } catch (IOException e) {
380             }
381         }
382         return getCodeSigners(name);
383     }
384 
385     /*
386      * Convert an array of signers into an array of concatenated certificate
387      * arrays.
388      */
mapSignersToCertArray( CodeSigner[] signers)389     private static java.security.cert.Certificate[] mapSignersToCertArray(
390         CodeSigner[] signers) {
391 
392         if (signers != null) {
393             ArrayList<java.security.cert.Certificate> certChains = new ArrayList<>();
394             for (int i = 0; i < signers.length; i++) {
395                 certChains.addAll(
396                     signers[i].getSignerCertPath().getCertificates());
397             }
398 
399             // Convert into a Certificate[]
400             return certChains.toArray(
401                     new java.security.cert.Certificate[certChains.size()]);
402         }
403         return null;
404     }
405 
406     /**
407      * returns true if there no files to verify.
408      * should only be called after all the META-INF entries
409      * have been processed.
410      */
nothingToVerify()411     boolean nothingToVerify()
412     {
413         return (anyToVerify == false);
414     }
415 
416     /**
417      * called to let us know we have processed all the
418      * META-INF entries, and if we re-read one of them, don't
419      * re-process it. Also gets rid of any data structures
420      * we needed when parsing META-INF entries.
421      */
doneWithMeta()422     void doneWithMeta()
423     {
424         parsingMeta = false;
425         anyToVerify = !sigFileSigners.isEmpty();
426         baos = null;
427         sigFileData = null;
428         pendingBlocks = null;
429         signerCache = null;
430         manDig = null;
431         // MANIFEST.MF is always treated as signed and verified,
432         // move its signers from sigFileSigners to verifiedSigners.
433         if (sigFileSigners.containsKey(JarFile.MANIFEST_NAME)) {
434             CodeSigner[] codeSigners = sigFileSigners.remove(JarFile.MANIFEST_NAME);
435             verifiedSigners.put(JarFile.MANIFEST_NAME, codeSigners);
436         }
437     }
438 
439     static class VerifierStream extends java.io.InputStream {
440 
441         private InputStream is;
442         private JarVerifier jv;
443         private ManifestEntryVerifier mev;
444         private long numLeft;
445 
VerifierStream(Manifest man, JarEntry je, InputStream is, JarVerifier jv)446         VerifierStream(Manifest man,
447                        JarEntry je,
448                        InputStream is,
449                        JarVerifier jv) throws IOException
450         {
451             // BEGIN Android-added: Throw IOE, not NPE, if stream is closed. http://b/110695212
452             // To know that null signals that the stream has been closed, we disallow
453             // it in the constructor. There's no need for anyone to pass null into this
454             // constructor, anyway.
455             if (is == null) {
456                 throw new NullPointerException("is == null");
457             }
458             // END Android-added: Throw IOE, not NPE, if stream is closed. http://b/110695212
459             this.is = is;
460             this.jv = jv;
461             this.mev = new ManifestEntryVerifier(man);
462             this.jv.beginEntry(je, mev);
463             this.numLeft = je.getSize();
464             if (this.numLeft == 0)
465                 this.jv.update(-1, this.mev);
466         }
467 
read()468         public int read() throws IOException
469         {
470             // BEGIN Android-added: Throw IOE, not NPE, if stream is closed. http://b/110695212
471             if (is == null) {
472                 throw new IOException("stream closed");
473             }
474             // END Android-added: Throw IOE, not NPE, if stream is closed. http://b/110695212
475             if (numLeft > 0) {
476                 int b = is.read();
477                 jv.update(b, mev);
478                 numLeft--;
479                 if (numLeft == 0)
480                     jv.update(-1, mev);
481                 return b;
482             } else {
483                 return -1;
484             }
485         }
486 
read(byte b[], int off, int len)487         public int read(byte b[], int off, int len) throws IOException {
488             // BEGIN Android-added: Throw IOE, not NPE, if stream is closed. http://b/110695212
489             if (is == null) {
490                 throw new IOException("stream closed");
491             }
492             // END Android-added: Throw IOE, not NPE, if stream is closed. http://b/110695212
493             if ((numLeft > 0) && (numLeft < len)) {
494                 len = (int)numLeft;
495             }
496 
497             if (numLeft > 0) {
498                 int n = is.read(b, off, len);
499                 jv.update(n, b, off, len, mev);
500                 numLeft -= n;
501                 if (numLeft == 0)
502                     jv.update(-1, b, off, len, mev);
503                 return n;
504             } else {
505                 return -1;
506             }
507         }
508 
close()509         public void close()
510             throws IOException
511         {
512             if (is != null)
513                 is.close();
514             is = null;
515             mev = null;
516             jv = null;
517         }
518 
available()519         public int available() throws IOException {
520             // BEGIN Android-added: Throw IOE, not NPE, if stream is closed. http://b/110695212
521             if (is == null) {
522                 throw new IOException("stream closed");
523             }
524             // END Android-added: Throw IOE, not NPE, if stream is closed. http://b/110695212
525             return is.available();
526         }
527 
528     }
529 
530     // Extended JavaUtilJarAccess CodeSource API Support
531 
532     private Map<URL, Map<CodeSigner[], CodeSource>> urlToCodeSourceMap = new HashMap<>();
533     private Map<CodeSigner[], CodeSource> signerToCodeSource = new HashMap<>();
534     private URL lastURL;
535     private Map<CodeSigner[], CodeSource> lastURLMap;
536 
537     /*
538      * Create a unique mapping from codeSigner cache entries to CodeSource.
539      * In theory, multiple URLs origins could map to a single locally cached
540      * and shared JAR file although in practice there will be a single URL in use.
541      */
mapSignersToCodeSource(URL url, CodeSigner[] signers)542     private synchronized CodeSource mapSignersToCodeSource(URL url, CodeSigner[] signers) {
543         Map<CodeSigner[], CodeSource> map;
544         if (url == lastURL) {
545             map = lastURLMap;
546         } else {
547             map = urlToCodeSourceMap.get(url);
548             if (map == null) {
549                 map = new HashMap<>();
550                 urlToCodeSourceMap.put(url, map);
551             }
552             lastURLMap = map;
553             lastURL = url;
554         }
555         CodeSource cs = map.get(signers);
556         if (cs == null) {
557             cs = new VerifierCodeSource(csdomain, url, signers);
558             signerToCodeSource.put(signers, cs);
559         }
560         return cs;
561     }
562 
mapSignersToCodeSources(URL url, List<CodeSigner[]> signers, boolean unsigned)563     private CodeSource[] mapSignersToCodeSources(URL url, List<CodeSigner[]> signers, boolean unsigned) {
564         List<CodeSource> sources = new ArrayList<>();
565 
566         for (int i = 0; i < signers.size(); i++) {
567             sources.add(mapSignersToCodeSource(url, signers.get(i)));
568         }
569         if (unsigned) {
570             sources.add(mapSignersToCodeSource(url, null));
571         }
572         return sources.toArray(new CodeSource[sources.size()]);
573     }
574     private CodeSigner[] emptySigner = new CodeSigner[0];
575 
576     /*
577      * Match CodeSource to a CodeSigner[] in the signer cache.
578      */
findMatchingSigners(CodeSource cs)579     private CodeSigner[] findMatchingSigners(CodeSource cs) {
580         if (cs instanceof VerifierCodeSource) {
581             VerifierCodeSource vcs = (VerifierCodeSource) cs;
582             if (vcs.isSameDomain(csdomain)) {
583                 return ((VerifierCodeSource) cs).getPrivateSigners();
584             }
585         }
586 
587         /*
588          * In practice signers should always be optimized above
589          * but this handles a CodeSource of any type, just in case.
590          */
591         CodeSource[] sources = mapSignersToCodeSources(cs.getLocation(), getJarCodeSigners(), true);
592         List<CodeSource> sourceList = new ArrayList<>();
593         for (int i = 0; i < sources.length; i++) {
594             sourceList.add(sources[i]);
595         }
596         int j = sourceList.indexOf(cs);
597         if (j != -1) {
598             CodeSigner[] match;
599             match = ((VerifierCodeSource) sourceList.get(j)).getPrivateSigners();
600             if (match == null) {
601                 match = emptySigner;
602             }
603             return match;
604         }
605         return null;
606     }
607 
608     /*
609      * Instances of this class hold uncopied references to internal
610      * signing data that can be compared by object reference identity.
611      */
612     private static class VerifierCodeSource extends CodeSource {
613         private static final long serialVersionUID = -9047366145967768825L;
614 
615         URL vlocation;
616         CodeSigner[] vsigners;
617         java.security.cert.Certificate[] vcerts;
618         Object csdomain;
619 
VerifierCodeSource(Object csdomain, URL location, CodeSigner[] signers)620         VerifierCodeSource(Object csdomain, URL location, CodeSigner[] signers) {
621             super(location, signers);
622             this.csdomain = csdomain;
623             vlocation = location;
624             vsigners = signers; // from signerCache
625         }
626 
VerifierCodeSource(Object csdomain, URL location, java.security.cert.Certificate[] certs)627         VerifierCodeSource(Object csdomain, URL location, java.security.cert.Certificate[] certs) {
628             super(location, certs);
629             this.csdomain = csdomain;
630             vlocation = location;
631             vcerts = certs; // from signerCache
632         }
633 
634         /*
635          * All VerifierCodeSource instances are constructed based on
636          * singleton signerCache or signerCacheCert entries for each unique signer.
637          * No CodeSigner<->Certificate[] conversion is required.
638          * We use these assumptions to optimize equality comparisons.
639          */
equals(Object obj)640         public boolean equals(Object obj) {
641             if (obj == this) {
642                 return true;
643             }
644             if (obj instanceof VerifierCodeSource) {
645                 VerifierCodeSource that = (VerifierCodeSource) obj;
646 
647                 /*
648                  * Only compare against other per-signer singletons constructed
649                  * on behalf of the same JarFile instance. Otherwise, compare
650                  * things the slower way.
651                  */
652                 if (isSameDomain(that.csdomain)) {
653                     if (that.vsigners != this.vsigners
654                             || that.vcerts != this.vcerts) {
655                         return false;
656                     }
657                     if (that.vlocation != null) {
658                         return that.vlocation.equals(this.vlocation);
659                     } else if (this.vlocation != null) {
660                         return this.vlocation.equals(that.vlocation);
661                     } else { // both null
662                         return true;
663                     }
664                 }
665             }
666             return super.equals(obj);
667         }
668 
isSameDomain(Object csdomain)669         boolean isSameDomain(Object csdomain) {
670             return this.csdomain == csdomain;
671         }
672 
getPrivateSigners()673         private CodeSigner[] getPrivateSigners() {
674             return vsigners;
675         }
676 
getPrivateCertificates()677         private java.security.cert.Certificate[] getPrivateCertificates() {
678             return vcerts;
679         }
680     }
681     private Map<String, CodeSigner[]> signerMap;
682 
signerMap()683     private synchronized Map<String, CodeSigner[]> signerMap() {
684         if (signerMap == null) {
685             /*
686              * Snapshot signer state so it doesn't change on us. We care
687              * only about the asserted signatures. Verification of
688              * signature validity happens via the JarEntry apis.
689              */
690             signerMap = new HashMap<>(verifiedSigners.size() + sigFileSigners.size());
691             signerMap.putAll(verifiedSigners);
692             signerMap.putAll(sigFileSigners);
693         }
694         return signerMap;
695     }
696 
entryNames(JarFile jar, final CodeSource[] cs)697     public synchronized Enumeration<String> entryNames(JarFile jar, final CodeSource[] cs) {
698         final Map<String, CodeSigner[]> map = signerMap();
699         final Iterator<Map.Entry<String, CodeSigner[]>> itor = map.entrySet().iterator();
700         boolean matchUnsigned = false;
701 
702         /*
703          * Grab a single copy of the CodeSigner arrays. Check
704          * to see if we can optimize CodeSigner equality test.
705          */
706         List<CodeSigner[]> req = new ArrayList<>(cs.length);
707         for (int i = 0; i < cs.length; i++) {
708             CodeSigner[] match = findMatchingSigners(cs[i]);
709             if (match != null) {
710                 if (match.length > 0) {
711                     req.add(match);
712                 } else {
713                     matchUnsigned = true;
714                 }
715             } else {
716                 matchUnsigned = true;
717             }
718         }
719 
720         final List<CodeSigner[]> signersReq = req;
721         final Enumeration<String> enum2 = (matchUnsigned) ? unsignedEntryNames(jar) : emptyEnumeration;
722 
723         return new Enumeration<String>() {
724 
725             String name;
726 
727             public boolean hasMoreElements() {
728                 if (name != null) {
729                     return true;
730                 }
731 
732                 while (itor.hasNext()) {
733                     Map.Entry<String, CodeSigner[]> e = itor.next();
734                     if (signersReq.contains(e.getValue())) {
735                         name = e.getKey();
736                         return true;
737                     }
738                 }
739                 while (enum2.hasMoreElements()) {
740                     name = enum2.nextElement();
741                     return true;
742                 }
743                 return false;
744             }
745 
746             public String nextElement() {
747                 if (hasMoreElements()) {
748                     String value = name;
749                     name = null;
750                     return value;
751                 }
752                 throw new NoSuchElementException();
753             }
754         };
755     }
756 
757     /*
758      * Like entries() but screens out internal JAR mechanism entries
759      * and includes signed entries with no ZIP data.
760      */
entries2(final JarFile jar, Enumeration<? extends ZipEntry> e)761     public Enumeration<JarEntry> entries2(final JarFile jar, Enumeration<? extends ZipEntry> e) {
762         final Map<String, CodeSigner[]> map = new HashMap<>();
763         map.putAll(signerMap());
764         final Enumeration<? extends ZipEntry> enum_ = e;
765         return new Enumeration<JarEntry>() {
766 
767             Enumeration<String> signers = null;
768             JarEntry entry;
769 
770             public boolean hasMoreElements() {
771                 if (entry != null) {
772                     return true;
773                 }
774                 while (enum_.hasMoreElements()) {
775                     ZipEntry ze = enum_.nextElement();
776                     if (JarVerifier.isSigningRelated(ze.getName())) {
777                         continue;
778                     }
779                     entry = jar.newEntry(ze);
780                     return true;
781                 }
782                 if (signers == null) {
783                     signers = Collections.enumeration(map.keySet());
784                 }
785                 while (signers.hasMoreElements()) {
786                     String name = signers.nextElement();
787                     entry = jar.newEntry(new ZipEntry(name));
788                     return true;
789                 }
790 
791                 // Any map entries left?
792                 return false;
793             }
794 
795             public JarEntry nextElement() {
796                 if (hasMoreElements()) {
797                     JarEntry je = entry;
798                     map.remove(je.getName());
799                     entry = null;
800                     return je;
801                 }
802                 throw new NoSuchElementException();
803             }
804         };
805     }
806     private Enumeration<String> emptyEnumeration = new Enumeration<String>() {
807 
808         public boolean hasMoreElements() {
809             return false;
810         }
811 
812         public String nextElement() {
813             throw new NoSuchElementException();
814         }
815     };
816 
817     // true if file is part of the signature mechanism itself
818     static boolean isSigningRelated(String name) {
819         return SignatureFileVerifier.isSigningRelated(name);
820     }
821 
822     private Enumeration<String> unsignedEntryNames(JarFile jar) {
823         final Map<String, CodeSigner[]> map = signerMap();
824         final Enumeration<JarEntry> entries = jar.entries();
825         return new Enumeration<String>() {
826 
827             String name;
828 
829             /*
830              * Grab entries from ZIP directory but screen out
831              * metadata.
832              */
833             public boolean hasMoreElements() {
834                 if (name != null) {
835                     return true;
836                 }
837                 while (entries.hasMoreElements()) {
838                     String value;
839                     ZipEntry e = entries.nextElement();
840                     value = e.getName();
841                     if (e.isDirectory() || isSigningRelated(value)) {
842                         continue;
843                     }
844                     if (map.get(value) == null) {
845                         name = value;
846                         return true;
847                     }
848                 }
849                 return false;
850             }
851 
852             public String nextElement() {
853                 if (hasMoreElements()) {
854                     String value = name;
855                     name = null;
856                     return value;
857                 }
858                 throw new NoSuchElementException();
859             }
860         };
861     }
862     private List<CodeSigner[]> jarCodeSigners;
863 
864     private synchronized List<CodeSigner[]> getJarCodeSigners() {
865         CodeSigner[] signers;
866         if (jarCodeSigners == null) {
867             HashSet<CodeSigner[]> set = new HashSet<>();
868             set.addAll(signerMap().values());
869             jarCodeSigners = new ArrayList<>();
870             jarCodeSigners.addAll(set);
871         }
872         return jarCodeSigners;
873     }
874 
875     public synchronized CodeSource[] getCodeSources(JarFile jar, URL url) {
876         boolean hasUnsigned = unsignedEntryNames(jar).hasMoreElements();
877 
878         return mapSignersToCodeSources(url, getJarCodeSigners(), hasUnsigned);
879     }
880 
881     public CodeSource getCodeSource(URL url, String name) {
882         CodeSigner[] signers;
883 
884         signers = signerMap().get(name);
885         return mapSignersToCodeSource(url, signers);
886     }
887 
888     public CodeSource getCodeSource(URL url, JarFile jar, JarEntry je) {
889         CodeSigner[] signers;
890 
891         return mapSignersToCodeSource(url, getCodeSigners(jar, je));
892     }
893 
894     public void setEagerValidation(boolean eager) {
895         eagerValidation = eager;
896     }
897 
898     public synchronized List<Object> getManifestDigests() {
899         return Collections.unmodifiableList(manifestDigests);
900     }
901 
902     static CodeSource getUnsignedCS(URL url) {
903         return new VerifierCodeSource(null, url, (java.security.cert.Certificate[]) null);
904     }
905 }
906