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