1
7
8 package java.util.jar;
9
10 import java.io.*;
11 import java.net.URL;
12 import java.lang.ref.SoftReference;
13 import java.util.*;
14 import java.util.zip.*;
15 import java.security.CodeSigner;
16 import java.security.CodeSource;
17 import java.security.cert.Certificate;
18 import java.security.AccessController;
19 import sun.security.action.GetPropertyAction;
20 import sun.security.util.ManifestEntryVerifier;
21 import sun.misc.SharedSecrets;
22
23
42 public
43 class JarFile extends ZipFile {
44 private SoftReference<Manifest> manRef;
45 private JarEntry manEntry;
46 private JarVerifier jv;
47 private boolean jvInitialized;
48 private boolean verify;
49 private boolean computedHasClassPathAttribute;
50 private boolean hasClassPathAttribute;
51
52 static {
54 SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl());
55 }
56
57
60 public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
61
62
71 public JarFile(String name) throws IOException {
72 this(new File(name), true, ZipFile.OPEN_READ);
73 }
74
75
85 public JarFile(String name, boolean verify) throws IOException {
86 this(new File(name), verify, ZipFile.OPEN_READ);
87 }
88
89
98 public JarFile(File file) throws IOException {
99 this(file, true, ZipFile.OPEN_READ);
100 }
101
102
103
113 public JarFile(File file, boolean verify) throws IOException {
114 this(file, verify, ZipFile.OPEN_READ);
115 }
116
117
118
134 public JarFile(File file, boolean verify, int mode) throws IOException {
135 super(file, mode);
136 this.verify = verify;
137 }
138
139
147 public Manifest getManifest() throws IOException {
148 return getManifestFromReference();
149 }
150
151 private Manifest getManifestFromReference() throws IOException {
152 Manifest man = manRef != null ? manRef.get() : null;
153
154 if (man == null) {
155
156 JarEntry manEntry = getManEntry();
157
158 if (manEntry != null) {
160 if (verify) {
161 byte[] b = getBytes(manEntry);
162 man = new Manifest(new ByteArrayInputStream(b));
163 if (!jvInitialized) {
164 jv = new JarVerifier(b);
165 }
166 } else {
167 man = new Manifest(super.getInputStream(manEntry));
168 }
169 manRef = new SoftReference(man);
170 }
171 }
172 return man;
173 }
174
175 private native String[] getMetaInfEntryNames();
176
177
190 public JarEntry getJarEntry(String name) {
191 return (JarEntry)getEntry(name);
192 }
193
194
207 public ZipEntry getEntry(String name) {
208 ZipEntry ze = super.getEntry(name);
209 if (ze != null) {
210 return new JarFileEntry(ze);
211 }
212 return null;
213 }
214
215
218 public Enumeration<JarEntry> entries() {
219 final Enumeration enum_ = super.entries();
220 return new Enumeration<JarEntry>() {
221 public boolean hasMoreElements() {
222 return enum_.hasMoreElements();
223 }
224 public JarFileEntry nextElement() {
225 ZipEntry ze = (ZipEntry)enum_.nextElement();
226 return new JarFileEntry(ze);
227 }
228 };
229 }
230
231 private class JarFileEntry extends JarEntry {
232 JarFileEntry(ZipEntry ze) {
233 super(ze);
234 }
235 public Attributes getAttributes() throws IOException {
236 Manifest man = JarFile.this.getManifest();
237 if (man != null) {
238 return man.getAttributes(getName());
239 } else {
240 return null;
241 }
242 }
243 public Certificate[] getCertificates() {
244 try {
245 maybeInstantiateVerifier();
246 } catch (IOException e) {
247 throw new RuntimeException(e);
248 }
249 if (certs == null && jv != null) {
250 certs = jv.getCerts(JarFile.this, this);
251 }
252 return certs == null ? null : (Certificate[]) certs.clone();
253 }
254 public CodeSigner[] getCodeSigners() {
255 try {
256 maybeInstantiateVerifier();
257 } catch (IOException e) {
258 throw new RuntimeException(e);
259 }
260 if (signers == null && jv != null) {
261 signers = jv.getCodeSigners(JarFile.this, this);
262 }
263 return signers == null ? null : (CodeSigner[]) signers.clone();
264 }
265 }
266
267
273 private void maybeInstantiateVerifier() throws IOException {
274 if (jv != null) {
275 return;
276 }
277
278 if (verify) {
279 String[] names = getMetaInfEntryNames();
280 if (names != null) {
281 for (int i = 0; i < names.length; i++) {
282 String name = names[i].toUpperCase(Locale.ENGLISH);
283 if (name.endsWith(".DSA") ||
284 name.endsWith(".RSA") ||
285 name.endsWith(".SF")) {
286 getManifest();
290 return;
291 }
292 }
293 }
294 verify = false;
297 }
298 }
299
300
301
305 private void initializeVerifier() {
306 ManifestEntryVerifier mev = null;
307
308 try {
310 String[] names = getMetaInfEntryNames();
311 if (names != null) {
312 for (int i = 0; i < names.length; i++) {
313 JarEntry e = getJarEntry(names[i]);
314 if (!e.isDirectory()) {
315 if (mev == null) {
316 mev = new ManifestEntryVerifier
317 (getManifestFromReference());
318 }
319 byte[] b = getBytes(e);
320 if (b != null && b.length > 0) {
321 jv.beginEntry(e, mev);
322 jv.update(b.length, b, 0, b.length, mev);
323 jv.update(-1, null, 0, 0, mev);
324 }
325 }
326 }
327 }
328 } catch (IOException ex) {
329 jv = null;
332 verify = false;
333 }
334
335
338 if (jv != null) {
339
340 jv.doneWithMeta();
341 if (JarVerifier.debug != null) {
342 JarVerifier.debug.println("done with meta!");
343 }
344
345 if (jv.nothingToVerify()) {
346 if (JarVerifier.debug != null) {
347 JarVerifier.debug.println("nothing to verify!");
348 }
349 jv = null;
350 verify = false;
351 }
352 }
353 }
354
355
359 private byte[] getBytes(ZipEntry ze) throws IOException {
360 byte[] b = new byte[(int)ze.getSize()];
361 DataInputStream is = new DataInputStream(super.getInputStream(ze));
362 is.readFully(b, 0, b.length);
363 is.close();
364 return b;
365 }
366
367
380 public synchronized InputStream getInputStream(ZipEntry ze)
381 throws IOException
382 {
383 maybeInstantiateVerifier();
384 if (jv == null) {
385 return super.getInputStream(ze);
386 }
387 if (!jvInitialized) {
388 initializeVerifier();
389 jvInitialized = true;
390 if (jv == null)
394 return super.getInputStream(ze);
395 }
396
397 return new JarVerifier.VerifierStream(
399 getManifestFromReference(),
400 ze instanceof JarFileEntry ?
401 (JarEntry) ze : getJarEntry(ze.getName()),
402 super.getInputStream(ze),
403 jv);
404 }
405
406 private static int[] lastOcc;
409 private static int[] optoSft;
411 private static char[] src = {'c','l','a','s','s','-','p','a','t','h'};
413 static {
414 lastOcc = new int[128];
415 optoSft = new int[10];
416 lastOcc[(int)'c']=1;
417 lastOcc[(int)'l']=2;
418 lastOcc[(int)'s']=5;
419 lastOcc[(int)'-']=6;
420 lastOcc[(int)'p']=7;
421 lastOcc[(int)'a']=8;
422 lastOcc[(int)'t']=9;
423 lastOcc[(int)'h']=10;
424 for (int i=0; i<9; i++)
425 optoSft[i]=10;
426 optoSft[9]=1;
427 }
428
429 private JarEntry getManEntry() {
430 if (manEntry == null) {
431 manEntry = getJarEntry(MANIFEST_NAME);
433 if (manEntry == null) {
434 String[] names = getMetaInfEntryNames();
437 if (names != null) {
438 for (int i = 0; i < names.length; i++) {
439 if (MANIFEST_NAME.equals(
440 names[i].toUpperCase(Locale.ENGLISH))) {
441 manEntry = getJarEntry(names[i]);
442 break;
443 }
444 }
445 }
446 }
447 }
448 return manEntry;
449 }
450
451 boolean hasClassPathAttribute() throws IOException {
456 if (computedHasClassPathAttribute) {
457 return hasClassPathAttribute;
458 }
459
460 hasClassPathAttribute = false;
461 if (!isKnownToNotHaveClassPathAttribute()) {
462 JarEntry manEntry = getManEntry();
463 if (manEntry != null) {
464 byte[] b = new byte[(int)manEntry.getSize()];
465 DataInputStream dis = new DataInputStream(
466 super.getInputStream(manEntry));
467 dis.readFully(b, 0, b.length);
468 dis.close();
469
470 int last = b.length - src.length;
471 int i = 0;
472 next:
473 while (i<=last) {
474 for (int j=9; j>=0; j--) {
475 char c = (char) b[i+j];
476 c = (((c-'A')|('Z'-c)) >= 0) ? (char)(c + 32) : c;
477 if (c != src[j]) {
478 i += Math.max(j + 1 - lastOcc[c&0x7F], optoSft[j]);
479 continue next;
480 }
481 }
482 hasClassPathAttribute = true;
483 break;
484 }
485 }
486 }
487 computedHasClassPathAttribute = true;
488 return hasClassPathAttribute;
489 }
490
491 private static String javaHome;
492 private static String[] jarNames;
493 private boolean isKnownToNotHaveClassPathAttribute() {
494 if (javaHome == null) {
499 javaHome = (String) AccessController.doPrivileged(
500 new GetPropertyAction("java.home"));
501 }
502 if (jarNames == null) {
503 String[] names = new String[10];
504 String fileSep = File.separator;
505 int i = 0;
506 names[i++] = fileSep + "rt.jar";
507 names[i++] = fileSep + "sunrsasign.jar";
508 names[i++] = fileSep + "jsse.jar";
509 names[i++] = fileSep + "jce.jar";
510 names[i++] = fileSep + "charsets.jar";
511 names[i++] = fileSep + "dnsns.jar";
512 names[i++] = fileSep + "ldapsec.jar";
513 names[i++] = fileSep + "localedata.jar";
514 names[i++] = fileSep + "sunjce_provider.jar";
515 names[i++] = fileSep + "sunpkcs11.jar";
516 jarNames = names;
517 }
518
519 String name = getName();
520 String localJavaHome = javaHome;
521 if (name.startsWith(localJavaHome)) {
522 String[] names = jarNames;
523 for (int i = 0; i < names.length; i++) {
524 if (name.endsWith(names[i])) {
525 return true;
526 }
527 }
528 }
529 return false;
530 }
531
532 private synchronized void ensureInitialization() {
533 try {
534 maybeInstantiateVerifier();
535 } catch (IOException e) {
536 throw new RuntimeException(e);
537 }
538 if (jv != null && !jvInitialized) {
539 initializeVerifier();
540 jvInitialized = true;
541 }
542 }
543
544 JarEntry newEntry(ZipEntry ze) {
545 return new JarFileEntry(ze);
546 }
547
548 Enumeration<String> entryNames(CodeSource[] cs) {
549 ensureInitialization();
550 if (jv != null) {
551 return jv.entryNames(this, cs);
552 }
553
554
558 boolean includeUnsigned = false;
559 for (int i=0; i < cs.length; i++) {
560 if (cs[i].getCodeSigners() == null) {
561 includeUnsigned = true;
562 break;
563 }
564 }
565 if (includeUnsigned) {
566 return unsignedEntryNames();
567 } else {
568 return new Enumeration<String>() {
569 public boolean hasMoreElements() {
570 return false;
571 }
572 public String nextElement() {
573 throw new NoSuchElementException();
574 }
575 };
576 }
577 }
578
579
584 Enumeration<JarEntry> entries2() {
585 ensureInitialization();
586 if (jv != null) {
587 return jv.entries2(this, super.entries());
588 }
589
590 final Enumeration enum_ = super.entries();
592 return new Enumeration<JarEntry>() {
593 ZipEntry entry;
594 public boolean hasMoreElements() {
595 if (entry != null) {
596 return true;
597 }
598 while (enum_.hasMoreElements()) {
599 ZipEntry ze = (ZipEntry) enum_.nextElement();
600 if (JarVerifier.isSigningRelated(ze.getName())) {
601 continue;
602 }
603 entry = ze;
604 return true;
605 }
606 return false;
607 }
608 public JarFileEntry nextElement() {
609 if (hasMoreElements()) {
610 ZipEntry ze = entry;
611 entry = null;
612 return new JarFileEntry(ze);
613 }
614 throw new NoSuchElementException();
615 }
616 };
617 }
618
619 CodeSource[] getCodeSources(URL url) {
620 ensureInitialization();
621 if (jv != null) {
622 return jv.getCodeSources(this, url);
623 }
624
625
629 Enumeration unsigned = unsignedEntryNames();
630 if (unsigned.hasMoreElements()) {
631 return new CodeSource[] { JarVerifier.getUnsignedCS(url) };
632 } else {
633 return null;
634 }
635 }
636
637 private Enumeration<String> unsignedEntryNames() {
638 final Enumeration entries = entries();
639 return new Enumeration<String>() {
640 String name;
641
642
646 public boolean hasMoreElements() {
647 if (name != null) {
648 return true;
649 }
650 while (entries.hasMoreElements()) {
651 String value;
652 ZipEntry e = (ZipEntry) entries.nextElement();
653 value = e.getName();
654 if (e.isDirectory() || JarVerifier.isSigningRelated(value)) {
655 continue;
656 }
657 name = value;
658 return true;
659 }
660 return false;
661 }
662 public String nextElement() {
663 if (hasMoreElements()) {
664 String value = name;
665 name = null;
666 return value;
667 }
668 throw new NoSuchElementException();
669 }
670 };
671 }
672
673 CodeSource getCodeSource(URL url, String name) {
674 ensureInitialization();
675 if (jv != null) {
676 if (jv.eagerValidation) {
677 CodeSource cs = null;
678 JarEntry je = getJarEntry(name);
679 if (je != null) {
680 cs = jv.getCodeSource(url, this, je);
681 } else {
682 cs = jv.getCodeSource(url, name);
683 }
684 return cs;
685 } else {
686 return jv.getCodeSource(url, name);
687 }
688 }
689
690 return JarVerifier.getUnsignedCS(url);
691 }
692
693 void setEagerValidation(boolean eager) {
694 try {
695 maybeInstantiateVerifier();
696 } catch (IOException e) {
697 throw new RuntimeException(e);
698 }
699 if (jv != null) {
700 jv.setEagerValidation(eager);
701 }
702 }
703
704 List getManifestDigests() {
705 ensureInitialization();
706 if (jv != null) {
707 return jv.getManifestDigests();
708 }
709 return new ArrayList();
710 }
711 }
712