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 legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2006-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.server.extensions;
028
029import org.forgerock.i18n.LocalizableMessage;
030import java.io.BufferedReader;
031import java.io.File;
032import java.io.FileInputStream;
033import java.io.FileReader;
034import java.io.IOException;
035import java.security.*;
036import java.util.List;
037import javax.net.ssl.TrustManager;
038import javax.net.ssl.TrustManagerFactory;
039import javax.net.ssl.X509TrustManager;
040
041import org.opends.server.admin.server.ConfigurationChangeListener;
042import org.opends.server.admin.std.server.TrustManagerProviderCfg;
043import org.opends.server.admin.std.server.FileBasedTrustManagerProviderCfg;
044import org.opends.server.api.TrustManagerProvider;
045import org.forgerock.opendj.config.server.ConfigException;
046import org.opends.server.core.DirectoryServer;
047import org.forgerock.opendj.config.server.ConfigChangeResult;
048import org.opends.server.types.DirectoryException;
049import org.opends.server.types.DN;
050import org.opends.server.types.InitializationException;
051import org.forgerock.opendj.ldap.ResultCode;
052import org.opends.server.util.ExpirationCheckTrustManager;
053
054import org.forgerock.i18n.slf4j.LocalizedLogger;
055import static org.opends.messages.ExtensionMessages.*;
056import static org.opends.server.util.StaticUtils.*;
057
058/**
059 * This class defines a trust manager provider that will reference certificates
060 * stored in a file located on the Directory Server filesystem.
061 */
062public class FileBasedTrustManagerProvider
063       extends TrustManagerProvider<FileBasedTrustManagerProviderCfg>
064       implements ConfigurationChangeListener<FileBasedTrustManagerProviderCfg>
065{
066  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
067
068
069
070
071  /** The DN of the configuration entry for this trust manager provider. */
072  private DN configEntryDN;
073
074  /** The PIN needed to access the trust store. */
075  private char[] trustStorePIN;
076
077  /** The handle to the configuration for this trust manager. */
078  private FileBasedTrustManagerProviderCfg currentConfig;
079
080  /** The path to the trust store backing file. */
081  private String trustStoreFile;
082
083  /** The trust store type to use. */
084  private String trustStoreType;
085
086
087
088  /**
089   * Creates a new instance of this file-based trust manager provider.  The
090   * <CODE>initializeTrustManagerProvider</CODE> method must be called on the
091   * resulting object before it may be used.
092   */
093  public FileBasedTrustManagerProvider()
094  {
095    // No implementation is required.
096  }
097
098
099
100  /** {@inheritDoc} */
101  @Override
102  public void initializeTrustManagerProvider(
103                   FileBasedTrustManagerProviderCfg configuration)
104         throws ConfigException, InitializationException
105  {
106    // Store the DN of the configuration entry and register to listen for any
107    // changes to the configuration entry.
108    currentConfig = configuration;
109    configEntryDN = configuration.dn();
110    configuration.addFileBasedChangeListener(this);
111
112
113    // Get the path to the trust store file.
114    trustStoreFile = configuration.getTrustStoreFile();
115    File f = getFileForPath(trustStoreFile);
116    if (!f.exists() || !f.isFile())
117    {
118      LocalizableMessage message = ERR_FILE_TRUSTMANAGER_NO_SUCH_FILE.get(trustStoreFile, configEntryDN);
119      throw new InitializationException(message);
120    }
121
122
123    // Get the trust store type.  If none is specified, then use the default
124    // type.
125    trustStoreType = configuration.getTrustStoreType();
126    if (trustStoreType == null)
127    {
128      trustStoreType = KeyStore.getDefaultType();
129    }
130
131    try
132    {
133      KeyStore.getInstance(trustStoreType);
134    }
135    catch (KeyStoreException kse)
136    {
137      logger.traceException(kse);
138
139      LocalizableMessage message = ERR_FILE_TRUSTMANAGER_INVALID_TYPE.
140          get(trustStoreType, configEntryDN, getExceptionMessage(kse));
141      throw new InitializationException(message);
142    }
143
144
145    // Get the PIN needed to access the contents of the trust store file.  We
146    // will offer several places to look for the PIN, and we will do so in the
147    // following order:
148    // - In a specified Java property
149    // - In a specified environment variable
150    // - In a specified file on the server filesystem.
151    // - As the value of a configuration attribute.
152    // In any case, the PIN must be in the clear.  If no PIN is provided, then
153    // it will be assumed that none is required to access the information in the
154    // trust store.
155    String pinProperty = configuration.getTrustStorePinProperty();
156    if (pinProperty == null)
157    {
158      String pinEnVar = configuration.getTrustStorePinEnvironmentVariable();
159      if (pinEnVar == null)
160      {
161        String pinFilePath = configuration.getTrustStorePinFile();
162        if (pinFilePath == null)
163        {
164          String pinStr = configuration.getTrustStorePin();
165          if (pinStr == null)
166          {
167            trustStorePIN = null;
168          }
169          else
170          {
171            trustStorePIN = pinStr.toCharArray();
172          }
173        }
174        else
175        {
176          File pinFile = getFileForPath(pinFilePath);
177          if (! pinFile.exists())
178          {
179            LocalizableMessage message = ERR_FILE_TRUSTMANAGER_PIN_NO_SUCH_FILE.get(pinFilePath, configEntryDN);
180            throw new InitializationException(message);
181          }
182          else
183          {
184            String pinStr;
185
186            BufferedReader br = null;
187            try
188            {
189              br = new BufferedReader(new FileReader(pinFile));
190              pinStr = br.readLine();
191            }
192            catch (IOException ioe)
193            {
194              LocalizableMessage message = ERR_FILE_TRUSTMANAGER_PIN_FILE_CANNOT_READ.
195                  get(pinFilePath, configEntryDN, getExceptionMessage(ioe));
196              throw new InitializationException(message, ioe);
197            }
198            finally
199            {
200              close(br);
201            }
202
203            if (pinStr == null)
204            {
205              LocalizableMessage message = ERR_FILE_TRUSTMANAGER_PIN_FILE_EMPTY.get(pinFilePath, configEntryDN);
206              throw new InitializationException(message);
207            }
208            else
209            {
210              trustStorePIN     = pinStr.toCharArray();
211            }
212          }
213        }
214      }
215      else
216      {
217        String pinStr = System.getenv(pinEnVar);
218        if (pinStr == null)
219        {
220          LocalizableMessage message = ERR_FILE_TRUSTMANAGER_PIN_ENVAR_NOT_SET.get(pinProperty, configEntryDN);
221          throw new InitializationException(message);
222        }
223        else
224        {
225          trustStorePIN = pinStr.toCharArray();
226        }
227      }
228    }
229    else
230    {
231      String pinStr = System.getProperty(pinProperty);
232      if (pinStr == null)
233      {
234        LocalizableMessage message = ERR_FILE_TRUSTMANAGER_PIN_PROPERTY_NOT_SET.get(pinProperty, configEntryDN);
235        throw new InitializationException(message);
236      }
237      else
238      {
239        trustStorePIN = pinStr.toCharArray();
240      }
241    }
242  }
243
244
245
246  /** {@inheritDoc} */
247  @Override
248  public void finalizeTrustManagerProvider()
249  {
250    currentConfig.removeFileBasedChangeListener(this);
251  }
252
253
254
255  /** {@inheritDoc} */
256  @Override
257  public TrustManager[] getTrustManagers()
258         throws DirectoryException
259  {
260    KeyStore trustStore;
261    try
262    {
263      trustStore = KeyStore.getInstance(trustStoreType);
264
265      FileInputStream inputStream =
266           new FileInputStream(getFileForPath(trustStoreFile));
267      trustStore.load(inputStream, trustStorePIN);
268      inputStream.close();
269    }
270    catch (Exception e)
271    {
272      logger.traceException(e);
273
274      LocalizableMessage message = ERR_FILE_TRUSTMANAGER_CANNOT_LOAD.get(
275          trustStoreFile, getExceptionMessage(e));
276      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
277                                   message, e);
278    }
279
280
281    try
282    {
283      String trustManagerAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
284      TrustManagerFactory trustManagerFactory =
285           TrustManagerFactory.getInstance(trustManagerAlgorithm);
286      trustManagerFactory.init(trustStore);
287      TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
288      TrustManager[] newTrustManagers = new TrustManager[trustManagers.length];
289      for (int i=0; i < trustManagers.length; i++)
290      {
291        newTrustManagers[i] = new ExpirationCheckTrustManager(
292                                       (X509TrustManager) trustManagers[i]);
293      }
294      return newTrustManagers;
295    }
296    catch (Exception e)
297    {
298      logger.traceException(e);
299
300      LocalizableMessage message = ERR_FILE_TRUSTMANAGER_CANNOT_CREATE_FACTORY.get(
301          trustStoreFile, getExceptionMessage(e));
302      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
303                                   message, e);
304    }
305  }
306
307
308
309  /** {@inheritDoc} */
310  @Override
311  public boolean isConfigurationAcceptable(
312                         TrustManagerProviderCfg configuration,
313                         List<LocalizableMessage> unacceptableReasons)
314  {
315    FileBasedTrustManagerProviderCfg config =
316            (FileBasedTrustManagerProviderCfg) configuration;
317    return isConfigurationChangeAcceptable(config, unacceptableReasons);
318  }
319
320
321
322  /** {@inheritDoc} */
323  public boolean isConfigurationChangeAcceptable(
324                      FileBasedTrustManagerProviderCfg configuration,
325                      List<LocalizableMessage> unacceptableReasons)
326  {
327    boolean configAcceptable = true;
328    DN cfgEntryDN = configuration.dn();
329
330
331    // Get the path to the trust store file.
332    String newTrustStoreFile = configuration.getTrustStoreFile();
333    try
334    {
335      File f = getFileForPath(newTrustStoreFile);
336      if (!f.exists() || !f.isFile())
337      {
338        unacceptableReasons.add(ERR_FILE_TRUSTMANAGER_NO_SUCH_FILE.get(newTrustStoreFile, cfgEntryDN));
339        configAcceptable = false;
340      }
341    }
342    catch (Exception e)
343    {
344      logger.traceException(e);
345
346      unacceptableReasons.add(ERR_FILE_TRUSTMANAGER_CANNOT_DETERMINE_FILE.get(cfgEntryDN, getExceptionMessage(e)));
347      configAcceptable = false;
348    }
349
350
351    // Check to see if the trust store type is acceptable.
352    String storeType = configuration.getTrustStoreType();
353    if (storeType != null)
354    {
355      try
356      {
357        KeyStore.getInstance(storeType);
358      }
359      catch (KeyStoreException kse)
360      {
361        logger.traceException(kse);
362
363        unacceptableReasons.add(ERR_FILE_TRUSTMANAGER_INVALID_TYPE.get(
364            storeType, cfgEntryDN, getExceptionMessage(kse)));
365        configAcceptable = false;
366      }
367    }
368
369
370    // If there is a PIN property, then make sure the corresponding
371    // property is set.
372    String pinProp = configuration.getTrustStorePinProperty();
373    if (pinProp != null && System.getProperty(pinProp) == null)
374    {
375      unacceptableReasons.add(ERR_FILE_TRUSTMANAGER_PIN_PROPERTY_NOT_SET.get(pinProp, cfgEntryDN));
376      configAcceptable = false;
377    }
378
379
380    // If there is a PIN environment variable, then make sure the corresponding
381    // environment variable is set.
382    String pinEnVar = configuration.getTrustStorePinEnvironmentVariable();
383    if (pinEnVar != null && System.getenv(pinEnVar) == null)
384    {
385      unacceptableReasons.add(ERR_FILE_TRUSTMANAGER_PIN_ENVAR_NOT_SET.get(pinEnVar, cfgEntryDN));
386      configAcceptable = false;
387    }
388
389
390    // If there is a PIN file, then make sure the file exists and is readable.
391    String pinFile = configuration.getTrustStorePinFile();
392    if (pinFile != null)
393    {
394      File f = getFileForPath(pinFile);
395      if (f.exists())
396      {
397        String pinStr = null;
398
399        BufferedReader br = null;
400        try
401        {
402          br = new BufferedReader(new FileReader(f));
403          pinStr = br.readLine();
404        }
405        catch (IOException ioe)
406        {
407          unacceptableReasons.add(ERR_FILE_TRUSTMANAGER_PIN_FILE_CANNOT_READ.get(
408              pinFile, cfgEntryDN, getExceptionMessage(ioe)));
409          configAcceptable = false;
410        }
411        finally
412        {
413          close(br);
414        }
415
416        if (pinStr == null)
417        {
418          LocalizableMessage message = ERR_FILE_TRUSTMANAGER_PIN_FILE_EMPTY.get(pinFile, cfgEntryDN);
419          unacceptableReasons.add(message);
420          configAcceptable = false;
421        }
422      }
423      else
424      {
425        LocalizableMessage message = ERR_FILE_TRUSTMANAGER_PIN_NO_SUCH_FILE.get(pinFile, cfgEntryDN);
426        unacceptableReasons.add(message);
427        configAcceptable = false;
428      }
429    }
430
431
432    return configAcceptable;
433  }
434
435  /** {@inheritDoc} */
436  public ConfigChangeResult applyConfigurationChange(
437                                 FileBasedTrustManagerProviderCfg configuration)
438  {
439    final ConfigChangeResult ccr = new ConfigChangeResult();
440
441
442    // Get the path to the trust store file.
443    String newTrustStoreFile = configuration.getTrustStoreFile();
444    File f = getFileForPath(newTrustStoreFile);
445    if (!f.exists() || !f.isFile())
446    {
447      ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
448      ccr.addMessage(ERR_FILE_TRUSTMANAGER_NO_SUCH_FILE.get(newTrustStoreFile, configEntryDN));
449    }
450
451    // Get the trust store type.  If none is specified, then use the default type.
452    String newTrustStoreType = configuration.getTrustStoreType();
453    if (newTrustStoreType == null)
454    {
455      newTrustStoreType = KeyStore.getDefaultType();
456    }
457
458    try
459    {
460      KeyStore.getInstance(newTrustStoreType);
461    }
462    catch (KeyStoreException kse)
463    {
464      logger.traceException(kse);
465
466      ccr.addMessage(ERR_FILE_TRUSTMANAGER_INVALID_TYPE.get(
467          newTrustStoreType, configEntryDN, getExceptionMessage(kse)));
468      ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
469    }
470
471
472    // Get the PIN needed to access the contents of the trust store file.  We
473    // will offer several places to look for the PIN, and we will do so in the
474    // following order:
475    // - In a specified Java property
476    // - In a specified environment variable
477    // - In a specified file on the server filesystem.
478    // - As the value of a configuration attribute.
479    // In any case, the PIN must be in the clear.  If no PIN is provided, then
480    // it will be assumed that none is required to access the information in the
481    // trust store.
482    char[] newPIN = null;
483    String newPINProperty = configuration.getTrustStorePinProperty();
484    if (newPINProperty == null)
485    {
486      String newPINEnVar = configuration.getTrustStorePinEnvironmentVariable();
487      if (newPINEnVar == null)
488      {
489        String newPINFile = configuration.getTrustStorePinFile();
490        if (newPINFile == null)
491        {
492          String pinStr = configuration.getTrustStorePin();
493          if (pinStr == null)
494          {
495            newPIN = null;
496          }
497          else
498          {
499            newPIN = pinStr.toCharArray();
500          }
501        }
502        else
503        {
504          File pinFile = getFileForPath(newPINFile);
505          if (! pinFile.exists())
506          {
507            ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
508            ccr.addMessage(ERR_FILE_TRUSTMANAGER_PIN_NO_SUCH_FILE.get(newPINFile, configEntryDN));
509          }
510          else
511          {
512            String pinStr = null;
513
514            BufferedReader br = null;
515            try
516            {
517              br = new BufferedReader(new FileReader(pinFile));
518              pinStr = br.readLine();
519            }
520            catch (IOException ioe)
521            {
522              ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
523              ccr.addMessage(ERR_FILE_TRUSTMANAGER_PIN_FILE_CANNOT_READ.get(
524                  newPINFile, configEntryDN, getExceptionMessage(ioe)));
525            }
526            finally
527            {
528              close(br);
529            }
530
531            if (pinStr == null)
532            {
533              ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
534              ccr.addMessage(ERR_FILE_TRUSTMANAGER_PIN_FILE_EMPTY.get(newPINFile, configEntryDN));
535            }
536            else
537            {
538              newPIN = pinStr.toCharArray();
539            }
540          }
541        }
542      }
543      else
544      {
545        String pinStr = System.getenv(newPINEnVar);
546        if (pinStr == null)
547        {
548          ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
549          ccr.addMessage(ERR_FILE_TRUSTMANAGER_PIN_ENVAR_NOT_SET.get(newPINEnVar, configEntryDN));
550        }
551        else
552        {
553          newPIN = pinStr.toCharArray();
554        }
555      }
556    }
557    else
558    {
559      String pinStr = System.getProperty(newPINProperty);
560      if (pinStr == null)
561      {
562        ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
563        ccr.addMessage(ERR_FILE_TRUSTMANAGER_PIN_PROPERTY_NOT_SET.get(newPINProperty, configEntryDN));
564      }
565      else
566      {
567        newPIN = pinStr.toCharArray();
568      }
569    }
570
571
572    if (ccr.getResultCode() == ResultCode.SUCCESS)
573    {
574      trustStoreFile = newTrustStoreFile;
575      trustStoreType = newTrustStoreType;
576      trustStorePIN  = newPIN;
577      currentConfig  = configuration;
578    }
579
580    return ccr;
581  }
582}