      1 /*
      2  * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.
      3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      4  *
      5  * This code is free software; you can redistribute it and/or modify it
      6  * under the terms of the GNU General Public License version 2 only, as
      7  * published by the Free Software Foundation.  Oracle designates this
      8  * particular file as subject to the "Classpath" exception as provided
      9  * by Oracle in the LICENSE file that accompanied this code.
     10  *
     11  * This code is distributed in the hope that it will be useful, but WITHOUT
     12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     14  * version 2 for more details (a copy is included in the LICENSE file that
     15  * accompanied this code).
     16  *
     17  * You should have received a copy of the GNU General Public License version
     18  * 2 along with this work; if not, write to the Free Software Foundation,
     19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     20  *
     21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     22  * or visit www.oracle.com if you need additional information or have any
     23  * questions.
     24  */
     25
     26 package javax.security.auth;
     27
     28 import java.security.AccessController;
     29 import java.security.Principal;
     30 import java.security.PrivilegedAction;
     31 import java.security.ProtectionDomain;
     32 import java.util.Set;
     33 import java.util.WeakHashMap;
     34 import java.lang.ref.WeakReference;
     35
     36 /**
     37  * A {@code SubjectDomainCombiner} updates ProtectionDomains
     38  * with Principals from the {@code Subject} associated with this
     39  * {@code SubjectDomainCombiner}.
     40  *
     41  * @since 1.4
     42  * @deprecated This class is only useful in conjunction with
     43  *       {@linkplain SecurityManager the Security Manager}, which is deprecated
     44  *       and subject to removal in a future release. Consequently, this class
     45  *       is also deprecated and subject to removal. There is no replacement for
     46  *       the Security Manager or this class.
     47  */
     48 @Deprecated(since="17", forRemoval=true)
     49 public class SubjectDomainCombiner implements java.security.DomainCombiner {
     50
     51     private Subject subject;
     52     private WeakKeyValueMap<ProtectionDomain, ProtectionDomain> cachedPDs =
     53                 new WeakKeyValueMap<>();
     54     private Set<Principal> principalSet;
     55     private Principal[] principals;
     56
     57     private static final sun.security.util.Debug debug =
     58         sun.security.util.Debug.getInstance("combiner",
     59                                         "\t[SubjectDomainCombiner]");
     60
     61     /**
     62      * Associate the provided {@code Subject} with this
     63      * {@code SubjectDomainCombiner}.
     64      *
     65      * @param subject the {@code Subject} to be associated with
     66      *          this {@code SubjectDomainCombiner}.
     67      */
     68     public SubjectDomainCombiner(Subject subject) {
     69         this.subject = subject;
     70
     71         if (subject.isReadOnly()) {
     72             principalSet = subject.getPrincipals();
     73             principals = principalSet.toArray
     74                         (new Principal[principalSet.size()]);
     75         }
     76     }
     77
     78     /**
     79      * Get the {@code Subject} associated with this
     80      * {@code SubjectDomainCombiner}.
     81      *
     82      * @return the {@code Subject} associated with this
     83      *          {@code SubjectDomainCombiner}, or {@code null}
     84      *          if no {@code Subject} is associated with this
     85      *          {@code SubjectDomainCombiner}.
     86      *
     87      * @exception SecurityException if the caller does not have permission
     88      *          to get the {@code Subject} associated with this
     89      *          {@code SubjectDomainCombiner}.
     90      */
     91     public Subject getSubject() {
     92         java.lang.SecurityManager sm = System.getSecurityManager();
     93         if (sm != null) {
     94             sm.checkPermission(new AuthPermission
     95                 ("getSubjectFromDomainCombiner"));
     96         }
     97         return subject;
     98     }
     99
    100     /**
    101      * Update the relevant ProtectionDomains with the Principals
    102      * from the {@code Subject} associated with this
    103      * {@code SubjectDomainCombiner}.
    104      *
    105      * <p> A new {@code ProtectionDomain} instance is created
    106      * for each non-static {@code ProtectionDomain} (
    107      * (staticPermissionsOnly() == false)
    108      * in the {@code currentDomains} array.  Each new {@code ProtectionDomain}
    109      * instance is created using the {@code CodeSource},
    110      * {@code Permission}s and {@code ClassLoader}
    111      * from the corresponding {@code ProtectionDomain} in
    112      * {@code currentDomains}, as well as with the Principals from
    113      * the {@code Subject} associated with this
    114      * {@code SubjectDomainCombiner}. Static ProtectionDomains are
    115      * combined as-is and no new instance is created.
    116      *
    117      * <p> All of the ProtectionDomains (static and newly instantiated) are
    118      * combined into a new array.  The ProtectionDomains from the
    119      * {@code assignedDomains} array are appended to this new array,
    120      * and the result is returned.
    121      *
    122      * <p> Note that optimizations such as the removal of duplicate
    123      * ProtectionDomains may have occurred.
    124      * In addition, caching of ProtectionDomains may be permitted.
    125      *
    126      * @param currentDomains the ProtectionDomains associated with the
    127      *          current execution Thread, up to the most recent
    128      *          privileged {@code ProtectionDomain}.
    129      *          The ProtectionDomains are listed in order of execution,
    130      *          with the most recently executing {@code ProtectionDomain}
    131      *          residing at the beginning of the array. This parameter may
    132      *          be {@code null} if the current execution Thread
    133      *          has no associated ProtectionDomains.
    134      *
    135      * @param assignedDomains the ProtectionDomains inherited from the
    136      *          parent Thread, or the ProtectionDomains from the
    137      *          privileged {@code context}, if a call to
    138      *          {@code AccessController.doPrivileged(..., context)}
    139      *          had occurred  This parameter may be {@code null}
    140      *          if there were no ProtectionDomains inherited from the
    141      *          parent Thread, or from the privileged {@code context}.
    142      *
    143      * @return a new array consisting of the updated ProtectionDomains,
    144      *          or {@code null}.
    145      */
    146     public ProtectionDomain[] combine(ProtectionDomain[] currentDomains,
    147                                 ProtectionDomain[] assignedDomains) {
    148         if (debug != null) {
    149             if (subject == null) {
    150                 debug.println("null subject");
    151             } else {
    152                 final Subject s = subject;
    153                 AccessController.doPrivileged
    154                     (new java.security.PrivilegedAction<Void>() {
    155                     public Void run() {
    156                         debug.println(s.toString());
    157                         return null;
    158                     }
    159                 });
    160             }
    161             printInputDomains(currentDomains, assignedDomains);
    162         }
    163
    164         if (currentDomains == null || currentDomains.length == 0) {
    165             // No need to optimize assignedDomains because it should
    166             // have been previously optimized (when it was set).
    167
    168             // Note that we are returning a direct reference
    169             // to the input array - since ACC does not clone
    170             // the arrays when it calls combiner.combine,
    171             // multiple ACC instances may share the same
    172             // array instance in this case
    173
    174             return assignedDomains;
    175         }
    176
    177         // optimize currentDomains
    178         //
    179         // No need to optimize assignedDomains because it should
    180         // have been previously optimized (when it was set).
    181
    182         currentDomains = optimize(currentDomains);
    183         if (debug != null) {
    184             debug.println("after optimize");
    185             printInputDomains(currentDomains, assignedDomains);
    186         }
    187
    188         if (currentDomains == null && assignedDomains == null) {
    189             return null;
    190         }
    191
    192         int cLen = (currentDomains == null ? 0 : currentDomains.length);
    193         int aLen = (assignedDomains == null ? 0 : assignedDomains.length);
    194
    195         // the ProtectionDomains for the new AccessControlContext
    196         // that we will return
    197         ProtectionDomain[] newDomains = new ProtectionDomain[cLen + aLen];
    198
    199         boolean allNew = true;
    200         synchronized(cachedPDs) {
    201             if (!subject.isReadOnly() &&
    202                 !subject.getPrincipals().equals(principalSet)) {
    203
    204                 // if the Subject was mutated, clear the PD cache
    205                 Set<Principal> newSet = subject.getPrincipals();
    206                 synchronized(newSet) {
    207                     principalSet = new java.util.HashSet<Principal>(newSet);
    208                 }
    209                 principals = principalSet.toArray
    210                         (new Principal[principalSet.size()]);
    211                 cachedPDs.clear();
    212
    213                 if (debug != null) {
    214                     debug.println("Subject mutated - clearing cache");
    215                 }
    216             }
    217
    218             ProtectionDomain subjectPd;
    219             for (int i = 0; i < cLen; i++) {
    220                 ProtectionDomain pd = currentDomains[i];
    221
    222                 subjectPd = cachedPDs.getValue(pd);
    223
    224                 if (subjectPd == null) {
    225                     if (pd.staticPermissionsOnly()) {
    226                         // keep static ProtectionDomain objects static
    227                         subjectPd = pd;
    228                     } else {
    229                         subjectPd = new ProtectionDomain(pd.getCodeSource(),
    230                                                 pd.getPermissions(),
    231                                                 pd.getClassLoader(),
    232                                                 principals);
    233                     }
    234                     cachedPDs.putValue(pd, subjectPd);
    235                 } else {
    236                     allNew = false;
    237                 }
    238                 newDomains[i] = subjectPd;
    239             }
    240         }
    241
    242         if (debug != null) {
    243             debug.println("updated current: ");
    244             for (int i = 0; i < cLen; i++) {
    245                 debug.println("\tupdated[" + i + "] = " +
    246                                 printDomain(newDomains[i]));
    247             }
    248         }
    249
    250         // now add on the assigned domains
    251         if (aLen > 0) {
    252             System.arraycopy(assignedDomains, 0, newDomains, cLen, aLen);
    253
    254             // optimize the result (cached PDs might exist in assignedDomains)
    255             if (!allNew) {
    256                 newDomains = optimize(newDomains);
    257             }
    258         }
    259
    260         // if aLen == 0 || allNew, no need to further optimize newDomains
    261
    262         if (debug != null) {
    263             if (newDomains == null || newDomains.length == 0) {
    264                 debug.println("returning null");
    265             } else {
    266                 debug.println("combinedDomains: ");
    267                 for (int i = 0; i < newDomains.length; i++) {
    268                     debug.println("newDomain " + i + ": " +
    269                                   printDomain(newDomains[i]));
    270                 }
    271             }
    272         }
    273
    274         // return the new ProtectionDomains
    275         if (newDomains == null || newDomains.length == 0) {
    276             return null;
    277         } else {
    278             return newDomains;
    279         }
    280     }
    281
    282     private static ProtectionDomain[] optimize(ProtectionDomain[] domains) {
    283         if (domains == null || domains.length == 0)
    284             return null;
    285
    286         ProtectionDomain[] optimized = new ProtectionDomain[domains.length];
    287         ProtectionDomain pd;
    288         int num = 0;
    289         for (int i = 0; i < domains.length; i++) {
    290
    291             // skip domains with AllPermission
    292             // XXX
    293             //
    294             //  if (domains[i].implies(ALL_PERMISSION))
    295             //  continue;
    296
    297             // skip System Domains
    298             if ((pd = domains[i]) != null) {
    299
    300                 // remove duplicates
    301                 boolean found = false;
    302                 for (int j = 0; j < num && !found; j++) {
    303                     found = (optimized[j] == pd);
    304                 }
    305                 if (!found) {
    306                     optimized[num++] = pd;
    307                 }
    308             }
    309         }
    310
    311         // resize the array if necessary
    312         if (num > 0 && num < domains.length) {
    313             ProtectionDomain[] downSize = new ProtectionDomain[num];
    314             System.arraycopy(optimized, 0, downSize, 0, downSize.length);
    315             optimized = downSize;
    316         }
    317
    318         return ((num == 0 || optimized.length == 0) ? null : optimized);
    319     }
    320
    321     private static void printInputDomains(ProtectionDomain[] currentDomains,
    322                                 ProtectionDomain[] assignedDomains) {
    323         if (currentDomains == null || currentDomains.length == 0) {
    324             debug.println("currentDomains null or 0 length");
    325         } else {
    326             for (int i = 0; currentDomains != null &&
    327                         i < currentDomains.length; i++) {
    328                 if (currentDomains[i] == null) {
    329                     debug.println("currentDomain " + i + ": SystemDomain");
    330                 } else {
    331                     debug.println("currentDomain " + i + ": " +
    332                                 printDomain(currentDomains[i]));
    333                 }
    334             }
    335         }
    336
    337         if (assignedDomains == null || assignedDomains.length == 0) {
    338             debug.println("assignedDomains null or 0 length");
    339         } else {
    340             debug.println("assignedDomains = ");
    341             for (int i = 0; assignedDomains != null &&
    342                         i < assignedDomains.length; i++) {
    343                 if (assignedDomains[i] == null) {
    344                     debug.println("assignedDomain " + i + ": SystemDomain");
    345                 } else {
    346                     debug.println("assignedDomain " + i + ": " +
    347                                 printDomain(assignedDomains[i]));
    348                 }
    349             }
    350         }
    351     }
    352
    353     private static String printDomain(final ProtectionDomain pd) {
    354         if (pd == null) {
    355             return "null";
    356         }
    357         return AccessController.doPrivileged(new PrivilegedAction<String>() {
    358             public String run() {
    359                 return pd.toString();
    360             }
    361         });
    362     }
    363
    364     /**
    365      * A HashMap that has weak keys and values.
    366      *
    367      * Key objects in this map are the "current" ProtectionDomain instances
    368      * received via the combine method.  Each "current" PD is mapped to a
    369      * new PD instance that holds both the contents of the "current" PD,
    370      * as well as the principals from the Subject associated with this combiner.
    371      *
    372      * The newly created "principal-based" PD values must be stored as
    373      * WeakReferences since they contain strong references to the
    374      * corresponding key object (the "current" non-principal-based PD),
    375      * which will prevent the key from being GC'd.  Specifically,
    376      * a "principal-based" PD contains strong references to the CodeSource,
    377      * signer certs, PermissionCollection and ClassLoader objects
    378      * in the "current PD".
    379      */
    380     private static class WeakKeyValueMap<K,V> extends
    381                                         WeakHashMap<K,WeakReference<V>> {
    382
    383         public V getValue(K key) {
    384             WeakReference<V> wr = super.get(key);
    385             if (wr != null) {
    386                 return wr.get();
    387             }
    388             return null;
    389         }
    390
    391         public V putValue(K key, V value) {
    392             WeakReference<V> wr = super.put(key, new WeakReference<V>(value));
    393             if (wr != null) {
    394                 return wr.get();
    395             }
    396             return null;
    397         }
    398     }
    399 }
    --- EOF ---