Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/javax/security/auth/SubjectDomainCombiner.java
38918 views
/*1* Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package javax.security.auth;2627import java.security.AccessController;28import java.security.Permission;29import java.security.Permissions;30import java.security.PermissionCollection;31import java.security.Policy;32import java.security.Principal;33import java.security.PrivilegedAction;34import java.security.ProtectionDomain;35import java.security.Security;36import java.util.Set;37import java.util.WeakHashMap;38import java.lang.ref.WeakReference;39import sun.misc.SharedSecrets;40import sun.misc.JavaSecurityProtectionDomainAccess;4142/**43* A {@code SubjectDomainCombiner} updates ProtectionDomains44* with Principals from the {@code Subject} associated with this45* {@code SubjectDomainCombiner}.46*47*/48public class SubjectDomainCombiner implements java.security.DomainCombiner {4950private Subject subject;51private WeakKeyValueMap<ProtectionDomain, ProtectionDomain> cachedPDs =52new WeakKeyValueMap<>();53private Set<Principal> principalSet;54private Principal[] principals;5556private static final sun.security.util.Debug debug =57sun.security.util.Debug.getInstance("combiner",58"\t[SubjectDomainCombiner]");5960@SuppressWarnings("deprecation")61// Note: check only at classloading time, not dynamically during combine()62private static final boolean useJavaxPolicy =63javax.security.auth.Policy.isCustomPolicySet(debug);6465// Relevant only when useJavaxPolicy is true66private static final boolean allowCaching =67(useJavaxPolicy && cachePolicy());6869private static final JavaSecurityProtectionDomainAccess pdAccess =70SharedSecrets.getJavaSecurityProtectionDomainAccess();7172/**73* Associate the provided {@code Subject} with this74* {@code SubjectDomainCombiner}.75*76* <p>77*78* @param subject the {@code Subject} to be associated with79* with this {@code SubjectDomainCombiner}.80*/81public SubjectDomainCombiner(Subject subject) {82this.subject = subject;8384if (subject.isReadOnly()) {85principalSet = subject.getPrincipals();86principals = principalSet.toArray87(new Principal[principalSet.size()]);88}89}9091/**92* Get the {@code Subject} associated with this93* {@code SubjectDomainCombiner}.94*95* <p>96*97* @return the {@code Subject} associated with this98* {@code SubjectDomainCombiner}, or {@code null}99* if no {@code Subject} is associated with this100* {@code SubjectDomainCombiner}.101*102* @exception SecurityException if the caller does not have permission103* to get the {@code Subject} associated with this104* {@code SubjectDomainCombiner}.105*/106public Subject getSubject() {107java.lang.SecurityManager sm = System.getSecurityManager();108if (sm != null) {109sm.checkPermission(new AuthPermission110("getSubjectFromDomainCombiner"));111}112return subject;113}114115/**116* Update the relevant ProtectionDomains with the Principals117* from the {@code Subject} associated with this118* {@code SubjectDomainCombiner}.119*120* <p> A new {@code ProtectionDomain} instance is created121* for each {@code ProtectionDomain} in the122* <i>currentDomains</i> array. Each new {@code ProtectionDomain}123* instance is created using the {@code CodeSource},124* {@code Permission}s and {@code ClassLoader}125* from the corresponding {@code ProtectionDomain} in126* <i>currentDomains</i>, as well as with the Principals from127* the {@code Subject} associated with this128* {@code SubjectDomainCombiner}.129*130* <p> All of the newly instantiated ProtectionDomains are131* combined into a new array. The ProtectionDomains from the132* <i>assignedDomains</i> array are appended to this new array,133* and the result is returned.134*135* <p> Note that optimizations such as the removal of duplicate136* ProtectionDomains may have occurred.137* In addition, caching of ProtectionDomains may be permitted.138*139* <p>140*141* @param currentDomains the ProtectionDomains associated with the142* current execution Thread, up to the most recent143* privileged {@code ProtectionDomain}.144* The ProtectionDomains are are listed in order of execution,145* with the most recently executing {@code ProtectionDomain}146* residing at the beginning of the array. This parameter may147* be {@code null} if the current execution Thread148* has no associated ProtectionDomains.<p>149*150* @param assignedDomains the ProtectionDomains inherited from the151* parent Thread, or the ProtectionDomains from the152* privileged <i>context</i>, if a call to153* AccessController.doPrivileged(..., <i>context</i>)154* had occurred This parameter may be {@code null}155* if there were no ProtectionDomains inherited from the156* parent Thread, or from the privileged <i>context</i>.157*158* @return a new array consisting of the updated ProtectionDomains,159* or {@code null}.160*/161public ProtectionDomain[] combine(ProtectionDomain[] currentDomains,162ProtectionDomain[] assignedDomains) {163if (debug != null) {164if (subject == null) {165debug.println("null subject");166} else {167final Subject s = subject;168AccessController.doPrivileged169(new java.security.PrivilegedAction<Void>() {170public Void run() {171debug.println(s.toString());172return null;173}174});175}176printInputDomains(currentDomains, assignedDomains);177}178179if (currentDomains == null || currentDomains.length == 0) {180// No need to optimize assignedDomains because it should181// have been previously optimized (when it was set).182183// Note that we are returning a direct reference184// to the input array - since ACC does not clone185// the arrays when it calls combiner.combine,186// multiple ACC instances may share the same187// array instance in this case188189return assignedDomains;190}191192// optimize currentDomains193//194// No need to optimize assignedDomains because it should195// have been previously optimized (when it was set).196197currentDomains = optimize(currentDomains);198if (debug != null) {199debug.println("after optimize");200printInputDomains(currentDomains, assignedDomains);201}202203if (currentDomains == null && assignedDomains == null) {204return null;205}206207// maintain backwards compatibility for developers who provide208// their own custom javax.security.auth.Policy implementations209if (useJavaxPolicy) {210return combineJavaxPolicy(currentDomains, assignedDomains);211}212213int cLen = (currentDomains == null ? 0 : currentDomains.length);214int aLen = (assignedDomains == null ? 0 : assignedDomains.length);215216// the ProtectionDomains for the new AccessControlContext217// that we will return218ProtectionDomain[] newDomains = new ProtectionDomain[cLen + aLen];219220boolean allNew = true;221synchronized(cachedPDs) {222if (!subject.isReadOnly() &&223!subject.getPrincipals().equals(principalSet)) {224225// if the Subject was mutated, clear the PD cache226Set<Principal> newSet = subject.getPrincipals();227synchronized(newSet) {228principalSet = new java.util.HashSet<Principal>(newSet);229}230principals = principalSet.toArray231(new Principal[principalSet.size()]);232cachedPDs.clear();233234if (debug != null) {235debug.println("Subject mutated - clearing cache");236}237}238239ProtectionDomain subjectPd;240for (int i = 0; i < cLen; i++) {241ProtectionDomain pd = currentDomains[i];242243subjectPd = cachedPDs.getValue(pd);244245if (subjectPd == null) {246if (pdAccess.getStaticPermissionsField(pd)) {247// Need to keep static ProtectionDomain objects static248subjectPd = new ProtectionDomain(pd.getCodeSource(),249pd.getPermissions());250} else {251subjectPd = new ProtectionDomain(pd.getCodeSource(),252pd.getPermissions(),253pd.getClassLoader(),254principals);255}256cachedPDs.putValue(pd, subjectPd);257} else {258allNew = false;259}260newDomains[i] = subjectPd;261}262}263264if (debug != null) {265debug.println("updated current: ");266for (int i = 0; i < cLen; i++) {267debug.println("\tupdated[" + i + "] = " +268printDomain(newDomains[i]));269}270}271272// now add on the assigned domains273if (aLen > 0) {274System.arraycopy(assignedDomains, 0, newDomains, cLen, aLen);275276// optimize the result (cached PDs might exist in assignedDomains)277if (!allNew) {278newDomains = optimize(newDomains);279}280}281282// if aLen == 0 || allNew, no need to further optimize newDomains283284if (debug != null) {285if (newDomains == null || newDomains.length == 0) {286debug.println("returning null");287} else {288debug.println("combinedDomains: ");289for (int i = 0; i < newDomains.length; i++) {290debug.println("newDomain " + i + ": " +291printDomain(newDomains[i]));292}293}294}295296// return the new ProtectionDomains297if (newDomains == null || newDomains.length == 0) {298return null;299} else {300return newDomains;301}302}303304/**305* Use the javax.security.auth.Policy implementation306*/307private ProtectionDomain[] combineJavaxPolicy(308ProtectionDomain[] currentDomains,309ProtectionDomain[] assignedDomains) {310311if (!allowCaching) {312java.security.AccessController.doPrivileged313(new PrivilegedAction<Void>() {314@SuppressWarnings("deprecation")315public Void run() {316// Call refresh only caching is disallowed317javax.security.auth.Policy.getPolicy().refresh();318return null;319}320});321}322323324int cLen = (currentDomains == null ? 0 : currentDomains.length);325int aLen = (assignedDomains == null ? 0 : assignedDomains.length);326327// the ProtectionDomains for the new AccessControlContext328// that we will return329ProtectionDomain[] newDomains = new ProtectionDomain[cLen + aLen];330331synchronized(cachedPDs) {332if (!subject.isReadOnly() &&333!subject.getPrincipals().equals(principalSet)) {334335// if the Subject was mutated, clear the PD cache336Set<Principal> newSet = subject.getPrincipals();337synchronized(newSet) {338principalSet = new java.util.HashSet<Principal>(newSet);339}340principals = principalSet.toArray341(new Principal[principalSet.size()]);342cachedPDs.clear();343344if (debug != null) {345debug.println("Subject mutated - clearing cache");346}347}348349for (int i = 0; i < cLen; i++) {350ProtectionDomain pd = currentDomains[i];351ProtectionDomain subjectPd = cachedPDs.getValue(pd);352353if (subjectPd == null) {354if (pdAccess.getStaticPermissionsField(pd)) {355// keep static ProtectionDomain objects static356subjectPd = new ProtectionDomain(pd.getCodeSource(),357pd.getPermissions());358} else {359// XXX360// we must first add the original permissions.361// that way when we later add the new JAAS permissions,362// any unresolved JAAS-related permissions will363// automatically get resolved.364365// get the original perms366Permissions perms = new Permissions();367PermissionCollection coll = pd.getPermissions();368java.util.Enumeration<Permission> e;369if (coll != null) {370synchronized (coll) {371e = coll.elements();372while (e.hasMoreElements()) {373Permission newPerm =374e.nextElement();375perms.add(newPerm);376}377}378}379380// get perms from the policy381final java.security.CodeSource finalCs = pd.getCodeSource();382final Subject finalS = subject;383PermissionCollection newPerms =384java.security.AccessController.doPrivileged385(new PrivilegedAction<PermissionCollection>() {386@SuppressWarnings("deprecation")387public PermissionCollection run() {388return389javax.security.auth.Policy.getPolicy().getPermissions390(finalS, finalCs);391}392});393394// add the newly granted perms,395// avoiding duplicates396synchronized (newPerms) {397e = newPerms.elements();398while (e.hasMoreElements()) {399Permission newPerm = e.nextElement();400if (!perms.implies(newPerm)) {401perms.add(newPerm);402if (debug != null)403debug.println (404"Adding perm " + newPerm + "\n");405}406}407}408subjectPd = new ProtectionDomain409(finalCs, perms, pd.getClassLoader(), principals);410}411if (allowCaching)412cachedPDs.putValue(pd, subjectPd);413}414newDomains[i] = subjectPd;415}416}417418if (debug != null) {419debug.println("updated current: ");420for (int i = 0; i < cLen; i++) {421debug.println("\tupdated[" + i + "] = " + newDomains[i]);422}423}424425// now add on the assigned domains426if (aLen > 0) {427System.arraycopy(assignedDomains, 0, newDomains, cLen, aLen);428}429430if (debug != null) {431if (newDomains == null || newDomains.length == 0) {432debug.println("returning null");433} else {434debug.println("combinedDomains: ");435for (int i = 0; i < newDomains.length; i++) {436debug.println("newDomain " + i + ": " +437newDomains[i].toString());438}439}440}441442// return the new ProtectionDomains443if (newDomains == null || newDomains.length == 0) {444return null;445} else {446return newDomains;447}448}449450private static ProtectionDomain[] optimize(ProtectionDomain[] domains) {451if (domains == null || domains.length == 0)452return null;453454ProtectionDomain[] optimized = new ProtectionDomain[domains.length];455ProtectionDomain pd;456int num = 0;457for (int i = 0; i < domains.length; i++) {458459// skip domains with AllPermission460// XXX461//462// if (domains[i].implies(ALL_PERMISSION))463// continue;464465// skip System Domains466if ((pd = domains[i]) != null) {467468// remove duplicates469boolean found = false;470for (int j = 0; j < num && !found; j++) {471found = (optimized[j] == pd);472}473if (!found) {474optimized[num++] = pd;475}476}477}478479// resize the array if necessary480if (num > 0 && num < domains.length) {481ProtectionDomain[] downSize = new ProtectionDomain[num];482System.arraycopy(optimized, 0, downSize, 0, downSize.length);483optimized = downSize;484}485486return ((num == 0 || optimized.length == 0) ? null : optimized);487}488489private static boolean cachePolicy() {490String s = AccessController.doPrivileged491(new PrivilegedAction<String>() {492public String run() {493return Security.getProperty("cache.auth.policy");494}495});496if (s != null) {497return Boolean.parseBoolean(s);498}499500// cache by default501return true;502}503504private static void printInputDomains(ProtectionDomain[] currentDomains,505ProtectionDomain[] assignedDomains) {506if (currentDomains == null || currentDomains.length == 0) {507debug.println("currentDomains null or 0 length");508} else {509for (int i = 0; currentDomains != null &&510i < currentDomains.length; i++) {511if (currentDomains[i] == null) {512debug.println("currentDomain " + i + ": SystemDomain");513} else {514debug.println("currentDomain " + i + ": " +515printDomain(currentDomains[i]));516}517}518}519520if (assignedDomains == null || assignedDomains.length == 0) {521debug.println("assignedDomains null or 0 length");522} else {523debug.println("assignedDomains = ");524for (int i = 0; assignedDomains != null &&525i < assignedDomains.length; i++) {526if (assignedDomains[i] == null) {527debug.println("assignedDomain " + i + ": SystemDomain");528} else {529debug.println("assignedDomain " + i + ": " +530printDomain(assignedDomains[i]));531}532}533}534}535536private static String printDomain(final ProtectionDomain pd) {537if (pd == null) {538return "null";539}540return AccessController.doPrivileged(new PrivilegedAction<String>() {541public String run() {542return pd.toString();543}544});545}546547/**548* A HashMap that has weak keys and values.549*550* Key objects in this map are the "current" ProtectionDomain instances551* received via the combine method. Each "current" PD is mapped to a552* new PD instance that holds both the contents of the "current" PD,553* as well as the principals from the Subject associated with this combiner.554*555* The newly created "principal-based" PD values must be stored as556* WeakReferences since they contain strong references to the557* corresponding key object (the "current" non-principal-based PD),558* which will prevent the key from being GC'd. Specifically,559* a "principal-based" PD contains strong references to the CodeSource,560* signer certs, PermissionCollection and ClassLoader objects561* in the "current PD".562*/563private static class WeakKeyValueMap<K,V> extends564WeakHashMap<K,WeakReference<V>> {565566public V getValue(K key) {567WeakReference<V> wr = super.get(key);568if (wr != null) {569return wr.get();570}571return null;572}573574public V putValue(K key, V value) {575WeakReference<V> wr = super.put(key, new WeakReference<V>(value));576if (wr != null) {577return wr.get();578}579return null;580}581}582}583584585