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