1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package javax.security.auth; 19 20 import java.io.IOException; 21 import java.io.ObjectInputStream; 22 import java.io.ObjectOutputStream; 23 import java.io.Serializable; 24 import java.security.AccessControlContext; 25 import java.security.AccessController; 26 import java.security.DomainCombiner; 27 import java.security.Permission; 28 import java.security.Principal; 29 import java.security.PrivilegedAction; 30 import java.security.PrivilegedActionException; 31 import java.security.PrivilegedExceptionAction; 32 import java.security.ProtectionDomain; 33 import java.util.AbstractSet; 34 import java.util.Collection; 35 import java.util.Iterator; 36 import java.util.LinkedList; 37 import java.util.Set; 38 39 /** 40 * The central class of the {@code javax.security.auth} package representing an 41 * authenticated user or entity (both referred to as "subject"). IT defines also 42 * the static methods that allow code to be run, and do modifications according 43 * to the subject's permissions. 44 * <p> 45 * A subject has the following features: 46 * <ul> 47 * <li>A set of {@code Principal} objects specifying the identities bound to a 48 * {@code Subject} that distinguish it.</li> 49 * <li>Credentials (public and private) such as certificates, keys, or 50 * authentication proofs such as tickets</li> 51 * </ul> 52 */ 53 public final class Subject implements Serializable { 54 55 private static final long serialVersionUID = -8308522755600156056L; 56 57 private static final AuthPermission _AS = new AuthPermission("doAs"); 58 59 private static final AuthPermission _AS_PRIVILEGED = new AuthPermission( 60 "doAsPrivileged"); 61 62 private static final AuthPermission _SUBJECT = new AuthPermission( 63 "getSubject"); 64 65 private static final AuthPermission _PRINCIPALS = new AuthPermission( 66 "modifyPrincipals"); 67 68 private static final AuthPermission _PRIVATE_CREDENTIALS = new AuthPermission( 69 "modifyPrivateCredentials"); 70 71 private static final AuthPermission _PUBLIC_CREDENTIALS = new AuthPermission( 72 "modifyPublicCredentials"); 73 74 private static final AuthPermission _READ_ONLY = new AuthPermission( 75 "setReadOnly"); 76 77 private final Set<Principal> principals; 78 79 private boolean readOnly; 80 81 // set of private credentials 82 private transient SecureSet<Object> privateCredentials; 83 84 // set of public credentials 85 private transient SecureSet<Object> publicCredentials; 86 87 /** 88 * The default constructor initializing the sets of public and private 89 * credentials and principals with the empty set. 90 */ 91 public Subject() { 92 principals = new SecureSet<Principal>(_PRINCIPALS); 93 publicCredentials = new SecureSet<Object>(_PUBLIC_CREDENTIALS); 94 privateCredentials = new SecureSet<Object>(_PRIVATE_CREDENTIALS); 95 96 readOnly = false; 97 } 98 99 /** 100 * The constructor for the subject, setting its public and private 101 * credentials and principals according to the arguments. 102 * 103 * @param readOnly 104 * {@code true} if this {@code Subject} is read-only, thus 105 * preventing any modifications to be done. 106 * @param subjPrincipals 107 * the set of Principals that are attributed to this {@code 108 * Subject}. 109 * @param pubCredentials 110 * the set of public credentials that distinguish this {@code 111 * Subject}. 112 * @param privCredentials 113 * the set of private credentials that distinguish this {@code 114 * Subject}. 115 */ 116 public Subject(boolean readOnly, Set<? extends Principal> subjPrincipals, 117 Set<?> pubCredentials, Set<?> privCredentials) { 118 119 if (subjPrincipals == null || pubCredentials == null || privCredentials == null) { 120 throw new NullPointerException(); 121 } 122 123 principals = new SecureSet<Principal>(_PRINCIPALS, subjPrincipals); 124 publicCredentials = new SecureSet<Object>(_PUBLIC_CREDENTIALS, pubCredentials); 125 privateCredentials = new SecureSet<Object>(_PRIVATE_CREDENTIALS, privCredentials); 126 127 this.readOnly = readOnly; 128 } 129 130 /** 131 * Runs the code defined by {@code action} using the permissions granted to 132 * the {@code Subject} itself and to the code as well. 133 * 134 * @param subject 135 * the distinguished {@code Subject}. 136 * @param action 137 * the code to be run. 138 * @return the {@code Object} returned when running the {@code action}. 139 */ 140 @SuppressWarnings("unchecked") 141 public static <T> T doAs(Subject subject, PrivilegedAction<T> action) { 142 return doAs_PrivilegedAction(subject, action, AccessController.getContext()); 143 } 144 145 /** 146 * Run the code defined by {@code action} using the permissions granted to 147 * the {@code Subject} and to the code itself, additionally providing a more 148 * specific context. 149 * 150 * @param subject 151 * the distinguished {@code Subject}. 152 * @param action 153 * the code to be run. 154 * @param context 155 * the specific context in which the {@code action} is invoked. 156 * if {@code null} a new {@link AccessControlContext} is 157 * instantiated. 158 * @return the {@code Object} returned when running the {@code action}. 159 */ 160 @SuppressWarnings("unchecked") 161 public static <T> T doAsPrivileged(Subject subject, PrivilegedAction<T> action, 162 AccessControlContext context) { 163 if (context == null) { 164 return doAs_PrivilegedAction(subject, action, new AccessControlContext( 165 new ProtectionDomain[0])); 166 } 167 return doAs_PrivilegedAction(subject, action, context); 168 } 169 170 // instantiates a new context and passes it to AccessController 171 @SuppressWarnings("unchecked") 172 private static <T> T doAs_PrivilegedAction(Subject subject, PrivilegedAction<T> action, 173 final AccessControlContext context) { 174 175 AccessControlContext newContext; 176 177 final SubjectDomainCombiner combiner; 178 if (subject == null) { 179 // performance optimization 180 // if subject is null there is nothing to combine 181 combiner = null; 182 } else { 183 combiner = new SubjectDomainCombiner(subject); 184 } 185 186 PrivilegedAction dccAction = new PrivilegedAction() { 187 public Object run() { 188 return new AccessControlContext(context, combiner); 189 } 190 }; 191 192 newContext = (AccessControlContext) AccessController.doPrivileged(dccAction); 193 194 return AccessController.doPrivileged(action, newContext); 195 } 196 197 /** 198 * Runs the code defined by {@code action} using the permissions granted to 199 * the subject and to the code itself. 200 * 201 * @param subject 202 * the distinguished {@code Subject}. 203 * @param action 204 * the code to be run. 205 * @return the {@code Object} returned when running the {@code action}. 206 * @throws PrivilegedActionException 207 * if running the {@code action} throws an exception. 208 */ 209 @SuppressWarnings("unchecked") 210 public static <T> T doAs(Subject subject, PrivilegedExceptionAction<T> action) 211 throws PrivilegedActionException { 212 return doAs_PrivilegedExceptionAction(subject, action, AccessController.getContext()); 213 } 214 215 /** 216 * Runs the code defined by {@code action} using the permissions granted to 217 * the subject and to the code itself, additionally providing a more 218 * specific context. 219 * 220 * @param subject 221 * the distinguished {@code Subject}. 222 * @param action 223 * the code to be run. 224 * @param context 225 * the specific context in which the {@code action} is invoked. 226 * if {@code null} a new {@link AccessControlContext} is 227 * instantiated. 228 * @return the {@code Object} returned when running the {@code action}. 229 * @throws PrivilegedActionException 230 * if running the {@code action} throws an exception. 231 */ 232 @SuppressWarnings("unchecked") 233 public static <T> T doAsPrivileged(Subject subject, 234 PrivilegedExceptionAction<T> action, AccessControlContext context) 235 throws PrivilegedActionException { 236 if (context == null) { 237 return doAs_PrivilegedExceptionAction(subject, action, 238 new AccessControlContext(new ProtectionDomain[0])); 239 } 240 return doAs_PrivilegedExceptionAction(subject, action, context); 241 } 242 243 // instantiates a new context and passes it to AccessController 244 @SuppressWarnings("unchecked") 245 private static <T> T doAs_PrivilegedExceptionAction(Subject subject, 246 PrivilegedExceptionAction<T> action, final AccessControlContext context) 247 throws PrivilegedActionException { 248 249 AccessControlContext newContext; 250 251 final SubjectDomainCombiner combiner; 252 if (subject == null) { 253 // performance optimization 254 // if subject is null there is nothing to combine 255 combiner = null; 256 } else { 257 combiner = new SubjectDomainCombiner(subject); 258 } 259 260 PrivilegedAction<AccessControlContext> dccAction = new PrivilegedAction<AccessControlContext>() { 261 public AccessControlContext run() { 262 return new AccessControlContext(context, combiner); 263 } 264 }; 265 266 newContext = AccessController.doPrivileged(dccAction); 267 268 return AccessController.doPrivileged(action, newContext); 269 } 270 271 /** 272 * Checks two Subjects for equality. More specifically if the principals, 273 * public and private credentials are equal, equality for two {@code 274 * Subjects} is implied. 275 * 276 * @param obj 277 * the {@code Object} checked for equality with this {@code 278 * Subject}. 279 * @return {@code true} if the specified {@code Subject} is equal to this 280 * one. 281 */ 282 @Override 283 public boolean equals(Object obj) { 284 285 if (this == obj) { 286 return true; 287 } 288 289 if (obj == null || this.getClass() != obj.getClass()) { 290 return false; 291 } 292 293 Subject that = (Subject) obj; 294 295 if (principals.equals(that.principals) 296 && publicCredentials.equals(that.publicCredentials) 297 && privateCredentials.equals(that.privateCredentials)) { 298 return true; 299 } 300 return false; 301 } 302 303 /** 304 * Returns this {@code Subject}'s {@link Principal}. 305 * 306 * @return this {@code Subject}'s {@link Principal}. 307 */ 308 public Set<Principal> getPrincipals() { 309 return principals; 310 } 311 312 313 /** 314 * Returns this {@code Subject}'s {@link Principal} which is a subclass of 315 * the {@code Class} provided. 316 * 317 * @param c 318 * the {@code Class} as a criteria which the {@code Principal} 319 * returned must satisfy. 320 * @return this {@code Subject}'s {@link Principal}. Modifications to the 321 * returned set of {@code Principal}s do not affect this {@code 322 * Subject}'s set. 323 */ 324 public <T extends Principal> Set<T> getPrincipals(Class<T> c) { 325 return ((SecureSet<Principal>) principals).get(c); 326 } 327 328 /** 329 * Returns the private credentials associated with this {@code Subject}. 330 * 331 * @return the private credentials associated with this {@code Subject}. 332 */ 333 public Set<Object> getPrivateCredentials() { 334 return privateCredentials; 335 } 336 337 /** 338 * Returns this {@code Subject}'s private credentials which are a subclass 339 * of the {@code Class} provided. 340 * 341 * @param c 342 * the {@code Class} as a criteria which the private credentials 343 * returned must satisfy. 344 * @return this {@code Subject}'s private credentials. Modifications to the 345 * returned set of credentials do not affect this {@code Subject}'s 346 * credentials. 347 */ 348 public <T> Set<T> getPrivateCredentials(Class<T> c) { 349 return privateCredentials.get(c); 350 } 351 352 /** 353 * Returns the public credentials associated with this {@code Subject}. 354 * 355 * @return the public credentials associated with this {@code Subject}. 356 */ 357 public Set<Object> getPublicCredentials() { 358 return publicCredentials; 359 } 360 361 362 /** 363 * Returns this {@code Subject}'s public credentials which are a subclass of 364 * the {@code Class} provided. 365 * 366 * @param c 367 * the {@code Class} as a criteria which the public credentials 368 * returned must satisfy. 369 * @return this {@code Subject}'s public credentials. Modifications to the 370 * returned set of credentials do not affect this {@code Subject}'s 371 * credentials. 372 */ 373 public <T> Set<T> getPublicCredentials(Class<T> c) { 374 return publicCredentials.get(c); 375 } 376 377 /** 378 * Returns a hash code of this {@code Subject}. 379 * 380 * @return a hash code of this {@code Subject}. 381 */ 382 @Override 383 public int hashCode() { 384 return principals.hashCode() + privateCredentials.hashCode() 385 + publicCredentials.hashCode(); 386 } 387 388 /** 389 * Prevents from modifications being done to the credentials and {@link 390 * Principal} sets. After setting it to read-only this {@code Subject} can 391 * not be made writable again. The destroy method on the credentials still 392 * works though. 393 */ 394 public void setReadOnly() { 395 readOnly = true; 396 } 397 398 /** 399 * Returns whether this {@code Subject} is read-only or not. 400 * 401 * @return whether this {@code Subject} is read-only or not. 402 */ 403 public boolean isReadOnly() { 404 return readOnly; 405 } 406 407 /** 408 * Returns a {@code String} representation of this {@code Subject}. 409 * 410 * @return a {@code String} representation of this {@code Subject}. 411 */ 412 @Override 413 public String toString() { 414 StringBuilder buf = new StringBuilder("Subject:\n"); 415 416 Iterator<?> it = principals.iterator(); 417 while (it.hasNext()) { 418 buf.append("\tPrincipal: "); 419 buf.append(it.next()); 420 buf.append('\n'); 421 } 422 423 it = publicCredentials.iterator(); 424 while (it.hasNext()) { 425 buf.append("\tPublic Credential: "); 426 buf.append(it.next()); 427 buf.append('\n'); 428 } 429 430 int offset = buf.length() - 1; 431 it = privateCredentials.iterator(); 432 try { 433 while (it.hasNext()) { 434 buf.append("\tPrivate Credential: "); 435 buf.append(it.next()); 436 buf.append('\n'); 437 } 438 } catch (SecurityException e) { 439 buf.delete(offset, buf.length()); 440 buf.append("\tPrivate Credentials: no accessible information\n"); 441 } 442 return buf.toString(); 443 } 444 445 private void readObject(ObjectInputStream in) throws IOException, 446 ClassNotFoundException { 447 448 in.defaultReadObject(); 449 450 publicCredentials = new SecureSet<Object>(_PUBLIC_CREDENTIALS); 451 privateCredentials = new SecureSet<Object>(_PRIVATE_CREDENTIALS); 452 } 453 454 private void writeObject(ObjectOutputStream out) throws IOException { 455 out.defaultWriteObject(); 456 } 457 458 /** 459 * Returns the {@code Subject} that was last associated with the {@code 460 * context} provided as argument. 461 * 462 * @param context 463 * the {@code context} that was associated with the 464 * {@code Subject}. 465 * @return the {@code Subject} that was last associated with the {@code 466 * context} provided as argument. 467 */ 468 public static Subject getSubject(final AccessControlContext context) { 469 if (context == null) { 470 throw new NullPointerException("AccessControlContext cannot be null"); 471 } 472 PrivilegedAction<DomainCombiner> action = new PrivilegedAction<DomainCombiner>() { 473 public DomainCombiner run() { 474 return context.getDomainCombiner(); 475 } 476 }; 477 DomainCombiner combiner = AccessController.doPrivileged(action); 478 479 if ((combiner == null) || !(combiner instanceof SubjectDomainCombiner)) { 480 return null; 481 } 482 return ((SubjectDomainCombiner) combiner).getSubject(); 483 } 484 485 private void checkState() { 486 if (readOnly) { 487 throw new IllegalStateException("Set is read-only"); 488 } 489 } 490 491 private final class SecureSet<SST> extends AbstractSet<SST> implements Serializable { 492 493 /** 494 * Compatibility issue: see comments for setType variable 495 */ 496 private static final long serialVersionUID = 7911754171111800359L; 497 498 private LinkedList<SST> elements; 499 500 /* 501 * Is used to define a set type for serialization. 502 * 503 * A type can be principal, priv. or pub. credential set. The spec. 504 * doesn't clearly says that priv. and pub. credential sets can be 505 * serialized and what classes they are. It is only possible to figure 506 * out from writeObject method comments that priv. credential set is 507 * serializable and it is an instance of SecureSet class. So pub. 508 * credential was implemented by analogy 509 * 510 * Compatibility issue: the class follows its specified serial form. 511 * Also according to the serialization spec. adding new field is a 512 * compatible change. So is ok for principal set (because the default 513 * value for integer is zero). But priv. or pub. credential set it is 514 * not compatible because most probably other implementations resolve 515 * this issue in other way 516 */ 517 private int setType; 518 519 // Defines principal set for serialization. 520 private static final int SET_Principal = 0; 521 522 // Defines private credential set for serialization. 523 private static final int SET_PrivCred = 1; 524 525 // Defines public credential set for serialization. 526 private static final int SET_PubCred = 2; 527 528 // permission required to modify set 529 private transient AuthPermission permission; 530 531 protected SecureSet(AuthPermission perm) { 532 permission = perm; 533 elements = new LinkedList<SST>(); 534 } 535 536 // creates set from specified collection with specified permission 537 // all collection elements are verified before adding 538 protected SecureSet(AuthPermission perm, Collection<? extends SST> s) { 539 this(perm); 540 541 // Subject's constructor receives a Set, we can trusts if a set is from bootclasspath, 542 // and not to check whether it contains duplicates or not 543 boolean trust = s.getClass().getClassLoader() == null; 544 545 for (SST o : s) { 546 verifyElement(o); 547 if (trust || !elements.contains(o)) { 548 elements.add(o); 549 } 550 } 551 } 552 553 // verifies new set element 554 private void verifyElement(Object o) { 555 556 if (o == null) { 557 throw new NullPointerException(); 558 } 559 if (permission == _PRINCIPALS && !(Principal.class.isAssignableFrom(o.getClass()))) { 560 throw new IllegalArgumentException("Element is not instance of java.security.Principal"); 561 } 562 } 563 564 /* 565 * verifies specified element, checks set state, and security permission 566 * to modify set before adding new element 567 */ 568 @Override 569 public boolean add(SST o) { 570 571 verifyElement(o); 572 573 checkState(); 574 575 if (!elements.contains(o)) { 576 elements.add(o); 577 return true; 578 } 579 return false; 580 } 581 582 // returns an instance of SecureIterator 583 @Override 584 public Iterator<SST> iterator() { 585 586 if (permission == _PRIVATE_CREDENTIALS) { 587 /* 588 * private credential set requires iterator with additional 589 * security check (PrivateCredentialPermission) 590 */ 591 return new SecureIterator(elements.iterator()) { 592 /* 593 * checks permission to access next private credential moves 594 * to the next element even SecurityException was thrown 595 */ 596 @Override 597 public SST next() { 598 SST obj = iterator.next(); 599 return obj; 600 } 601 }; 602 } 603 return new SecureIterator(elements.iterator()); 604 } 605 606 @Override 607 public boolean retainAll(Collection<?> c) { 608 609 if (c == null) { 610 throw new NullPointerException(); 611 } 612 return super.retainAll(c); 613 } 614 615 @Override 616 public int size() { 617 return elements.size(); 618 } 619 620 /** 621 * return set with elements that are instances or subclasses of the 622 * specified class 623 */ 624 protected final <E> Set<E> get(final Class<E> c) { 625 626 if (c == null) { 627 throw new NullPointerException(); 628 } 629 630 AbstractSet<E> s = new AbstractSet<E>() { 631 private LinkedList<E> elements = new LinkedList<E>(); 632 633 @Override 634 public boolean add(E o) { 635 if (!c.isAssignableFrom(o.getClass())) { 636 throw new IllegalArgumentException("Invalid type: " + o.getClass()); 637 } 638 639 if (elements.contains(o)) { 640 return false; 641 } 642 elements.add(o); 643 return true; 644 } 645 646 @Override 647 public Iterator<E> iterator() { 648 return elements.iterator(); 649 } 650 651 @Override 652 public boolean retainAll(Collection<?> c) { 653 654 if (c == null) { 655 throw new NullPointerException(); 656 } 657 return super.retainAll(c); 658 } 659 660 @Override 661 public int size() { 662 return elements.size(); 663 } 664 }; 665 666 // FIXME must have permissions for requested priv. credentials 667 for (SST o : this) { 668 if (c.isAssignableFrom(o.getClass())) { 669 s.add(c.cast(o)); 670 } 671 } 672 return s; 673 } 674 675 private void readObject(ObjectInputStream in) throws IOException, 676 ClassNotFoundException { 677 in.defaultReadObject(); 678 679 switch (setType) { 680 case SET_Principal: 681 permission = _PRINCIPALS; 682 break; 683 case SET_PrivCred: 684 permission = _PRIVATE_CREDENTIALS; 685 break; 686 case SET_PubCred: 687 permission = _PUBLIC_CREDENTIALS; 688 break; 689 default: 690 throw new IllegalArgumentException(); 691 } 692 693 for (SST element : elements) { 694 verifyElement(element); 695 } 696 } 697 698 @SuppressWarnings("UnusedDeclaration") 699 private void writeObject(ObjectOutputStream out) throws IOException { 700 701 if (permission == _PRIVATE_CREDENTIALS) { 702 setType = SET_PrivCred; 703 } else if (permission == _PRINCIPALS) { 704 setType = SET_Principal; 705 } else { 706 setType = SET_PubCred; 707 } 708 709 out.defaultWriteObject(); 710 } 711 712 /** 713 * Represents iterator for subject's secure set 714 */ 715 private class SecureIterator implements Iterator<SST> { 716 protected Iterator<SST> iterator; 717 718 protected SecureIterator(Iterator<SST> iterator) { 719 this.iterator = iterator; 720 } 721 722 public boolean hasNext() { 723 return iterator.hasNext(); 724 } 725 726 public SST next() { 727 return iterator.next(); 728 } 729 730 /** 731 * checks set state, and security permission to modify set before 732 * removing current element 733 */ 734 public void remove() { 735 checkState(); 736 iterator.remove(); 737 } 738 } 739 } 740 } 741