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