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 }