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