1. /*
  2. * @(#)JarFile.java 1.58 04/05/05
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.util.jar;
  8. import java.io.*;
  9. import java.util.*;
  10. import java.util.zip.*;
  11. import java.security.CodeSigner;
  12. import java.security.cert.Certificate;
  13. import java.security.AccessController;
  14. import sun.security.action.GetPropertyAction;
  15. import sun.security.util.ManifestEntryVerifier;
  16. import sun.misc.SharedSecrets;
  17. /**
  18. * The <code>JarFile</code> class is used to read the contents of a jar file
  19. * from any file that can be opened with <code>java.io.RandomAccessFile</code>.
  20. * It extends the class <code>java.util.zip.ZipFile</code> with support
  21. * for reading an optional <code>Manifest</code> entry. The
  22. * <code>Manifest</code> can be used to specify meta-information about the
  23. * jar file and its entries.
  24. *
  25. * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
  26. * or method in this class will cause a {@link NullPointerException} to be
  27. * thrown.
  28. *
  29. * @author David Connelly
  30. * @version 1.58, 05/05/04
  31. * @see Manifest
  32. * @see java.util.zip.ZipFile
  33. * @see java.util.jar.JarEntry
  34. * @since 1.2
  35. */
  36. public
  37. class JarFile extends ZipFile {
  38. private Manifest man;
  39. private JarEntry manEntry;
  40. private boolean manLoaded;
  41. private JarVerifier jv;
  42. private boolean jvInitialized;
  43. private boolean verify;
  44. private boolean computedHasClassPathAttribute;
  45. private boolean hasClassPathAttribute;
  46. // Set up JavaUtilJarAccess in SharedSecrets
  47. static {
  48. SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl());
  49. }
  50. /**
  51. * The JAR manifest file name.
  52. */
  53. public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
  54. /**
  55. * Creates a new <code>JarFile</code> to read from the specified
  56. * file <code>name</code>. The <code>JarFile</code> will be verified if
  57. * it is signed.
  58. * @param name the name of the jar file to be opened for reading
  59. * @throws IOException if an I/O error has occurred
  60. * @throws SecurityException if access to the file is denied
  61. * by the SecurityManager
  62. */
  63. public JarFile(String name) throws IOException {
  64. this(new File(name), true, ZipFile.OPEN_READ);
  65. }
  66. /**
  67. * Creates a new <code>JarFile</code> to read from the specified
  68. * file <code>name</code>.
  69. * @param name the name of the jar file to be opened for reading
  70. * @param verify whether or not to verify the jar file if
  71. * it is signed.
  72. * @throws IOException if an I/O error has occurred
  73. * @throws SecurityException if access to the file is denied
  74. * by the SecurityManager
  75. */
  76. public JarFile(String name, boolean verify) throws IOException {
  77. this(new File(name), verify, ZipFile.OPEN_READ);
  78. }
  79. /**
  80. * Creates a new <code>JarFile</code> to read from the specified
  81. * <code>File</code> object. The <code>JarFile</code> will be verified if
  82. * it is signed.
  83. * @param file the jar file to be opened for reading
  84. * @throws IOException if an I/O error has occurred
  85. * @throws SecurityException if access to the file is denied
  86. * by the SecurityManager
  87. */
  88. public JarFile(File file) throws IOException {
  89. this(file, true, ZipFile.OPEN_READ);
  90. }
  91. /**
  92. * Creates a new <code>JarFile</code> to read from the specified
  93. * <code>File</code> object.
  94. * @param file the jar file to be opened for reading
  95. * @param verify whether or not to verify the jar file if
  96. * it is signed.
  97. * @throws IOException if an I/O error has occurred
  98. * @throws SecurityException if access to the file is denied
  99. * by the SecurityManager.
  100. */
  101. public JarFile(File file, boolean verify) throws IOException {
  102. this(file, verify, ZipFile.OPEN_READ);
  103. }
  104. /**
  105. * Creates a new <code>JarFile</code> to read from the specified
  106. * <code>File</code> object in the specified mode. The mode argument
  107. * must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>.
  108. *
  109. * @param file the jar file to be opened for reading
  110. * @param verify whether or not to verify the jar file if
  111. * it is signed.
  112. * @param mode the mode in which the file is to be opened
  113. * @throws IOException if an I/O error has occurred
  114. * @throws IllegalArgumentException
  115. * if the <tt>mode</tt> argument is invalid
  116. * @throws SecurityException if access to the file is denied
  117. * by the SecurityManager
  118. */
  119. public JarFile(File file, boolean verify, int mode) throws IOException {
  120. super(file, mode);
  121. this.verify = verify;
  122. }
  123. /**
  124. * Returns the jar file manifest, or <code>null</code> if none.
  125. *
  126. * @return the jar file manifest, or <code>null</code> if none
  127. *
  128. * @throws IllegalStateException
  129. * may be thrown if the jar file has been closed
  130. */
  131. public Manifest getManifest() throws IOException {
  132. if (!manLoaded) {
  133. JarEntry manEntry = getManEntry();
  134. // If found then load the manifest
  135. if (manEntry != null) {
  136. if (verify) {
  137. byte[] b = getBytes(manEntry);
  138. man = new Manifest(new ByteArrayInputStream(b));
  139. jv = new JarVerifier(man, b);
  140. } else {
  141. man = new Manifest(super.getInputStream(manEntry));
  142. }
  143. }
  144. manLoaded = true;
  145. }
  146. return man;
  147. }
  148. private native String[] getMetaInfEntryNames();
  149. /**
  150. * Returns the <code>JarEntry</code> for the given entry name or
  151. * <code>null</code> if not found.
  152. *
  153. * @param name the jar file entry name
  154. * @return the <code>JarEntry</code> for the given entry name or
  155. * <code>null</code> if not found.
  156. *
  157. * @throws IllegalStateException
  158. * may be thrown if the jar file has been closed
  159. *
  160. * @see java.util.jar.JarEntry
  161. */
  162. public JarEntry getJarEntry(String name) {
  163. return (JarEntry)getEntry(name);
  164. }
  165. /**
  166. * Returns the <code>ZipEntry</code> for the given entry name or
  167. * <code>null</code> if not found.
  168. *
  169. * @param name the jar file entry name
  170. * @return the <code>ZipEntry</code> for the given entry name or
  171. * <code>null</code> if not found
  172. *
  173. * @throws IllegalStateException
  174. * may be thrown if the jar file has been closed
  175. *
  176. * @see java.util.zip.ZipEntry
  177. */
  178. public ZipEntry getEntry(String name) {
  179. ZipEntry ze = super.getEntry(name);
  180. if (ze != null) {
  181. return new JarFileEntry(ze);
  182. }
  183. return null;
  184. }
  185. /**
  186. * Returns an enumeration of the zip file entries.
  187. */
  188. public Enumeration<JarEntry> entries() {
  189. final Enumeration enum_ = super.entries();
  190. return new Enumeration<JarEntry>() {
  191. public boolean hasMoreElements() {
  192. return enum_.hasMoreElements();
  193. }
  194. public JarFileEntry nextElement() {
  195. ZipEntry ze = (ZipEntry)enum_.nextElement();
  196. return new JarFileEntry(ze);
  197. }
  198. };
  199. }
  200. private class JarFileEntry extends JarEntry {
  201. JarFileEntry(ZipEntry ze) {
  202. super(ze);
  203. }
  204. public Attributes getAttributes() throws IOException {
  205. Manifest man = JarFile.this.getManifest();
  206. if (man != null) {
  207. return man.getAttributes(getName());
  208. } else {
  209. return null;
  210. }
  211. }
  212. public java.security.cert.Certificate[] getCertificates() {
  213. try {
  214. maybeInstantiateVerifier();
  215. } catch (IOException e) {
  216. throw new RuntimeException(e);
  217. }
  218. if (certs == null && jv != null) {
  219. Certificate[] cs = jv.getCerts(getName());
  220. if (cs != null) {
  221. certs = (Certificate[])cs.clone();
  222. }
  223. }
  224. return certs;
  225. }
  226. public CodeSigner[] getCodeSigners() {
  227. try {
  228. maybeInstantiateVerifier();
  229. } catch (IOException e) {
  230. throw new RuntimeException(e);
  231. }
  232. if (signers == null && jv != null) {
  233. CodeSigner[] csg = jv.getCodeSigners(getName());
  234. if (csg != null) {
  235. signers = (CodeSigner[])csg.clone();
  236. }
  237. }
  238. return signers;
  239. }
  240. }
  241. /*
  242. * Ensures that the JarVerifier has been created if one is
  243. * necessary (i.e., the jar appears to be signed.) This is done as
  244. * a quick check to avoid processing of the manifest for unsigned
  245. * jars.
  246. */
  247. private void maybeInstantiateVerifier() throws IOException {
  248. if (jv != null) {
  249. return;
  250. }
  251. if (verify) {
  252. String[] names = getMetaInfEntryNames();
  253. if (names != null) {
  254. for (int i = 0; i < names.length; i++) {
  255. String name = names[i].toUpperCase(Locale.ENGLISH);
  256. if (name.endsWith(".DSA") ||
  257. name.endsWith(".RSA") ||
  258. name.endsWith(".SF")) {
  259. // Assume since we found a signature-related file
  260. // that the jar is signed and that we therefore
  261. // need a JarVerifier and Manifest
  262. getManifest();
  263. return;
  264. }
  265. }
  266. }
  267. // No signature-related files; don't instantiate a
  268. // verifier
  269. verify = false;
  270. }
  271. }
  272. /*
  273. * Initializes the verifier object by reading all the manifest
  274. * entries and passing them to the verifier.
  275. */
  276. private void initializeVerifier() {
  277. ManifestEntryVerifier mev = null;
  278. // Verify "META-INF/" entries...
  279. try {
  280. String[] names = getMetaInfEntryNames();
  281. if (names != null) {
  282. for (int i = 0; i < names.length; i++) {
  283. JarEntry e = getJarEntry(names[i]);
  284. if (!e.isDirectory()) {
  285. if (mev == null) {
  286. mev = new ManifestEntryVerifier(man);
  287. }
  288. byte[] b = getBytes(e);
  289. if (b != null && b.length > 0) {
  290. jv.beginEntry(e, mev);
  291. jv.update(b.length, b, 0, b.length, mev);
  292. jv.update(-1, null, 0, 0, mev);
  293. }
  294. }
  295. }
  296. }
  297. } catch (IOException ex) {
  298. // if we had an error parsing any blocks, just
  299. // treat the jar file as being unsigned
  300. jv = null;
  301. verify = false;
  302. }
  303. // if after initializing the verifier we have nothing
  304. // signed, we null it out.
  305. if (jv != null) {
  306. jv.doneWithMeta();
  307. if (JarVerifier.debug != null) {
  308. JarVerifier.debug.println("done with meta!");
  309. }
  310. if (jv.nothingToVerify()) {
  311. if (JarVerifier.debug != null) {
  312. JarVerifier.debug.println("nothing to verify!");
  313. }
  314. jv = null;
  315. verify = false;
  316. }
  317. }
  318. }
  319. /*
  320. * Reads all the bytes for a given entry. Used to process the
  321. * META-INF files.
  322. */
  323. private byte[] getBytes(ZipEntry ze) throws IOException {
  324. byte[] b = new byte[(int)ze.getSize()];
  325. DataInputStream is = new DataInputStream(super.getInputStream(ze));
  326. is.readFully(b, 0, b.length);
  327. is.close();
  328. return b;
  329. }
  330. /**
  331. * Returns an input stream for reading the contents of the specified
  332. * zip file entry.
  333. * @param ze the zip file entry
  334. * @return an input stream for reading the contents of the specified
  335. * zip file entry
  336. * @throws ZipException if a zip file format error has occurred
  337. * @throws IOException if an I/O error has occurred
  338. * @throws SecurityException if any of the jar file entries
  339. * are incorrectly signed.
  340. * @throws IllegalStateException
  341. * may be thrown if the jar file has been closed
  342. */
  343. public synchronized InputStream getInputStream(ZipEntry ze)
  344. throws IOException
  345. {
  346. maybeInstantiateVerifier();
  347. if (jv == null) {
  348. return super.getInputStream(ze);
  349. }
  350. if (!jvInitialized) {
  351. initializeVerifier();
  352. jvInitialized = true;
  353. // could be set to null after a call to
  354. // initializeVerifier if we have nothing to
  355. // verify
  356. if (jv == null)
  357. return super.getInputStream(ze);
  358. }
  359. // wrap a verifier stream around the real stream
  360. return new JarVerifier.VerifierStream(man, (JarEntry)ze,
  361. super.getInputStream(ze), jv);
  362. }
  363. // Statics for hand-coded Boyer-Moore search in hasClassPathAttribute()
  364. // The bad character shift for "class-path"
  365. private static int[] lastOcc;
  366. // The good suffix shift for "class-path"
  367. private static int[] optoSft;
  368. // Initialize the shift arrays to search for "class-path"
  369. private static char[] src = {'c','l','a','s','s','-','p','a','t','h'};
  370. static {
  371. lastOcc = new int[128];
  372. optoSft = new int[10];
  373. lastOcc[(int)'c']=1;
  374. lastOcc[(int)'l']=2;
  375. lastOcc[(int)'s']=5;
  376. lastOcc[(int)'-']=6;
  377. lastOcc[(int)'p']=7;
  378. lastOcc[(int)'a']=8;
  379. lastOcc[(int)'t']=9;
  380. lastOcc[(int)'h']=10;
  381. for (int i=0; i<9; i++)
  382. optoSft[i]=10;
  383. optoSft[9]=1;
  384. }
  385. private JarEntry getManEntry() {
  386. if (manEntry == null) {
  387. // First look up manifest entry using standard name
  388. manEntry = getJarEntry(MANIFEST_NAME);
  389. if (manEntry == null) {
  390. // If not found, then iterate through all the "META-INF/"
  391. // entries to find a match.
  392. String[] names = getMetaInfEntryNames();
  393. if (names != null) {
  394. for (int i = 0; i < names.length; i++) {
  395. if (MANIFEST_NAME.equals(
  396. names[i].toUpperCase(Locale.ENGLISH))) {
  397. manEntry = getJarEntry(names[i]);
  398. break;
  399. }
  400. }
  401. }
  402. }
  403. }
  404. return manEntry;
  405. }
  406. // Returns true iff this jar file has a manifest with a class path
  407. // attribute. Returns false if there is no manifest or the manifest
  408. // does not contain a "Class-Path" attribute. Currently exported to
  409. // core libraries via sun.misc.SharedSecrets.
  410. boolean hasClassPathAttribute() throws IOException {
  411. if (computedHasClassPathAttribute) {
  412. return hasClassPathAttribute;
  413. }
  414. hasClassPathAttribute = false;
  415. if (!isKnownToNotHaveClassPathAttribute()) {
  416. JarEntry manEntry = getManEntry();
  417. if (manEntry != null) {
  418. byte[] b = new byte[(int)manEntry.getSize()];
  419. DataInputStream dis = new DataInputStream(
  420. super.getInputStream(manEntry));
  421. dis.readFully(b, 0, b.length);
  422. dis.close();
  423. int last = b.length - src.length;
  424. int i = 0;
  425. next:
  426. while (i<=last) {
  427. for (int j=9; j>=0; j--) {
  428. char c = (char) b[i+j];
  429. c = (((c-'A')|('Z'-c)) >= 0) ? (char)(c + 32) : c;
  430. if (c != src[j]) {
  431. i += Math.max(j + 1 - lastOcc[c&0x7F], optoSft[j]);
  432. continue next;
  433. }
  434. }
  435. hasClassPathAttribute = true;
  436. break;
  437. }
  438. }
  439. }
  440. computedHasClassPathAttribute = true;
  441. return hasClassPathAttribute;
  442. }
  443. private static String javaHome;
  444. private boolean isKnownToNotHaveClassPathAttribute() {
  445. // Optimize away even scanning of manifest for jar files we
  446. // deliver which don't have a class-path attribute. If one of
  447. // these jars is changed to include such an attribute this code
  448. // must be changed.
  449. if (javaHome == null) {
  450. javaHome = (String) AccessController.doPrivileged(
  451. new GetPropertyAction("java.home"));
  452. }
  453. String name = getName();
  454. String localJavaHome = javaHome;
  455. if (name.startsWith(localJavaHome)) {
  456. if (name.endsWith("rt.jar") ||
  457. name.endsWith("sunrsasign.jar") ||
  458. name.endsWith("jsse.jar") ||
  459. name.endsWith("jce.jar") ||
  460. name.endsWith("charsets.jar") ||
  461. name.endsWith("dnsns.jar") ||
  462. name.endsWith("ldapsec.jar") ||
  463. name.endsWith("localedata.jar") ||
  464. name.endsWith("sunjce_provider.jar")) {
  465. return true;
  466. }
  467. }
  468. return false;
  469. }
  470. }