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