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