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