      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  */

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