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 {@code 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 }