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