1
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 jdk.internal.util.jar.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
45 class JarVerifier {
46
47
48 static final Debug debug = Debug.getInstance("jar");
49
50
52 private Hashtable<String, CodeSigner[]> verifiedSigners;
53
54
56 private Hashtable<String, CodeSigner[]> sigFileSigners;
57
58
59 private Hashtable<String, byte[]> sigFileData;
60
61
63 private ArrayList<SignatureFileVerifier> pendingBlocks;
64
65
66 private ArrayList<CodeSigner[]> signerCache;
67
68
69 private boolean parsingBlockOrSF = false;
70
71
72 private boolean parsingMeta = true;
73
74
75 private boolean anyToVerify = true;
76
77
79 private ByteArrayOutputStream baos;
80
81
82 private volatile ManifestDigester manDig;
83
84
85 byte manifestRawBytes[] = null;
86
87
88 boolean eagerValidation;
89
90
91 private Object csdomain = new Object();
92
93
94 private List<Object> manifestDigests;
95
96 public JarVerifier(byte rawBytes[]) {
97 manifestRawBytes = rawBytes;
98 sigFileSigners = new Hashtable<>();
99 verifiedSigners = new Hashtable<>();
100 sigFileData = new Hashtable<>(11);
101 pendingBlocks = new ArrayList<>();
102 baos = new ByteArrayOutputStream();
103 manifestDigests = new ArrayList<>();
104 }
105
106
111 public void beginEntry(JarEntry je, ManifestEntryVerifier mev)
112 throws IOException
113 {
114 if (je == null)
115 return;
116
117 if (debug != null) {
118 debug.println("beginEntry "+je.getName());
119 }
120
121 String name = je.getName();
122
123
132
133 if (parsingMeta) {
134 String uname = name.toUpperCase(Locale.ENGLISH);
135 if ((uname.startsWith("META-INF/") ||
136 uname.startsWith("/META-INF/"))) {
137
138 if (je.isDirectory()) {
139 mev.setEntry(null, je);
140 return;
141 }
142
143 if (uname.equals(JarFile.MANIFEST_NAME) ||
144 uname.equals(JarIndex.INDEX_NAME)) {
145 return;
146 }
147
148 if (SignatureFileVerifier.isBlockOrSF(uname)) {
149
150 parsingBlockOrSF = true;
151 baos.reset();
152 mev.setEntry(null, je);
153 return;
154 }
155
156
157
158
159 }
160 }
161
162 if (parsingMeta) {
163 doneWithMeta();
164 }
165
166 if (je.isDirectory()) {
167 mev.setEntry(null, je);
168 return;
169 }
170
171
172
173 if (name.startsWith("./"))
174 name = name.substring(2);
175
176
177
178 if (name.startsWith("/"))
179 name = name.substring(1);
180
181
182
183 if (!name.equals(JarFile.MANIFEST_NAME)) {
184 if (sigFileSigners.get(name) != null ||
185 verifiedSigners.get(name) != null) {
186 mev.setEntry(name, je);
187 return;
188 }
189 }
190
191
192 mev.setEntry(null, je);
193
194 return;
195 }
196
197
200
201 public void update(int b, ManifestEntryVerifier mev)
202 throws IOException
203 {
204 if (b != -1) {
205 if (parsingBlockOrSF) {
206 baos.write(b);
207 } else {
208 mev.update((byte)b);
209 }
210 } else {
211 processEntry(mev);
212 }
213 }
214
215
218
219 public void update(int n, byte[] b, int off, int len,
220 ManifestEntryVerifier mev)
221 throws IOException
222 {
223 if (n != -1) {
224 if (parsingBlockOrSF) {
225 baos.write(b, off, n);
226 } else {
227 mev.update(b, off, n);
228 }
229 } else {
230 processEntry(mev);
231 }
232 }
233
234
237 private void processEntry(ManifestEntryVerifier mev)
238 throws IOException
239 {
240 if (!parsingBlockOrSF) {
241 JarEntry je = mev.getEntry();
242 if ((je != null) && (je.signers == null)) {
243 je.signers = mev.verify(verifiedSigners, sigFileSigners);
244 je.certs = mapSignersToCertArray(je.signers);
245 }
246 } else {
247
248 try {
249 parsingBlockOrSF = false;
250
251 if (debug != null) {
252 debug.println("processEntry: processing block");
253 }
254
255 String uname = mev.getEntry().getName()
256 .toUpperCase(Locale.ENGLISH);
257
258 if (uname.endsWith(".SF")) {
259 String key = uname.substring(0, uname.length()-3);
260 byte bytes[] = baos.toByteArray();
261
262 sigFileData.put(key, bytes);
263
264
265 for (SignatureFileVerifier sfv : pendingBlocks) {
266 if (sfv.needSignatureFile(key)) {
267 if (debug != null) {
268 debug.println(
269 "processEntry: processing pending block");
270 }
271
272 sfv.setSignatureFile(bytes);
273 sfv.process(sigFileSigners, manifestDigests);
274 }
275 }
276 return;
277 }
278
279
280
281 String key = uname.substring(0, uname.lastIndexOf('.'));
282
283 if (signerCache == null)
284 signerCache = new ArrayList<>();
285
286 if (manDig == null) {
287 synchronized(manifestRawBytes) {
288 if (manDig == null) {
289 manDig = new ManifestDigester(manifestRawBytes);
290 manifestRawBytes = null;
291 }
292 }
293 }
294
295 SignatureFileVerifier sfv =
296 new SignatureFileVerifier(signerCache,
297 manDig, uname, baos.toByteArray());
298
299 if (sfv.needSignatureFileBytes()) {
300
301 byte[] bytes = sigFileData.get(key);
302
303 if (bytes == null) {
304
305
306
307 if (debug != null) {
308 debug.println("adding pending block");
309 }
310 pendingBlocks.add(sfv);
311 return;
312 } else {
313 sfv.setSignatureFile(bytes);
314 }
315 }
316 sfv.process(sigFileSigners, manifestDigests);
317
318 } catch (IOException | CertificateException |
319 NoSuchAlgorithmException | SignatureException e) {
320 if (debug != null) debug.println("processEntry caught: "+e);
321
322 }
323 }
324 }
325
326
331 @Deprecated
332 public java.security.cert.Certificate[] getCerts(String name)
333 {
334 return mapSignersToCertArray(getCodeSigners(name));
335 }
336
337 public java.security.cert.Certificate[] getCerts(JarFile jar, JarEntry entry)
338 {
339 return mapSignersToCertArray(getCodeSigners(jar, entry));
340 }
341
342
347 public CodeSigner[] getCodeSigners(String name)
348 {
349 return verifiedSigners.get(name);
350 }
351
352 public CodeSigner[] getCodeSigners(JarFile jar, JarEntry entry)
353 {
354 String name = entry.getName();
355 if (eagerValidation && sigFileSigners.get(name) != null) {
356
360 try {
361 InputStream s = jar.getInputStream(entry);
362 byte[] buffer = new byte[1024];
363 int n = buffer.length;
364 while (n != -1) {
365 n = s.read(buffer, 0, buffer.length);
366 }
367 s.close();
368 } catch (IOException e) {
369 }
370 }
371 return getCodeSigners(name);
372 }
373
374
378 private static java.security.cert.Certificate[] mapSignersToCertArray(
379 CodeSigner[] signers) {
380
381 if (signers != null) {
382 ArrayList<java.security.cert.Certificate> certChains = new ArrayList<>();
383 for (CodeSigner signer : signers) {
384 certChains.addAll(
385 signer.getSignerCertPath().getCertificates());
386 }
387
388
389 return certChains.toArray(
390 new java.security.cert.Certificate[certChains.size()]);
391 }
392 return null;
393 }
394
395
400 boolean nothingToVerify()
401 {
402 return (anyToVerify == false);
403 }
404
405
411 void doneWithMeta()
412 {
413 parsingMeta = false;
414 anyToVerify = !sigFileSigners.isEmpty();
415 baos = null;
416 sigFileData = null;
417 pendingBlocks = null;
418 signerCache = null;
419 manDig = null;
420
421
422 if (sigFileSigners.containsKey(JarFile.MANIFEST_NAME)) {
423 CodeSigner[] codeSigners = sigFileSigners.remove(JarFile.MANIFEST_NAME);
424 verifiedSigners.put(JarFile.MANIFEST_NAME, codeSigners);
425 }
426 }
427
428 static class VerifierStream extends java.io.InputStream {
429
430 private InputStream is;
431 private JarVerifier jv;
432 private ManifestEntryVerifier mev;
433 private long numLeft;
434
435 VerifierStream(Manifest man,
436 JarEntry je,
437 InputStream is,
438 JarVerifier jv) throws IOException
439 {
440 this.is = is;
441 this.jv = jv;
442 this.mev = new ManifestEntryVerifier(man);
443 this.jv.beginEntry(je, mev);
444 this.numLeft = je.getSize();
445 if (this.numLeft == 0)
446 this.jv.update(-1, this.mev);
447 }
448
449 public int read() throws IOException
450 {
451 if (numLeft > 0) {
452 int b = is.read();
453 jv.update(b, mev);
454 numLeft--;
455 if (numLeft == 0)
456 jv.update(-1, mev);
457 return b;
458 } else {
459 return -1;
460 }
461 }
462
463 public int read(byte b[], int off, int len) throws IOException {
464 if ((numLeft > 0) && (numLeft < len)) {
465 len = (int)numLeft;
466 }
467
468 if (numLeft > 0) {
469 int n = is.read(b, off, len);
470 jv.update(n, b, off, len, mev);
471 numLeft -= n;
472 if (numLeft == 0)
473 jv.update(-1, b, off, len, mev);
474 return n;
475 } else {
476 return -1;
477 }
478 }
479
480 public void close()
481 throws IOException
482 {
483 if (is != null)
484 is.close();
485 is = null;
486 mev = null;
487 jv = null;
488 }
489
490 public int available() throws IOException {
491 return is.available();
492 }
493
494 }
495
496
497
498 private Map<URL, Map<CodeSigner[], CodeSource>> urlToCodeSourceMap = new HashMap<>();
499 private Map<CodeSigner[], CodeSource> signerToCodeSource = new HashMap<>();
500 private URL lastURL;
501 private Map<CodeSigner[], CodeSource> lastURLMap;
502
503
508 private synchronized CodeSource mapSignersToCodeSource(URL url, CodeSigner[] signers) {
509 Map<CodeSigner[], CodeSource> map;
510 if (url == lastURL) {
511 map = lastURLMap;
512 } else {
513 map = urlToCodeSourceMap.get(url);
514 if (map == null) {
515 map = new HashMap<>();
516 urlToCodeSourceMap.put(url, map);
517 }
518 lastURLMap = map;
519 lastURL = url;
520 }
521 CodeSource cs = map.get(signers);
522 if (cs == null) {
523 cs = new VerifierCodeSource(csdomain, url, signers);
524 signerToCodeSource.put(signers, cs);
525 }
526 return cs;
527 }
528
529 private CodeSource[] mapSignersToCodeSources(URL url, List<CodeSigner[]> signers, boolean unsigned) {
530 List<CodeSource> sources = new ArrayList<>();
531
532 for (CodeSigner[] signer : signers) {
533 sources.add(mapSignersToCodeSource(url, signer));
534 }
535 if (unsigned) {
536 sources.add(mapSignersToCodeSource(url, null));
537 }
538 return sources.toArray(new CodeSource[sources.size()]);
539 }
540 private CodeSigner[] emptySigner = new CodeSigner[0];
541
542
545 private CodeSigner[] findMatchingSigners(CodeSource cs) {
546 if (cs instanceof VerifierCodeSource) {
547 VerifierCodeSource vcs = (VerifierCodeSource) cs;
548 if (vcs.isSameDomain(csdomain)) {
549 return ((VerifierCodeSource) cs).getPrivateSigners();
550 }
551 }
552
553
557 CodeSource[] sources = mapSignersToCodeSources(cs.getLocation(), getJarCodeSigners(), true);
558 List<CodeSource> sourceList = new ArrayList<>();
559 for (CodeSource source : sources) {
560 sourceList.add(source);
561 }
562 int j = sourceList.indexOf(cs);
563 if (j != -1) {
564 CodeSigner[] match;
565 match = ((VerifierCodeSource) sourceList.get(j)).getPrivateSigners();
566 if (match == null) {
567 match = emptySigner;
568 }
569 return match;
570 }
571 return null;
572 }
573
574
578 private static class VerifierCodeSource extends CodeSource {
579 private static final long serialVersionUID = -9047366145967768825L;
580
581 URL vlocation;
582 CodeSigner[] vsigners;
583 java.security.cert.Certificate[] vcerts;
584 Object csdomain;
585
586 VerifierCodeSource(Object csdomain, URL location, CodeSigner[] signers) {
587 super(location, signers);
588 this.csdomain = csdomain;
589 vlocation = location;
590 vsigners = signers;
591 }
592
593 VerifierCodeSource(Object csdomain, URL location, java.security.cert.Certificate[] certs) {
594 super(location, certs);
595 this.csdomain = csdomain;
596 vlocation = location;
597 vcerts = certs;
598 }
599
600
606 public boolean equals(Object obj) {
607 if (obj == this) {
608 return true;
609 }
610 if (obj instanceof VerifierCodeSource) {
611 VerifierCodeSource that = (VerifierCodeSource) obj;
612
613
618 if (isSameDomain(that.csdomain)) {
619 if (that.vsigners != this.vsigners
620 || that.vcerts != this.vcerts) {
621 return false;
622 }
623 if (that.vlocation != null) {
624 return that.vlocation.equals(this.vlocation);
625 } else if (this.vlocation != null) {
626 return this.vlocation.equals(that.vlocation);
627 } else {
628 return true;
629 }
630 }
631 }
632 return super.equals(obj);
633 }
634
635 boolean isSameDomain(Object csdomain) {
636 return this.csdomain == csdomain;
637 }
638
639 private CodeSigner[] getPrivateSigners() {
640 return vsigners;
641 }
642
643 private java.security.cert.Certificate[] getPrivateCertificates() {
644 return vcerts;
645 }
646 }
647 private Map<String, CodeSigner[]> signerMap;
648
649 private synchronized Map<String, CodeSigner[]> signerMap() {
650 if (signerMap == null) {
651
656 signerMap = new HashMap<>(verifiedSigners.size() + sigFileSigners.size());
657 signerMap.putAll(verifiedSigners);
658 signerMap.putAll(sigFileSigners);
659 }
660 return signerMap;
661 }
662
663 public synchronized Enumeration<String> entryNames(JarFile jar, final CodeSource[] cs) {
664 final Map<String, CodeSigner[]> map = signerMap();
665 final Iterator<Map.Entry<String, CodeSigner[]>> itor = map.entrySet().iterator();
666 boolean matchUnsigned = false;
667
668
672 List<CodeSigner[]> req = new ArrayList<>(cs.length);
673 for (CodeSource c : cs) {
674 CodeSigner[] match = findMatchingSigners(c);
675 if (match != null) {
676 if (match.length > 0) {
677 req.add(match);
678 } else {
679 matchUnsigned = true;
680 }
681 } else {
682 matchUnsigned = true;
683 }
684 }
685
686 final List<CodeSigner[]> signersReq = req;
687 final Enumeration<String> enum2 = matchUnsigned ? unsignedEntryNames(jar) : Collections.emptyEnumeration();
688
689 return new Enumeration<>() {
690
691 String name;
692
693 public boolean hasMoreElements() {
694 if (name != null) {
695 return true;
696 }
697
698 while (itor.hasNext()) {
699 Map.Entry<String, CodeSigner[]> e = itor.next();
700 if (signersReq.contains(e.getValue())) {
701 name = e.getKey();
702 return true;
703 }
704 }
705 while (enum2.hasMoreElements()) {
706 name = enum2.nextElement();
707 return true;
708 }
709 return false;
710 }
711
712 public String nextElement() {
713 if (hasMoreElements()) {
714 String value = name;
715 name = null;
716 return value;
717 }
718 throw new NoSuchElementException();
719 }
720 };
721 }
722
723
727 public Enumeration<JarEntry> entries2(final JarFile jar, Enumeration<JarEntry> e) {
728 final Map<String, CodeSigner[]> map = new HashMap<>();
729 map.putAll(signerMap());
730 final Enumeration<JarEntry> enum_ = e;
731 return new Enumeration<>() {
732
733 Enumeration<String> signers = null;
734 JarEntry entry;
735
736 public boolean hasMoreElements() {
737 if (entry != null) {
738 return true;
739 }
740 while (enum_.hasMoreElements()) {
741 JarEntry je = enum_.nextElement();
742 if (JarVerifier.isSigningRelated(je.getName())) {
743 continue;
744 }
745 entry = jar.newEntry(je);
746 return true;
747 }
748 if (signers == null) {
749 signers = Collections.enumeration(map.keySet());
750 }
751 while (signers.hasMoreElements()) {
752 String name = signers.nextElement();
753 entry = jar.newEntry(name);
754 return true;
755 }
756
757
758 return false;
759 }
760
761 public JarEntry nextElement() {
762 if (hasMoreElements()) {
763 JarEntry je = entry;
764 map.remove(je.getName());
765 entry = null;
766 return je;
767 }
768 throw new NoSuchElementException();
769 }
770 };
771 }
772
773
774 static boolean isSigningRelated(String name) {
775 return SignatureFileVerifier.isSigningRelated(name);
776 }
777
778 private Enumeration<String> unsignedEntryNames(JarFile jar) {
779 final Map<String, CodeSigner[]> map = signerMap();
780 final Enumeration<JarEntry> entries = jar.entries();
781 return new Enumeration<>() {
782
783 String name;
784
785
789 public boolean hasMoreElements() {
790 if (name != null) {
791 return true;
792 }
793 while (entries.hasMoreElements()) {
794 String value;
795 ZipEntry e = entries.nextElement();
796 value = e.getName();
797 if (e.isDirectory() || isSigningRelated(value)) {
798 continue;
799 }
800 if (map.get(value) == null) {
801 name = value;
802 return true;
803 }
804 }
805 return false;
806 }
807
808 public String nextElement() {
809 if (hasMoreElements()) {
810 String value = name;
811 name = null;
812 return value;
813 }
814 throw new NoSuchElementException();
815 }
816 };
817 }
818 private List<CodeSigner[]> jarCodeSigners;
819
820 private synchronized List<CodeSigner[]> getJarCodeSigners() {
821 CodeSigner[] signers;
822 if (jarCodeSigners == null) {
823 HashSet<CodeSigner[]> set = new HashSet<>();
824 set.addAll(signerMap().values());
825 jarCodeSigners = new ArrayList<>();
826 jarCodeSigners.addAll(set);
827 }
828 return jarCodeSigners;
829 }
830
831 public synchronized CodeSource[] getCodeSources(JarFile jar, URL url) {
832 boolean hasUnsigned = unsignedEntryNames(jar).hasMoreElements();
833
834 return mapSignersToCodeSources(url, getJarCodeSigners(), hasUnsigned);
835 }
836
837 public CodeSource getCodeSource(URL url, String name) {
838 CodeSigner[] signers;
839
840 signers = signerMap().get(name);
841 return mapSignersToCodeSource(url, signers);
842 }
843
844 public CodeSource getCodeSource(URL url, JarFile jar, JarEntry je) {
845 CodeSigner[] signers;
846
847 return mapSignersToCodeSource(url, getCodeSigners(jar, je));
848 }
849
850 public void setEagerValidation(boolean eager) {
851 eagerValidation = eager;
852 }
853
854 public synchronized List<Object> getManifestDigests() {
855 return Collections.unmodifiableList(manifestDigests);
856 }
857
858 static CodeSource getUnsignedCS(URL url) {
859 return new VerifierCodeSource(null, url, (java.security.cert.Certificate[]) null);
860 }
861
862
866 boolean isTrustedManifestEntry(String name) {
867
868 CodeSigner[] forMan = verifiedSigners.get(JarFile.MANIFEST_NAME);
869 if (forMan == null) {
870 return true;
871 }
872
873
874 CodeSigner[] forName = sigFileSigners.get(name);
875 if (forName == null) {
876 forName = verifiedSigners.get(name);
877 }
878
879 return forName != null && forName.length == forMan.length;
880 }
881 }
882