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