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.FileInputStream;
035    import java.io.FileReader;
036    import java.io.IOException;
037    import java.security.KeyStore;
038    import java.security.KeyStoreException;
039    import java.util.ArrayList;
040    import java.util.List;
041    import javax.net.ssl.KeyManager;
042    import javax.net.ssl.KeyManagerFactory;
043    
044    import org.opends.server.admin.server.ConfigurationChangeListener;
045    import org.opends.server.admin.std.server.FileBasedKeyManagerProviderCfg;
046    import org.opends.server.api.KeyManagerProvider;
047    import org.opends.server.config.ConfigException;
048    import org.opends.server.core.DirectoryServer;
049    import org.opends.server.types.ConfigChangeResult;
050    import org.opends.server.types.DirectoryException;
051    import org.opends.server.types.DN;
052    import org.opends.server.types.InitializationException;
053    import org.opends.server.types.ResultCode;
054    
055    import static org.opends.server.loggers.debug.DebugLogger.*;
056    import org.opends.server.loggers.debug.DebugTracer;
057    import org.opends.server.types.DebugLogLevel;
058    import static org.opends.messages.ExtensionMessages.*;
059    
060    import static org.opends.server.util.StaticUtils.*;
061    
062    
063    
064    /**
065     * This class defines a key manager provider that will access keys stored in a
066     * file located on the Directory Server filesystem.
067     */
068    public class FileBasedKeyManagerProvider
069           extends KeyManagerProvider<FileBasedKeyManagerProviderCfg>
070           implements ConfigurationChangeListener<FileBasedKeyManagerProviderCfg>
071    {
072      /**
073       * The tracer object for the debug logger.
074       */
075      private static final DebugTracer TRACER = getTracer();
076    
077    
078    
079      // The DN of the configuration entry for this key manager provider.
080      private DN configEntryDN;
081    
082      // The PIN needed to access the keystore.
083      private char[] keyStorePIN;
084    
085      // The configuration for this key manager provider.
086      private FileBasedKeyManagerProviderCfg currentConfig;
087    
088      // The path to the key store backing file.
089      private String keyStoreFile;
090    
091      // The key store type to use.
092      private String keyStoreType;
093    
094    
095    
096      /**
097       * Creates a new instance of this file-based 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 FileBasedKeyManagerProvider()
102      {
103        // No implementation is required.
104      }
105    
106    
107    
108      /**
109       * {@inheritDoc}
110       */
111      @Override
112      public void initializeKeyManagerProvider(
113          FileBasedKeyManagerProviderCfg configuration)
114          throws ConfigException, InitializationException {
115        // Store the DN of the configuration entry and register as a change
116        // listener.
117        currentConfig = configuration;
118        configEntryDN = configuration.dn();
119        configuration.addFileBasedChangeListener(this);
120    
121    
122        // Get the path to the key store file.
123        keyStoreFile = configuration.getKeyStoreFile();
124        try {
125          File f = getFileForPath(keyStoreFile);
126          if (!(f.exists() && f.isFile())) {
127            Message message = ERR_FILE_KEYMANAGER_NO_SUCH_FILE.get(
128                String.valueOf(keyStoreFile), String.valueOf(configEntryDN));
129            throw new InitializationException(message);
130          }
131        } catch (SecurityException e) {
132          if (debugEnabled())
133          {
134            TRACER.debugCaught(DebugLogLevel.ERROR, e);
135          }
136    
137          Message message = ERR_FILE_KEYMANAGER_CANNOT_DETERMINE_FILE.get(
138              String.valueOf(configEntryDN), getExceptionMessage(e));
139          throw new InitializationException(message, e);
140        }
141    
142        // Get the keystore type. If none is specified, then use the
143        // default type.
144        if (configuration.getKeyStoreType() != null) {
145          try {
146            KeyStore.getInstance(configuration.getKeyStoreType());
147            keyStoreType = configuration.getKeyStoreType();
148          } catch (KeyStoreException kse) {
149            if (debugEnabled())
150            {
151              TRACER.debugCaught(DebugLogLevel.ERROR, kse);
152            }
153    
154            Message message = ERR_FILE_KEYMANAGER_INVALID_TYPE.
155                get(String.valueOf(configuration.getKeyStoreType()),
156                    String.valueOf(configEntryDN), getExceptionMessage(kse));
157            throw new InitializationException(message);
158          }
159        } else {
160          keyStoreType = KeyStore.getDefaultType();
161        }
162    
163        // Get the PIN needed to access the contents of the keystore file.
164        //
165        // We will offer several places to look for the PIN, and we will
166        // do so in the following order:
167        //
168        // - In a specified Java property
169        // - In a specified environment variable
170        // - In a specified file on the server filesystem.
171        // - As the value of a configuration attribute.
172        //
173        // In any case, the PIN must be in the clear.
174        keyStorePIN = null;
175    
176        if (configuration.getKeyStorePinProperty() != null) {
177          String propertyName = configuration.getKeyStorePinProperty();
178          String pinStr = System.getProperty(propertyName);
179    
180          if (pinStr == null) {
181            Message message = ERR_FILE_KEYMANAGER_PIN_PROPERTY_NOT_SET.get(
182                String.valueOf(propertyName), String.valueOf(configEntryDN));
183            throw new InitializationException(message);
184          }
185    
186          keyStorePIN = pinStr.toCharArray();
187        } else if (configuration.getKeyStorePinEnvironmentVariable() != null) {
188          String enVarName = configuration
189              .getKeyStorePinEnvironmentVariable();
190          String pinStr = System.getenv(enVarName);
191    
192          if (pinStr == null) {
193            Message message = ERR_FILE_KEYMANAGER_PIN_ENVAR_NOT_SET.get(
194                String.valueOf(enVarName), String.valueOf(configEntryDN));
195            throw new InitializationException(message);
196          }
197    
198          keyStorePIN = pinStr.toCharArray();
199        } else if (configuration.getKeyStorePinFile() != null) {
200          String fileName = configuration.getKeyStorePinFile();
201          File pinFile = getFileForPath(fileName);
202    
203          if (!pinFile.exists()) {
204            Message message = ERR_FILE_KEYMANAGER_PIN_NO_SUCH_FILE.get(
205                String.valueOf(fileName), String.valueOf(configEntryDN));
206            throw new InitializationException(message);
207          }
208    
209          String pinStr;
210          try {
211            BufferedReader br = new BufferedReader(
212                new FileReader(pinFile));
213            pinStr = br.readLine();
214            br.close();
215          } catch (IOException ioe) {
216            Message message = ERR_FILE_KEYMANAGER_PIN_FILE_CANNOT_READ.
217                get(String.valueOf(fileName), String.valueOf(configEntryDN),
218                    getExceptionMessage(ioe));
219            throw new InitializationException(message, ioe);
220          }
221    
222          if (pinStr == null) {
223            Message message = ERR_FILE_KEYMANAGER_PIN_FILE_EMPTY.get(
224                String.valueOf(fileName), String.valueOf(configEntryDN));
225            throw new InitializationException(message);
226          }
227    
228          keyStorePIN = pinStr.toCharArray();
229        } else if (configuration.getKeyStorePin() != null) {
230          keyStorePIN = configuration.getKeyStorePin().toCharArray();
231        } else {
232          // Pin wasn't defined anywhere.
233          Message message =
234              ERR_FILE_KEYMANAGER_NO_PIN.get(String.valueOf(configEntryDN));
235          throw new ConfigException(message);
236        }
237      }
238    
239    
240    
241      /**
242       * Performs any finalization that may be necessary for this key
243       * manager provider.
244       */
245      public void finalizeKeyManagerProvider()
246      {
247        currentConfig.removeFileBasedChangeListener(this);
248      }
249    
250    
251    
252      /**
253       * Retrieves a set of <CODE>KeyManager</CODE> objects that may be used for
254       * interactions requiring access to a key manager.
255       *
256       * @return  A set of <CODE>KeyManager</CODE> objects that may be used for
257       *          interactions requiring access to a key manager.
258       *
259       * @throws  DirectoryException  If a problem occurs while attempting to obtain
260       *                              the set of key managers.
261       */
262      public KeyManager[] getKeyManagers()
263             throws DirectoryException
264      {
265        KeyStore keyStore;
266        try
267        {
268          keyStore = KeyStore.getInstance(keyStoreType);
269    
270          FileInputStream inputStream =
271               new FileInputStream(getFileForPath(keyStoreFile));
272          keyStore.load(inputStream, keyStorePIN);
273          inputStream.close();
274        }
275        catch (Exception e)
276        {
277          if (debugEnabled())
278          {
279            TRACER.debugCaught(DebugLogLevel.ERROR, e);
280          }
281    
282          Message message = ERR_FILE_KEYMANAGER_CANNOT_LOAD.get(
283              keyStoreFile, getExceptionMessage(e));
284          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
285                                       message, e);
286        }
287    
288    
289        try
290        {
291          String keyManagerAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
292          KeyManagerFactory keyManagerFactory =
293               KeyManagerFactory.getInstance(keyManagerAlgorithm);
294          keyManagerFactory.init(keyStore, keyStorePIN);
295          return keyManagerFactory.getKeyManagers();
296        }
297        catch (Exception e)
298        {
299          if (debugEnabled())
300          {
301            TRACER.debugCaught(DebugLogLevel.ERROR, e);
302          }
303    
304          Message message = ERR_FILE_KEYMANAGER_CANNOT_CREATE_FACTORY.get(
305              keyStoreFile, getExceptionMessage(e));
306          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
307                                       message, e);
308        }
309      }
310    
311    
312    
313      /**
314       * {@inheritDoc}
315       */
316      @Override()
317      public boolean isConfigurationAcceptable(
318                            FileBasedKeyManagerProviderCfg configuration,
319                            List<Message> unacceptableReasons)
320      {
321        return isConfigurationChangeAcceptable(configuration, unacceptableReasons);
322      }
323    
324    
325    
326      /**
327       * {@inheritDoc}
328       */
329      public boolean isConfigurationChangeAcceptable(
330                          FileBasedKeyManagerProviderCfg configuration,
331                          List<Message> unacceptableReasons)
332      {
333        boolean configAcceptable = true;
334        DN cfgEntryDN = configuration.dn();
335    
336    
337        // Get the path to the key store file.
338        String newKeyStoreFile = configuration.getKeyStoreFile();
339        try
340        {
341          File f = getFileForPath(newKeyStoreFile);
342          if (!(f.exists() && f.isFile()))
343          {
344            unacceptableReasons.add(ERR_FILE_KEYMANAGER_NO_SUCH_FILE.get(
345                    String.valueOf(newKeyStoreFile),
346                    String.valueOf(cfgEntryDN)));
347            configAcceptable = false;
348          }
349        }
350        catch (Exception e)
351        {
352          if (debugEnabled())
353          {
354            TRACER.debugCaught(DebugLogLevel.ERROR, e);
355          }
356    
357          unacceptableReasons.add(ERR_FILE_KEYMANAGER_CANNOT_DETERMINE_FILE.get(
358                  String.valueOf(cfgEntryDN),
359                  getExceptionMessage(e)));
360          configAcceptable = false;
361        }
362    
363        // Get the keystore type. If none is specified, then use the default type.
364        if (configuration.getKeyStoreType() != null)
365        {
366          try
367          {
368            KeyStore.getInstance(configuration.getKeyStoreType());
369          }
370          catch (KeyStoreException kse)
371          {
372            if (debugEnabled())
373            {
374              TRACER.debugCaught(DebugLogLevel.ERROR, kse);
375            }
376    
377            unacceptableReasons.add(ERR_FILE_KEYMANAGER_INVALID_TYPE.get(
378                    String.valueOf(configuration.getKeyStoreType()),
379                   String.valueOf(cfgEntryDN), getExceptionMessage(kse)));
380            configAcceptable = false;
381          }
382        }
383    
384        // Get the PIN needed to access the contents of the keystore file.
385        //
386        // We will offer several places to look for the PIN, and we will
387        // do so in the following order:
388        //
389        // - In a specified Java property
390        // - In a specified environment variable
391        // - In a specified file on the server filesystem.
392        // - As the value of a configuration attribute.
393        //
394        // In any case, the PIN must be in the clear.
395        if (configuration.getKeyStorePinProperty() != null)
396        {
397          String propertyName = configuration.getKeyStorePinProperty();
398          String pinStr = System.getProperty(propertyName);
399    
400          if (pinStr == null)
401          {
402            unacceptableReasons.add(ERR_FILE_KEYMANAGER_PIN_PROPERTY_NOT_SET.get(
403                    String.valueOf(propertyName),
404                    String.valueOf(cfgEntryDN)));
405            configAcceptable = false;
406          }
407        }
408        else if (configuration.getKeyStorePinEnvironmentVariable() != null)
409        {
410          String enVarName = configuration.getKeyStorePinEnvironmentVariable();
411          String pinStr    = System.getenv(enVarName);
412    
413          if (pinStr == null)
414          {
415            unacceptableReasons.add(ERR_FILE_KEYMANAGER_PIN_ENVAR_NOT_SET.get(
416                    String.valueOf(enVarName),
417                    String.valueOf(cfgEntryDN)));
418            configAcceptable = false;
419          }
420        }
421        else if (configuration.getKeyStorePinFile() != null)
422        {
423          String fileName = configuration.getKeyStorePinFile();
424          File   pinFile  = getFileForPath(fileName);
425    
426          if (!pinFile.exists())
427          {
428            unacceptableReasons.add(ERR_FILE_KEYMANAGER_PIN_NO_SUCH_FILE.get(
429                    String.valueOf(fileName),
430                    String.valueOf(cfgEntryDN)));
431            configAcceptable = false;
432          }
433          else
434          {
435            String pinStr = null;
436            BufferedReader br = null;
437            try {
438              br = new BufferedReader(new FileReader(pinFile));
439              pinStr = br.readLine();
440            }
441            catch (IOException ioe)
442            {
443              unacceptableReasons.add(ERR_FILE_KEYMANAGER_PIN_FILE_CANNOT_READ.get(
444                      String.valueOf(fileName),
445                      String.valueOf(cfgEntryDN),
446                      getExceptionMessage(ioe)));
447              configAcceptable = false;
448            }
449            finally
450            {
451              try
452              {
453                br.close();
454              } catch (Exception e) {}
455            }
456    
457            if (pinStr == null)
458            {
459              unacceptableReasons.add(ERR_FILE_KEYMANAGER_PIN_FILE_EMPTY.get(
460                      String.valueOf(fileName),
461                      String.valueOf(cfgEntryDN)));
462              configAcceptable = false;
463            }
464          }
465        }
466        else if (configuration.getKeyStorePin() != null)
467        {
468          configuration.getKeyStorePin().toCharArray();
469        }
470        else
471        {
472          // Pin wasn't defined anywhere.
473          unacceptableReasons.add(ERR_FILE_KEYMANAGER_NO_PIN.get(
474                  String.valueOf(cfgEntryDN)));
475          configAcceptable = false;
476        }
477    
478        return configAcceptable;
479      }
480    
481    
482    
483      /**
484       * {@inheritDoc}
485       */
486      public ConfigChangeResult applyConfigurationChange(
487                                     FileBasedKeyManagerProviderCfg configuration)
488      {
489        ResultCode        resultCode          = ResultCode.SUCCESS;
490        boolean           adminActionRequired = false;
491        ArrayList<Message> messages            = new ArrayList<Message>();
492    
493    
494        // Get the path to the key store file.
495        String newKeyStoreFile = configuration.getKeyStoreFile();
496        try
497        {
498          File f = getFileForPath(newKeyStoreFile);
499          if (!(f.exists() && f.isFile()))
500          {
501            resultCode = DirectoryServer.getServerErrorResultCode();
502    
503            messages.add(ERR_FILE_KEYMANAGER_NO_SUCH_FILE.get(
504                    String.valueOf(newKeyStoreFile),
505                    String.valueOf(configEntryDN)));
506          }
507        }
508        catch (Exception e)
509        {
510          if (debugEnabled())
511          {
512            TRACER.debugCaught(DebugLogLevel.ERROR, e);
513          }
514    
515          resultCode = DirectoryServer.getServerErrorResultCode();
516    
517          messages.add(ERR_FILE_KEYMANAGER_CANNOT_DETERMINE_FILE.get(
518                  String.valueOf(configEntryDN),
519                  getExceptionMessage(e)));
520        }
521    
522        // Get the keystore type. If none is specified, then use the default type.
523        String newKeyStoreType = KeyStore.getDefaultType();
524        if (configuration.getKeyStoreType() != null)
525        {
526          try
527          {
528            KeyStore.getInstance(configuration.getKeyStoreType());
529            newKeyStoreType = configuration.getKeyStoreType();
530          }
531          catch (KeyStoreException kse)
532          {
533            if (debugEnabled())
534            {
535              TRACER.debugCaught(DebugLogLevel.ERROR, kse);
536            }
537    
538            resultCode = DirectoryServer.getServerErrorResultCode();
539    
540            messages.add(ERR_FILE_KEYMANAGER_INVALID_TYPE.get(
541                    String.valueOf(configuration.getKeyStoreType()),
542                    String.valueOf(configEntryDN),
543                    getExceptionMessage(kse)));
544          }
545        }
546    
547        // Get the PIN needed to access the contents of the keystore file.
548        //
549        // We will offer several places to look for the PIN, and we will
550        // do so in the following order:
551        //
552        // - In a specified Java property
553        // - In a specified environment variable
554        // - In a specified file on the server filesystem.
555        // - As the value of a configuration attribute.
556        //
557        // In any case, the PIN must be in the clear.
558        char[] newPIN = null;
559    
560        if (configuration.getKeyStorePinProperty() != null)
561        {
562          String propertyName = configuration.getKeyStorePinProperty();
563          String pinStr = System.getProperty(propertyName);
564    
565          if (pinStr == null)
566          {
567            resultCode = DirectoryServer.getServerErrorResultCode();
568    
569            messages.add(ERR_FILE_KEYMANAGER_PIN_PROPERTY_NOT_SET.get(
570                    String.valueOf(propertyName),
571                    String.valueOf(configEntryDN)));
572          }
573          else
574          {
575            newPIN = pinStr.toCharArray();
576          }
577        }
578        else if (configuration.getKeyStorePinEnvironmentVariable() != null)
579        {
580          String enVarName = configuration.getKeyStorePinEnvironmentVariable();
581          String pinStr    = System.getenv(enVarName);
582    
583          if (pinStr == null)
584          {
585            resultCode = DirectoryServer.getServerErrorResultCode();
586    
587            messages.add(ERR_FILE_KEYMANAGER_PIN_ENVAR_NOT_SET.get(
588                    String.valueOf(enVarName),
589                    String.valueOf(configEntryDN)));
590          }
591          else
592          {
593            newPIN = pinStr.toCharArray();
594          }
595        }
596        else if (configuration.getKeyStorePinFile() != null)
597        {
598          String fileName = configuration.getKeyStorePinFile();
599          File   pinFile  = getFileForPath(fileName);
600    
601          if (!pinFile.exists())
602          {
603            resultCode = DirectoryServer.getServerErrorResultCode();
604    
605            messages.add(ERR_FILE_KEYMANAGER_PIN_NO_SUCH_FILE.get(
606                    String.valueOf(fileName),
607                    String.valueOf(configEntryDN)));
608          }
609          else
610          {
611            String pinStr = null;
612            BufferedReader br = null;
613            try {
614              br = new BufferedReader(new FileReader(pinFile));
615              pinStr = br.readLine();
616            }
617            catch (IOException ioe)
618            {
619              resultCode = DirectoryServer.getServerErrorResultCode();
620    
621              messages.add(ERR_FILE_KEYMANAGER_PIN_FILE_CANNOT_READ.get(
622                      String.valueOf(fileName),
623                      String.valueOf(configEntryDN),
624                      getExceptionMessage(ioe)));
625            }
626            finally
627            {
628              try
629              {
630                br.close();
631              } catch (Exception e) {}
632            }
633    
634            if (pinStr == null)
635            {
636              resultCode = DirectoryServer.getServerErrorResultCode();
637    
638              messages.add(ERR_FILE_KEYMANAGER_PIN_FILE_EMPTY.get(
639                      String.valueOf(fileName),
640                      String.valueOf(configEntryDN)));
641            }
642            else
643            {
644              newPIN = pinStr.toCharArray();
645            }
646          }
647        }
648        else if (configuration.getKeyStorePin() != null)
649        {
650          newPIN = configuration.getKeyStorePin().toCharArray();
651        }
652        else
653        {
654          // Pin wasn't defined anywhere.
655          resultCode = DirectoryServer.getServerErrorResultCode();
656    
657          messages.add(ERR_FILE_KEYMANAGER_NO_PIN.get(
658                  String.valueOf(configEntryDN)));
659        }
660    
661    
662        if (resultCode == ResultCode.SUCCESS)
663        {
664          currentConfig = configuration;
665          keyStorePIN   = newPIN;
666          keyStoreFile  = newKeyStoreFile;
667          keyStoreType  = newKeyStoreType;
668        }
669    
670    
671        return new ConfigChangeResult(resultCode, adminActionRequired, messages);
672      }
673    }
674