1. /*
  2. * @(#)Subject.java 1.123 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 javax.security.auth;
  8. import java.util.*;
  9. import java.io.*;
  10. import java.lang.reflect.*;
  11. import java.text.MessageFormat;
  12. import java.security.AccessController;
  13. import java.security.AccessControlContext;
  14. import java.security.DomainCombiner;
  15. import java.security.Permission;
  16. import java.security.PermissionCollection;
  17. import java.security.Principal;
  18. import java.security.PrivilegedAction;
  19. import java.security.PrivilegedExceptionAction;
  20. import java.security.PrivilegedActionException;
  21. import java.security.ProtectionDomain;
  22. import sun.security.util.ResourcesMgr;
  23. import sun.security.util.SecurityConstants;
  24. /**
  25. * <p> A <code>Subject</code> represents a grouping of related information
  26. * for a single entity, such as a person.
  27. * Such information includes the Subject's identities as well as
  28. * its security-related attributes
  29. * (passwords and cryptographic keys, for example).
  30. *
  31. * <p> Subjects may potentially have multiple identities.
  32. * Each identity is represented as a <code>Principal</code>
  33. * within the <code>Subject</code>. Principals simply bind names to a
  34. * <code>Subject</code>. For example, a <code>Subject</code> that happens
  35. * to be a person, Alice, might have two Principals:
  36. * one which binds "Alice Bar", the name on her driver license,
  37. * to the <code>Subject</code>, and another which binds,
  38. * "999-99-9999", the number on her student identification card,
  39. * to the <code>Subject</code>. Both Principals refer to the same
  40. * <code>Subject</code> even though each has a different name.
  41. *
  42. * <p> A <code>Subject</code> may also own security-related attributes,
  43. * which are referred to as credentials.
  44. * Sensitive credentials that require special protection, such as
  45. * private cryptographic keys, are stored within a private credential
  46. * <code>Set</code>. Credentials intended to be shared, such as
  47. * public key certificates or Kerberos server tickets are stored
  48. * within a public credential <code>Set</code>. Different permissions
  49. * are required to access and modify the different credential Sets.
  50. *
  51. * <p> To retrieve all the Principals associated with a <code>Subject</code>,
  52. * invoke the <code>getPrincipals</code> method. To retrieve
  53. * all the public or private credentials belonging to a <code>Subject</code>,
  54. * invoke the <code>getPublicCredentials</code> method or
  55. * <code>getPrivateCredentials</code> method, respectively.
  56. * To modify the returned <code>Set</code> of Principals and credentials,
  57. * use the methods defined in the <code>Set</code> class.
  58. * For example:
  59. * <pre>
  60. * Subject subject;
  61. * Principal principal;
  62. * Object credential;
  63. *
  64. * // add a Principal and credential to the Subject
  65. * subject.getPrincipals().add(principal);
  66. * subject.getPublicCredentials().add(credential);
  67. * </pre>
  68. *
  69. * <p> This <code>Subject</code> class implements <code>Serializable</code>.
  70. * While the Principals associated with the <code>Subject</code> are serialized,
  71. * the credentials associated with the <code>Subject</code> are not.
  72. * Note that the <code>java.security.Principal</code> class
  73. * does not implement <code>Serializable</code>. Therefore all concrete
  74. * <code>Principal</code> implementations associated with Subjects
  75. * must implement <code>Serializable</code>.
  76. *
  77. * @version 1.123, 05/05/04
  78. * @see java.security.Principal
  79. * @see java.security.DomainCombiner
  80. */
  81. public final class Subject implements java.io.Serializable {
  82. private static final long serialVersionUID = -8308522755600156056L;
  83. /**
  84. * A <code>Set</code> that provides a view of all of this
  85. * Subject's Principals
  86. *
  87. * <p>
  88. *
  89. * @serial Each element in this set is a
  90. * <code>java.security.Principal</code>.
  91. * The set is a <code>Subject.SecureSet</code>.
  92. */
  93. Set principals;
  94. /**
  95. * Sets that provide a view of all of this
  96. * Subject's Credentials
  97. */
  98. transient Set pubCredentials;
  99. transient Set privCredentials;
  100. /**
  101. * Whether this Subject is read-only
  102. *
  103. * @serial
  104. */
  105. private volatile boolean readOnly = false;
  106. private static final int PRINCIPAL_SET = 1;
  107. private static final int PUB_CREDENTIAL_SET = 2;
  108. private static final int PRIV_CREDENTIAL_SET = 3;
  109. /**
  110. * Create an instance of a <code>Subject</code>
  111. * with an empty <code>Set</code> of Principals and empty
  112. * Sets of public and private credentials.
  113. *
  114. * <p> The newly constructed Sets check whether this <code>Subject</code>
  115. * has been set read-only before permitting subsequent modifications.
  116. * The newly created Sets also prevent illegal modifications
  117. * by ensuring that callers have sufficient permissions.
  118. *
  119. * <p> To modify the Principals Set, the caller must have
  120. * <code>AuthPermission("modifyPrincipals")</code>.
  121. * To modify the public credential Set, the caller must have
  122. * <code>AuthPermission("modifyPublicCredentials")</code>.
  123. * To modify the private credential Set, the caller must have
  124. * <code>AuthPermission("modifyPrivateCredentials")</code>.
  125. */
  126. public Subject() {
  127. this.principals = Collections.synchronizedSet
  128. (new SecureSet(this, PRINCIPAL_SET));
  129. this.pubCredentials = Collections.synchronizedSet
  130. (new SecureSet(this, PUB_CREDENTIAL_SET));
  131. this.privCredentials = Collections.synchronizedSet
  132. (new SecureSet(this, PRIV_CREDENTIAL_SET));
  133. }
  134. /**
  135. * Create an instance of a <code>Subject</code> with
  136. * Principals and credentials.
  137. *
  138. * <p> The Principals and credentials from the specified Sets
  139. * are copied into newly constructed Sets.
  140. * These newly created Sets check whether this <code>Subject</code>
  141. * has been set read-only before permitting subsequent modifications.
  142. * The newly created Sets also prevent illegal modifications
  143. * by ensuring that callers have sufficient permissions.
  144. *
  145. * <p> To modify the Principals Set, the caller must have
  146. * <code>AuthPermission("modifyPrincipals")</code>.
  147. * To modify the public credential Set, the caller must have
  148. * <code>AuthPermission("modifyPublicCredentials")</code>.
  149. * To modify the private credential Set, the caller must have
  150. * <code>AuthPermission("modifyPrivateCredentials")</code>.
  151. * <p>
  152. *
  153. * @param readOnly true if the <code>Subject</code> is to be read-only,
  154. * and false otherwise. <p>
  155. *
  156. * @param principals the <code>Set</code> of Principals
  157. * to be associated with this <code>Subject</code>. <p>
  158. *
  159. * @param pubCredentials the <code>Set</code> of public credentials
  160. * to be associated with this <code>Subject</code>. <p>
  161. *
  162. * @param privCredentials the <code>Set</code> of private credentials
  163. * to be associated with this <code>Subject</code>.
  164. *
  165. * @exception NullPointerException if the specified
  166. * <code>principals</code>, <code>pubCredentials</code>,
  167. * or <code>privCredentials</code> are <code>null</code>.
  168. */
  169. public Subject(boolean readOnly, Set<? extends Principal> principals,
  170. Set<?> pubCredentials, Set<?> privCredentials)
  171. {
  172. if (principals == null ||
  173. pubCredentials == null ||
  174. privCredentials == null)
  175. throw new NullPointerException
  176. (ResourcesMgr.getString("invalid null input(s)"));
  177. this.principals = Collections.synchronizedSet(new SecureSet
  178. (this, PRINCIPAL_SET, principals));
  179. this.pubCredentials = Collections.synchronizedSet(new SecureSet
  180. (this, PUB_CREDENTIAL_SET, pubCredentials));
  181. this.privCredentials = Collections.synchronizedSet(new SecureSet
  182. (this, PRIV_CREDENTIAL_SET, privCredentials));
  183. this.readOnly = readOnly;
  184. }
  185. /**
  186. * Set this <code>Subject</code> to be read-only.
  187. *
  188. * <p> Modifications (additions and removals) to this Subject's
  189. * <code>Principal</code> <code>Set</code> and
  190. * credential Sets will be disallowed.
  191. * The <code>destroy</code> operation on this Subject's credentials will
  192. * still be permitted.
  193. *
  194. * <p> Subsequent attempts to modify the Subject's <code>Principal</code>
  195. * and credential Sets will result in an
  196. * <code>IllegalStateException</code> being thrown.
  197. * Also, once a <code>Subject</code> is read-only,
  198. * it can not be reset to being writable again.
  199. *
  200. * <p>
  201. *
  202. * @exception SecurityException if the caller does not have permission
  203. * to set this <code>Subject</code> to be read-only.
  204. */
  205. public void setReadOnly() {
  206. java.lang.SecurityManager sm = System.getSecurityManager();
  207. if (sm != null) {
  208. sm.checkPermission(new AuthPermission("setReadOnly"));
  209. }
  210. this.readOnly = true;
  211. }
  212. /**
  213. * Query whether this <code>Subject</code> is read-only.
  214. *
  215. * <p>
  216. *
  217. * @return true if this <code>Subject</code> is read-only, false otherwise.
  218. */
  219. public boolean isReadOnly() {
  220. return this.readOnly;
  221. }
  222. /**
  223. * Get the <code>Subject</code> associated with the provided
  224. * <code>AccessControlContext</code>.
  225. *
  226. * <p> The <code>AccessControlContext</code> may contain many
  227. * Subjects (from nested <code>doAs</code> calls).
  228. * In this situation, the most recent <code>Subject</code> associated
  229. * with the <code>AccessControlContext</code> is returned.
  230. *
  231. * <p>
  232. *
  233. * @param acc the <code>AccessControlContext</code> from which to retrieve
  234. * the <code>Subject</code>.
  235. *
  236. * @return the <code>Subject</code> associated with the provided
  237. * <code>AccessControlContext</code>, or <code>null</code>
  238. * if no <code>Subject</code> is associated
  239. * with the provided <code>AccessControlContext</code>.
  240. *
  241. * @exception SecurityException if the caller does not have permission
  242. * to get the <code>Subject</code>. <p>
  243. *
  244. * @exception NullPointerException if the provided
  245. * <code>AccessControlContext</code> is <code>null</code>.
  246. */
  247. public static Subject getSubject(final AccessControlContext acc) {
  248. java.lang.SecurityManager sm = System.getSecurityManager();
  249. if (sm != null) {
  250. sm.checkPermission(new AuthPermission("getSubject"));
  251. }
  252. if (acc == null) {
  253. throw new NullPointerException(ResourcesMgr.getString
  254. ("invalid null AccessControlContext provided"));
  255. }
  256. // return the Subject from the DomainCombiner of the provided context
  257. return (Subject)AccessController.doPrivileged
  258. (new java.security.PrivilegedAction() {
  259. public Object run() {
  260. DomainCombiner dc = acc.getDomainCombiner();
  261. if (!(dc instanceof SubjectDomainCombiner))
  262. return null;
  263. SubjectDomainCombiner sdc = (SubjectDomainCombiner)dc;
  264. return sdc.getSubject();
  265. }
  266. });
  267. }
  268. /**
  269. * Perform work as a particular <code>Subject</code>.
  270. *
  271. * <p> This method first retrieves the current Thread's
  272. * <code>AccessControlContext</code> via
  273. * <code>AccessController.getContext</code>,
  274. * and then instantiates a new <code>AccessControlContext</code>
  275. * using the retrieved context along with a new
  276. * <code>SubjectDomainCombiner</code> (constructed using
  277. * the provided <code>Subject</code>).
  278. * Finally, this method invokes <code>AccessController.doPrivileged</code>,
  279. * passing it the provided <code>PrivilegedAction</code>,
  280. * as well as the newly constructed <code>AccessControlContext</code>.
  281. *
  282. * <p>
  283. *
  284. * @param subject the <code>Subject</code> that the specified
  285. * <code>action</code> will run as. This parameter
  286. * may be <code>null</code>. <p>
  287. *
  288. * @param action the code to be run as the specified
  289. * <code>Subject</code>. <p>
  290. *
  291. * @return the <code>Object</code> returned by the PrivilegedAction's
  292. * <code>run</code> method.
  293. *
  294. * @exception NullPointerException if the <code>PrivilegedAction</code>
  295. * is <code>null</code>. <p>
  296. *
  297. * @exception SecurityException if the caller does not have permission
  298. * to invoke this method.
  299. */
  300. public static Object doAs(final Subject subject,
  301. final java.security.PrivilegedAction action) {
  302. java.lang.SecurityManager sm = System.getSecurityManager();
  303. if (sm != null) {
  304. sm.checkPermission(SecurityConstants.DO_AS_PERMISSION);
  305. }
  306. if (action == null)
  307. throw new NullPointerException
  308. (ResourcesMgr.getString("invalid null action provided"));
  309. // set up the new Subject-based AccessControlContext
  310. // for doPrivileged
  311. final AccessControlContext currentAcc = AccessController.getContext();
  312. // call doPrivileged and push this new context on the stack
  313. return java.security.AccessController.doPrivileged
  314. (action,
  315. createContext(subject, currentAcc));
  316. }
  317. /**
  318. * Perform work as a particular <code>Subject</code>.
  319. *
  320. * <p> This method first retrieves the current Thread's
  321. * <code>AccessControlContext</code> via
  322. * <code>AccessController.getContext</code>,
  323. * and then instantiates a new <code>AccessControlContext</code>
  324. * using the retrieved context along with a new
  325. * <code>SubjectDomainCombiner</code> (constructed using
  326. * the provided <code>Subject</code>).
  327. * Finally, this method invokes <code>AccessController.doPrivileged</code>,
  328. * passing it the provided <code>PrivilegedExceptionAction</code>,
  329. * as well as the newly constructed <code>AccessControlContext</code>.
  330. *
  331. * <p>
  332. *
  333. * @param subject the <code>Subject</code> that the specified
  334. * <code>action</code> will run as. This parameter
  335. * may be <code>null</code>. <p>
  336. *
  337. * @param action the code to be run as the specified
  338. * <code>Subject</code>. <p>
  339. *
  340. * @return the <code>Object</code> returned by the
  341. * PrivilegedExceptionAction's <code>run</code> method.
  342. *
  343. * @exception PrivilegedActionException if the
  344. * <code>PrivilegedExceptionAction.run</code>
  345. * method throws a checked exception. <p>
  346. *
  347. * @exception NullPointerException if the specified
  348. * <code>PrivilegedExceptionAction</code> is
  349. * <code>null</code>. <p>
  350. *
  351. * @exception SecurityException if the caller does not have permission
  352. * to invoke this method.
  353. */
  354. public static Object doAs(final Subject subject,
  355. final java.security.PrivilegedExceptionAction action)
  356. throws java.security.PrivilegedActionException {
  357. java.lang.SecurityManager sm = System.getSecurityManager();
  358. if (sm != null) {
  359. sm.checkPermission(SecurityConstants.DO_AS_PERMISSION);
  360. }
  361. if (action == null)
  362. throw new NullPointerException
  363. (ResourcesMgr.getString("invalid null action provided"));
  364. // set up the new Subject-based AccessControlContext for doPrivileged
  365. final AccessControlContext currentAcc = AccessController.getContext();
  366. // call doPrivileged and push this new context on the stack
  367. return java.security.AccessController.doPrivileged
  368. (action,
  369. createContext(subject, currentAcc));
  370. }
  371. /**
  372. * Perform privileged work as a particular <code>Subject</code>.
  373. *
  374. * <p> This method behaves exactly as <code>Subject.doAs</code>,
  375. * except that instead of retrieving the current Thread's
  376. * <code>AccessControlContext</code>, it uses the provided
  377. * <code>AccessControlContext</code>. If the provided
  378. * <code>AccessControlContext</code> is <code>null</code>,
  379. * this method instantiates a new <code>AccessControlContext</code>
  380. * with an empty collection of ProtectionDomains.
  381. *
  382. * <p>
  383. *
  384. * @param subject the <code>Subject</code> that the specified
  385. * <code>action</code> will run as. This parameter
  386. * may be <code>null</code>. <p>
  387. *
  388. * @param action the code to be run as the specified
  389. * <code>Subject</code>. <p>
  390. *
  391. * @param acc the <code>AccessControlContext</code> to be tied to the
  392. * specified <i>subject</i> and <i>action</i>. <p>
  393. *
  394. * @return the <code>Object</code> returned by the PrivilegedAction's
  395. * <code>run</code> method.
  396. *
  397. * @exception NullPointerException if the <code>PrivilegedAction</code>
  398. * is <code>null</code>. <p>
  399. *
  400. * @exception SecurityException if the caller does not have permission
  401. * to invoke this method.
  402. */
  403. public static Object doAsPrivileged(final Subject subject,
  404. final java.security.PrivilegedAction action,
  405. final java.security.AccessControlContext acc) {
  406. java.lang.SecurityManager sm = System.getSecurityManager();
  407. if (sm != null) {
  408. sm.checkPermission(SecurityConstants.DO_AS_PRIVILEGED_PERMISSION);
  409. }
  410. if (action == null)
  411. throw new NullPointerException
  412. (ResourcesMgr.getString("invalid null action provided"));
  413. // set up the new Subject-based AccessControlContext
  414. // for doPrivileged
  415. final AccessControlContext callerAcc =
  416. (acc == null ?
  417. new AccessControlContext(new ProtectionDomain[0]) :
  418. acc);
  419. // call doPrivileged and push this new context on the stack
  420. return java.security.AccessController.doPrivileged
  421. (action,
  422. createContext(subject, callerAcc));
  423. }
  424. /**
  425. * Perform privileged work as a particular <code>Subject</code>.
  426. *
  427. * <p> This method behaves exactly as <code>Subject.doAs</code>,
  428. * except that instead of retrieving the current Thread's
  429. * <code>AccessControlContext</code>, it uses the provided
  430. * <code>AccessControlContext</code>. If the provided
  431. * <code>AccessControlContext</code> is <code>null</code>,
  432. * this method instantiates a new <code>AccessControlContext</code>
  433. * with an empty collection of ProtectionDomains.
  434. *
  435. * <p>
  436. *
  437. * @param subject the <code>Subject</code> that the specified
  438. * <code>action</code> will run as. This parameter
  439. * may be <code>null</code>. <p>
  440. *
  441. * @param action the code to be run as the specified
  442. * <code>Subject</code>. <p>
  443. *
  444. * @param acc the <code>AccessControlContext</code> to be tied to the
  445. * specified <i>subject</i> and <i>action</i>. <p>
  446. *
  447. * @return the <code>Object</code> returned by the
  448. * PrivilegedExceptionAction's <code>run</code> method.
  449. *
  450. * @exception PrivilegedActionException if the
  451. * <code>PrivilegedExceptionAction.run</code>
  452. * method throws a checked exception. <p>
  453. *
  454. * @exception NullPointerException if the specified
  455. * <code>PrivilegedExceptionAction</code> is
  456. * <code>null</code>. <p>
  457. *
  458. * @exception SecurityException if the caller does not have permission
  459. * to invoke this method.
  460. */
  461. public static Object doAsPrivileged(final Subject subject,
  462. final java.security.PrivilegedExceptionAction action,
  463. final java.security.AccessControlContext acc)
  464. throws java.security.PrivilegedActionException {
  465. java.lang.SecurityManager sm = System.getSecurityManager();
  466. if (sm != null) {
  467. sm.checkPermission(SecurityConstants.DO_AS_PRIVILEGED_PERMISSION);
  468. }
  469. if (action == null)
  470. throw new NullPointerException
  471. (ResourcesMgr.getString("invalid null action provided"));
  472. // set up the new Subject-based AccessControlContext for doPrivileged
  473. final AccessControlContext callerAcc =
  474. (acc == null ?
  475. new AccessControlContext(new ProtectionDomain[0]) :
  476. acc);
  477. // call doPrivileged and push this new context on the stack
  478. return java.security.AccessController.doPrivileged
  479. (action,
  480. createContext(subject, callerAcc));
  481. }
  482. private static AccessControlContext createContext(final Subject subject,
  483. final AccessControlContext acc) {
  484. return (AccessControlContext)
  485. java.security.AccessController.doPrivileged
  486. (new java.security.PrivilegedAction() {
  487. public Object run() {
  488. if (subject == null)
  489. return new AccessControlContext(acc, null);
  490. else
  491. return new AccessControlContext
  492. (acc,
  493. new SubjectDomainCombiner(subject));
  494. }
  495. });
  496. }
  497. /**
  498. * Return the <code>Set</code> of Principals associated with this
  499. * <code>Subject</code>. Each <code>Principal</code> represents
  500. * an identity for this <code>Subject</code>.
  501. *
  502. * <p> The returned <code>Set</code> is backed by this Subject's
  503. * internal <code>Principal</code> <code>Set</code>. Any modification
  504. * to the returned <code>Set</code> affects the internal
  505. * <code>Principal</code> <code>Set</code> as well.
  506. *
  507. * <p>
  508. *
  509. * @return The <code>Set</code> of Principals associated with this
  510. * <code>Subject</code>.
  511. */
  512. public Set<Principal> getPrincipals() {
  513. // always return an empty Set instead of null
  514. // so LoginModules can add to the Set if necessary
  515. return principals;
  516. }
  517. /**
  518. * Return a <code>Set</code> of Principals associated with this
  519. * <code>Subject</code> that are instances or subclasses of the specified
  520. * <code>Class</code>.
  521. *
  522. * <p> The returned <code>Set</code> is not backed by this Subject's
  523. * internal <code>Principal</code> <code>Set</code>. A new
  524. * <code>Set</code> is created and returned for each method invocation.
  525. * Modifications to the returned <code>Set</code>
  526. * will not affect the internal <code>Principal</code> <code>Set</code>.
  527. *
  528. * <p>
  529. *
  530. * @param c the returned <code>Set</code> of Principals will all be
  531. * instances of this class.
  532. *
  533. * @return a <code>Set</code> of Principals that are instances of the
  534. * specified <code>Class</code>.
  535. *
  536. * @exception NullPointerException if the specified <code>Class</code>
  537. * is <code>null</code>.
  538. */
  539. public <T extends Principal> Set<T> getPrincipals(Class<T> c) {
  540. if (c == null)
  541. throw new NullPointerException
  542. (ResourcesMgr.getString("invalid null Class provided"));
  543. // always return an empty Set instead of null
  544. // so LoginModules can add to the Set if necessary
  545. return new ClassSet(PRINCIPAL_SET, c);
  546. }
  547. /**
  548. * Return the <code>Set</code> of public credentials held by this
  549. * <code>Subject</code>.
  550. *
  551. * <p> The returned <code>Set</code> is backed by this Subject's
  552. * internal public Credential <code>Set</code>. Any modification
  553. * to the returned <code>Set</code> affects the internal public
  554. * Credential <code>Set</code> as well.
  555. *
  556. * <p>
  557. *
  558. * @return A <code>Set</code> of public credentials held by this
  559. * <code>Subject</code>.
  560. */
  561. public Set<Object> getPublicCredentials() {
  562. // always return an empty Set instead of null
  563. // so LoginModules can add to the Set if necessary
  564. return pubCredentials;
  565. }
  566. /**
  567. * Return the <code>Set</code> of private credentials held by this
  568. * <code>Subject</code>.
  569. *
  570. * <p> The returned <code>Set</code> is backed by this Subject's
  571. * internal private Credential <code>Set</code>. Any modification
  572. * to the returned <code>Set</code> affects the internal private
  573. * Credential <code>Set</code> as well.
  574. *
  575. * <p> A caller requires permissions to access the Credentials
  576. * in the returned <code>Set</code>, or to modify the
  577. * <code>Set</code> itself. A <code>SecurityException</code>
  578. * is thrown if the caller does not have the proper permissions.
  579. *
  580. * <p> While iterating through the <code>Set</code>,
  581. * a <code>SecurityException</code> is thrown
  582. * if the caller does not have permission to access a
  583. * particular Credential. The <code>Iterator</code>
  584. * is nevertheless advanced to next element in the <code>Set</code>.
  585. *
  586. * <p>
  587. *
  588. * @return A <code>Set</code> of private credentials held by this
  589. * <code>Subject</code>.
  590. */
  591. public Set<Object> getPrivateCredentials() {
  592. // XXX
  593. // we do not need a security check for
  594. // AuthPermission(getPrivateCredentials)
  595. // because we already restrict access to private credentials
  596. // via the PrivateCredentialPermission. all the extra AuthPermission
  597. // would do is protect the set operations themselves
  598. // (like size()), which don't seem security-sensitive.
  599. // always return an empty Set instead of null
  600. // so LoginModules can add to the Set if necessary
  601. return privCredentials;
  602. }
  603. /**
  604. * Return a <code>Set</code> of public credentials associated with this
  605. * <code>Subject</code> that are instances or subclasses of the specified
  606. * <code>Class</code>.
  607. *
  608. * <p> The returned <code>Set</code> is not backed by this Subject's
  609. * internal public Credential <code>Set</code>. A new
  610. * <code>Set</code> is created and returned for each method invocation.
  611. * Modifications to the returned <code>Set</code>
  612. * will not affect the internal public Credential <code>Set</code>.
  613. *
  614. * <p>
  615. *
  616. * @param c the returned <code>Set</code> of public credentials will all be
  617. * instances of this class.
  618. *
  619. * @return a <code>Set</code> of public credentials that are instances
  620. * of the specified <code>Class</code>.
  621. *
  622. * @exception NullPointerException if the specified <code>Class</code>
  623. * is <code>null</code>.
  624. */
  625. public <T> Set<T> getPublicCredentials(Class<T> c) {
  626. if (c == null)
  627. throw new NullPointerException
  628. (ResourcesMgr.getString("invalid null Class provided"));
  629. // always return an empty Set instead of null
  630. // so LoginModules can add to the Set if necessary
  631. return new ClassSet<T>(PUB_CREDENTIAL_SET, c);
  632. }
  633. /**
  634. * Return a <code>Set</code> of private credentials associated with this
  635. * <code>Subject</code> that are instances or subclasses of the specified
  636. * <code>Class</code>.
  637. *
  638. * <p> The caller must have permission to access all of the
  639. * requested Credentials, or a <code>SecurityException</code>
  640. * will be thrown.
  641. *
  642. * <p> The returned <code>Set</code> is not backed by this Subject's
  643. * internal private Credential <code>Set</code>. A new
  644. * <code>Set</code> is created and returned for each method invocation.
  645. * Modifications to the returned <code>Set</code>
  646. * will not affect the internal private Credential <code>Set</code>.
  647. *
  648. * <p>
  649. *
  650. * @param c the returned <code>Set</code> of private credentials will all be
  651. * instances of this class.
  652. *
  653. * @return a <code>Set</code> of private credentials that are instances
  654. * of the specified <code>Class</code>.
  655. *
  656. * @exception NullPointerException if the specified <code>Class</code>
  657. * is <code>null</code>.
  658. */
  659. public <T> Set<T> getPrivateCredentials(Class<T> c) {
  660. // XXX
  661. // we do not need a security check for
  662. // AuthPermission(getPrivateCredentials)
  663. // because we already restrict access to private credentials
  664. // via the PrivateCredentialPermission. all the extra AuthPermission
  665. // would do is protect the set operations themselves
  666. // (like size()), which don't seem security-sensitive.
  667. if (c == null)
  668. throw new NullPointerException
  669. (ResourcesMgr.getString("invalid null Class provided"));
  670. // always return an empty Set instead of null
  671. // so LoginModules can add to the Set if necessary
  672. return new ClassSet<T>(PRIV_CREDENTIAL_SET, c);
  673. }
  674. /**
  675. * Compares the specified Object with this <code>Subject</code>
  676. * for equality. Returns true if the given object is also a Subject
  677. * and the two <code>Subject</code> instances are equivalent.
  678. * More formally, two <code>Subject</code> instances are
  679. * equal if their <code>Principal</code> and <code>Credential</code>
  680. * Sets are equal.
  681. *
  682. * <p>
  683. *
  684. * @param o Object to be compared for equality with this
  685. * <code>Subject</code>.
  686. *
  687. * @return true if the specified Object is equal to this
  688. * <code>Subject</code>.
  689. *
  690. * @exception SecurityException if the caller does not have permission
  691. * to access the private credentials for this <code>Subject</code>,
  692. * or if the caller does not have permission to access the
  693. * private credentials for the provided <code>Subject</code>.
  694. */
  695. public boolean equals(Object o) {
  696. if (o == null)
  697. return false;
  698. if (this == o)
  699. return true;
  700. if (o instanceof Subject) {
  701. final Subject that = (Subject)o;
  702. // check the principal and credential sets
  703. Set thatPrincipals;
  704. synchronized(that.principals) {
  705. // avoid deadlock from dual locks
  706. thatPrincipals = new HashSet(that.principals);
  707. }
  708. if (!principals.equals(thatPrincipals)) {
  709. return false;
  710. }
  711. Set thatPubCredentials;
  712. synchronized(that.pubCredentials) {
  713. // avoid deadlock from dual locks
  714. thatPubCredentials = new HashSet(that.pubCredentials);
  715. }
  716. if (!pubCredentials.equals(thatPubCredentials)) {
  717. return false;
  718. }
  719. Set thatPrivCredentials;
  720. synchronized(that.privCredentials) {
  721. // avoid deadlock from dual locks
  722. thatPrivCredentials = new HashSet(that.privCredentials);
  723. }
  724. if (!privCredentials.equals(thatPrivCredentials)) {
  725. return false;
  726. }
  727. return true;
  728. }
  729. return false;
  730. }
  731. /**
  732. * Return the String representation of this <code>Subject</code>.
  733. *
  734. * <p>
  735. *
  736. * @return the String representation of this <code>Subject</code>.
  737. */
  738. public String toString() {
  739. return toString(true);
  740. }
  741. /**
  742. * package private convenience method to print out the Subject
  743. * without firing off a security check when trying to access
  744. * the Private Credentials
  745. */
  746. String toString(boolean includePrivateCredentials) {
  747. String s = new String(ResourcesMgr.getString("Subject:\n"));
  748. String suffix = new String();
  749. synchronized(principals) {
  750. Iterator pI = principals.iterator();
  751. while (pI.hasNext()) {
  752. Principal p = (Principal)pI.next();
  753. suffix = suffix + ResourcesMgr.getString("\tPrincipal: ") +
  754. p.toString() + ResourcesMgr.getString("\n");
  755. }
  756. }
  757. synchronized(pubCredentials) {
  758. Iterator pI = pubCredentials.iterator();
  759. while (pI.hasNext()) {
  760. Object o = pI.next();
  761. suffix = suffix +
  762. ResourcesMgr.getString("\tPublic Credential: ") +
  763. o.toString() + ResourcesMgr.getString("\n");
  764. }
  765. }
  766. if (includePrivateCredentials) {
  767. synchronized(privCredentials) {
  768. Iterator pI = privCredentials.iterator();
  769. while (pI.hasNext()) {
  770. try {
  771. Object o = pI.next();
  772. suffix += ResourcesMgr.getString
  773. ("\tPrivate Credential: ") +
  774. o.toString() +
  775. ResourcesMgr.getString("\n");
  776. } catch (SecurityException se) {
  777. suffix += ResourcesMgr.getString
  778. ("\tPrivate Credential inaccessible\n");
  779. break;
  780. }
  781. }
  782. }
  783. }
  784. return s + suffix;
  785. }
  786. /**
  787. * Returns a hashcode for this <code>Subject</code>.
  788. *
  789. * <p>
  790. *
  791. * @return a hashcode for this <code>Subject</code>.
  792. *
  793. * @exception SecurityException if the caller does not have permission
  794. * to access this Subject's private credentials.
  795. */
  796. public int hashCode() {
  797. /**
  798. * The hashcode is derived exclusive or-ing the
  799. * hashcodes of this Subject's Principals and credentials.
  800. *
  801. * If a particular credential was destroyed
  802. * (<code>credential.hashCode()</code> throws an
  803. * <code>IllegalStateException</code>),
  804. * the hashcode for that credential is derived via:
  805. * <code>credential.getClass().toString().hashCode()</code>.
  806. */
  807. int hashCode = 0;
  808. synchronized(principals) {
  809. Iterator pIterator = principals.iterator();
  810. while (pIterator.hasNext()) {
  811. Principal p = (Principal)pIterator.next();
  812. hashCode ^= p.hashCode();
  813. }
  814. }
  815. synchronized(pubCredentials) {
  816. Iterator pubCIterator = pubCredentials.iterator();
  817. while (pubCIterator.hasNext()) {
  818. hashCode ^= getCredHashCode(pubCIterator.next());
  819. }
  820. }
  821. return hashCode;
  822. }
  823. /**
  824. * get a credential's hashcode
  825. */
  826. private int getCredHashCode(Object o) {
  827. try {
  828. return o.hashCode();
  829. } catch (IllegalStateException ise) {
  830. return o.getClass().toString().hashCode();
  831. }
  832. }
  833. /**
  834. * Writes this object out to a stream (i.e., serializes it).
  835. */
  836. private void writeObject(java.io.ObjectOutputStream oos)
  837. throws java.io.IOException {
  838. synchronized(principals) {
  839. oos.defaultWriteObject();
  840. }
  841. }
  842. /**
  843. * Reads this object from a stream (i.e., deserializes it)
  844. */
  845. private void readObject(java.io.ObjectInputStream s)
  846. throws java.io.IOException, ClassNotFoundException {
  847. s.defaultReadObject();
  848. // The Credential <code>Set</code> is not serialized, but we do not
  849. // want the default deserialization routine to set it to null.
  850. this.pubCredentials = Collections.synchronizedSet
  851. (new SecureSet(this, PUB_CREDENTIAL_SET));
  852. this.privCredentials = Collections.synchronizedSet
  853. (new SecureSet(this, PRIV_CREDENTIAL_SET));
  854. }
  855. /**
  856. * Prevent modifications unless caller has permission.
  857. *
  858. * @serial include
  859. */
  860. private static class SecureSet
  861. extends AbstractSet
  862. implements java.io.Serializable {
  863. private static final long serialVersionUID = 7911754171111800359L;
  864. /**
  865. * @serialField this$0 Subject The outer Subject instance.
  866. * @serialField elements LinkedList The elements in this set.
  867. */
  868. private static final ObjectStreamField[] serialPersistentFields = {
  869. new ObjectStreamField("this$0", Subject.class),
  870. new ObjectStreamField("elements", LinkedList.class),
  871. new ObjectStreamField("which", int.class)
  872. };
  873. Subject subject;
  874. LinkedList elements;
  875. /**
  876. * @serial An integer identifying the type of objects contained
  877. * in this set. If <code>which == 1</code>,
  878. * this is a Principal set and all the elements are
  879. * of type <code>java.security.Principal</code>.
  880. * If <code>which == 2</code>, this is a public credential
  881. * set and all the elements are of type <code>Object</code>.
  882. * If <code>which == 3</code>, this is a private credential
  883. * set and all the elements are of type <code>Object</code>.
  884. */
  885. private int which;
  886. SecureSet(Subject subject, int which) {
  887. this.subject = subject;
  888. this.which = which;
  889. this.elements = new LinkedList();
  890. }
  891. SecureSet(Subject subject, int which, Set set) {
  892. this.subject = subject;
  893. this.which = which;
  894. this.elements = new LinkedList(set);
  895. }
  896. public int size() {
  897. return elements.size();
  898. }
  899. public Iterator iterator() {
  900. final LinkedList list = elements;
  901. return new Iterator() {
  902. ListIterator i = list.listIterator(0);
  903. public boolean hasNext() {return i.hasNext();}
  904. public Object next() {
  905. if (which != Subject.PRIV_CREDENTIAL_SET) {
  906. return i.next();
  907. }
  908. SecurityManager sm = System.getSecurityManager();
  909. if (sm != null) {
  910. try {
  911. sm.checkPermission(new PrivateCredentialPermission
  912. (list.get(i.nextIndex()).getClass().getName(),
  913. subject.getPrincipals()));
  914. } catch (SecurityException se) {
  915. i.next();
  916. throw (se);
  917. }
  918. }
  919. return i.next();
  920. }
  921. public void remove() {
  922. if (subject.isReadOnly()) {
  923. throw new IllegalStateException(ResourcesMgr.getString
  924. ("Subject is read-only"));
  925. }
  926. java.lang.SecurityManager sm = System.getSecurityManager();
  927. if (sm != null) {
  928. switch (which) {
  929. case Subject.PRINCIPAL_SET:
  930. sm.checkPermission(new AuthPermission
  931. ("modifyPrincipals"));
  932. break;
  933. case Subject.PUB_CREDENTIAL_SET:
  934. sm.checkPermission(new AuthPermission
  935. ("modifyPublicCredentials"));
  936. break;
  937. default:
  938. sm.checkPermission(new AuthPermission
  939. ("modifyPrivateCredentials"));
  940. break;
  941. }
  942. }
  943. i.remove();
  944. }
  945. };
  946. }
  947. public boolean add(Object o) {
  948. if (subject.isReadOnly()) {
  949. throw new IllegalStateException
  950. (ResourcesMgr.getString("Subject is read-only"));
  951. }
  952. java.lang.SecurityManager sm = System.getSecurityManager();
  953. if (sm != null) {
  954. switch (which) {
  955. case Subject.PRINCIPAL_SET:
  956. sm.checkPermission
  957. (new AuthPermission("modifyPrincipals"));
  958. break;
  959. case Subject.PUB_CREDENTIAL_SET:
  960. sm.checkPermission
  961. (new AuthPermission("modifyPublicCredentials"));
  962. break;
  963. default:
  964. sm.checkPermission
  965. (new AuthPermission("modifyPrivateCredentials"));
  966. break;
  967. }
  968. }
  969. switch (which) {
  970. case Subject.PRINCIPAL_SET:
  971. if (!(o instanceof Principal)) {
  972. throw new SecurityException(ResourcesMgr.getString
  973. ("attempting to add an object which is not an " +
  974. "instance of java.security.Principal to a " +
  975. "Subject's Principal Set"));
  976. }
  977. break;
  978. default:
  979. // ok to add Objects of any kind to credential sets
  980. break;
  981. }
  982. // check for duplicates
  983. if (!elements.contains(o))
  984. return elements.add(o);
  985. else
  986. return false;
  987. }
  988. public boolean remove(Object o) {
  989. final Iterator e = iterator();
  990. while (e.hasNext()) {
  991. Object next;
  992. if (which != Subject.PRIV_CREDENTIAL_SET) {
  993. next = e.next();
  994. } else {
  995. next = (Object)java.security.AccessController.doPrivileged
  996. (new java.security.PrivilegedAction() {
  997. public Object run() {
  998. return e.next();
  999. }
  1000. });
  1001. }
  1002. if (next == null) {
  1003. if (o == null) {
  1004. e.remove();
  1005. return true;
  1006. }
  1007. } else if (next.equals(o)) {
  1008. e.remove();
  1009. return true;
  1010. }
  1011. }
  1012. return false;
  1013. }
  1014. public boolean contains(Object o) {
  1015. final Iterator e = iterator();
  1016. while (e.hasNext()) {
  1017. Object next;
  1018. if (which != Subject.PRIV_CREDENTIAL_SET) {
  1019. next = e.next();
  1020. } else {
  1021. // For private credentials:
  1022. // If the caller does not have read permission for
  1023. // for o.getClass(), we throw a SecurityException.
  1024. // Otherwise we check the private cred set to see whether
  1025. // it contains the Object
  1026. SecurityManager sm = System.getSecurityManager();
  1027. if (sm != null) {
  1028. sm.checkPermission(new PrivateCredentialPermission
  1029. (o.getClass().getName(),
  1030. subject.getPrincipals()));
  1031. }
  1032. next = (Object)java.security.AccessController.doPrivileged
  1033. (new java.security.PrivilegedAction() {
  1034. public Object run() {
  1035. return e.next();
  1036. }
  1037. });
  1038. }
  1039. if (next == null) {
  1040. if (o == null) {
  1041. return true;
  1042. }
  1043. } else if (next.equals(o)) {
  1044. return true;
  1045. }
  1046. }
  1047. return false;
  1048. }
  1049. public boolean removeAll(Collection c) {
  1050. boolean modified = false;
  1051. final Iterator e = iterator();
  1052. while (e.hasNext()) {
  1053. Object next;
  1054. if (which != Subject.PRIV_CREDENTIAL_SET) {
  1055. next = e.next();
  1056. } else {
  1057. next = (Object)java.security.AccessController.doPrivileged
  1058. (new java.security.PrivilegedAction() {
  1059. public Object run() {
  1060. return e.next();
  1061. }
  1062. });
  1063. }
  1064. Iterator ce = c.iterator();
  1065. while (ce.hasNext()) {
  1066. Object o = ce.next();
  1067. if (next == null) {
  1068. if (o == null) {
  1069. e.remove();
  1070. modified = true;
  1071. break;
  1072. }
  1073. } else if (next.equals(o)) {
  1074. e.remove();
  1075. modified = true;
  1076. break;
  1077. }
  1078. }
  1079. }
  1080. return modified;
  1081. }
  1082. public boolean retainAll(Collection c) {
  1083. boolean modified = false;
  1084. boolean retain = false;
  1085. final Iterator e = iterator();
  1086. while (e.hasNext()) {
  1087. retain = false;
  1088. Object next;
  1089. if (which != Subject.PRIV_CREDENTIAL_SET) {
  1090. next = e.next();
  1091. } else {
  1092. next = (Object)java.security.AccessController.doPrivileged
  1093. (new java.security.PrivilegedAction() {
  1094. public Object run() {
  1095. return e.next();
  1096. }
  1097. });
  1098. }
  1099. Iterator ce = c.iterator();
  1100. while (ce.hasNext()) {
  1101. Object o = ce.next();
  1102. if (next == null) {
  1103. if (o == null) {
  1104. retain = true;
  1105. break;
  1106. }
  1107. } else if (next.equals(o)) {
  1108. retain = true;
  1109. break;
  1110. }
  1111. }
  1112. if (!retain) {
  1113. e.remove();
  1114. retain = false;
  1115. modified = true;
  1116. }
  1117. }
  1118. return modified;
  1119. }
  1120. public void clear() {
  1121. final Iterator e = iterator();
  1122. while (e.hasNext()) {
  1123. Object next;
  1124. if (which != Subject.PRIV_CREDENTIAL_SET) {
  1125. next = e.next();
  1126. } else {
  1127. next = (Object)java.security.AccessController.doPrivileged
  1128. (new java.security.PrivilegedAction() {
  1129. public Object run() {
  1130. return e.next();
  1131. }
  1132. });
  1133. }
  1134. e.remove();
  1135. }
  1136. }
  1137. /**
  1138. * Writes this object out to a stream (i.e., serializes it).
  1139. *
  1140. * <p>
  1141. *
  1142. * @serialData If this is a private credential set,
  1143. * a security check is performed to ensure that
  1144. * the caller has permission to access each credential
  1145. * in the set. If the security check passes,
  1146. * the set is serialized.
  1147. */
  1148. private synchronized void writeObject(java.io.ObjectOutputStream oos)
  1149. throws java.io.IOException {
  1150. if (which == Subject.PRIV_CREDENTIAL_SET) {
  1151. // check permissions before serializing
  1152. Iterator i = iterator();
  1153. while (i.hasNext()) {
  1154. i.next();
  1155. }
  1156. }
  1157. ObjectOutputStream.PutField fields = oos.putFields();
  1158. fields.put("this$0", subject);
  1159. fields.put("elements", elements);
  1160. fields.put("which", which);
  1161. oos.writeFields();
  1162. }
  1163. private void readObject(ObjectInputStream ois)
  1164. throws IOException, ClassNotFoundException
  1165. {
  1166. ObjectInputStream.GetField fields = ois.readFields();
  1167. subject = (Subject) fields.get("this$0", null);
  1168. elements = (LinkedList) fields.get("elements", null);
  1169. which = fields.get("which", 0);
  1170. }
  1171. }
  1172. /**
  1173. * This class implements a <code>Set</code> which returns only
  1174. * members that are an instance of a specified Class.
  1175. */
  1176. private class ClassSet<T> extends AbstractSet<T> {
  1177. private int which;
  1178. private Class c;
  1179. private Set<T> set;
  1180. ClassSet(int which, Class c) {
  1181. this.which = which;
  1182. this.c = c;
  1183. set = new HashSet();
  1184. switch (which) {
  1185. case Subject.PRINCIPAL_SET:
  1186. synchronized(principals) { populateSet(); }
  1187. break;
  1188. case Subject.PUB_CREDENTIAL_SET:
  1189. synchronized(pubCredentials) { populateSet(); }
  1190. break;
  1191. default:
  1192. synchronized(privCredentials) { populateSet(); }
  1193. break;
  1194. }
  1195. }
  1196. private void populateSet() {
  1197. final Iterator iterator;
  1198. switch(which) {
  1199. case Subject.PRINCIPAL_SET:
  1200. iterator = Subject.this.principals.iterator();
  1201. break;
  1202. case Subject.PUB_CREDENTIAL_SET:
  1203. iterator = Subject.this.pubCredentials.iterator();
  1204. break;
  1205. default:
  1206. iterator = Subject.this.privCredentials.iterator();
  1207. break;
  1208. }
  1209. // Check whether the caller has permisson to get
  1210. // credentials of Class c
  1211. while (iterator.hasNext()) {
  1212. Object next;
  1213. if (which == Subject.PRIV_CREDENTIAL_SET) {
  1214. next = (Object)java.security.AccessController.doPrivileged
  1215. (new java.security.PrivilegedAction() {
  1216. public Object run() {
  1217. return iterator.next();
  1218. }
  1219. });
  1220. } else {
  1221. next = iterator.next();
  1222. }
  1223. if (c.isAssignableFrom(next.getClass())) {
  1224. if (which != Subject.PRIV_CREDENTIAL_SET) {
  1225. set.add((T)next);
  1226. } else {
  1227. // Check permission for private creds
  1228. SecurityManager sm = System.getSecurityManager();
  1229. if (sm != null) {
  1230. sm.checkPermission(new PrivateCredentialPermission
  1231. (next.getClass().getName(),
  1232. Subject.this.getPrincipals()));
  1233. }
  1234. set.add((T)next);
  1235. }
  1236. }
  1237. }
  1238. }
  1239. public int size() {
  1240. return set.size();
  1241. }
  1242. public Iterator<T> iterator() {
  1243. return set.iterator();
  1244. }
  1245. public boolean add(T o) {
  1246. if (!o.getClass().isAssignableFrom(c)) {
  1247. MessageFormat form = new MessageFormat(ResourcesMgr.getString
  1248. ("attempting to add an object which is not an " +
  1249. "instance of class"));
  1250. Object[] source = {c.toString()};
  1251. throw new SecurityException(form.format(source));
  1252. }
  1253. return set.add(o);
  1254. }
  1255. }
  1256. }