1. /*
  2. * @(#)CodeSource.java 1.28 00/02/02
  3. *
  4. * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package java.security;
  11. import java.net.URL;
  12. import java.net.SocketPermission;
  13. import java.util.Hashtable;
  14. import java.io.ByteArrayInputStream;
  15. import java.io.IOException;
  16. import java.security.cert.*;
  17. /**
  18. *
  19. * <p>This class extends the concept of a codebase to
  20. * encapsulate not only the location (URL) but also the certificate(s)
  21. * that were used to verify signed code originating from that
  22. * location.
  23. *
  24. * @version 1.28, 02/02/00
  25. * @author Li Gong
  26. * @author Roland Schemers
  27. */
  28. public class CodeSource implements java.io.Serializable {
  29. /**
  30. * The code location.
  31. *
  32. * @serial
  33. */
  34. private URL location;
  35. // certificates
  36. private transient java.security.cert.Certificate certs[];
  37. // cached SocketPermission used for matchLocation
  38. private transient SocketPermission sp;
  39. /**
  40. * Constructs a CodeSource and associates it with the specified
  41. * location and set of certificates.
  42. *
  43. * @param url the location (URL).
  44. *
  45. * @param certs the certificate(s).
  46. */
  47. public CodeSource(URL url, java.security.cert.Certificate certs[]) {
  48. this.location = url;
  49. if (certs != null)
  50. this.certs = (java.security.cert.Certificate[]) certs.clone();
  51. }
  52. /**
  53. * Returns the hash code value for this object.
  54. *
  55. * @return a hash code value for this object.
  56. */
  57. public int hashCode() {
  58. if (location != null)
  59. return location.hashCode();
  60. else
  61. return 0;
  62. }
  63. /**
  64. * Tests for equality between the specified object and this
  65. * object. Two CodeSource objects are considered equal if their
  66. * locations are of identical value and if the two sets of
  67. * certificates are of identical values. It is not required that
  68. * the certificates be in the same order.
  69. *
  70. * @param obj the object to test for equality with this object.
  71. *
  72. * @return true if the objects are considered equal, false otherwise.
  73. */
  74. public boolean equals(Object obj) {
  75. if (obj == this)
  76. return true;
  77. // objects types must be equal
  78. if (!(obj instanceof CodeSource))
  79. return false;
  80. CodeSource cs = (CodeSource) obj;
  81. // URLs must match
  82. if (location == null) {
  83. // if location is null, then cs.location must be null as well
  84. if (cs.location != null) return false;
  85. } else {
  86. // if location is not null, then it must equal cs.location
  87. if (!location.equals(cs.location)) return false;
  88. }
  89. // certs must match
  90. if (certs == null) {
  91. // if certs is null, then cs.certs must be null as well
  92. if (cs.certs != null) return false;
  93. } else {
  94. // if certs is not null, then it must equal cs.certs
  95. // equality means that both arrays of certs are the same set
  96. // step 1 -- every cert in certs[] must match one in cs.certs[]
  97. if (cs.certs == null)
  98. return false;
  99. boolean match;
  100. for (int i = 0; i < certs.length; i++) {
  101. match = false;
  102. for (int j = 0; j < cs.certs.length; j++) {
  103. if (certs[i].equals(cs.certs[j])) {
  104. match = true;
  105. break;
  106. }
  107. }
  108. if (!match) return false;
  109. }
  110. // step 2 -- every key in cs.certs[] must match one in certs[]
  111. for (int i = 0; i < cs.certs.length; i++) {
  112. match = false;
  113. for (int j = 0; j < certs.length; j++) {
  114. if (cs.certs[i].equals(certs[j])) {
  115. match = true;
  116. break;
  117. }
  118. }
  119. if (!match) return false;
  120. }
  121. }
  122. // they must be equal if we got here...
  123. return true;
  124. }
  125. /**
  126. * Returns the location associated with this CodeSource.
  127. *
  128. * @return the location (URL).
  129. */
  130. public final URL getLocation() {
  131. /* since URL is practically immutable, returning itself is not
  132. a security problem */
  133. return this.location;
  134. }
  135. /**
  136. * Returns the certificates associated with this CodeSource.
  137. *
  138. * @return the certificates
  139. */
  140. public final java.security.cert.Certificate[] getCertificates() {
  141. /* return a clone copy, to avoid malicious modification to the
  142. original object */
  143. if (this.certs != null) {
  144. return (java.security.cert.Certificate[])this.certs.clone();
  145. } else {
  146. return null;
  147. }
  148. }
  149. /**
  150. * Returns true if this CodeSource object "implies" the specified CodeSource.
  151. * <P>
  152. * More specifically, this method makes the following checks, in order.
  153. * If any fail, it returns false. If they all succeed, it returns true.<p>
  154. * <ol>
  155. * <li> <i>codesource</i> must not be null.
  156. * <li> If this object's certificates are not null, then all
  157. * of this object's certificates must be present in <i>codesource</i>'s
  158. * certificates.
  159. * <li> If this object's location (getLocation()) is not null, then the
  160. * following checks are made against this object's location and
  161. * <i>codesource</i>'s:<p>
  162. * <ol>
  163. * <li> <i>codesource</i>'s location must not be null.
  164. *
  165. * <li> If this object's location
  166. * equals <i>codesource</i>'s location, then return true.
  167. *
  168. * <li> This object's protocol (getLocation().getProtocol()) must be
  169. * equal to <i>codesource</i>'s protocol.
  170. *
  171. * <li> If this object's host (getLocation().getHost()) is not null,
  172. * then the SocketPermission
  173. * constructed with this object's host must imply the
  174. * SocketPermission constructed with <i>codesource</i>'s host.
  175. *
  176. * <li> If this object's port (getLocation().getPort()) is not
  177. * equal to -1 (that is, if a port is specified), it must equal
  178. * <i>codesource</i>'s port.
  179. *
  180. * <li> If this object's file (getLocation().getFile()) doesn't equal
  181. * <i>codesource</i>'s file, then the following checks are made:
  182. * If this object's file ends with "/-",
  183. * then <i>codesource</i>'s file must start with this object's
  184. * file (exclusive the trailing "-").
  185. * If this object's file ends with a "/*",
  186. * then <i>codesource</i>'s file must start with this object's
  187. * file and must not have any further "/" separators.
  188. * If this object's file doesn't end with a "/",
  189. * then <i>codesource</i>'s file must match this object's
  190. * file with a '/' appended.
  191. *
  192. * <li> If this object's reference (getLocation().getRef()) is
  193. * not null, it must equal <i>codesource</i>'s reference.
  194. *
  195. * </ol>
  196. * </ol>
  197. * <p>
  198. * For example, the codesource objects with the following locations
  199. * and null certificates all imply
  200. * the codesource with the location "http://java.sun.com/classes/foo.jar"
  201. * and null certificates:
  202. * <pre>
  203. * http:
  204. * http://*.sun.com/classes/*
  205. * http://java.sun.com/classes/-
  206. * http://java.sun.com/classes/foo.jar
  207. * </pre>
  208. *
  209. * @param codesource CodeSource to compare against.
  210. *
  211. * @return true if the specified codesource is implied by this codesource,
  212. * false if not.
  213. */
  214. public boolean implies(CodeSource codesource)
  215. {
  216. if (codesource == null)
  217. return false;
  218. return matchCerts(codesource) && matchLocation(codesource);
  219. }
  220. /**
  221. * Returns true if all the certs in this
  222. * CodeSource are also in <i>that</i>.
  223. *
  224. * @param that the CodeSource to check against.
  225. */
  226. private boolean matchCerts(CodeSource that)
  227. {
  228. // match any key
  229. if (this.certs == null)
  230. return true;
  231. // if certs are null, and this.certs is not null, return false
  232. if (that.certs == null)
  233. return false;
  234. boolean match;
  235. for (int i=0; i < this.certs.length; i++) {
  236. match = false;
  237. for (int j=0; j < that.certs.length; j++) {
  238. if (this.certs[i].equals(that.certs[j])) {
  239. match = true;
  240. break;
  241. }
  242. }
  243. if (!match) return false;
  244. }
  245. return true;
  246. }
  247. /**
  248. * Returns true if two CodeSource's have the "same" location.
  249. *
  250. * @param that CodeSource to compare against
  251. */
  252. private boolean matchLocation(CodeSource that)
  253. {
  254. if (location == null) {
  255. return true;
  256. }
  257. if ((that == null) || (that.location == null))
  258. return false;
  259. if (location.equals(that.location))
  260. return true;
  261. if (!location.getProtocol().equals(that.location.getProtocol()))
  262. return false;
  263. if ((location.getHost() != null)) {
  264. if ((location.getHost().equals("") ||
  265. location.getHost().equals("localhost")) &&
  266. (that.location.getHost().equals("") ||
  267. that.location.getHost().equals("localhost"))) {
  268. // ok
  269. } else if (!location.getHost().equals(
  270. that.location.getHost())) {
  271. if (this.sp == null) {
  272. this.sp =
  273. new SocketPermission(location.getHost(),"resolve");
  274. }
  275. if (that.sp == null) {
  276. if (that.location.getHost() == null ||
  277. that.location.getHost().equals(""))
  278. return false;
  279. that.sp =
  280. new SocketPermission(that.location.getHost(),"resolve");
  281. }
  282. boolean ok = this.sp.implies(that.sp);
  283. if (!ok)
  284. return false;
  285. }
  286. }
  287. if (location.getPort() != -1) {
  288. if (location.getPort() != that.location.getPort())
  289. return false;
  290. }
  291. if (location.getFile().endsWith("/-")) {
  292. // Matches the directory and (recursively) all files
  293. // and subdirectories contained in that directory.
  294. // For example, "/a/b/-" implies anything that starts with
  295. // "/a/b/"
  296. String thisPath = location.getFile().substring(0,
  297. location.getFile().length()-1);
  298. if (!that.location.getFile().startsWith(thisPath))
  299. return false;
  300. } else if (location.getFile().endsWith("/*")) {
  301. // Matches the directory and all the files contained in that
  302. // directory.
  303. // For example, "/a/b/*" implies anything that starts with
  304. // "/a/b/" but has no further slashes
  305. int last = that.location.getFile().lastIndexOf('/');
  306. if (last == -1)
  307. return false;
  308. String thisPath = location.getFile().substring(0,
  309. location.getFile().length()-1);
  310. String thatPath = that.location.getFile().substring(0, last+1);
  311. if (!thatPath.equals(thisPath))
  312. return false;
  313. } else {
  314. // Exact matches only.
  315. // For example, "/a/b" and "/a/b/" both imply "/a/b/"
  316. if ((!that.location.getFile().equals(location.getFile()))
  317. && (!that.location.getFile().equals(location.getFile()+"/"))) {
  318. return false;
  319. }
  320. }
  321. if (location.getRef() == null)
  322. return true;
  323. else
  324. return location.getRef().equals(that.location.getRef());
  325. }
  326. /**
  327. * Returns a string describing this CodeSource, telling its
  328. * URL and certificates.
  329. *
  330. * @return information about this CodeSource.
  331. */
  332. public String toString() {
  333. StringBuffer sb = new StringBuffer();
  334. sb.append("(");
  335. sb.append(this.location);
  336. if (this.certs != null && this.certs.length > 0) {
  337. for (int i=0; i < this.certs.length; i++) {
  338. sb.append( " "+this.certs[i]);
  339. }
  340. } else {
  341. sb.append(" <no certificates>");
  342. }
  343. sb.append(")");
  344. return sb.toString();
  345. }
  346. /**
  347. * Writes this object out to a stream (i.e., serializes it).
  348. *
  349. * @serialData An initial <code>URL</code> is followed by an
  350. * <code>int</code> indicating the number of certificates to follow
  351. * (a value of "zero" denotes that there are no certificates associated
  352. * with this object).
  353. * Each certificate is written out starting with a <code>String</code>
  354. * denoting the certificate type, followed by an
  355. * <code>int</code> specifying the length of the certificate encoding,
  356. * followed by the certificate encoding itself which is written out as an
  357. * array of bytes.
  358. */
  359. private synchronized void writeObject(java.io.ObjectOutputStream oos)
  360. throws IOException
  361. {
  362. oos.defaultWriteObject();
  363. if (certs==null || certs.length==0) {
  364. oos.writeInt(0);
  365. } else {
  366. // write out the total number of certs
  367. oos.writeInt(certs.length);
  368. // write out each cert, including its type
  369. for (int i=0; i < certs.length; i++) {
  370. java.security.cert.Certificate cert = certs[i];
  371. try {
  372. oos.writeUTF(cert.getType());
  373. byte[] encoded = cert.getEncoded();
  374. oos.writeInt(encoded.length);
  375. oos.write(encoded);
  376. } catch (CertificateEncodingException cee) {
  377. throw new IOException(cee.getMessage());
  378. }
  379. }
  380. }
  381. }
  382. /**
  383. * Restores this object from a stream (i.e., deserializes it).
  384. */
  385. private synchronized void readObject(java.io.ObjectInputStream ois)
  386. throws IOException, ClassNotFoundException
  387. {
  388. CertificateFactory cf;
  389. Hashtable cfs=null;
  390. ois.defaultReadObject();
  391. // process any new-style certs in the stream (if present)
  392. int size = ois.readInt();
  393. if (size > 0) {
  394. // we know of 3 different cert types: X.509, PGP, SDSI, which
  395. // could all be present in the stream at the same time
  396. cfs = new Hashtable(3);
  397. this.certs = new java.security.cert.Certificate[size];
  398. }
  399. for (int i=0; i<size; i++) {
  400. // read the certificate type, and instantiate a certificate
  401. // factory of that type (reuse existing factory if possible)
  402. String certType = ois.readUTF();
  403. if (cfs.containsKey(certType)) {
  404. // reuse certificate factory
  405. cf = (CertificateFactory)cfs.get(certType);
  406. } else {
  407. // create new certificate factory
  408. try {
  409. cf = CertificateFactory.getInstance(certType);
  410. } catch (CertificateException ce) {
  411. throw new ClassNotFoundException
  412. ("Certificate factory for "+certType+" not found");
  413. }
  414. // store the certificate factory so we can reuse it later
  415. cfs.put(certType, cf);
  416. }
  417. // parse the certificate
  418. byte[] encoded=null;
  419. try {
  420. encoded = new byte[ois.readInt()];
  421. } catch (OutOfMemoryError oome) {
  422. throw new IOException("Certificate too big");
  423. }
  424. ois.readFully(encoded);
  425. ByteArrayInputStream bais = new ByteArrayInputStream(encoded);
  426. try {
  427. this.certs[i] = cf.generateCertificate(bais);
  428. } catch (CertificateException ce) {
  429. throw new IOException(ce.getMessage());
  430. }
  431. bais.close();
  432. }
  433. }
  434. }