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