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