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