Home | History | Annotate | Download | only in jca
      1 /*
      2  * Copyright (c) 2003, 2011, 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 sun.security.jca;
     27 
     28 import java.util.*;
     29 
     30 import java.security.*;
     31 import java.security.Provider.Service;
     32 
     33 /**
     34  * List of Providers. Used to represent the provider preferences.
     35  *
     36  * The system starts out with a ProviderList that only has the classNames
     37  * of the Providers. Providers are loaded on demand only when needed.
     38  *
     39  * For compatibility reasons, Providers that could not be loaded are ignored
     40  * and internally presented as the instance EMPTY_PROVIDER. However, those
     41  * objects cannot be presented to applications. Call the convert() method
     42  * to force all Providers to be loaded and to obtain a ProviderList with
     43  * invalid entries removed. All this is handled by the Security class.
     44  *
     45  * Note that all indices used by this class are 0-based per general Java
     46  * convention. These must be converted to the 1-based indices used by the
     47  * Security class externally when needed.
     48  *
     49  * Instances of this class are immutable. This eliminates the need for
     50  * cloning and synchronization in consumers. The add() and remove() style
     51  * methods are static in order to avoid confusion about the immutability.
     52  *
     53  * @author  Andreas Sterbenz
     54  * @since   1.5
     55  */
     56 public final class ProviderList {
     57 
     58     final static sun.security.util.Debug debug =
     59         sun.security.util.Debug.getInstance("jca", "ProviderList");
     60 
     61     private final static ProviderConfig[] PC0 = new ProviderConfig[0];
     62 
     63     private final static Provider[] P0 = new Provider[0];
     64 
     65     // constant for an ProviderList with no elements
     66     static final ProviderList EMPTY = new ProviderList(PC0, true);
     67 
     68     // dummy provider object to use during initialization
     69     // used to avoid explicit null checks in various places
     70     private static final Provider EMPTY_PROVIDER =
     71         new Provider("##Empty##", 1.0d, "initialization in progress") {
     72             // override getService() to return null slightly faster
     73             public Service getService(String type, String algorithm) {
     74                 return null;
     75             }
     76         };
     77 
     78     // construct a ProviderList from the security properties
     79     // (static provider configuration in the java.security file)
     80     static ProviderList fromSecurityProperties() {
     81         // doPrivileged() because of Security.getProperty()
     82         return AccessController.doPrivileged(
     83                         new PrivilegedAction<ProviderList>() {
     84             public ProviderList run() {
     85                 return new ProviderList();
     86             }
     87         });
     88     }
     89 
     90     public static ProviderList add(ProviderList providerList, Provider p) {
     91         return insertAt(providerList, p, -1);
     92     }
     93 
     94     public static ProviderList insertAt(ProviderList providerList, Provider p,
     95             int position) {
     96         if (providerList.getProvider(p.getName()) != null) {
     97             return providerList;
     98         }
     99         List<ProviderConfig> list = new ArrayList<>
    100                                     (Arrays.asList(providerList.configs));
    101         int n = list.size();
    102         if ((position < 0) || (position > n)) {
    103             position = n;
    104         }
    105         list.add(position, new ProviderConfig(p));
    106         return new ProviderList(list.toArray(PC0), true);
    107     }
    108 
    109     public static ProviderList remove(ProviderList providerList, String name) {
    110         // make sure provider exists
    111         if (providerList.getProvider(name) == null) {
    112             return providerList;
    113         }
    114         // copy all except matching to new list
    115         ProviderConfig[] configs = new ProviderConfig[providerList.size() - 1];
    116         int j = 0;
    117         for (ProviderConfig config : providerList.configs) {
    118             if (config.getProvider().getName().equals(name) == false) {
    119                 configs[j++] = config;
    120             }
    121         }
    122         return new ProviderList(configs, true);
    123     }
    124 
    125     // Create a new ProviderList from the specified Providers.
    126     // This method is for use by SunJSSE.
    127     public static ProviderList newList(Provider ... providers) {
    128         ProviderConfig[] configs = new ProviderConfig[providers.length];
    129         for (int i = 0; i < providers.length; i++) {
    130             configs[i] = new ProviderConfig(providers[i]);
    131         }
    132         return new ProviderList(configs, true);
    133     }
    134 
    135     // configuration of the providers
    136     private final ProviderConfig[] configs;
    137 
    138     // flag indicating whether all configs have been loaded successfully
    139     private volatile boolean allLoaded;
    140 
    141     // List returned by providers()
    142     private final List<Provider> userList = new AbstractList<Provider>() {
    143         public int size() {
    144             return configs.length;
    145         }
    146         public Provider get(int index) {
    147             return getProvider(index);
    148         }
    149     };
    150 
    151     /**
    152      * Create a new ProviderList from an array of configs
    153      */
    154     private ProviderList(ProviderConfig[] configs, boolean allLoaded) {
    155         this.configs = configs;
    156         this.allLoaded = allLoaded;
    157     }
    158 
    159     /**
    160      * Return a new ProviderList parsed from the java.security Properties.
    161      */
    162     private ProviderList() {
    163         List<ProviderConfig> configList = new ArrayList<>();
    164         for (int i = 1; true; i++) {
    165             String entry = Security.getProperty("security.provider." + i);
    166             if (entry == null) {
    167                 break;
    168             }
    169             entry = entry.trim();
    170             if (entry.length() == 0) {
    171                 System.err.println("invalid entry for " +
    172                                    "security.provider." + i);
    173                 break;
    174             }
    175             int k = entry.indexOf(' ');
    176             ProviderConfig config;
    177             if (k == -1) {
    178                 config = new ProviderConfig(entry);
    179             } else {
    180                 String className = entry.substring(0, k);
    181                 String argument = entry.substring(k + 1).trim();
    182                 config = new ProviderConfig(className, argument);
    183             }
    184 
    185             // Get rid of duplicate providers.
    186             if (configList.contains(config) == false) {
    187                 configList.add(config);
    188             }
    189         }
    190         configs = configList.toArray(PC0);
    191         if (debug != null) {
    192             debug.println("provider configuration: " + configList);
    193         }
    194     }
    195 
    196     /**
    197      * Construct a special ProviderList for JAR verification. It consists
    198      * of the providers specified via jarClassNames, which must be on the
    199      * bootclasspath and cannot be in signed JAR files. This is to avoid
    200      * possible recursion and deadlock during verification.
    201      */
    202     ProviderList getJarList(String[] jarClassNames) {
    203         List<ProviderConfig> newConfigs = new ArrayList<>();
    204         for (String className : jarClassNames) {
    205             ProviderConfig newConfig = new ProviderConfig(className);
    206             for (ProviderConfig config : configs) {
    207                 // if the equivalent object is present in this provider list,
    208                 // use the old object rather than the new object.
    209                 // this ensures that when the provider is loaded in the
    210                 // new thread local list, it will also become available
    211                 // in this provider list
    212                 if (config.equals(newConfig)) {
    213                     newConfig = config;
    214                     break;
    215                 }
    216             }
    217             newConfigs.add(newConfig);
    218         }
    219         ProviderConfig[] configArray = newConfigs.toArray(PC0);
    220         return new ProviderList(configArray, false);
    221     }
    222 
    223     public int size() {
    224         return configs.length;
    225     }
    226 
    227     /**
    228      * Return the Provider at the specified index. Returns EMPTY_PROVIDER
    229      * if the provider could not be loaded at this time.
    230      */
    231     Provider getProvider(int index) {
    232         Provider p = configs[index].getProvider();
    233         return (p != null) ? p : EMPTY_PROVIDER;
    234     }
    235 
    236     /**
    237      * Return an unmodifiable List of all Providers in this List. The
    238      * individual Providers are loaded on demand. Elements that could not
    239      * be initialized are replaced with EMPTY_PROVIDER.
    240      */
    241     public List<Provider> providers() {
    242         return userList;
    243     }
    244 
    245     private ProviderConfig getProviderConfig(String name) {
    246         int index = getIndex(name);
    247         return (index != -1) ? configs[index] : null;
    248     }
    249 
    250     // return the Provider with the specified name or null
    251     public Provider getProvider(String name) {
    252         ProviderConfig config = getProviderConfig(name);
    253         return (config == null) ? null : config.getProvider();
    254     }
    255 
    256     /**
    257      * Return the index at which the provider with the specified name is
    258      * installed or -1 if it is not present in this ProviderList.
    259      */
    260     public int getIndex(String name) {
    261         for (int i = 0; i < configs.length; i++) {
    262             Provider p = getProvider(i);
    263             if (p.getName().equals(name)) {
    264                 return i;
    265             }
    266         }
    267         return -1;
    268     }
    269 
    270     // attempt to load all Providers not already loaded
    271     private int loadAll() {
    272         if (allLoaded) {
    273             return configs.length;
    274         }
    275         if (debug != null) {
    276             debug.println("Loading all providers");
    277             new Exception("Call trace").printStackTrace();
    278         }
    279         int n = 0;
    280         for (int i = 0; i < configs.length; i++) {
    281             Provider p = configs[i].getProvider();
    282             if (p != null) {
    283                 n++;
    284             }
    285         }
    286         if (n == configs.length) {
    287             allLoaded = true;
    288         }
    289         return n;
    290     }
    291 
    292     /**
    293      * Try to load all Providers and return the ProviderList. If one or
    294      * more Providers could not be loaded, a new ProviderList with those
    295      * entries removed is returned. Otherwise, the method returns this.
    296      */
    297     ProviderList removeInvalid() {
    298         int n = loadAll();
    299         if (n == configs.length) {
    300             return this;
    301         }
    302         ProviderConfig[] newConfigs = new ProviderConfig[n];
    303         for (int i = 0, j = 0; i < configs.length; i++) {
    304             ProviderConfig config = configs[i];
    305             if (config.isLoaded()) {
    306                 newConfigs[j++] = config;
    307             }
    308         }
    309         return new ProviderList(newConfigs, true);
    310     }
    311 
    312     // return the providers as an array
    313     public Provider[] toArray() {
    314         return providers().toArray(P0);
    315     }
    316 
    317     // return a String representation of this ProviderList
    318     public String toString() {
    319         return Arrays.asList(configs).toString();
    320     }
    321 
    322     /**
    323      * Return a Service describing an implementation of the specified
    324      * algorithm from the Provider with the highest precedence that
    325      * supports that algorithm. Return null if no Provider supports this
    326      * algorithm.
    327      */
    328     public Service getService(String type, String name) {
    329         for (int i = 0; i < configs.length; i++) {
    330             Provider p = getProvider(i);
    331             Service s = p.getService(type, name);
    332             if (s != null) {
    333                 return s;
    334             }
    335         }
    336         return null;
    337     }
    338 
    339     /**
    340      * Return a List containing all the Services describing implementations
    341      * of the specified algorithms in precedence order. If no implementation
    342      * exists, this method returns an empty List.
    343      *
    344      * The elements of this list are determined lazily on demand.
    345      *
    346      * The List returned is NOT thread safe.
    347      */
    348     public List<Service> getServices(String type, String algorithm) {
    349         return new ServiceList(type, algorithm);
    350     }
    351 
    352     /**
    353      * This method exists for compatibility with JCE only. It will be removed
    354      * once JCE has been changed to use the replacement method.
    355      * @deprecated use getServices(List<ServiceId>) instead
    356      */
    357     @Deprecated
    358     public List<Service> getServices(String type, List<String> algorithms) {
    359         List<ServiceId> ids = new ArrayList<>();
    360         for (String alg : algorithms) {
    361             ids.add(new ServiceId(type, alg));
    362         }
    363         return getServices(ids);
    364     }
    365 
    366     public List<Service> getServices(List<ServiceId> ids) {
    367         return new ServiceList(ids);
    368     }
    369 
    370     /**
    371      * Inner class for a List of Services. Custom List implementation in
    372      * order to delay Provider initialization and lookup.
    373      * Not thread safe.
    374      */
    375     private final class ServiceList extends AbstractList<Service> {
    376 
    377         // type and algorithm for simple lookup
    378         // avoid allocating/traversing the ServiceId list for these lookups
    379         private final String type;
    380         private final String algorithm;
    381 
    382         // list of ids for parallel lookup
    383         // if ids is non-null, type and algorithm are null
    384         private final List<ServiceId> ids;
    385 
    386         // first service we have found
    387         // it is stored in a separate variable so that we can avoid
    388         // allocating the services list if we do not need the second service.
    389         // this is the case if we don't failover (failovers are typically rare)
    390         private Service firstService;
    391 
    392         // list of the services we have found so far
    393         private List<Service> services;
    394 
    395         // index into config[] of the next provider we need to query
    396         private int providerIndex;
    397 
    398         ServiceList(String type, String algorithm) {
    399             this.type = type;
    400             this.algorithm = algorithm;
    401             this.ids = null;
    402         }
    403 
    404         ServiceList(List<ServiceId> ids) {
    405             this.type = null;
    406             this.algorithm = null;
    407             this.ids = ids;
    408         }
    409 
    410         private void addService(Service s) {
    411             if (firstService == null) {
    412                 firstService = s;
    413             } else {
    414                 if (services == null) {
    415                     services = new ArrayList<Service>(4);
    416                     services.add(firstService);
    417                 }
    418                 services.add(s);
    419             }
    420         }
    421 
    422         private Service tryGet(int index) {
    423             while (true) {
    424                 if ((index == 0) && (firstService != null)) {
    425                     return firstService;
    426                 } else if ((services != null) && (services.size() > index)) {
    427                     return services.get(index);
    428                 }
    429                 if (providerIndex >= configs.length) {
    430                     return null;
    431                 }
    432                 // check all algorithms in this provider before moving on
    433                 Provider p = getProvider(providerIndex++);
    434                 if (type != null) {
    435                     // simple lookup
    436                     Service s = p.getService(type, algorithm);
    437                     if (s != null) {
    438                         addService(s);
    439                     }
    440                 } else {
    441                     // parallel lookup
    442                     for (ServiceId id : ids) {
    443                         Service s = p.getService(id.type, id.algorithm);
    444                         if (s != null) {
    445                             addService(s);
    446                         }
    447                     }
    448                 }
    449             }
    450         }
    451 
    452         public Service get(int index) {
    453             Service s = tryGet(index);
    454             if (s == null) {
    455                 throw new IndexOutOfBoundsException();
    456             }
    457             return s;
    458         }
    459 
    460         public int size() {
    461             int n;
    462             if (services != null) {
    463                 n = services.size();
    464             } else {
    465                 n = (firstService != null) ? 1 : 0;
    466             }
    467             while (tryGet(n) != null) {
    468                 n++;
    469             }
    470             return n;
    471         }
    472 
    473         // override isEmpty() and iterator() to not call size()
    474         // this avoids loading + checking all Providers
    475 
    476         public boolean isEmpty() {
    477             return (tryGet(0) == null);
    478         }
    479 
    480         public Iterator<Service> iterator() {
    481             return new Iterator<Service>() {
    482                 int index;
    483 
    484                 public boolean hasNext() {
    485                     return tryGet(index) != null;
    486                 }
    487 
    488                 public Service next() {
    489                     Service s = tryGet(index);
    490                     if (s == null) {
    491                         throw new NoSuchElementException();
    492                     }
    493                     index++;
    494                     return s;
    495                 }
    496 
    497                 public void remove() {
    498                     throw new UnsupportedOperationException();
    499                 }
    500             };
    501         }
    502     }
    503 
    504 }
    505