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 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
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 Iterator<SignatureFileVerifier> it = pendingBlocks.iterator();
266 while (it.hasNext()) {
267 SignatureFileVerifier sfv = it.next();
268 if (sfv.needSignatureFile(key)) {
269 if (debug != null) {
270 debug.println(
271 "processEntry: processing pending block");
272 }
273
274 sfv.setSignatureFile(bytes);
275 sfv.process(sigFileSigners, manifestDigests);
276 }
277 }
278 return;
279 }
280
281
282
283 String key = uname.substring(0, uname.lastIndexOf("."));
284
285 if (signerCache == null)
286 signerCache = new ArrayList<>();
287
288 if (manDig == null) {
289 synchronized(manifestRawBytes) {
290 if (manDig == null) {
291 manDig = new ManifestDigester(manifestRawBytes);
292 manifestRawBytes = null;
293 }
294 }
295 }
296
297 SignatureFileVerifier sfv =
298 new SignatureFileVerifier(signerCache,
299 manDig, uname, baos.toByteArray());
300
301 if (sfv.needSignatureFileBytes()) {
302
303 byte[] bytes = sigFileData.get(key);
304
305 if (bytes == null) {
306
307
308
309 if (debug != null) {
310 debug.println("adding pending block");
311 }
312 pendingBlocks.add(sfv);
313 return;
314 } else {
315 sfv.setSignatureFile(bytes);
316 }
317 }
318 sfv.process(sigFileSigners, manifestDigests);
319
320 } catch (IOException ioe) {
321
322 if (debug != null) debug.println("processEntry caught: "+ioe);
323
324 } catch (SignatureException se) {
325 if (debug != null) debug.println("processEntry caught: "+se);
326
327 } catch (NoSuchAlgorithmException nsae) {
328 if (debug != null) debug.println("processEntry caught: "+nsae);
329
330 } catch (CertificateException ce) {
331 if (debug != null) debug.println("processEntry caught: "+ce);
332
333 }
334 }
335 }
336
337
342 @Deprecated
343 public java.security.cert.Certificate[] getCerts(String name)
344 {
345 return mapSignersToCertArray(getCodeSigners(name));
346 }
347
348 public java.security.cert.Certificate[] getCerts(JarFile jar, JarEntry entry)
349 {
350 return mapSignersToCertArray(getCodeSigners(jar, entry));
351 }
352
353
358 public CodeSigner[] getCodeSigners(String name)
359 {
360 return verifiedSigners.get(name);
361 }
362
363 public CodeSigner[] getCodeSigners(JarFile jar, JarEntry entry)
364 {
365 String name = entry.getName();
366 if (eagerValidation && sigFileSigners.get(name) != null) {
367
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
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
400 return certChains.toArray(
401 new java.security.cert.Certificate[certChains.size()]);
402 }
403 return null;
404 }
405
406
411 boolean nothingToVerify()
412 {
413 return (anyToVerify == false);
414 }
415
416
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
432
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
446 VerifierStream(Manifest man,
447 JarEntry je,
448 InputStream is,
449 JarVerifier jv) throws IOException
450 {
451 this.is = is;
452 this.jv = jv;
453 this.mev = new ManifestEntryVerifier(man);
454 this.jv.beginEntry(je, mev);
455 this.numLeft = je.getSize();
456 if (this.numLeft == 0)
457 this.jv.update(-1, this.mev);
458 }
459
460 public int read() throws IOException
461 {
462 if (numLeft > 0) {
463 int b = is.read();
464 jv.update(b, mev);
465 numLeft--;
466 if (numLeft == 0)
467 jv.update(-1, mev);
468 return b;
469 } else {
470 return -1;
471 }
472 }
473
474 public int read(byte b[], int off, int len) throws IOException {
475 if ((numLeft > 0) && (numLeft < len)) {
476 len = (int)numLeft;
477 }
478
479 if (numLeft > 0) {
480 int n = is.read(b, off, len);
481 jv.update(n, b, off, len, mev);
482 numLeft -= n;
483 if (numLeft == 0)
484 jv.update(-1, b, off, len, mev);
485 return n;
486 } else {
487 return -1;
488 }
489 }
490
491 public void close()
492 throws IOException
493 {
494 if (is != null)
495 is.close();
496 is = null;
497 mev = null;
498 jv = null;
499 }
500
501 public int available() throws IOException {
502 return is.available();
503 }
504
505 }
506
507
508
509 private Map<URL, Map<CodeSigner[], CodeSource>> urlToCodeSourceMap = new HashMap<>();
510 private Map<CodeSigner[], CodeSource> signerToCodeSource = new HashMap<>();
511 private URL lastURL;
512 private Map<CodeSigner[], CodeSource> lastURLMap;
513
514
519 private synchronized CodeSource mapSignersToCodeSource(URL url, CodeSigner[] signers) {
520 Map<CodeSigner[], CodeSource> map;
521 if (url == lastURL) {
522 map = lastURLMap;
523 } else {
524 map = urlToCodeSourceMap.get(url);
525 if (map == null) {
526 map = new HashMap<>();
527 urlToCodeSourceMap.put(url, map);
528 }
529 lastURLMap = map;
530 lastURL = url;
531 }
532 CodeSource cs = map.get(signers);
533 if (cs == null) {
534 cs = new VerifierCodeSource(csdomain, url, signers);
535 signerToCodeSource.put(signers, cs);
536 }
537 return cs;
538 }
539
540 private CodeSource[] mapSignersToCodeSources(URL url, List<CodeSigner[]> signers, boolean unsigned) {
541 List<CodeSource> sources = new ArrayList<>();
542
543 for (int i = 0; i < signers.size(); i++) {
544 sources.add(mapSignersToCodeSource(url, signers.get(i)));
545 }
546 if (unsigned) {
547 sources.add(mapSignersToCodeSource(url, null));
548 }
549 return sources.toArray(new CodeSource[sources.size()]);
550 }
551 private CodeSigner[] emptySigner = new CodeSigner[0];
552
553
556 private CodeSigner[] findMatchingSigners(CodeSource cs) {
557 if (cs instanceof VerifierCodeSource) {
558 VerifierCodeSource vcs = (VerifierCodeSource) cs;
559 if (vcs.isSameDomain(csdomain)) {
560 return ((VerifierCodeSource) cs).getPrivateSigners();
561 }
562 }
563
564
568 CodeSource[] sources = mapSignersToCodeSources(cs.getLocation(), getJarCodeSigners(), true);
569 List<CodeSource> sourceList = new ArrayList<>();
570 for (int i = 0; i < sources.length; i++) {
571 sourceList.add(sources[i]);
572 }
573 int j = sourceList.indexOf(cs);
574 if (j != -1) {
575 CodeSigner[] match;
576 match = ((VerifierCodeSource) sourceList.get(j)).getPrivateSigners();
577 if (match == null) {
578 match = emptySigner;
579 }
580 return match;
581 }
582 return null;
583 }
584
585
589 private static class VerifierCodeSource extends CodeSource {
590 private static final long serialVersionUID = -9047366145967768825L;
591
592 URL vlocation;
593 CodeSigner[] vsigners;
594 java.security.cert.Certificate[] vcerts;
595 Object csdomain;
596
597 VerifierCodeSource(Object csdomain, URL location, CodeSigner[] signers) {
598 super(location, signers);
599 this.csdomain = csdomain;
600 vlocation = location;
601 vsigners = signers;
602 }
603
604 VerifierCodeSource(Object csdomain, URL location, java.security.cert.Certificate[] certs) {
605 super(location, certs);
606 this.csdomain = csdomain;
607 vlocation = location;
608 vcerts = certs;
609 }
610
611
617 public boolean equals(Object obj) {
618 if (obj == this) {
619 return true;
620 }
621 if (obj instanceof VerifierCodeSource) {
622 VerifierCodeSource that = (VerifierCodeSource) obj;
623
624
629 if (isSameDomain(that.csdomain)) {
630 if (that.vsigners != this.vsigners
631 || that.vcerts != this.vcerts) {
632 return false;
633 }
634 if (that.vlocation != null) {
635 return that.vlocation.equals(this.vlocation);
636 } else if (this.vlocation != null) {
637 return this.vlocation.equals(that.vlocation);
638 } else {
639 return true;
640 }
641 }
642 }
643 return super.equals(obj);
644 }
645
646 boolean isSameDomain(Object csdomain) {
647 return this.csdomain == csdomain;
648 }
649
650 private CodeSigner[] getPrivateSigners() {
651 return vsigners;
652 }
653
654 private java.security.cert.Certificate[] getPrivateCertificates() {
655 return vcerts;
656 }
657 }
658 private Map<String, CodeSigner[]> signerMap;
659
660 private synchronized Map<String, CodeSigner[]> signerMap() {
661 if (signerMap == null) {
662
667 signerMap = new HashMap<>(verifiedSigners.size() + sigFileSigners.size());
668 signerMap.putAll(verifiedSigners);
669 signerMap.putAll(sigFileSigners);
670 }
671 return signerMap;
672 }
673
674 public synchronized Enumeration<String> entryNames(JarFile jar, final CodeSource[] cs) {
675 final Map<String, CodeSigner[]> map = signerMap();
676 final Iterator<Map.Entry<String, CodeSigner[]>> itor = map.entrySet().iterator();
677 boolean matchUnsigned = false;
678
679
683 List<CodeSigner[]> req = new ArrayList<>(cs.length);
684 for (int i = 0; i < cs.length; i++) {
685 CodeSigner[] match = findMatchingSigners(cs[i]);
686 if (match != null) {
687 if (match.length > 0) {
688 req.add(match);
689 } else {
690 matchUnsigned = true;
691 }
692 } else {
693 matchUnsigned = true;
694 }
695 }
696
697 final List<CodeSigner[]> signersReq = req;
698 final Enumeration<String> enum2 = (matchUnsigned) ? unsignedEntryNames(jar) : emptyEnumeration;
699
700 return new Enumeration<String>() {
701
702 String name;
703
704 public boolean hasMoreElements() {
705 if (name != null) {
706 return true;
707 }
708
709 while (itor.hasNext()) {
710 Map.Entry<String, CodeSigner[]> e = itor.next();
711 if (signersReq.contains(e.getValue())) {
712 name = e.getKey();
713 return true;
714 }
715 }
716 while (enum2.hasMoreElements()) {
717 name = enum2.nextElement();
718 return true;
719 }
720 return false;
721 }
722
723 public String nextElement() {
724 if (hasMoreElements()) {
725 String value = name;
726 name = null;
727 return value;
728 }
729 throw new NoSuchElementException();
730 }
731 };
732 }
733
734
738 public Enumeration<JarEntry> entries2(final JarFile jar, Enumeration<? extends ZipEntry> e) {
739 final Map<String, CodeSigner[]> map = new HashMap<>();
740 map.putAll(signerMap());
741 final Enumeration<? extends ZipEntry> enum_ = e;
742 return new Enumeration<JarEntry>() {
743
744 Enumeration<String> signers = null;
745 JarEntry entry;
746
747 public boolean hasMoreElements() {
748 if (entry != null) {
749 return true;
750 }
751 while (enum_.hasMoreElements()) {
752 ZipEntry ze = enum_.nextElement();
753 if (JarVerifier.isSigningRelated(ze.getName())) {
754 continue;
755 }
756 entry = jar.newEntry(ze);
757 return true;
758 }
759 if (signers == null) {
760 signers = Collections.enumeration(map.keySet());
761 }
762 while (signers.hasMoreElements()) {
763 String name = signers.nextElement();
764 entry = jar.newEntry(new ZipEntry(name));
765 return true;
766 }
767
768
769 return false;
770 }
771
772 public JarEntry nextElement() {
773 if (hasMoreElements()) {
774 JarEntry je = entry;
775 map.remove(je.getName());
776 entry = null;
777 return je;
778 }
779 throw new NoSuchElementException();
780 }
781 };
782 }
783 private Enumeration<String> emptyEnumeration = new Enumeration<String>() {
784
785 public boolean hasMoreElements() {
786 return false;
787 }
788
789 public String nextElement() {
790 throw new NoSuchElementException();
791 }
792 };
793
794
795 static boolean isSigningRelated(String name) {
796 return SignatureFileVerifier.isSigningRelated(name);
797 }
798
799 private Enumeration<String> unsignedEntryNames(JarFile jar) {
800 final Map<String, CodeSigner[]> map = signerMap();
801 final Enumeration<JarEntry> entries = jar.entries();
802 return new Enumeration<String>() {
803
804 String name;
805
806
810 public boolean hasMoreElements() {
811 if (name != null) {
812 return true;
813 }
814 while (entries.hasMoreElements()) {
815 String value;
816 ZipEntry e = entries.nextElement();
817 value = e.getName();
818 if (e.isDirectory() || isSigningRelated(value)) {
819 continue;
820 }
821 if (map.get(value) == null) {
822 name = value;
823 return true;
824 }
825 }
826 return false;
827 }
828
829 public String nextElement() {
830 if (hasMoreElements()) {
831 String value = name;
832 name = null;
833 return value;
834 }
835 throw new NoSuchElementException();
836 }
837 };
838 }
839 private List<CodeSigner[]> jarCodeSigners;
840
841 private synchronized List<CodeSigner[]> getJarCodeSigners() {
842 CodeSigner[] signers;
843 if (jarCodeSigners == null) {
844 HashSet<CodeSigner[]> set = new HashSet<>();
845 set.addAll(signerMap().values());
846 jarCodeSigners = new ArrayList<>();
847 jarCodeSigners.addAll(set);
848 }
849 return jarCodeSigners;
850 }
851
852 public synchronized CodeSource[] getCodeSources(JarFile jar, URL url) {
853 boolean hasUnsigned = unsignedEntryNames(jar).hasMoreElements();
854
855 return mapSignersToCodeSources(url, getJarCodeSigners(), hasUnsigned);
856 }
857
858 public CodeSource getCodeSource(URL url, String name) {
859 CodeSigner[] signers;
860
861 signers = signerMap().get(name);
862 return mapSignersToCodeSource(url, signers);
863 }
864
865 public CodeSource getCodeSource(URL url, JarFile jar, JarEntry je) {
866 CodeSigner[] signers;
867
868 return mapSignersToCodeSource(url, getCodeSigners(jar, je));
869 }
870
871 public void setEagerValidation(boolean eager) {
872 eagerValidation = eager;
873 }
874
875 public synchronized List<Object> getManifestDigests() {
876 return Collections.unmodifiableList(manifestDigests);
877 }
878
879 static CodeSource getUnsignedCS(URL url) {
880 return new VerifierCodeSource(null, url, (java.security.cert.Certificate[]) null);
881 }
882 }
883