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