• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)
fromSecurityProperties()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