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