001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.extensions;
028    import org.opends.messages.Message;
029    
030    
031    
032    import java.io.BufferedReader;
033    import java.io.File;
034    import java.io.FileReader;
035    import java.io.IOException;
036    import java.security.KeyStore;
037    import java.util.ArrayList;
038    import java.util.List;
039    import javax.net.ssl.KeyManager;
040    import javax.net.ssl.KeyManagerFactory;
041    
042    import org.opends.server.admin.server.ConfigurationChangeListener;
043    import org.opends.server.admin.std.server.PKCS11KeyManagerProviderCfg;
044    import org.opends.server.api.KeyManagerProvider;
045    import org.opends.server.config.ConfigException;
046    import org.opends.server.core.DirectoryServer;
047    import org.opends.server.types.ConfigChangeResult;
048    import org.opends.server.types.DirectoryException;
049    import org.opends.server.types.DN;
050    import org.opends.server.types.InitializationException;
051    import org.opends.server.types.ResultCode;
052    
053    import static org.opends.server.loggers.debug.DebugLogger.*;
054    import org.opends.server.loggers.debug.DebugTracer;
055    import org.opends.server.types.DebugLogLevel;
056    import static org.opends.messages.ExtensionMessages.*;
057    
058    import static org.opends.server.util.StaticUtils.*;
059    
060    
061    
062    /**
063     * This class defines a key manager provider that will access keys stored on a
064     * PKCS#11 device.  It will use the Java PKCS#11 interface, which may need to be
065     * configured on the underlying system.
066     */
067    public class PKCS11KeyManagerProvider
068        extends KeyManagerProvider<PKCS11KeyManagerProviderCfg>
069        implements ConfigurationChangeListener<PKCS11KeyManagerProviderCfg>
070    {
071      /**
072       * The tracer object for the debug logger.
073       */
074      private static final DebugTracer TRACER = getTracer();
075    
076    
077    
078      /**
079       * The keystore type to use when accessing the PKCS#11 keystore.
080       */
081      public static final String PKCS11_KEYSTORE_TYPE = "PKCS11";
082    
083    
084    
085      // The DN of the configuration entry for this key manager provider.
086      private DN configEntryDN;
087    
088      // The PIN needed to access the keystore.
089      private char[] keyStorePIN;
090    
091      // The current configuration for this key manager provider.
092      private PKCS11KeyManagerProviderCfg currentConfig;
093    
094    
095    
096      /**
097       * Creates a new instance of this PKCS#11 key manager provider.  The
098       * <CODE>initializeKeyManagerProvider</CODE> method must be called on the
099       * resulting object before it may be used.
100       */
101      public PKCS11KeyManagerProvider()
102      {
103        // No implementation is required.
104      }
105    
106    
107    
108      /**
109       * {@inheritDoc}
110       */
111      @Override
112      public void initializeKeyManagerProvider(
113                        PKCS11KeyManagerProviderCfg configuration)
114             throws ConfigException, InitializationException
115      {
116        // Store the DN of the configuration entry and register to be notified of
117        // configuration changes.
118        currentConfig = configuration;
119        configEntryDN = configuration.dn();
120        configuration.addPKCS11ChangeListener(this);
121    
122        // Get the PIN needed to access the contents of the PKCS#11
123        // keystore. We will offer several places to look for the PIN, and
124        // we will do so in the following order:
125        //
126        // - In a specified Java property
127        // - In a specified environment variable
128        // - In a specified file on the server filesystem.
129        // - As the value of a configuration attribute.
130        //
131        // In any case, the PIN must be in the clear.
132        keyStorePIN = null;
133    
134        if (configuration.getKeyStorePinProperty() != null) {
135          String propertyName = configuration.getKeyStorePinProperty();
136          String pinStr = System.getProperty(propertyName);
137    
138          if (pinStr == null) {
139            Message message = ERR_PKCS11_KEYMANAGER_PIN_PROPERTY_NOT_SET.get(
140                String.valueOf(propertyName), String.valueOf(configEntryDN));
141            throw new InitializationException(message);
142          }
143    
144          keyStorePIN = pinStr.toCharArray();
145        } else if (configuration.getKeyStorePinEnvironmentVariable() != null) {
146          String enVarName = configuration
147              .getKeyStorePinEnvironmentVariable();
148          String pinStr = System.getenv(enVarName);
149    
150          if (pinStr == null) {
151            Message message = ERR_PKCS11_KEYMANAGER_PIN_ENVAR_NOT_SET.get(
152                String.valueOf(enVarName), String.valueOf(configEntryDN));
153            throw new InitializationException(message);
154          }
155    
156          keyStorePIN = pinStr.toCharArray();
157        } else if (configuration.getKeyStorePinFile() != null) {
158          String fileName = configuration.getKeyStorePinFile();
159          File pinFile = getFileForPath(fileName);
160    
161          if (!pinFile.exists()) {
162            Message message = ERR_PKCS11_KEYMANAGER_PIN_NO_SUCH_FILE.get(
163                String.valueOf(fileName), String.valueOf(configEntryDN));
164            throw new InitializationException(message);
165          }
166    
167          String pinStr;
168          try {
169            BufferedReader br = new BufferedReader(
170                new FileReader(pinFile));
171            pinStr = br.readLine();
172            br.close();
173          } catch (IOException ioe) {
174            if (debugEnabled())
175            {
176              TRACER.debugCaught(DebugLogLevel.ERROR, ioe);
177            }
178    
179            Message message = ERR_PKCS11_KEYMANAGER_PIN_FILE_CANNOT_READ.
180                get(String.valueOf(fileName), String.valueOf(configEntryDN),
181                    getExceptionMessage(ioe));
182            throw new InitializationException(message, ioe);
183          }
184    
185          if (pinStr == null) {
186            Message message = ERR_PKCS11_KEYMANAGER_PIN_FILE_EMPTY.get(
187                String.valueOf(fileName), String.valueOf(configEntryDN));
188            throw new InitializationException(message);
189          }
190    
191          keyStorePIN = pinStr.toCharArray();
192        } else if (configuration.getKeyStorePin() != null) {
193          keyStorePIN = configuration.getKeyStorePin().toCharArray();
194        } else {
195          // Pin wasn't defined anywhere.
196          Message message =
197              ERR_PKCS11_KEYMANAGER_NO_PIN.get(String.valueOf(configEntryDN));
198          throw new ConfigException(message);
199        }
200      }
201    
202    
203    
204      /**
205       * Performs any finalization that may be necessary for this key
206       * manager provider.
207       */
208      public void finalizeKeyManagerProvider()
209      {
210        currentConfig.removePKCS11ChangeListener(this);
211      }
212    
213    
214    
215      /**
216       * Retrieves a set of <CODE>KeyManager</CODE> objects that may be used for
217       * interactions requiring access to a key manager.
218       *
219       * @return  A set of <CODE>KeyManager</CODE> objects that may be used for
220       *          interactions requiring access to a key manager.
221       *
222       * @throws  DirectoryException  If a problem occurs while attempting to obtain
223       *                              the set of key managers.
224       */
225      public KeyManager[] getKeyManagers()
226             throws DirectoryException
227      {
228        KeyStore keyStore;
229        try
230        {
231          keyStore = KeyStore.getInstance(PKCS11_KEYSTORE_TYPE);
232          keyStore.load(null, keyStorePIN);
233        }
234        catch (Exception e)
235        {
236          if (debugEnabled())
237          {
238            TRACER.debugCaught(DebugLogLevel.ERROR, e);
239          }
240    
241          Message message =
242              ERR_PKCS11_KEYMANAGER_CANNOT_LOAD.get(getExceptionMessage(e));
243          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
244                                       message, e);
245        }
246    
247    
248        try
249        {
250          String keyManagerAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
251          KeyManagerFactory keyManagerFactory =
252               KeyManagerFactory.getInstance(keyManagerAlgorithm);
253          keyManagerFactory.init(keyStore, keyStorePIN);
254          return keyManagerFactory.getKeyManagers();
255        }
256        catch (Exception e)
257        {
258          if (debugEnabled())
259          {
260            TRACER.debugCaught(DebugLogLevel.ERROR, e);
261          }
262    
263          Message message = ERR_PKCS11_KEYMANAGER_CANNOT_CREATE_FACTORY.get(
264              getExceptionMessage(e));
265          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
266                                       message, e);
267        }
268      }
269    
270    
271    
272      /**
273       * {@inheritDoc}
274       */
275      @Override()
276      public boolean isConfigurationAcceptable(
277                            PKCS11KeyManagerProviderCfg configuration,
278                              List<Message> unacceptableReasons)
279      {
280        return isConfigurationChangeAcceptable(configuration, unacceptableReasons);
281      }
282    
283    
284    
285      /**
286       * {@inheritDoc}
287       */
288      public boolean isConfigurationChangeAcceptable(
289                          PKCS11KeyManagerProviderCfg configuration,
290                          List<Message> unacceptableReasons)
291      {
292        boolean configAcceptable = true;
293        DN cfgEntryDN = configuration.dn();
294    
295    
296        // Get the PIN needed to access the contents of the keystore file.
297        //
298        // We will offer several places to look for the PIN, and we will
299        // do so in the following order:
300        //
301        // - In a specified Java property
302        // - In a specified environment variable
303        // - In a specified file on the server filesystem.
304        // - As the value of a configuration attribute.
305        //
306        // In any case, the PIN must be in the clear.
307        if (configuration.getKeyStorePinProperty() != null)
308        {
309          String propertyName = configuration.getKeyStorePinProperty();
310          String pinStr = System.getProperty(propertyName);
311    
312          if (pinStr == null)
313          {
314            unacceptableReasons.add(ERR_PKCS11_KEYMANAGER_PIN_PROPERTY_NOT_SET.get(
315                    String.valueOf(propertyName),
316                    String.valueOf(cfgEntryDN)));
317            configAcceptable = false;
318          }
319        }
320        else if (configuration.getKeyStorePinEnvironmentVariable() != null)
321        {
322          String enVarName = configuration.getKeyStorePinEnvironmentVariable();
323          String pinStr    = System.getenv(enVarName);
324    
325          if (pinStr == null)
326          {
327            unacceptableReasons.add(ERR_PKCS11_KEYMANAGER_PIN_ENVAR_NOT_SET.get(
328                    String.valueOf(enVarName),
329                    String.valueOf(cfgEntryDN)));
330            configAcceptable = false;
331          }
332        }
333        else if (configuration.getKeyStorePinFile() != null)
334        {
335          String fileName = configuration.getKeyStorePinFile();
336          File   pinFile  = getFileForPath(fileName);
337    
338          if (!pinFile.exists())
339          {
340            unacceptableReasons.add(ERR_PKCS11_KEYMANAGER_PIN_NO_SUCH_FILE.get(
341                    String.valueOf(fileName),
342                    String.valueOf(cfgEntryDN)));
343            configAcceptable = false;
344          }
345          else
346          {
347            String pinStr = null;
348            BufferedReader br = null;
349            try {
350              br = new BufferedReader(new FileReader(pinFile));
351              pinStr = br.readLine();
352            }
353            catch (IOException ioe)
354            {
355              unacceptableReasons.add(
356                      ERR_PKCS11_KEYMANAGER_PIN_FILE_CANNOT_READ.get(
357                              String.valueOf(fileName),
358                              String.valueOf(cfgEntryDN),
359                              getExceptionMessage(ioe)));
360              configAcceptable = false;
361            }
362            finally
363            {
364              try
365              {
366                br.close();
367              } catch (Exception e) {}
368            }
369    
370            if (pinStr == null)
371            {
372    
373              unacceptableReasons.add(ERR_PKCS11_KEYMANAGER_PIN_FILE_EMPTY.get(
374                      String.valueOf(fileName),
375                      String.valueOf(cfgEntryDN)));
376              configAcceptable = false;
377            }
378          }
379        }
380        else if (configuration.getKeyStorePin() != null)
381        {
382          configuration.getKeyStorePin().toCharArray();
383        }
384        else
385        {
386          // Pin wasn't defined anywhere.
387          unacceptableReasons.add(ERR_PKCS11_KEYMANAGER_NO_PIN.get(
388                  String.valueOf(cfgEntryDN)));
389          configAcceptable = false;
390        }
391    
392        return configAcceptable;
393      }
394    
395    
396    
397      /**
398       * {@inheritDoc}
399       */
400      public ConfigChangeResult applyConfigurationChange(
401                                     PKCS11KeyManagerProviderCfg configuration)
402      {
403        ResultCode        resultCode          = ResultCode.SUCCESS;
404        boolean           adminActionRequired = false;
405        ArrayList<Message> messages            = new ArrayList<Message>();
406    
407    
408        // Get the PIN needed to access the contents of the keystore file.
409        //
410        // We will offer several places to look for the PIN, and we will
411        // do so in the following order:
412        //
413        // - In a specified Java property
414        // - In a specified environment variable
415        // - In a specified file on the server filesystem.
416        // - As the value of a configuration attribute.
417        //
418        // In any case, the PIN must be in the clear.
419        char[] newPIN = null;
420    
421        if (configuration.getKeyStorePinProperty() != null)
422        {
423          String propertyName = configuration.getKeyStorePinProperty();
424          String pinStr = System.getProperty(propertyName);
425    
426          if (pinStr == null)
427          {
428            resultCode = DirectoryServer.getServerErrorResultCode();
429    
430            messages.add(ERR_PKCS11_KEYMANAGER_PIN_PROPERTY_NOT_SET.get(
431                    String.valueOf(propertyName),
432                    String.valueOf(configEntryDN)));
433          }
434          else
435          {
436            newPIN = pinStr.toCharArray();
437          }
438        }
439        else if (configuration.getKeyStorePinEnvironmentVariable() != null)
440        {
441          String enVarName = configuration.getKeyStorePinEnvironmentVariable();
442          String pinStr    = System.getenv(enVarName);
443    
444          if (pinStr == null)
445          {
446            resultCode = DirectoryServer.getServerErrorResultCode();
447    
448            messages.add(ERR_PKCS11_KEYMANAGER_PIN_ENVAR_NOT_SET.get(
449                    String.valueOf(enVarName),
450                    String.valueOf(configEntryDN)));
451          }
452          else
453          {
454            newPIN = pinStr.toCharArray();
455          }
456        }
457        else if (configuration.getKeyStorePinFile() != null)
458        {
459          String fileName = configuration.getKeyStorePinFile();
460          File   pinFile  = getFileForPath(fileName);
461    
462          if (!pinFile.exists())
463          {
464            resultCode = DirectoryServer.getServerErrorResultCode();
465    
466            messages.add(ERR_PKCS11_KEYMANAGER_PIN_NO_SUCH_FILE.get(
467                    String.valueOf(fileName),
468                    String.valueOf(configEntryDN)));
469          }
470          else
471          {
472            String pinStr = null;
473            BufferedReader br = null;
474            try {
475              br = new BufferedReader(new FileReader(pinFile));
476              pinStr = br.readLine();
477            }
478            catch (IOException ioe)
479            {
480              resultCode = DirectoryServer.getServerErrorResultCode();
481    
482              messages.add(ERR_PKCS11_KEYMANAGER_PIN_FILE_CANNOT_READ.get(
483                      String.valueOf(fileName),
484                      String.valueOf(configEntryDN),
485                      getExceptionMessage(ioe)));
486            }
487            finally
488            {
489              try
490              {
491                br.close();
492              } catch (Exception e) {}
493            }
494    
495            if (pinStr == null)
496            {
497              resultCode = DirectoryServer.getServerErrorResultCode();
498    
499              messages.add(ERR_PKCS11_KEYMANAGER_PIN_FILE_EMPTY.get(
500                      String.valueOf(fileName),
501                      String.valueOf(configEntryDN)));
502            }
503            else
504            {
505              newPIN = pinStr.toCharArray();
506            }
507          }
508        }
509        else if (configuration.getKeyStorePin() != null)
510        {
511          newPIN = configuration.getKeyStorePin().toCharArray();
512        }
513        else
514        {
515          // Pin wasn't defined anywhere.
516          resultCode = DirectoryServer.getServerErrorResultCode();
517    
518    
519          messages.add(ERR_PKCS11_KEYMANAGER_NO_PIN.get(
520                  String.valueOf(configEntryDN)));
521        }
522    
523    
524        if (resultCode == ResultCode.SUCCESS)
525        {
526          currentConfig = configuration;
527          keyStorePIN   = newPIN;
528        }
529    
530    
531        return new ConfigChangeResult(resultCode, adminActionRequired, messages);
532      }
533    }
534