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 2007-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.backends;
028    
029    
030    
031    import java.io.File;
032    import java.io.BufferedReader;
033    import java.io.FileReader;
034    import java.io.IOException;
035    import java.io.FileInputStream;
036    import java.io.FileWriter;
037    import java.io.PrintWriter;
038    import java.io.FileOutputStream;
039    import java.net.UnknownHostException;
040    import java.security.Key;
041    import java.security.KeyStore;
042    import java.security.KeyStoreException;
043    import java.security.cert.Certificate;
044    import java.util.ArrayList;
045    import java.util.HashSet;
046    import java.util.LinkedHashMap;
047    import java.util.LinkedHashSet;
048    import java.util.List;
049    import java.util.Random;
050    import java.util.SortedSet;
051    import javax.naming.ldap.Rdn;
052    import javax.net.ssl.KeyManager;
053    import javax.net.ssl.KeyManagerFactory;
054    import javax.net.ssl.TrustManager;
055    import javax.net.ssl.TrustManagerFactory;
056    
057    import org.opends.messages.Message;
058    import org.opends.server.admin.Configuration;
059    import org.opends.server.admin.server.ConfigurationChangeListener;
060    import org.opends.server.admin.std.server.TrustStoreBackendCfg;
061    import org.opends.server.api.Backend;
062    import org.opends.server.config.ConfigException;
063    import org.opends.server.core.AddOperation;
064    import org.opends.server.core.DeleteOperation;
065    import org.opends.server.core.DirectoryServer;
066    import org.opends.server.core.ModifyOperation;
067    import org.opends.server.core.ModifyDNOperation;
068    import org.opends.server.core.SearchOperation;
069    import org.opends.server.loggers.ErrorLogger;
070    import org.opends.server.loggers.debug.DebugTracer;
071    import org.opends.server.protocols.asn1.ASN1OctetString;
072    import org.opends.server.types.Attribute;
073    import org.opends.server.types.AttributeType;
074    import org.opends.server.types.AttributeValue;
075    import org.opends.server.types.BackupConfig;
076    import org.opends.server.types.BackupDirectory;
077    import org.opends.server.types.ByteString;
078    import org.opends.server.types.ConditionResult;
079    import org.opends.server.types.ConfigChangeResult;
080    import org.opends.server.types.DebugLogLevel;
081    import org.opends.server.types.DirectoryException;
082    import org.opends.server.types.DN;
083    import org.opends.server.types.Entry;
084    import org.opends.server.types.FilePermission;
085    import org.opends.server.types.IndexType;
086    import org.opends.server.types.InitializationException;
087    import org.opends.server.types.LDIFExportConfig;
088    import org.opends.server.types.LDIFImportConfig;
089    import org.opends.server.types.LDIFImportResult;
090    import org.opends.server.types.ObjectClass;
091    import org.opends.server.types.RDN;
092    import org.opends.server.types.RestoreConfig;
093    import org.opends.server.types.ResultCode;
094    import org.opends.server.types.SearchFilter;
095    import org.opends.server.types.SearchScope;
096    import org.opends.server.util.CertificateManager;
097    import org.opends.server.util.Validator;
098    
099    import static org.opends.messages.BackendMessages.*;
100    import static org.opends.server.config.ConfigConstants.*;
101    import static org.opends.server.loggers.debug.DebugLogger.*;
102    import static org.opends.server.util.ServerConstants.*;
103    import static org.opends.server.util.StaticUtils.*;
104    
105    
106    
107    /**
108     * This class defines a backend used to provide an LDAP view of public keys
109     * stored in a key store.
110     */
111    public class TrustStoreBackend
112         extends Backend
113           implements ConfigurationChangeListener<TrustStoreBackendCfg>
114    {
115      /**
116       * The tracer object for the debug logger.
117       */
118      private static final DebugTracer TRACER = getTracer();
119    
120    
121    
122      // The current configuration state.
123      private TrustStoreBackendCfg configuration;
124    
125      // The DN for the base entry.
126      private DN baseDN;
127    
128      // The set of base DNs for this backend.
129      private DN[] baseDNs;
130    
131      // The base entry.
132      private Entry baseEntry;
133    
134      // The set of supported controls for this backend.
135      private HashSet<String> supportedControls;
136    
137      // The set of supported features for this backend.
138      private HashSet<String> supportedFeatures;
139    
140      // The PIN needed to access the trust store backing file.
141      private char[] trustStorePIN;
142    
143      // The path to the trust store backing file.
144      private String trustStoreFile;
145    
146      // The type of trust store backing file to use.
147      private String trustStoreType;
148    
149      // The certificate manager for the trust store.
150      private CertificateManager certificateManager;
151    
152    
153    
154      /**
155       * Creates a new backend.  All backend
156       * implementations must implement a default constructor that use
157       * <CODE>super()</CODE> to invoke this constructor.
158       */
159      public TrustStoreBackend()
160      {
161        super();
162    
163        // Perform all initialization in initializeBackend.
164      }
165    
166    
167    
168      /**
169       * {@inheritDoc}
170       */
171      @Override()
172      public void configureBackend(Configuration config) throws ConfigException
173      {
174        Validator.ensureNotNull(config);
175        Validator.ensureTrue(config instanceof TrustStoreBackendCfg);
176    
177        configuration = (TrustStoreBackendCfg)config;
178      }
179    
180    
181    
182      /**
183       * {@inheritDoc}
184       */
185      @Override()
186      public void initializeBackend()
187             throws ConfigException, InitializationException
188      {
189        DN configEntryDN = configuration.dn();
190    
191        // Create the set of base DNs that we will handle.  In this case, it's just
192        // the DN of the base trust store entry.
193        SortedSet<DN> baseDNSet = configuration.getBaseDN();
194        if (baseDNSet.size() != 1)
195        {
196          Message message = ERR_TRUSTSTORE_REQUIRES_ONE_BASE_DN.get(
197               String.valueOf(configEntryDN));
198          throw new InitializationException(message);
199        }
200        baseDN = baseDNSet.first();
201        baseDNs = new DN[] {baseDN};
202    
203        // Get the path to the trust store file.
204        trustStoreFile = configuration.getTrustStoreFile();
205    
206    
207        // Get the trust store type.  If none is specified, then use the default
208        // type.
209        trustStoreType = configuration.getTrustStoreType();
210        if (trustStoreType == null)
211        {
212          trustStoreType = KeyStore.getDefaultType();
213        }
214    
215        try
216        {
217          KeyStore.getInstance(trustStoreType);
218        }
219        catch (KeyStoreException kse)
220        {
221          if (debugEnabled())
222          {
223            TRACER.debugCaught(DebugLogLevel.ERROR, kse);
224          }
225    
226          Message message = ERR_TRUSTSTORE_INVALID_TYPE.
227              get(String.valueOf(trustStoreType), String.valueOf(configEntryDN),
228                  getExceptionMessage(kse));
229          throw new InitializationException(message);
230        }
231    
232    
233        // Get the PIN needed to access the contents of the trust store file.  We
234        // will offer several places to look for the PIN, and we will do so in the
235        // following order:
236        // - In a specified Java property
237        // - In a specified environment variable
238        // - In a specified file on the server filesystem.
239        // - As the value of a configuration attribute.
240        // In any case, the PIN must be in the clear.  If no PIN is provided, then
241        // it will be assumed that none is required to access the information in the
242        // trust store.
243        String pinProperty = configuration.getTrustStorePinProperty();
244        if (pinProperty == null)
245        {
246          String pinEnVar = configuration.getTrustStorePinEnvironmentVariable();
247          if (pinEnVar == null)
248          {
249            String pinFilePath = configuration.getTrustStorePinFile();
250            if (pinFilePath == null)
251            {
252              String pinStr = configuration.getTrustStorePin();
253              if (pinStr == null)
254              {
255                trustStorePIN = null;
256              }
257              else
258              {
259                trustStorePIN = pinStr.toCharArray();
260              }
261            }
262            else
263            {
264              File pinFile = getFileForPath(pinFilePath);
265              if (! pinFile.exists())
266              {
267                try
268                {
269                  // Generate a PIN.
270                  trustStorePIN = createKeystorePassword();
271    
272                  // Store the PIN in the pin file.
273                  createPINFile(pinFile.getPath(), new String(trustStorePIN));
274                }
275                catch (Exception e)
276                {
277                  Message message = ERR_TRUSTSTORE_PIN_FILE_CANNOT_CREATE.get(
278                       String.valueOf(pinFilePath), String.valueOf(configEntryDN));
279                  throw new InitializationException(message);
280                }
281              }
282              else
283              {
284                String pinStr;
285    
286                BufferedReader br = null;
287                try
288                {
289                  br = new BufferedReader(new FileReader(pinFile));
290                  pinStr = br.readLine();
291                }
292                catch (IOException ioe)
293                {
294                  Message message = ERR_TRUSTSTORE_PIN_FILE_CANNOT_READ.
295                      get(String.valueOf(pinFilePath),
296                          String.valueOf(configEntryDN), getExceptionMessage(ioe));
297                  throw new InitializationException(message, ioe);
298                }
299                finally
300                {
301                  try
302                  {
303                    br.close();
304                  } catch (Exception e) {
305                    // ignore
306                  }
307                }
308    
309                if (pinStr == null)
310                {
311                  Message message = ERR_TRUSTSTORE_PIN_FILE_EMPTY.get(
312                      String.valueOf(pinFilePath), String.valueOf(configEntryDN));
313                  throw new InitializationException(message);
314                }
315                else
316                {
317                  trustStorePIN     = pinStr.toCharArray();
318                }
319              }
320            }
321          }
322          else
323          {
324            String pinStr = System.getenv(pinEnVar);
325            if (pinStr == null)
326            {
327              Message message = ERR_TRUSTSTORE_PIN_ENVAR_NOT_SET.get(
328                  String.valueOf(pinProperty), String.valueOf(configEntryDN));
329              throw new InitializationException(message);
330            }
331            else
332            {
333              trustStorePIN = pinStr.toCharArray();
334            }
335          }
336        }
337        else
338        {
339          String pinStr = System.getProperty(pinProperty);
340          if (pinStr == null)
341          {
342            Message message = ERR_TRUSTSTORE_PIN_PROPERTY_NOT_SET.get(
343                String.valueOf(pinProperty), String.valueOf(configEntryDN));
344            throw new InitializationException(message);
345          }
346          else
347          {
348            trustStorePIN = pinStr.toCharArray();
349          }
350        }
351    
352        // Create a certificate manager.
353        certificateManager =
354             new CertificateManager(getFileForPath(trustStoreFile).getPath(),
355                                    trustStoreType,
356                                    new String(trustStorePIN));
357    
358        // Generate a self-signed certificate, if there is none.
359        generateInstanceCertificateIfAbsent();
360    
361        // Construct the trust store base entry.
362        LinkedHashMap<ObjectClass,String> objectClasses =
363             new LinkedHashMap<ObjectClass,String>(2);
364        objectClasses.put(DirectoryServer.getTopObjectClass(), OC_TOP);
365    
366        ObjectClass branchOC =
367             DirectoryServer.getObjectClass("ds-cfg-branch", true);
368        objectClasses.put(branchOC, "ds-cfg-branch");
369    
370        LinkedHashMap<AttributeType,List<Attribute>> opAttrs =
371             new LinkedHashMap<AttributeType,List<Attribute>>(0);
372        LinkedHashMap<AttributeType,List<Attribute>> userAttrs =
373             new LinkedHashMap<AttributeType,List<Attribute>>(1);
374    
375        RDN rdn = baseDN.getRDN();
376        int numAVAs = rdn.getNumValues();
377        for (int i=0; i < numAVAs; i++)
378        {
379          LinkedHashSet<AttributeValue> valueSet =
380               new LinkedHashSet<AttributeValue>(1);
381          valueSet.add(rdn.getAttributeValue(i));
382    
383          AttributeType attrType = rdn.getAttributeType(i);
384          ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
385          attrList.add(new Attribute(attrType, attrType.getNameOrOID(),
386                                     valueSet));
387    
388          userAttrs.put(attrType, attrList);
389        }
390    
391        baseEntry = new Entry(baseDN, objectClasses, userAttrs,
392                                    opAttrs);
393    
394    
395        // Define empty sets for the supported controls and features.
396        supportedControls = new HashSet<String>(0);
397        supportedFeatures = new HashSet<String>(0);
398    
399    
400        // Register this as a change listener.
401        configuration.addTrustStoreChangeListener(this);
402    
403    
404        // Register the trust store base as a private suffix.
405        try
406        {
407          DirectoryServer.registerBaseDN(baseDN, this, true);
408        }
409        catch (Exception e)
410        {
411          if (debugEnabled())
412          {
413            TRACER.debugCaught(DebugLogLevel.ERROR, e);
414          }
415    
416          Message message = ERR_BACKEND_CANNOT_REGISTER_BASEDN.get(
417              String.valueOf(baseDN), String.valueOf(e));
418          throw new InitializationException(message, e);
419        }
420      }
421    
422    
423    
424      /**
425       * {@inheritDoc}
426       */
427      @Override()
428      public void finalizeBackend()
429      {
430        configuration.addTrustStoreChangeListener(this);
431    
432        try
433        {
434          DirectoryServer.deregisterBaseDN(baseDN);
435        }
436        catch (Exception e)
437        {
438          if (debugEnabled())
439          {
440            TRACER.debugCaught(DebugLogLevel.ERROR, e);
441          }
442        }
443      }
444    
445    
446    
447      /**
448       * {@inheritDoc}
449       */
450      @Override()
451      public DN[] getBaseDNs()
452      {
453        return baseDNs;
454      }
455    
456    
457    
458      /**
459       * {@inheritDoc}
460       */
461      @Override()
462      public long getEntryCount()
463      {
464        int numEntries = 1;
465    
466        try
467        {
468          String[] aliases = certificateManager.getCertificateAliases();
469          if (aliases != null)
470          {
471            numEntries += aliases.length;
472          }
473        }
474        catch (KeyStoreException e)
475        {
476          if (debugEnabled())
477          {
478            TRACER.debugCaught(DebugLogLevel.ERROR, e);
479          }
480        }
481    
482        return numEntries;
483      }
484    
485    
486    
487      /**
488       * {@inheritDoc}
489       */
490      @Override()
491      public boolean isLocal()
492      {
493        // For the purposes of this method, this is a local backend.
494        return true;
495      }
496    
497    
498    
499      /**
500       * {@inheritDoc}
501       */
502      @Override()
503      public boolean isIndexed(AttributeType attributeType, IndexType indexType)
504      {
505        // All searches in this backend will always be considered indexed.
506        return true;
507      }
508    
509    
510    
511      /**
512       * {@inheritDoc}
513       */
514      @Override()
515      public Entry getEntry(DN entryDN)
516             throws DirectoryException
517      {
518        // If the requested entry was null, then throw an exception.
519        if (entryDN == null)
520        {
521          Message message = ERR_TRUSTSTORE_GET_ENTRY_NULL.get();
522          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
523                                       message);
524        }
525    
526    
527        // If the requested entry was the backend base entry, then retrieve it.
528        if (entryDN.equals(baseDN))
529        {
530          return baseEntry.duplicate(true);
531        }
532    
533    
534        // See if the requested entry was one level below the backend base entry.
535        // If so, then it must point to a trust store entry.
536        DN parentDN = entryDN.getParentDNInSuffix();
537        if (parentDN == null)
538        {
539          return null;
540        }
541        else if (parentDN.equals(baseDN))
542        {
543          try
544          {
545            return getCertEntry(entryDN);
546          }
547          catch (DirectoryException e)
548          {
549            return null;
550          }
551        }
552        else
553        {
554          return null;
555        }
556      }
557    
558    
559    
560      /**
561       * Generates an entry for a certificate based on the provided DN.  The
562       * DN must contain an RDN component that specifies the alias of the
563       * certificate, and that certificate alias must exist in the key store.
564       *
565       * @param  entryDN  The DN of the certificate to retrieve.
566       *
567       * @return  The requested certificate entry.
568       *
569       * @throws  DirectoryException  If the specified alias does not exist, or if
570       *                              the DN does not specify any alias.
571       */
572      private Entry getCertEntry(DN entryDN)
573             throws DirectoryException
574      {
575        // Make sure that the DN specifies a certificate alias.
576        AttributeType t =
577             DirectoryServer.getAttributeType(ATTR_CRYPTO_KEY_ID, true);
578        AttributeValue v = entryDN.getRDN().getAttributeValue(t);
579        if (v == null)
580        {
581          Message message = ERR_TRUSTSTORE_DN_DOES_NOT_SPECIFY_CERTIFICATE.
582               get(String.valueOf(entryDN));
583          throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message,
584                                       baseDN, null);
585        }
586    
587        String certAlias = v.getStringValue();
588        ByteString certValue;
589        try
590        {
591          Certificate cert = certificateManager.getCertificate(certAlias);
592          if (cert == null)
593          {
594            Message message = ERR_TRUSTSTORE_CERTIFICATE_NOT_FOUND.get(
595                String.valueOf(entryDN), certAlias);
596            throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
597          }
598          certValue = new ASN1OctetString(cert.getEncoded());
599        }
600        catch (Exception e)
601        {
602          if (debugEnabled())
603          {
604            TRACER.debugCaught(DebugLogLevel.VERBOSE, e);
605          }
606    
607          Message message = ERR_TRUSTSTORE_CANNOT_RETRIEVE_CERT.get(
608              certAlias, trustStoreFile, e.getMessage());
609          throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
610        }
611    
612        // Construct the certificate entry to return.
613        LinkedHashMap<ObjectClass,String> ocMap =
614            new LinkedHashMap<ObjectClass,String>(2);
615        ocMap.put(DirectoryServer.getTopObjectClass(), OC_TOP);
616    
617        ObjectClass objectClass =
618             DirectoryServer.getObjectClass(OC_CRYPTO_INSTANCE_KEY, true);
619        ocMap.put(objectClass, OC_CRYPTO_INSTANCE_KEY);
620    
621        LinkedHashMap<AttributeType,List<Attribute>> opAttrs =
622             new LinkedHashMap<AttributeType,List<Attribute>>(0);
623        LinkedHashMap<AttributeType,List<Attribute>> userAttrs =
624             new LinkedHashMap<AttributeType,List<Attribute>>(3);
625    
626        LinkedHashSet<AttributeValue> valueSet =
627             new LinkedHashSet<AttributeValue>(1);
628        valueSet.add(v);
629    
630        ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
631        attrList.add(new Attribute(t, t.getNameOrOID(), valueSet));
632        userAttrs.put(t, attrList);
633    
634    
635        t = DirectoryServer.getAttributeType(
636             ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE, true);
637        valueSet = new LinkedHashSet<AttributeValue>(1);
638        valueSet.add(new AttributeValue(t,
639                              certValue));
640        attrList = new ArrayList<Attribute>(1);
641        LinkedHashSet<String> options = new LinkedHashSet<String>(1);
642        options.add("binary");
643        attrList.add(new Attribute(t, t.getNameOrOID(), options, valueSet));
644        userAttrs.put(t, attrList);
645    
646    
647        Entry e = new Entry(entryDN, ocMap, userAttrs, opAttrs);
648        e.processVirtualAttributes();
649        return e;
650      }
651    
652    
653    
654      /**
655       * {@inheritDoc}
656       */
657      @Override()
658      public void addEntry(Entry entry, AddOperation addOperation)
659             throws DirectoryException
660      {
661        DN entryDN = entry.getDN();
662    
663        if (entryDN.equals(baseDN))
664        {
665          Message message = ERR_TRUSTSTORE_INVALID_BASE.get(
666               String.valueOf(entryDN));
667          throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, message);
668        }
669    
670        DN parentDN = entryDN.getParentDNInSuffix();
671        if (parentDN == null)
672        {
673          Message message = ERR_TRUSTSTORE_INVALID_BASE.get(
674               String.valueOf(entryDN));
675          throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
676        }
677    
678        if (parentDN.equals(baseDN))
679        {
680          addCertificate(entry);
681        }
682        else
683        {
684          Message message = ERR_TRUSTSTORE_INVALID_BASE.get(
685               String.valueOf(entryDN));
686          throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
687        }
688    
689      }
690    
691    
692    
693      /**
694       * {@inheritDoc}
695       */
696      @Override()
697      public void deleteEntry(DN entryDN, DeleteOperation deleteOperation)
698             throws DirectoryException
699      {
700        if (entryDN.equals(baseDN))
701        {
702          Message message = ERR_TRUSTSTORE_INVALID_BASE.get(
703               String.valueOf(entryDN));
704          throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
705        }
706    
707        DN parentDN = entryDN.getParentDNInSuffix();
708        if (parentDN == null || !parentDN.equals(baseDN))
709        {
710          Message message = ERR_TRUSTSTORE_INVALID_BASE.get(
711               String.valueOf(entryDN));
712          throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
713        }
714    
715        deleteCertificate(entryDN);
716      }
717    
718    
719    
720      /**
721       * {@inheritDoc}
722       */
723      @Override()
724      public void replaceEntry(Entry entry, ModifyOperation modifyOperation)
725             throws DirectoryException
726      {
727        Message message = ERR_TRUSTSTORE_MODIFY_NOT_SUPPORTED.get();
728        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
729      }
730    
731    
732    
733      /**
734       * {@inheritDoc}
735       */
736      @Override()
737      public void renameEntry(DN currentDN, Entry entry,
738                              ModifyDNOperation modifyDNOperation)
739             throws DirectoryException
740      {
741        Message message = ERR_TRUSTSTORE_MODIFY_DN_NOT_SUPPORTED.get();
742        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
743      }
744    
745    
746    
747      /**
748       * {@inheritDoc}
749       */
750      @Override()
751      public void search(SearchOperation searchOperation)
752             throws DirectoryException
753      {
754        // Get the base entry for the search, if possible.  If it doesn't exist,
755        // then this will throw an exception.
756        DN    baseDN    = searchOperation.getBaseDN();
757        Entry baseEntry = getEntry(baseDN);
758    
759    
760        // Look at the base DN and see if it's the trust store base DN, or a
761        // trust store entry DN.
762        SearchScope  scope  = searchOperation.getScope();
763        SearchFilter filter = searchOperation.getFilter();
764        if (this.baseDN.equals(baseDN))
765        {
766          if ((scope == SearchScope.BASE_OBJECT) ||
767              (scope == SearchScope.WHOLE_SUBTREE))
768          {
769            if (filter.matchesEntry(baseEntry))
770            {
771              searchOperation.returnEntry(baseEntry, null);
772            }
773          }
774    
775          String[] aliases = null;
776          try
777          {
778            aliases = certificateManager.getCertificateAliases();
779          }
780          catch (KeyStoreException e)
781          {
782            if (debugEnabled())
783            {
784              TRACER.debugCaught(DebugLogLevel.ERROR, e);
785            }
786          }
787    
788          if (aliases == null)
789          {
790            aliases = new String[0];
791          }
792    
793          if ((scope != SearchScope.BASE_OBJECT) && (! (aliases.length == 0) ))
794          {
795            AttributeType certAliasType =
796                 DirectoryServer.getAttributeType(ATTR_CRYPTO_KEY_ID, true);
797            for (String alias : aliases)
798            {
799              DN certDN = makeChildDN(this.baseDN, certAliasType,
800                                      alias);
801    
802              Entry certEntry;
803              try
804              {
805                certEntry = getCertEntry(certDN);
806              }
807              catch (Exception e)
808              {
809                if (debugEnabled())
810                {
811                  TRACER.debugCaught(DebugLogLevel.VERBOSE, e);
812                }
813    
814                continue;
815              }
816    
817              if (filter.matchesEntry(certEntry))
818              {
819                searchOperation.returnEntry(certEntry, null);
820              }
821    
822            }
823          }
824        }
825        else if (this.baseDN.equals(baseDN.getParentDNInSuffix()))
826        {
827          Entry certEntry = getCertEntry(baseDN);
828    
829          if ((scope == SearchScope.BASE_OBJECT) ||
830              (scope == SearchScope.WHOLE_SUBTREE))
831          {
832            if (filter.matchesEntry(certEntry))
833            {
834              searchOperation.returnEntry(certEntry, null);
835            }
836          }
837        }
838        else
839        {
840          Message message = ERR_TRUSTSTORE_INVALID_BASE.get(String.valueOf(baseDN));
841          throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
842        }
843      }
844    
845    
846    
847      /**
848       * {@inheritDoc}
849       */
850      @Override()
851      public HashSet<String> getSupportedControls()
852      {
853        return supportedControls;
854      }
855    
856    
857    
858      /**
859       * {@inheritDoc}
860       */
861      @Override()
862      public HashSet<String> getSupportedFeatures()
863      {
864        return supportedFeatures;
865      }
866    
867    
868    
869      /**
870       * {@inheritDoc}
871       */
872      @Override()
873      public boolean supportsLDIFExport()
874      {
875        // We do not support LDIF exports.
876        return false;
877      }
878    
879    
880    
881      /**
882       * {@inheritDoc}
883       */
884      @Override()
885      public void exportLDIF(LDIFExportConfig exportConfig)
886             throws DirectoryException
887      {
888        Message message = ERR_TRUSTSTORE_IMPORT_AND_EXPORT_NOT_SUPPORTED.get();
889        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
890      }
891    
892    
893    
894      /**
895       * {@inheritDoc}
896       */
897      @Override()
898      public boolean supportsLDIFImport()
899      {
900        // This backend does not support LDIF imports.
901        return false;
902      }
903    
904    
905    
906      /**
907       * {@inheritDoc}
908       */
909      @Override()
910      public LDIFImportResult importLDIF(LDIFImportConfig importConfig)
911             throws DirectoryException
912      {
913        // This backend does not support LDIF imports.
914        Message message = ERR_TRUSTSTORE_IMPORT_AND_EXPORT_NOT_SUPPORTED.get();
915        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
916      }
917    
918    
919    
920      /**
921       * {@inheritDoc}
922       */
923      @Override()
924      public boolean supportsBackup()
925      {
926        // This backend does not provide a backup/restore mechanism.
927        return false;
928      }
929    
930    
931    
932      /**
933       * {@inheritDoc}
934       */
935      @Override()
936      public boolean supportsBackup(BackupConfig backupConfig,
937                                    StringBuilder unsupportedReason)
938      {
939        // This backend does not provide a backup/restore mechanism.
940        return false;
941      }
942    
943    
944    
945      /**
946       * {@inheritDoc}
947       */
948      @Override()
949      public void createBackup(BackupConfig backupConfig)
950           throws DirectoryException
951      {
952        // This backend does not provide a backup/restore mechanism.
953        Message message = ERR_TRUSTSTORE_BACKUP_AND_RESTORE_NOT_SUPPORTED.get();
954        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
955      }
956    
957    
958    
959      /**
960       * {@inheritDoc}
961       */
962      @Override()
963      public void removeBackup(BackupDirectory backupDirectory,
964                               String backupID)
965             throws DirectoryException
966      {
967        // This backend does not provide a backup/restore mechanism.
968        Message message = ERR_TRUSTSTORE_BACKUP_AND_RESTORE_NOT_SUPPORTED.get();
969        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
970      }
971    
972    
973    
974      /**
975       * {@inheritDoc}
976       */
977      @Override()
978      public boolean supportsRestore()
979      {
980        // This backend does not provide a backup/restore mechanism.
981        return false;
982      }
983    
984    
985    
986      /**
987       * {@inheritDoc}
988       */
989      @Override()
990      public void restoreBackup(RestoreConfig restoreConfig)
991             throws DirectoryException
992      {
993        // This backend does not provide a backup/restore mechanism.
994        Message message = ERR_TRUSTSTORE_BACKUP_AND_RESTORE_NOT_SUPPORTED.get();
995        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
996      }
997    
998    
999    
1000      /**
1001       * {@inheritDoc}
1002       */
1003      @Override()
1004      public ConditionResult hasSubordinates(DN entryDN)
1005          throws DirectoryException
1006      {
1007        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
1008            ERR_HAS_SUBORDINATES_NOT_SUPPORTED.get());
1009      }
1010    
1011    
1012    
1013      /**
1014       * {@inheritDoc}
1015       */
1016      @Override()
1017      public long numSubordinates(DN entryDN, boolean subtree)
1018          throws DirectoryException
1019      {
1020        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
1021            ERR_NUM_SUBORDINATES_NOT_SUPPORTED.get());
1022      }
1023    
1024    
1025    
1026      /**
1027       * {@inheritDoc}
1028       */
1029      public boolean isConfigurationChangeAcceptable(
1030           TrustStoreBackendCfg configuration, List<Message> unacceptableReasons)
1031      {
1032        boolean configAcceptable = true;
1033        DN cfgEntryDN = configuration.dn();
1034    
1035    
1036        // Get the path to the trust store file.
1037        String newTrustStoreFile = configuration.getTrustStoreFile();
1038        try
1039        {
1040          File f = getFileForPath(newTrustStoreFile);
1041          if (!(f.exists() && f.isFile()))
1042          {
1043            unacceptableReasons.add(ERR_TRUSTSTORE_NO_SUCH_FILE.get(
1044                    String.valueOf(newTrustStoreFile),
1045                    String.valueOf(cfgEntryDN)));
1046            configAcceptable = false;
1047          }
1048        }
1049        catch (Exception e)
1050        {
1051          if (debugEnabled())
1052          {
1053            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1054          }
1055    
1056          unacceptableReasons.add(ERR_TRUSTSTORE_CANNOT_DETERMINE_FILE.get(
1057                  String.valueOf(cfgEntryDN),
1058                  getExceptionMessage(e)));
1059          configAcceptable = false;
1060        }
1061    
1062    
1063        // Check to see if the trust store type is acceptable.
1064        String storeType = configuration.getTrustStoreType();
1065        if (storeType != null)
1066        {
1067          try
1068          {
1069            KeyStore.getInstance(storeType);
1070          }
1071          catch (KeyStoreException kse)
1072          {
1073            if (debugEnabled())
1074            {
1075              TRACER.debugCaught(DebugLogLevel.ERROR, kse);
1076            }
1077    
1078            Message message = ERR_TRUSTSTORE_INVALID_TYPE.get(
1079                    String.valueOf(storeType),
1080                    String.valueOf(cfgEntryDN),
1081                    getExceptionMessage(kse));
1082            unacceptableReasons.add(message);
1083            configAcceptable = false;
1084          }
1085        }
1086    
1087    
1088        // If there is a PIN property, then make sure the corresponding
1089        // property is set.
1090        String pinProp = configuration.getTrustStorePinProperty();
1091        if (pinProp != null)
1092        {
1093          if (System.getProperty(pinProp) == null)
1094          {
1095            Message message = ERR_TRUSTSTORE_PIN_PROPERTY_NOT_SET.get(
1096                    String.valueOf(pinProp),
1097                    String.valueOf(cfgEntryDN));
1098            unacceptableReasons.add(message);
1099            configAcceptable = false;
1100          }
1101        }
1102    
1103    
1104        // If there is a PIN environment variable, then make sure the corresponding
1105        // environment variable is set.
1106        String pinEnVar = configuration.getTrustStorePinEnvironmentVariable();
1107        if (pinEnVar != null)
1108        {
1109          if (System.getenv(pinEnVar) == null)
1110          {
1111            Message message = ERR_TRUSTSTORE_PIN_ENVAR_NOT_SET.get(
1112                    String.valueOf(pinEnVar),
1113                    String.valueOf(cfgEntryDN));
1114            unacceptableReasons.add(message);
1115            configAcceptable = false;
1116          }
1117        }
1118    
1119    
1120        // If there is a PIN file, then make sure the file is readable if it exists.
1121        String pinFile = configuration.getTrustStorePinFile();
1122        if (pinFile != null)
1123        {
1124          File f = new File(pinFile);
1125          if (f.exists())
1126          {
1127            String pinStr = null;
1128    
1129            BufferedReader br = null;
1130            try
1131            {
1132              br = new BufferedReader(new FileReader(pinFile));
1133              pinStr = br.readLine();
1134            }
1135            catch (IOException ioe)
1136            {
1137              Message message = ERR_TRUSTSTORE_PIN_FILE_CANNOT_READ.get(
1138                      String.valueOf(pinFile),
1139                      String.valueOf(cfgEntryDN),
1140                      getExceptionMessage(ioe));
1141              unacceptableReasons.add(message);
1142              configAcceptable = false;
1143            }
1144            finally
1145            {
1146              try
1147              {
1148                br.close();
1149              } catch (Exception e) {
1150                // ignore
1151              }
1152            }
1153    
1154            if (pinStr == null)
1155            {
1156              Message message =  ERR_TRUSTSTORE_PIN_FILE_EMPTY.get(
1157                      String.valueOf(pinFile),
1158                      String.valueOf(cfgEntryDN));
1159              unacceptableReasons.add(message);
1160              configAcceptable = false;
1161            }
1162          }
1163        }
1164    
1165    
1166        return configAcceptable;
1167      }
1168    
1169    
1170    
1171      /**
1172       * {@inheritDoc}
1173       */
1174      public ConfigChangeResult applyConfigurationChange(TrustStoreBackendCfg cfg)
1175      {
1176        ResultCode        resultCode          = ResultCode.SUCCESS;
1177        boolean           adminActionRequired = false;
1178        ArrayList<Message> messages            = new ArrayList<Message>();
1179        DN configEntryDN = cfg.dn();
1180    
1181        // Get the path to the trust store file.
1182        String newTrustStoreFile = cfg.getTrustStoreFile();
1183        File f = getFileForPath(newTrustStoreFile);
1184        if (! (f.exists() && f.isFile()))
1185        {
1186          resultCode = DirectoryServer.getServerErrorResultCode();
1187    
1188          messages.add(ERR_TRUSTSTORE_NO_SUCH_FILE.get(
1189                  String.valueOf(newTrustStoreFile),
1190                  String.valueOf(configEntryDN)));
1191        }
1192    
1193    
1194        // Get the trust store type.  If none is specified, then use the default
1195        // type.
1196        String newTrustStoreType = cfg.getTrustStoreType();
1197        if (newTrustStoreType == null)
1198        {
1199          newTrustStoreType = KeyStore.getDefaultType();
1200        }
1201    
1202        try
1203        {
1204          KeyStore.getInstance(newTrustStoreType);
1205        }
1206        catch (KeyStoreException kse)
1207        {
1208          if (debugEnabled())
1209          {
1210            TRACER.debugCaught(DebugLogLevel.ERROR, kse);
1211          }
1212    
1213          messages.add(ERR_TRUSTSTORE_INVALID_TYPE.get(
1214                  String.valueOf(newTrustStoreType),
1215                  String.valueOf(configEntryDN),
1216                  getExceptionMessage(kse)));
1217    
1218          resultCode = DirectoryServer.getServerErrorResultCode();
1219        }
1220    
1221    
1222        // Get the PIN needed to access the contents of the trust store file.  We
1223        // will offer several places to look for the PIN, and we will do so in the
1224        // following order:
1225        // - In a specified Java property
1226        // - In a specified environment variable
1227        // - In a specified file on the server filesystem.
1228        // - As the value of a configuration attribute.
1229        // In any case, the PIN must be in the clear.  If no PIN is provided, then
1230        // it will be assumed that none is required to access the information in the
1231        // trust store.
1232        char[] newPIN = null;
1233        String newPINProperty = cfg.getTrustStorePinProperty();
1234        if (newPINProperty == null)
1235        {
1236          String newPINEnVar = cfg.getTrustStorePinEnvironmentVariable();
1237          if (newPINEnVar == null)
1238          {
1239            String newPINFile = cfg.getTrustStorePinFile();
1240            if (newPINFile == null)
1241            {
1242              String pinStr = cfg.getTrustStorePin();
1243              if (pinStr == null)
1244              {
1245                newPIN = null;
1246              }
1247              else
1248              {
1249                newPIN = pinStr.toCharArray();
1250              }
1251            }
1252            else
1253            {
1254              File pinFile = getFileForPath(newPINFile);
1255              if (! pinFile.exists())
1256              {
1257                try
1258                {
1259                  // Generate a PIN.
1260                  newPIN = createKeystorePassword();
1261    
1262                  // Store the PIN in the pin file.
1263                  createPINFile(pinFile.getPath(), new String(newPIN));
1264                }
1265                catch (Exception e)
1266                {
1267                  resultCode = DirectoryServer.getServerErrorResultCode();
1268    
1269                  messages.add(ERR_TRUSTSTORE_PIN_FILE_CANNOT_CREATE.get(
1270                          String.valueOf(newPINFile),
1271                          String.valueOf(configEntryDN)));
1272                }
1273              }
1274              else
1275              {
1276                String pinStr = null;
1277    
1278                BufferedReader br = null;
1279                try
1280                {
1281                  br = new BufferedReader(new FileReader(pinFile));
1282                  pinStr = br.readLine();
1283                }
1284                catch (IOException ioe)
1285                {
1286                  resultCode = DirectoryServer.getServerErrorResultCode();
1287    
1288                  messages.add(ERR_TRUSTSTORE_PIN_FILE_CANNOT_READ.get(
1289                          String.valueOf(newPINFile),
1290                          String.valueOf(configEntryDN),
1291                          getExceptionMessage(ioe)));
1292                }
1293                finally
1294                {
1295                  try
1296                  {
1297                    br.close();
1298                  } catch (Exception e) {
1299                    // ignore
1300                  }
1301                }
1302    
1303                if (pinStr == null)
1304                {
1305                  resultCode = DirectoryServer.getServerErrorResultCode();
1306    
1307                  messages.add(ERR_TRUSTSTORE_PIN_FILE_EMPTY.get(
1308                          String.valueOf(newPINFile),
1309                          String.valueOf(configEntryDN)));
1310                }
1311                else
1312                {
1313                  newPIN = pinStr.toCharArray();
1314                }
1315              }
1316            }
1317          }
1318          else
1319          {
1320            String pinStr = System.getenv(newPINEnVar);
1321            if (pinStr == null)
1322            {
1323              resultCode = DirectoryServer.getServerErrorResultCode();
1324    
1325              messages.add(ERR_TRUSTSTORE_PIN_ENVAR_NOT_SET.get(
1326                      String.valueOf(newPINEnVar),
1327                      String.valueOf(configEntryDN)));
1328            }
1329            else
1330            {
1331              newPIN = pinStr.toCharArray();
1332            }
1333          }
1334        }
1335        else
1336        {
1337          String pinStr = System.getProperty(newPINProperty);
1338          if (pinStr == null)
1339          {
1340            resultCode = DirectoryServer.getServerErrorResultCode();
1341    
1342            messages.add(ERR_TRUSTSTORE_PIN_PROPERTY_NOT_SET.get(
1343                    String.valueOf(newPINProperty),
1344                    String.valueOf(configEntryDN)));
1345          }
1346          else
1347          {
1348            newPIN = pinStr.toCharArray();
1349          }
1350        }
1351    
1352    
1353        if (resultCode == ResultCode.SUCCESS)
1354        {
1355          trustStoreFile = newTrustStoreFile;
1356          trustStoreType = newTrustStoreType;
1357          trustStorePIN  = newPIN;
1358          configuration  = cfg;
1359          certificateManager =
1360               new CertificateManager(getFileForPath(trustStoreFile).getPath(),
1361                                      trustStoreType,
1362                                      new String(trustStorePIN));
1363        }
1364    
1365    
1366        return new ConfigChangeResult(resultCode, adminActionRequired, messages);
1367      }
1368    
1369      /**
1370       * Create a new child DN from a given parent DN.  The child RDN is formed
1371       * from a given attribute type and string value.
1372       * @param parentDN The DN of the parent.
1373       * @param rdnAttrType The attribute type of the RDN.
1374       * @param rdnStringValue The string value of the RDN.
1375       * @return A new child DN.
1376       */
1377      public static DN makeChildDN(DN parentDN, AttributeType rdnAttrType,
1378                                   String rdnStringValue)
1379      {
1380        AttributeValue attrValue =
1381             new AttributeValue(rdnAttrType, rdnStringValue);
1382        return parentDN.concat(RDN.create(rdnAttrType, attrValue));
1383      }
1384    
1385    
1386      /**
1387       * Retrieves a set of <CODE>KeyManager</CODE> objects that may be used for
1388       * interactions requiring access to a key manager.
1389       *
1390       * @return  A set of <CODE>KeyManager</CODE> objects that may be used for
1391       *          interactions requiring access to a key manager.
1392       *
1393       * @throws DirectoryException  If a problem occurs while attempting to obtain
1394       *                             the set of key managers.
1395       */
1396      public KeyManager[] getKeyManagers()
1397             throws DirectoryException
1398      {
1399        KeyStore keyStore;
1400        try
1401        {
1402          keyStore = KeyStore.getInstance(trustStoreType);
1403    
1404          FileInputStream inputStream =
1405               new FileInputStream(getFileForPath(trustStoreFile));
1406          keyStore.load(inputStream, trustStorePIN);
1407          inputStream.close();
1408        }
1409        catch (Exception e)
1410        {
1411          if (debugEnabled())
1412          {
1413            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1414          }
1415    
1416          Message message = ERR_TRUSTSTORE_CANNOT_LOAD.get(
1417              trustStoreFile, getExceptionMessage(e));
1418          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1419                                       message, e);
1420        }
1421    
1422    
1423        try
1424        {
1425          String keyManagerAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
1426          KeyManagerFactory keyManagerFactory =
1427               KeyManagerFactory.getInstance(keyManagerAlgorithm);
1428          keyManagerFactory.init(keyStore, trustStorePIN);
1429          return keyManagerFactory.getKeyManagers();
1430        }
1431        catch (Exception e)
1432        {
1433          if (debugEnabled())
1434          {
1435            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1436          }
1437    
1438          Message message = ERR_TRUSTSTORE_CANNOT_CREATE_FACTORY.get(
1439              trustStoreFile, getExceptionMessage(e));
1440          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1441                                       message, e);
1442        }
1443      }
1444    
1445    
1446      /**
1447       * Retrieves a set of {@code TrustManager} objects that may be used
1448       * for interactions requiring access to a trust manager.
1449       *
1450       * @return  A set of {@code TrustManager} objects that may be used
1451       *          for interactions requiring access to a trust manager.
1452       *
1453       * @throws  DirectoryException  If a problem occurs while attempting
1454       *                              to obtain the set of trust managers.
1455       */
1456      public TrustManager[] getTrustManagers()
1457             throws DirectoryException
1458      {
1459        KeyStore trustStore;
1460        try
1461        {
1462          trustStore = KeyStore.getInstance(trustStoreType);
1463    
1464          FileInputStream inputStream =
1465               new FileInputStream(getFileForPath(trustStoreFile));
1466          trustStore.load(inputStream, trustStorePIN);
1467          inputStream.close();
1468        }
1469        catch (Exception e)
1470        {
1471          if (debugEnabled())
1472          {
1473            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1474          }
1475    
1476          Message message = ERR_TRUSTSTORE_CANNOT_LOAD.get(
1477              trustStoreFile, getExceptionMessage(e));
1478          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1479                                       message, e);
1480        }
1481    
1482    
1483        try
1484        {
1485          String trustManagerAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
1486          TrustManagerFactory trustManagerFactory =
1487               TrustManagerFactory.getInstance(trustManagerAlgorithm);
1488          trustManagerFactory.init(trustStore);
1489          TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
1490    //    TrustManager[] newTrustManagers = new TrustManager[trustManagers.length];
1491    //    for (int i=0; i < trustManagers.length; i++)
1492    //    {
1493    //      newTrustManagers[i] = new ExpirationCheckTrustManager(
1494    //                                     (X509TrustManager) trustManagers[i]);
1495    //    }
1496          return trustManagers;
1497        }
1498        catch (Exception e)
1499        {
1500          if (debugEnabled())
1501          {
1502            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1503          }
1504    
1505          Message message = ERR_TRUSTSTORE_CANNOT_CREATE_FACTORY.get(
1506              trustStoreFile, getExceptionMessage(e));
1507          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1508                                       message, e);
1509        }
1510      }
1511    
1512    
1513      /**
1514       * Returns the key associated with the given alias, using the trust
1515       * store pin to recover it.
1516       *
1517       * @param   alias The alias name.
1518       *
1519       * @return  The requested key, or null if the given alias does not exist
1520       *          or does not identify a key-related entry.
1521       *
1522       * @throws  DirectoryException  If an error occurs while retrieving the key.
1523       */
1524      public Key getKey(String alias)
1525             throws DirectoryException
1526      {
1527        KeyStore trustStore;
1528        try
1529        {
1530          trustStore = KeyStore.getInstance(trustStoreType);
1531    
1532          FileInputStream inputStream =
1533               new FileInputStream(getFileForPath(trustStoreFile));
1534          trustStore.load(inputStream, trustStorePIN);
1535          inputStream.close();
1536        }
1537        catch (Exception e)
1538        {
1539          if (debugEnabled())
1540          {
1541            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1542          }
1543    
1544          Message message = ERR_TRUSTSTORE_CANNOT_LOAD.get(
1545              trustStoreFile, getExceptionMessage(e));
1546          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1547                                       message, e);
1548        }
1549    
1550    
1551        try
1552        {
1553          return trustStore.getKey(alias, trustStorePIN);
1554        }
1555        catch (Exception e)
1556        {
1557          if (debugEnabled())
1558          {
1559            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1560          }
1561    
1562          Message message = ERR_TRUSTSTORE_ERROR_READING_KEY.get(
1563               alias, trustStoreFile, getExceptionMessage(e));
1564          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1565                                       message, e);
1566        }
1567      }
1568    
1569    
1570      private void addCertificate(Entry entry)
1571           throws DirectoryException
1572      {
1573        DN entryDN = entry.getDN();
1574    
1575        // Make sure that the DN specifies a certificate alias.
1576        AttributeType t =
1577             DirectoryServer.getAttributeType(ATTR_CRYPTO_KEY_ID, true);
1578        AttributeValue v = entryDN.getRDN().getAttributeValue(t);
1579        if (v == null)
1580        {
1581          Message message = ERR_TRUSTSTORE_DN_DOES_NOT_SPECIFY_CERTIFICATE.get(
1582               String.valueOf(entryDN));
1583          throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message,
1584                                       baseDN, null);
1585        }
1586        String certAlias = v.getStringValue();
1587    
1588        try
1589        {
1590          if (certificateManager.aliasInUse(certAlias))
1591          {
1592            Message message = ERR_TRUSTSTORE_ALIAS_IN_USE.get(
1593                 String.valueOf(entryDN));
1594            throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS,
1595                                         message);
1596          }
1597    
1598          ObjectClass ocSelfSignedCertRequest =
1599               DirectoryServer.getObjectClass(OC_SELF_SIGNED_CERT_REQUEST, true);
1600          if (entry.hasObjectClass(ocSelfSignedCertRequest))
1601          {
1602            try
1603            {
1604              certificateManager.generateSelfSignedCertificate(
1605                 certAlias,
1606                 getADSCertificateSubjectDN(),
1607                 getADSCertificateValidity());
1608            }
1609            catch (Exception e)
1610            {
1611              Message message = ERR_TRUSTSTORE_CANNOT_GENERATE_CERT.get(
1612                  certAlias, trustStoreFile, getExceptionMessage(e));
1613              throw new DirectoryException(
1614                   DirectoryServer.getServerErrorResultCode(), message, e);
1615            }
1616          }
1617          else
1618          {
1619            List<Attribute> certAttrs = entry.getAttribute(
1620                 ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE);
1621            if (certAttrs == null)
1622            {
1623              Message message =
1624                   ERR_TRUSTSTORE_ENTRY_MISSING_CERT_ATTR.get(
1625                        String.valueOf(entryDN),
1626                        ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE);
1627              throw new DirectoryException(
1628                   DirectoryServer.getServerErrorResultCode(), message);
1629            }
1630            if (certAttrs.size() != 1)
1631            {
1632              Message message =
1633                   ERR_TRUSTSTORE_ENTRY_HAS_MULTIPLE_CERT_ATTRS.get(
1634                        String.valueOf(entryDN),
1635                        ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE);
1636              throw new DirectoryException(
1637                   DirectoryServer.getServerErrorResultCode(), message);
1638            }
1639    
1640            LinkedHashSet<AttributeValue> certValues = certAttrs.get(0).getValues();
1641            if (certValues == null)
1642            {
1643              Message message =
1644                   ERR_TRUSTSTORE_ENTRY_MISSING_CERT_VALUE.get(
1645                        String.valueOf(entryDN),
1646                        ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE);
1647              throw new DirectoryException(
1648                   DirectoryServer.getServerErrorResultCode(), message);
1649            }
1650            if (certValues.size() != 1)
1651            {
1652              Message message =
1653                   ERR_TRUSTSTORE_ENTRY_HAS_MULTIPLE_CERT_VALUES.get(
1654                        String.valueOf(entryDN),
1655                        ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE);
1656              throw new DirectoryException(
1657                   DirectoryServer.getServerErrorResultCode(), message);
1658            }
1659    
1660            byte[] certBytes = certValues.iterator().next().getValueBytes();
1661            try
1662            {
1663              File tempDir = getFileForPath("config");
1664              File tempFile = File.createTempFile(configuration.getBackendId(),
1665                                                  certAlias, tempDir);
1666              try
1667              {
1668                FileOutputStream outputStream =
1669                     new FileOutputStream(tempFile.getPath(), false);
1670                try
1671                {
1672                  outputStream.write(certBytes);
1673                }
1674                finally
1675                {
1676                  outputStream.close();
1677                }
1678    
1679                certificateManager.addCertificate(certAlias, tempFile);
1680              }
1681              finally
1682              {
1683                tempFile.delete();
1684              }
1685            }
1686            catch (IOException e)
1687            {
1688              Message message = ERR_TRUSTSTORE_CANNOT_WRITE_CERT.get(
1689                  certAlias, getExceptionMessage(e));
1690              throw new DirectoryException(
1691                   DirectoryServer.getServerErrorResultCode(), message, e);
1692            }
1693          }
1694        }
1695        catch (Exception e)
1696        {
1697          Message message = ERR_TRUSTSTORE_CANNOT_ADD_CERT.get(
1698               certAlias, trustStoreFile, getExceptionMessage(e));
1699          throw new DirectoryException(
1700               DirectoryServer.getServerErrorResultCode(), message, e);
1701        }
1702    
1703      }
1704    
1705    
1706      private void deleteCertificate(DN entryDN)
1707           throws DirectoryException
1708      {
1709        // Make sure that the DN specifies a certificate alias.
1710        AttributeType t =
1711             DirectoryServer.getAttributeType(ATTR_CRYPTO_KEY_ID, true);
1712        AttributeValue v = entryDN.getRDN().getAttributeValue(t);
1713        if (v == null)
1714        {
1715          Message message = ERR_TRUSTSTORE_DN_DOES_NOT_SPECIFY_CERTIFICATE.get(
1716               String.valueOf(entryDN));
1717          throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message,
1718                                       baseDN, null);
1719        }
1720        String certAlias = v.getStringValue();
1721    
1722        try
1723        {
1724          if (!certificateManager.aliasInUse(certAlias))
1725          {
1726            Message message = ERR_TRUSTSTORE_INVALID_BASE.get(
1727                 String.valueOf(entryDN));
1728            throw new DirectoryException(ResultCode.NO_SUCH_OBJECT,
1729                                         message);
1730          }
1731    
1732          certificateManager.removeCertificate(certAlias);
1733        }
1734        catch (Exception e)
1735        {
1736          Message message = ERR_TRUSTSTORE_CANNOT_DELETE_CERT.get(
1737               certAlias, trustStoreFile, getExceptionMessage(e));
1738          throw new DirectoryException(
1739               DirectoryServer.getServerErrorResultCode(), message, e);
1740        }
1741    
1742      }
1743    
1744    
1745      /**
1746       * Returns the validity period to be used to generate the ADS certificate.
1747       * @return The validity period to be used to generate the ADS certificate.
1748       */
1749      private static int getADSCertificateValidity()
1750      {
1751        return 20 * 365;
1752      }
1753    
1754      /**
1755       * Returns the Subject DN to be used to generate the ADS certificate.
1756       * @return The Subject DN to be used to generate the ADS certificate.
1757       * @throws java.net.UnknownHostException If the server host name could not be
1758       *                                       determined.
1759       */
1760      private static String getADSCertificateSubjectDN()
1761           throws UnknownHostException
1762      {
1763        String hostname =
1764             java.net.InetAddress.getLocalHost().getCanonicalHostName();
1765        return "cn=" + Rdn.escapeValue(hostname) + ",O=OpenDS Certificate";
1766      }
1767    
1768      /**
1769       * Create a randomly generated password for a certificate keystore.
1770       * @return A randomly generated password for a certificate keystore.
1771       */
1772      private static char[] createKeystorePassword() {
1773        int pwdLength = 50;
1774        char[] pwd = new char[pwdLength];
1775        Random random = new Random();
1776        for (int pos=0; pos < pwdLength; pos++) {
1777            int type = getRandomInt(random,3);
1778            char nextChar = getRandomChar(random,type);
1779            pwd[pos] = nextChar;
1780        }
1781        return pwd;
1782      }
1783    
1784      private static char getRandomChar(Random random, int type)
1785      {
1786        char generatedChar;
1787        int next = random.nextInt();
1788        int d;
1789    
1790        switch (type)
1791        {
1792        case 0:
1793          // Will return a digit
1794          d = next % 10;
1795          if (d < 0)
1796          {
1797            d = d * (-1);
1798          }
1799          generatedChar = (char) (d+48);
1800          break;
1801        case 1:
1802          // Will return a lower case letter
1803          d = next % 26;
1804          if (d < 0)
1805          {
1806            d = d * (-1);
1807          }
1808          generatedChar =  (char) (d + 97);
1809          break;
1810        default:
1811          // Will return a capital letter
1812          d = (next % 26);
1813          if (d < 0)
1814          {
1815            d = d * (-1);
1816          }
1817          generatedChar = (char) (d + 65) ;
1818        }
1819    
1820        return generatedChar;
1821      }
1822    
1823      private static int getRandomInt(Random random,int modulo)
1824      {
1825        return (random.nextInt() & modulo);
1826      }
1827    
1828      /**
1829       * Creates a PIN file on the specified path.
1830       * @param path the path where the PIN file will be created.
1831       * @param pin The PIN to store in the file.
1832       * @throws IOException if something goes wrong.
1833       */
1834      public static void createPINFile(String path, String pin)
1835           throws IOException
1836      {
1837        FileWriter file = new FileWriter(path);
1838        PrintWriter out = new PrintWriter(file);
1839    
1840        out.println(pin);
1841    
1842        out.flush();
1843        out.close();
1844    
1845        if(FilePermission.canSetPermissions()) {
1846          try {
1847            if (!FilePermission.setPermissions(new File(path),
1848                                               new FilePermission(0600)))
1849            {
1850              // Log a warning that the permissions were not set.
1851              Message message = WARN_TRUSTSTORE_SET_PERMISSIONS_FAILED.get(path);
1852              ErrorLogger.logError(message);
1853            }
1854          } catch(DirectoryException e) {
1855            // Log a warning that the permissions were not set.
1856            Message message = WARN_TRUSTSTORE_SET_PERMISSIONS_FAILED.get(path);
1857            ErrorLogger.logError(message);
1858          }
1859        }
1860      }
1861    
1862      /**
1863       * Generates a self-signed certificate with well-known alias if there is none.
1864       * @throws InitializationException If an error occurs while interacting with
1865       *                                 the key store.
1866       */
1867      private void generateInstanceCertificateIfAbsent()
1868           throws InitializationException
1869      {
1870        String certAlias = ADS_CERTIFICATE_ALIAS;
1871    
1872        try
1873        {
1874          if (certificateManager.aliasInUse(certAlias))
1875          {
1876            return;
1877          }
1878        }
1879        catch (Exception e)
1880        {
1881          Message message = ERR_TRUSTSTORE_CANNOT_ADD_CERT.get(
1882               certAlias, trustStoreFile, getExceptionMessage(e));
1883          throw new InitializationException(message, e);
1884        }
1885    
1886        try
1887        {
1888          certificateManager.generateSelfSignedCertificate(
1889               certAlias,
1890               getADSCertificateSubjectDN(),
1891               getADSCertificateValidity());
1892        }
1893        catch (Exception e)
1894        {
1895          Message message = ERR_TRUSTSTORE_CANNOT_GENERATE_CERT.get(
1896               certAlias, trustStoreFile, getExceptionMessage(e));
1897          throw new InitializationException(message, e);
1898        }
1899    
1900      }
1901    
1902    
1903    
1904      /**
1905       * {@inheritDoc}
1906       */
1907      public void preloadEntryCache() throws UnsupportedOperationException {
1908        throw new UnsupportedOperationException("Operation not supported.");
1909      }
1910    }
1911