001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2006-2009 Sun Microsystems, Inc. 015 * Portions Copyright 2012-2016 ForgeRock AS. 016 */ 017package org.opends.server.tools; 018 019import static com.forgerock.opendj.cli.ArgumentConstants.*; 020import static com.forgerock.opendj.cli.CliMessages.INFO_FILE_PLACEHOLDER; 021import static com.forgerock.opendj.cli.CliMessages.INFO_JMXPORT_PLACEHOLDER; 022import static com.forgerock.opendj.cli.CliMessages.INFO_PORT_PLACEHOLDER; 023import static com.forgerock.opendj.cli.CommonArguments.*; 024import static com.forgerock.opendj.cli.Utils.*; 025 026import static org.opends.messages.ConfigMessages.*; 027import static org.opends.messages.ToolMessages.*; 028import static org.opends.server.config.ConfigConstants.*; 029import static org.opends.server.util.ServerConstants.*; 030import static org.opends.server.util.StaticUtils.*; 031 032import java.io.File; 033import java.io.OutputStream; 034import java.io.PrintStream; 035import java.io.StringReader; 036import java.net.InetAddress; 037import java.security.GeneralSecurityException; 038import java.util.Collection; 039import java.util.HashSet; 040import java.util.LinkedList; 041import java.util.List; 042import java.util.Set; 043 044import javax.crypto.Cipher; 045 046import org.forgerock.i18n.LocalizableMessage; 047import org.forgerock.i18n.LocalizedIllegalArgumentException; 048import org.forgerock.opendj.adapter.server3x.Converters; 049import org.forgerock.opendj.config.DefaultBehaviorProvider; 050import org.forgerock.opendj.config.DefinedDefaultBehaviorProvider; 051import org.forgerock.opendj.config.ManagedObjectDefinition; 052import org.forgerock.opendj.config.StringPropertyDefinition; 053import org.forgerock.opendj.config.server.ConfigException; 054import org.forgerock.opendj.ldap.AttributeDescription; 055import org.forgerock.opendj.ldap.DN; 056import org.forgerock.opendj.ldap.LinkedAttribute; 057import org.forgerock.opendj.ldap.LinkedHashMapEntry; 058import org.forgerock.opendj.ldap.schema.AttributeType; 059import org.forgerock.opendj.ldap.schema.CoreSchema; 060import org.forgerock.opendj.ldap.schema.Syntax; 061import org.forgerock.opendj.server.config.client.BackendCfgClient; 062import org.forgerock.opendj.server.config.meta.CryptoManagerCfgDefn; 063import org.forgerock.opendj.server.config.server.BackendCfg; 064import org.opends.quicksetup.installer.Installer; 065import org.opends.server.config.ConfigurationHandler; 066import org.opends.server.core.DirectoryServer; 067import org.opends.server.core.LockFileManager; 068import org.opends.server.extensions.SaltedSHA512PasswordStorageScheme; 069import org.opends.server.protocols.ldap.LDAPResultCode; 070import org.opends.server.types.DirectoryEnvironmentConfig; 071import org.opends.server.types.DirectoryException; 072import org.opends.server.types.Entry; 073import org.opends.server.types.InitializationException; 074import org.opends.server.types.LDIFImportConfig; 075import org.opends.server.types.NullOutputStream; 076import org.opends.server.util.LDIFReader; 077import org.opends.server.util.ServerConstants; 078 079import com.forgerock.opendj.cli.Argument; 080import com.forgerock.opendj.cli.ArgumentException; 081import com.forgerock.opendj.cli.ArgumentParser; 082import com.forgerock.opendj.cli.BooleanArgument; 083import com.forgerock.opendj.cli.CliConstants; 084import com.forgerock.opendj.cli.FileBasedArgument; 085import com.forgerock.opendj.cli.IntegerArgument; 086import com.forgerock.opendj.cli.StringArgument; 087 088/** 089 * This class provides a very basic tool that can be used to configure some of 090 * the most important settings in the Directory Server. This configuration is 091 * performed by editing the server's configuration files and therefore the 092 * Directory Server must be offline. This utility will be used during the 093 * Directory Server installation process. 094 * <BR><BR> 095 * The options that this tool can currently set include: 096 * <BR> 097 * <UL> 098 * <LI>The port on which the server will listen for LDAP communication</LI> 099 * <LI>The DN and password for the initial root user. 100 * <LI>The set of base DNs for user data</LI> 101 * </UL> 102 */ 103public class ConfigureDS 104{ 105 private static final boolean WRONG_USAGE = true; 106 107 /** Private exception class to handle error message printing. */ 108 @SuppressWarnings("serial") 109 private class ConfigureDSException extends Exception 110 { 111 private final int returnedErrorCode; 112 private final LocalizableMessage errorMessage; 113 private final boolean wrongUsage; 114 115 ConfigureDSException(final LocalizableMessage errorMessage) 116 { 117 this(new Exception("An error occured in ConfigureDS: " + errorMessage), errorMessage, false); 118 } 119 120 ConfigureDSException(final Exception parentException, final LocalizableMessage errorMessage) 121 { 122 this(parentException, errorMessage, false); 123 } 124 125 ConfigureDSException(final LocalizableMessage errorMessage, final boolean showUsage) 126 { 127 this(new Exception("An error occured in ConfigureDS: " + errorMessage), errorMessage, showUsage); 128 } 129 130 ConfigureDSException(final Exception parentException, final LocalizableMessage errorMessage, 131 final boolean showUsage) 132 { 133 this(parentException, errorMessage, showUsage, ERROR); 134 } 135 136 ConfigureDSException(final Exception parentException, final LocalizableMessage errorMessage, 137 final boolean wrongUsage, final int retCode) 138 { 139 super(parentException); 140 this.errorMessage = errorMessage; 141 this.wrongUsage = wrongUsage; 142 returnedErrorCode = retCode; 143 } 144 145 private LocalizableMessage getErrorMessage() 146 { 147 return errorMessage; 148 } 149 150 private boolean isWrongUsage() 151 { 152 return wrongUsage; 153 } 154 155 private int getErrorCode() 156 { 157 return returnedErrorCode; 158 } 159 } 160 161 private static final String NEW_LINE = System.getProperty("line.separator"); 162 163 // FIXME: Find a better way to prevent hardcoded ldif entries. 164 private static final String JCKES_KEY_MANAGER_DN = "cn=JCEKS,cn=Key Manager Providers,cn=config"; 165 private static final String JCKES_KEY_MANAGER_LDIF_ENTRY = 166 "dn: " + JCKES_KEY_MANAGER_DN + NEW_LINE 167 + "objectClass: top" + NEW_LINE 168 + "objectClass: ds-cfg-key-manager-provider" + NEW_LINE 169 + "objectClass: ds-cfg-file-based-key-manager-provider" + NEW_LINE 170 + "cn: JCEKS" + NEW_LINE 171 + "ds-cfg-java-class: org.opends.server.extensions.FileBasedKeyManagerProvider" + NEW_LINE 172 + "ds-cfg-enabled: true" + NEW_LINE 173 + "ds-cfg-key-store-type: JCEKS" + NEW_LINE 174 + "ds-cfg-key-store-file: config/keystore.jceks" + NEW_LINE 175 + "ds-cfg-key-store-pin-file: config/keystore.pin" + NEW_LINE; 176 177 private static final String JCKES_TRUST_MANAGER_DN = "cn=JCEKS,cn=Trust Manager Providers,cn=config"; 178 private static final String JCKES_TRUST_MANAGER_LDIF_ENTRY = 179 "dn: " + JCKES_TRUST_MANAGER_DN + NEW_LINE 180 + "objectClass: top" + NEW_LINE 181 + "objectClass: ds-cfg-trust-manager-provider" + NEW_LINE 182 + "objectClass: ds-cfg-file-based-trust-manager-provider" + NEW_LINE 183 + "cn: JCEKS" + NEW_LINE 184 + "ds-cfg-java-class: org.opends.server.extensions.FileBasedTrustManagerProvider" + NEW_LINE 185 + "ds-cfg-enabled: false" + NEW_LINE 186 + "ds-cfg-trust-store-type: JCEKS" + NEW_LINE 187 + "ds-cfg-trust-store-file: config/truststore" + NEW_LINE; 188 189 /** The DN of the configuration entry defining the LDAP connection handler. */ 190 private static final String DN_LDAP_CONNECTION_HANDLER = "cn=LDAP Connection Handler," + DN_CONNHANDLER_BASE; 191 /** The DN of the configuration entry defining the Administration connector. */ 192 private static final String DN_ADMIN_CONNECTOR = "cn=Administration Connector," + DN_CONFIG_ROOT; 193 /** The DN of the configuration entry defining the LDAPS connection handler. */ 194 private static final String DN_LDAPS_CONNECTION_HANDLER = "cn=LDAPS Connection Handler," + DN_CONNHANDLER_BASE; 195 /** The DN of the configuration entry defining the HTTP connection handler. */ 196 private static final String DN_HTTP_CONNECTION_HANDLER = 197 "cn=HTTP Connection Handler,cn=Connection Handlers,cn=config"; 198 /** The DN of the configuration entry defining the JMX connection handler. */ 199 private static final String DN_JMX_CONNECTION_HANDLER = "cn=JMX Connection Handler," + DN_CONNHANDLER_BASE; 200 /** The DN of the configuration entry defining the initial root user. */ 201 private static final String DN_ROOT_USER = "cn=Directory Manager," + DN_ROOT_DN_CONFIG_BASE; 202 /** The DN of the Crypto Manager. */ 203 private static final String DN_CRYPTO_MANAGER = "cn=Crypto Manager,cn=config"; 204 /** The DN of the DIGEST-MD5 SASL mechanism handler. */ 205 private static final String DN_DIGEST_MD5_SASL_MECHANISM = "cn=DIGEST-MD5,cn=SASL Mechanisms,cn=config"; 206 207 private static final int SUCCESS = 0; 208 private static final int ERROR = 1; 209 210 /** 211 * Provides the command-line arguments to the <CODE>configMain</CODE> method 212 * for processing. 213 * 214 * @param args The set of command-line arguments provided to this program. 215 */ 216 public static void main(String[] args) 217 { 218 final int exitCode = configMain(args, System.out, System.err); 219 if (exitCode != SUCCESS) 220 { 221 System.exit(filterExitCode(exitCode)); 222 } 223 } 224 225 /** 226 * Parses the provided command-line arguments and makes the appropriate 227 * changes to the Directory Server configuration. 228 * 229 * @param args The command-line arguments provided to this program. 230 * 231 * @param outStream Output stream. 232 * @param errStream Error stream. 233 * @return The exit code from the configuration processing. A nonzero value 234 * indicates that there was some kind of problem during the 235 * configuration processing. 236 */ 237 public static int configMain(final String[] args, final OutputStream outStream, final OutputStream errStream) 238 { 239 final ConfigureDS tool = new ConfigureDS(args, outStream, errStream); 240 return tool.run(); 241 } 242 243 private final String[] arguments; 244 private final PrintStream out; 245 private final PrintStream err; 246 247 private final ArgumentParser argParser; 248 249 private BooleanArgument showUsage; 250 private BooleanArgument enableStartTLS; 251 private FileBasedArgument rootPasswordFile; 252 private StringArgument hostName; 253 private IntegerArgument ldapPort; 254 private IntegerArgument adminConnectorPort; 255 private IntegerArgument ldapsPort; 256 private IntegerArgument jmxPort; 257 private StringArgument baseDNString; 258 private StringArgument configFile; 259 private StringArgument rootDNString; 260 private StringArgument rootPassword; 261 private StringArgument keyManagerProviderDN; 262 private StringArgument trustManagerProviderDN; 263 private StringArgument certNickNames; 264 private StringArgument keyManagerPath; 265 private StringArgument serverRoot; 266 private StringArgument backendType; 267 268 private final String serverLockFileName = LockFileManager.getServerLockFileName(); 269 private final StringBuilder failureReason = new StringBuilder(); 270 private ConfigurationHandler configHandler; 271 272 private ConfigureDS(final String[] args, final OutputStream outStream, final OutputStream errStream) 273 { 274 arguments = args; 275 out = NullOutputStream.wrapOrNullStream(outStream); 276 err = NullOutputStream.wrapOrNullStream(errStream); 277 argParser = new ArgumentParser(ConfigureDS.class.getName(), INFO_CONFIGDS_TOOL_DESCRIPTION.get(), false); 278 } 279 280 private int run() 281 { 282 try 283 { 284 initializeArguments(); 285 parseArguments(); 286 if (argParser.usageOrVersionDisplayed()) 287 { 288 return SUCCESS; 289 } 290 291 checkArgumentsConsistency(); 292 checkPortArguments(); 293 294 tryAcquireExclusiveLocks(); 295 updateBaseDNs(parseProvidedBaseDNs()); 296 297 initializeDirectoryServer(); 298 299 final DN rootDN = parseRootDN(); 300 final String rootPW = parseRootDNPassword(); 301 302 configHandler = DirectoryServer.getConfigurationHandler(); 303 304 checkManagerProvider(keyManagerProviderDN, JCKES_KEY_MANAGER_DN, JCKES_KEY_MANAGER_LDIF_ENTRY, true); 305 checkManagerProvider(trustManagerProviderDN, JCKES_TRUST_MANAGER_DN, JCKES_TRUST_MANAGER_LDIF_ENTRY, false); 306 // Check that the keystore path values are valid. 307 if (keyManagerPath.isPresent() && !keyManagerProviderDN.isPresent()) 308 { 309 final LocalizableMessage message = ERR_CONFIGDS_KEYMANAGER_PROVIDER_DN_REQUIRED.get( 310 keyManagerProviderDN.getLongIdentifier(), keyManagerPath.getLongIdentifier()); 311 throw new ConfigureDSException(message); 312 } 313 314 updateLdapPort(); 315 updateAdminConnectorPort(); 316 updateLdapSecurePort(); 317 updateJMXport(); 318 updateStartTLS(); 319 updateKeyManager(); 320 updateTrustManager(); 321 updateRootUser(rootDN, rootPW); 322 addFQDNDigestMD5(); 323 updateCryptoCipher(); 324 printWrappedText(out, INFO_CONFIGDS_WROTE_UPDATED_CONFIG.get()); 325 326 return SUCCESS; 327 } 328 catch (final ConfigureDSException e) 329 { 330 if (e.isWrongUsage()) 331 { 332 argParser.displayMessageAndUsageReference(err, e.getErrorMessage()); 333 } 334 else 335 { 336 printWrappedText(err, e.getErrorMessage()); 337 } 338 return e.getErrorCode(); 339 } 340 finally 341 { 342 LockFileManager.releaseLock(serverLockFileName, failureReason); 343 } 344 } 345 346 private void initializeArguments() throws ConfigureDSException 347 { 348 try 349 { 350 configFile = 351 StringArgument.builder("configFile") 352 .shortIdentifier('c') 353 .description(INFO_DESCRIPTION_CONFIG_FILE.get()) 354 .hidden() 355 .required() 356 .valuePlaceholder(INFO_CONFIGFILE_PLACEHOLDER.get()) 357 .buildAndAddToParser(argParser); 358 String defaultHostName; 359 try 360 { 361 defaultHostName = InetAddress.getLocalHost().getHostName(); 362 } 363 catch (final Exception e) 364 { 365 // Not much we can do here. 366 defaultHostName = "localhost"; 367 } 368 369 hostName = 370 StringArgument.builder(OPTION_LONG_HOST) 371 .shortIdentifier(OPTION_SHORT_HOST) 372 .description(INFO_INSTALLDS_DESCRIPTION_HOST_NAME.get()) 373 .defaultValue(defaultHostName) 374 .valuePlaceholder(INFO_HOST_PLACEHOLDER.get()) 375 .buildAndAddToParser(argParser); 376 ldapPort = 377 IntegerArgument.builder("ldapPort") 378 .shortIdentifier(OPTION_SHORT_PORT) 379 .description(INFO_CONFIGDS_DESCRIPTION_LDAP_PORT.get()) 380 .range(1, 65535) 381 .defaultValue(389) 382 .valuePlaceholder(INFO_LDAPPORT_PLACEHOLDER.get()) 383 .buildAndAddToParser(argParser); 384 adminConnectorPort = 385 IntegerArgument.builder("adminConnectorPort") 386 .description(INFO_INSTALLDS_DESCRIPTION_ADMINCONNECTORPORT.get()) 387 .range(1, 65535) 388 .defaultValue(4444) 389 .valuePlaceholder(INFO_PORT_PLACEHOLDER.get()) 390 .buildAndAddToParser(argParser); 391 ldapsPort = 392 IntegerArgument.builder("ldapsPort") 393 .shortIdentifier('P') 394 .description(INFO_CONFIGDS_DESCRIPTION_LDAPS_PORT.get()) 395 .range(1, 65535) 396 .defaultValue(636) 397 .valuePlaceholder(INFO_LDAPPORT_PLACEHOLDER.get()) 398 .buildAndAddToParser(argParser); 399 enableStartTLS = 400 BooleanArgument.builder("enableStartTLS") 401 .shortIdentifier(OPTION_SHORT_START_TLS) 402 .description(INFO_CONFIGDS_DESCRIPTION_ENABLE_START_TLS.get()) 403 .buildAndAddToParser(argParser); 404 jmxPort = 405 IntegerArgument.builder("jmxPort") 406 .shortIdentifier('x') 407 .description(INFO_CONFIGDS_DESCRIPTION_JMX_PORT.get()) 408 .range(1, 65535) 409 .defaultValue(CliConstants.DEFAULT_JMX_PORT) 410 .valuePlaceholder(INFO_JMXPORT_PLACEHOLDER.get()) 411 .buildAndAddToParser(argParser); 412 keyManagerProviderDN = 413 StringArgument.builder("keyManagerProviderDN") 414 .shortIdentifier('k') 415 .description(INFO_CONFIGDS_DESCRIPTION_KEYMANAGER_PROVIDER_DN.get()) 416 .valuePlaceholder(INFO_KEY_MANAGER_PROVIDER_DN_PLACEHOLDER.get()) 417 .buildAndAddToParser(argParser); 418 trustManagerProviderDN = 419 StringArgument.builder("trustManagerProviderDN") 420 .shortIdentifier('t') 421 .description(INFO_CONFIGDS_DESCRIPTION_TRUSTMANAGER_PROVIDER_DN.get()) 422 .valuePlaceholder(INFO_TRUST_MANAGER_PROVIDER_DN_PLACEHOLDER.get()) 423 .buildAndAddToParser(argParser); 424 keyManagerPath = 425 StringArgument.builder("keyManagerPath") 426 .shortIdentifier('m') 427 .description(INFO_CONFIGDS_DESCRIPTION_KEYMANAGER_PATH.get()) 428 .valuePlaceholder(INFO_KEY_MANAGER_PATH_PLACEHOLDER.get()) 429 .buildAndAddToParser(argParser); 430 certNickNames = 431 StringArgument.builder("certNickName") 432 .shortIdentifier('a') 433 .description(INFO_CONFIGDS_DESCRIPTION_CERTNICKNAME.get()) 434 .multiValued() 435 .valuePlaceholder(INFO_NICKNAME_PLACEHOLDER.get()) 436 .buildAndAddToParser(argParser); 437 baseDNString = 438 StringArgument.builder(OPTION_LONG_BASEDN) 439 .shortIdentifier(OPTION_SHORT_BASEDN) 440 .description(INFO_CONFIGDS_DESCRIPTION_BASE_DN.get()) 441 .multiValued() 442 .defaultValue("dc=example,dc=com") 443 .valuePlaceholder(INFO_BASEDN_PLACEHOLDER.get()) 444 .buildAndAddToParser(argParser); 445 rootDNString = 446 StringArgument.builder(OPTION_LONG_ROOT_USER_DN) 447 .shortIdentifier(OPTION_SHORT_ROOT_USER_DN) 448 .description(INFO_CONFIGDS_DESCRIPTION_ROOT_DN.get()) 449 .defaultValue("cn=Directory Manager") 450 .valuePlaceholder(INFO_ROOT_USER_DN_PLACEHOLDER.get()) 451 .buildAndAddToParser(argParser); 452 rootPassword = 453 StringArgument.builder("rootPassword") 454 .shortIdentifier(OPTION_SHORT_BINDPWD) 455 .description(INFO_CONFIGDS_DESCRIPTION_ROOT_PW.get()) 456 .valuePlaceholder(INFO_ROOT_USER_PWD_PLACEHOLDER.get()) 457 .buildAndAddToParser(argParser); 458 rootPasswordFile = 459 FileBasedArgument.builder("rootPasswordFile") 460 .shortIdentifier(OPTION_SHORT_BINDPWD_FILE) 461 .description(INFO_CONFIGDS_DESCRIPTION_ROOT_PW_FILE.get()) 462 .valuePlaceholder(INFO_FILE_PLACEHOLDER.get()) 463 .buildAndAddToParser(argParser); 464 465 showUsage = showUsageArgument(); 466 argParser.addArgument(showUsage); 467 argParser.setUsageArgument(showUsage); 468 469 serverRoot = 470 StringArgument.builder(OPTION_LONG_SERVER_ROOT) 471 .shortIdentifier(OPTION_SHORT_SERVER_ROOT) 472 .hidden() 473 .valuePlaceholder(INFO_SERVER_ROOT_DIR_PLACEHOLDER.get()) 474 .buildAndAddToParser(argParser); 475 backendType = 476 StringArgument.builder(OPTION_LONG_BACKEND_TYPE) 477 .description(INFO_INSTALLDS_DESCRIPTION_BACKEND_TYPE.get()) 478 .valuePlaceholder(INFO_INSTALLDS_BACKEND_TYPE_PLACEHOLDER.get()) 479 .buildAndAddToParser(argParser); 480 } 481 catch (final ArgumentException ae) 482 { 483 throw new ConfigureDSException(ae, ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage())); 484 } 485 } 486 487 private int parseArguments() throws ConfigureDSException 488 { 489 try 490 { 491 argParser.parseArguments(arguments); 492 return SUCCESS; 493 } 494 catch (final ArgumentException ae) 495 { 496 throw new ConfigureDSException(ae, ERR_ERROR_PARSING_ARGS.get(ae.getMessage()), 497 WRONG_USAGE, LDAPResultCode.CLIENT_SIDE_PARAM_ERROR); 498 } 499 } 500 501 /** Make sure that the user actually tried to configure something. */ 502 private void checkArgumentsConsistency() throws ConfigureDSException 503 { 504 if (!baseDNString.isPresent() 505 && !ldapPort.isPresent() 506 && !jmxPort.isPresent() 507 && !rootDNString.isPresent()) 508 { 509 throw new ConfigureDSException(ERR_CONFIGDS_NO_CONFIG_CHANGES.get(), WRONG_USAGE); 510 } 511 } 512 513 private void checkPortArguments() throws ConfigureDSException 514 { 515 try 516 { 517 final IntegerArgument[] portArgs = {ldapPort, adminConnectorPort, ldapsPort, jmxPort}; 518 final Set<Integer> portsAdded = new HashSet<>(); 519 520 for (final IntegerArgument portArg : portArgs) 521 { 522 if (portArg.isPresent()) 523 { 524 final int portNumber = portArg.getIntValue(); 525 if (portsAdded.contains(portNumber)) 526 { 527 throw new ConfigureDSException(ERR_CONFIGDS_PORT_ALREADY_SPECIFIED.get(portArg.getIntValue()), WRONG_USAGE); 528 } 529 portsAdded.add(portNumber); 530 } 531 } 532 } 533 catch (final ArgumentException ae) 534 { 535 throw new ConfigureDSException(ae, ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage())); 536 } 537 } 538 539 private void initializeDirectoryServer() throws ConfigureDSException 540 { 541 if (serverRoot.isPresent()) { 542 final DirectoryEnvironmentConfig env = DirectoryServer.getEnvironmentConfig(); 543 final String root = serverRoot.getValue(); 544 try { 545 env.setServerRoot(new File(serverRoot.getValue())); 546 } catch (final InitializationException e) { 547 ERR_INITIALIZE_SERVER_ROOT.get(root, e.getMessageObject()); 548 } 549 } 550 551 // Initialize the Directory Server configuration handler using the 552 // information that was provided. 553 final DirectoryServer directoryServer = DirectoryServer.getInstance(); 554 DirectoryServer.bootstrapClient(); 555 556 try 557 { 558 DirectoryServer.initializeJMX(); 559 } 560 catch (final Exception e) 561 { 562 final LocalizableMessage msg = ERR_CONFIGDS_CANNOT_INITIALIZE_JMX.get(configFile.getValue(), e.getMessage()); 563 throw new ConfigureDSException(e, msg); 564 } 565 566 try 567 { 568 directoryServer.initializeConfiguration(configFile.getValue()); 569 } 570 catch (final Exception e) 571 { 572 final LocalizableMessage msg = ERR_CONFIGDS_CANNOT_INITIALIZE_CONFIG.get(configFile.getValue(), e.getMessage()); 573 throw new ConfigureDSException(e, msg); 574 } 575 576 try 577 { 578 directoryServer.initializeSchema(); 579 } 580 catch (final Exception e) 581 { 582 final LocalizableMessage msg = ERR_CONFIGDS_CANNOT_INITIALIZE_SCHEMA.get(configFile.getValue(), e.getMessage()); 583 throw new ConfigureDSException(e, msg); 584 } 585 } 586 587 /** 588 * Make sure that we can get an exclusive lock for the Directory Server, so 589 * that no other operation will be allowed while this is in progress. 590 * 591 * @throws ConfigureDSException 592 */ 593 private void tryAcquireExclusiveLocks() throws ConfigureDSException 594 { 595 if (! LockFileManager.acquireExclusiveLock(serverLockFileName, failureReason)) 596 { 597 throw new ConfigureDSException(ERR_CONFIGDS_CANNOT_ACQUIRE_SERVER_LOCK.get(serverLockFileName, failureReason)); 598 } 599 } 600 601 private LinkedList<DN> parseProvidedBaseDNs() throws ConfigureDSException 602 { 603 LinkedList<DN> baseDNs = new LinkedList<>(); 604 if (baseDNString.isPresent()) 605 { 606 for (final String dnString : baseDNString.getValues()) 607 { 608 try 609 { 610 baseDNs.add(DN.valueOf(dnString)); 611 } 612 catch (final Exception e) 613 { 614 throw new ConfigureDSException(e, ERR_CONFIGDS_CANNOT_PARSE_BASE_DN.get(dnString, e.getMessage())); 615 } 616 } 617 } 618 619 return baseDNs; 620 } 621 622 private DN parseRootDN() throws ConfigureDSException 623 { 624 DN rootDN = null; 625 if (rootDNString.isPresent()) 626 { 627 try 628 { 629 rootDN = DN.valueOf(rootDNString.getValue()); 630 } 631 catch (final LocalizedIllegalArgumentException e) 632 { 633 final LocalizableMessage msg = ERR_CONFIGDS_CANNOT_PARSE_ROOT_DN.get( 634 rootDNString.getValue(), e.getMessageObject()); 635 throw new ConfigureDSException(e, msg); 636 } 637 } 638 return rootDN; 639 } 640 641 private String parseRootDNPassword() throws ConfigureDSException 642 { 643 String rootPW = null; 644 if (rootDNString.isPresent()) 645 { 646 if (rootPassword.isPresent()) 647 { 648 rootPW = rootPassword.getValue(); 649 } 650 else if (rootPasswordFile.isPresent()) 651 { 652 rootPW = rootPasswordFile.getValue(); 653 } 654 else 655 { 656 throw new ConfigureDSException(ERR_CONFIGDS_NO_ROOT_PW.get()); 657 } 658 } 659 return rootPW; 660 } 661 662 private void checkManagerProvider(final Argument arg, final String jckesDN, final String ldifEntry, 663 final boolean isKeyManager) throws ConfigureDSException 664 { 665 if (arg.isPresent()) 666 { 667 DN dn = null; 668 DN JCEKSManagerDN = null; 669 try 670 { 671 dn = DN.valueOf(trustManagerProviderDN.getValue()); 672 JCEKSManagerDN = DN.valueOf(jckesDN); 673 } 674 catch (final LocalizedIllegalArgumentException e) 675 { 676 final String value = trustManagerProviderDN.getValue(); 677 final LocalizableMessage errorMessage = e.getMessageObject(); 678 final LocalizableMessage message = 679 isKeyManager ? ERR_CONFIGDS_CANNOT_PARSE_KEYMANAGER_PROVIDER_DN.get(value, errorMessage) 680 : ERR_CONFIGDS_CANNOT_PARSE_TRUSTMANAGER_PROVIDER_DN.get(value, errorMessage); 681 throw new ConfigureDSException(e, message); 682 } 683 684 if (dn.equals(JCEKSManagerDN)) 685 { 686 LDIFReader reader = null; 687 try 688 { 689 690 final String ldif = ldifEntry; 691 final LDIFImportConfig ldifImportConfig = new LDIFImportConfig(new StringReader(ldif)); 692 reader = new LDIFReader(ldifImportConfig); 693 Entry mangerConfigEntry; 694 while ((mangerConfigEntry = reader.readEntry()) != null) 695 { 696 configHandler.addEntry(Converters.from(mangerConfigEntry)); 697 } 698 } 699 catch (final Exception e) 700 { 701 final LocalizableMessage message = isKeyManager ? ERR_CONFIG_KEYMANAGER_CANNOT_CREATE_JCEKS_PROVIDER.get(e) 702 : ERR_CONFIG_KEYMANAGER_CANNOT_GET_BASE.get(e); 703 throw new ConfigureDSException(e, message); 704 } 705 finally 706 { 707 close(reader); 708 } 709 } 710 else 711 { 712 try 713 { 714 configHandler.getEntry(dn); 715 } 716 catch (final Exception e) 717 { 718 final LocalizableMessage message = isKeyManager ? ERR_CONFIG_KEYMANAGER_CANNOT_GET_BASE.get(e) 719 : ERR_CONFIG_TRUSTMANAGER_CANNOT_GET_BASE.get(e); 720 throw new ConfigureDSException(e, message); 721 } 722 } 723 } 724 } 725 726 @SuppressWarnings("unchecked") 727 private void updateBaseDNs(final List<DN> baseDNs) throws ConfigureDSException 728 { 729 if (!baseDNs.isEmpty()) 730 { 731 final String backendTypeName = backendType.getValue(); 732 final BackendTypeHelper backendTypeHelper = new BackendTypeHelper(); 733 final ManagedObjectDefinition<?, ?> backend = backendTypeHelper.retrieveBackendTypeFromName(backendTypeName); 734 if (backend == null) 735 { 736 throw new ConfigureDSException( 737 ERR_CONFIGDS_BACKEND_TYPE_UNKNOWN.get(backendTypeName, backendTypeHelper.getPrintableBackendTypeNames())); 738 } 739 740 try 741 { 742 BackendCreationHelper.createBackendOffline(Installer.ROOT_BACKEND_NAME, baseDNs, 743 (ManagedObjectDefinition<? extends BackendCfgClient, ? extends BackendCfg>) backend); 744 } 745 catch (Exception e) 746 { 747 throw new ConfigureDSException(ERR_CONFIGDS_SET_BACKEND_TYPE.get(backendTypeName, e.getMessage())); 748 } 749 } 750 } 751 752 private void updateLdapPort() throws ConfigureDSException 753 { 754 if (ldapPort.isPresent()) 755 { 756 try 757 { 758 updateConfigEntryWithAttribute( 759 DN_LDAP_CONNECTION_HANDLER, ATTR_LISTEN_PORT, 760 CoreSchema.getIntegerSyntax(), 761 ldapPort.getIntValue()); 762 } 763 catch (final Exception e) 764 { 765 throw new ConfigureDSException(e, ERR_CONFIGDS_CANNOT_UPDATE_LDAP_PORT.get(e)); 766 } 767 } 768 } 769 770 private void updateAdminConnectorPort() throws ConfigureDSException 771 { 772 if (adminConnectorPort.isPresent()) 773 { 774 try 775 { 776 updateConfigEntryWithAttribute( 777 DN_ADMIN_CONNECTOR, 778 ATTR_LISTEN_PORT, 779 CoreSchema.getIntegerSyntax(), 780 adminConnectorPort.getIntValue()); 781 } 782 catch (final Exception e) 783 { 784 throw new ConfigureDSException(e, ERR_CONFIGDS_CANNOT_UPDATE_ADMIN_CONNECTOR_PORT.get(e)); 785 } 786 } 787 } 788 789 private void updateLdapSecurePort() throws ConfigureDSException 790 { 791 if (ldapsPort.isPresent()) 792 { 793 try 794 { 795 updateConfigEntryWithAttribute( 796 DN_LDAPS_CONNECTION_HANDLER, 797 ATTR_LISTEN_PORT, 798 CoreSchema.getIntegerSyntax(), 799 ldapsPort.getIntValue()); 800 801 updateConfigEntryWithAttribute( 802 DN_LDAPS_CONNECTION_HANDLER, 803 ATTR_CONNECTION_HANDLER_ENABLED, 804 CoreSchema.getBooleanSyntax(), 805 ServerConstants.TRUE_VALUE); 806 } 807 catch (final Exception e) 808 { 809 throw new ConfigureDSException(e, ERR_CONFIGDS_CANNOT_UPDATE_LDAPS_PORT.get(e)); 810 } 811 } 812 } 813 814 private void updateJMXport() throws ConfigureDSException 815 { 816 if (jmxPort.isPresent()) 817 { 818 try 819 { 820 updateConfigEntryWithAttribute( 821 DN_JMX_CONNECTION_HANDLER, 822 ATTR_LISTEN_PORT, 823 CoreSchema.getIntegerSyntax(), 824 jmxPort.getIntValue()); 825 826 updateConfigEntryWithAttribute( 827 DN_JMX_CONNECTION_HANDLER, 828 ATTR_CONNECTION_HANDLER_ENABLED, 829 CoreSchema.getBooleanSyntax(), 830 ServerConstants.TRUE_VALUE); 831 } 832 catch (final Exception e) 833 { 834 throw new ConfigureDSException(e, ERR_CONFIGDS_CANNOT_UPDATE_JMX_PORT.get(e)); 835 } 836 } 837 } 838 839 private void updateStartTLS() throws ConfigureDSException 840 { 841 if (enableStartTLS.isPresent()) 842 { 843 try 844 { 845 updateConfigEntryWithAttribute( 846 DN_LDAP_CONNECTION_HANDLER, 847 ATTR_ALLOW_STARTTLS, 848 CoreSchema.getBooleanSyntax(), 849 ServerConstants.TRUE_VALUE); 850 } 851 catch (final Exception e) 852 { 853 throw new ConfigureDSException(e, ERR_CONFIGDS_CANNOT_ENABLE_STARTTLS.get(e)); 854 } 855 } 856 } 857 858 private void updateKeyManager() throws ConfigureDSException 859 { 860 if (keyManagerProviderDN.isPresent()) 861 { 862 if (enableStartTLS.isPresent() || ldapsPort.isPresent()) 863 { 864 try 865 { 866 // Enable the key manager 867 updateConfigEntryWithAttribute( 868 keyManagerProviderDN.getValue(), 869 ATTR_KEYMANAGER_ENABLED, 870 CoreSchema.getBooleanSyntax(), 871 ServerConstants.TRUE_VALUE); 872 } 873 catch (final Exception e) 874 { 875 throw new ConfigureDSException(e, ERR_CONFIGDS_CANNOT_ENABLE_KEYMANAGER.get(e)); 876 } 877 } 878 879 putKeyManagerConfigAttribute(enableStartTLS, DN_LDAP_CONNECTION_HANDLER); 880 putKeyManagerConfigAttribute(ldapsPort, DN_LDAPS_CONNECTION_HANDLER); 881 putKeyManagerConfigAttribute(ldapsPort, DN_HTTP_CONNECTION_HANDLER); 882 883 if (keyManagerPath.isPresent()) 884 { 885 try 886 { 887 updateConfigEntryWithAttribute( 888 keyManagerProviderDN.getValue(), 889 ATTR_KEYSTORE_FILE, 890 CoreSchema.getDirectoryStringSyntax(), 891 keyManagerPath.getValue()); 892 } 893 catch (final Exception e) 894 { 895 throw new ConfigureDSException(e, LocalizableMessage.raw(e.toString())); 896 } 897 } 898 } 899 } 900 901 private void putKeyManagerConfigAttribute(final Argument arg, final String attributeDN) 902 throws ConfigureDSException 903 { 904 if (arg.isPresent()) 905 { 906 try 907 { 908 updateConfigEntryWithAttribute( 909 attributeDN, 910 ATTR_KEYMANAGER_DN, 911 CoreSchema.getDirectoryStringSyntax(), 912 keyManagerProviderDN.getValue()); 913 } 914 catch (final Exception e) 915 { 916 throw new ConfigureDSException(e, ERR_CONFIGDS_CANNOT_UPDATE_KEYMANAGER_REFERENCE.get(e)); 917 } 918 } 919 } 920 921 private void updateTrustManager() throws ConfigureDSException 922 { 923 if (trustManagerProviderDN.isPresent()) 924 { 925 if (enableStartTLS.isPresent() || ldapsPort.isPresent()) 926 { 927 try 928 { 929 updateConfigEntryWithAttribute( 930 trustManagerProviderDN.getValue(), 931 ATTR_TRUSTMANAGER_ENABLED, 932 CoreSchema.getBooleanSyntax(), 933 ServerConstants.TRUE_VALUE); 934 } 935 catch (final Exception e) 936 { 937 throw new ConfigureDSException(e, ERR_CONFIGDS_CANNOT_ENABLE_TRUSTMANAGER.get(e)); 938 } 939 } 940 putTrustManagerAttribute(enableStartTLS, DN_LDAP_CONNECTION_HANDLER); 941 putTrustManagerAttribute(ldapsPort, DN_LDAPS_CONNECTION_HANDLER); 942 putTrustManagerAttribute(ldapsPort, DN_HTTP_CONNECTION_HANDLER); 943 } 944 945 if (certNickNames.isPresent()) 946 { 947 final List<String> attrValues = certNickNames.getValues(); 948 updateCertNicknameEntry(ldapPort, DN_LDAP_CONNECTION_HANDLER, ATTR_SSL_CERT_NICKNAME, attrValues); 949 updateCertNicknameEntry(ldapsPort, DN_LDAPS_CONNECTION_HANDLER, ATTR_SSL_CERT_NICKNAME, attrValues); 950 updateCertNicknameEntry(certNickNames, DN_HTTP_CONNECTION_HANDLER, ATTR_SSL_CERT_NICKNAME, attrValues); 951 updateCertNicknameEntry(jmxPort, DN_JMX_CONNECTION_HANDLER, ATTR_SSL_CERT_NICKNAME, attrValues); 952 } 953 else 954 { 955 // Use the key manager specified for connection handlers 956 removeSSLCertNicknameAttribute(DN_LDAP_CONNECTION_HANDLER); 957 removeSSLCertNicknameAttribute(DN_LDAPS_CONNECTION_HANDLER); 958 removeSSLCertNicknameAttribute(DN_HTTP_CONNECTION_HANDLER); 959 removeSSLCertNicknameAttribute(DN_JMX_CONNECTION_HANDLER); 960 } 961 } 962 963 private void putTrustManagerAttribute(final Argument arg, final String attributeDN) throws ConfigureDSException 964 { 965 if (arg.isPresent()) 966 { 967 try 968 { 969 updateConfigEntryWithAttribute( 970 attributeDN, 971 ATTR_TRUSTMANAGER_DN, 972 CoreSchema.getDirectoryStringSyntax(), 973 trustManagerProviderDN.getValue()); 974 } 975 catch (final Exception e) 976 { 977 throw new ConfigureDSException(e, ERR_CONFIGDS_CANNOT_UPDATE_TRUSTMANAGER_REFERENCE.get(e)); 978 } 979 } 980 } 981 982 private void updateCertNicknameEntry(final Argument arg, final String attributeDN, 983 final String attrName, final List<String> attrValues) throws ConfigureDSException 984 { 985 try 986 { 987 if (arg.isPresent()) 988 { 989 updateConfigEntryWithAttribute( 990 attributeDN, 991 attrName, 992 CoreSchema.getDirectoryStringSyntax(), 993 attrValues.toArray(new Object[attrValues.size()])); 994 } 995 else 996 { 997 updateConfigEntryByRemovingAttribute(attributeDN, ATTR_SSL_CERT_NICKNAME); 998 } 999 } 1000 catch (final Exception e) 1001 { 1002 throw new ConfigureDSException(e, ERR_CONFIGDS_CANNOT_UPDATE_CERT_NICKNAME.get(e)); 1003 } 1004 } 1005 1006 private void removeSSLCertNicknameAttribute(final String attributeDN) throws ConfigureDSException 1007 { 1008 try 1009 { 1010 updateConfigEntryByRemovingAttribute(attributeDN, ATTR_SSL_CERT_NICKNAME); 1011 } 1012 catch (final Exception e) 1013 { 1014 throw new ConfigureDSException(e, ERR_CONFIGDS_CANNOT_UPDATE_CERT_NICKNAME.get(e)); 1015 } 1016 } 1017 1018 private void updateRootUser(final DN rootDN, final String rootPW) throws ConfigureDSException 1019 { 1020 if (rootDN != null) 1021 { 1022 try 1023 { 1024 updateConfigEntryWithAttribute( 1025 DN_ROOT_USER, 1026 ATTR_ROOTDN_ALTERNATE_BIND_DN, 1027 CoreSchema.getDirectoryStringSyntax(), 1028 rootDN); 1029 final String encodedPassword = SaltedSHA512PasswordStorageScheme.encodeOffline(getBytes(rootPW)); 1030 updateConfigEntryWithAttribute( 1031 DN_ROOT_USER, 1032 ATTR_USER_PASSWORD, 1033 CoreSchema.getDirectoryStringSyntax(), 1034 encodedPassword); 1035 } 1036 catch (final Exception e) 1037 { 1038 throw new ConfigureDSException(e, ERR_CONFIGDS_CANNOT_UPDATE_ROOT_USER.get(e)); 1039 } 1040 } 1041 } 1042 1043 /** Set the FQDN for the DIGEST-MD5 SASL mechanism. */ 1044 private void addFQDNDigestMD5() throws ConfigureDSException 1045 { 1046 try 1047 { 1048 updateConfigEntryWithAttribute( 1049 DN_DIGEST_MD5_SASL_MECHANISM, 1050 "ds-cfg-server-fqdn", 1051 CoreSchema.getDirectoryStringSyntax(), 1052 hostName.getValue()); 1053 } 1054 catch (final Exception e) 1055 { 1056 throw new ConfigureDSException(e, ERR_CONFIGDS_CANNOT_UPDATE_DIGEST_MD5_FQDN.get(e)); 1057 } 1058 } 1059 1060 /** 1061 * Check that the cipher specified is supported. This is intended to fix 1062 * issues with JVM that do not support the default cipher (see issue 3075 for 1063 * instance). 1064 * 1065 * @throws ConfigureDSException 1066 */ 1067 private void updateCryptoCipher() throws ConfigureDSException 1068 { 1069 final CryptoManagerCfgDefn cryptoManager = CryptoManagerCfgDefn.getInstance(); 1070 final StringPropertyDefinition prop = cryptoManager.getKeyWrappingTransformationPropertyDefinition(); 1071 String defaultCipher = null; 1072 1073 final DefaultBehaviorProvider<?> p = prop.getDefaultBehaviorProvider(); 1074 if (p instanceof DefinedDefaultBehaviorProvider) 1075 { 1076 final Collection<?> defaultValues = ((DefinedDefaultBehaviorProvider<?>) p).getDefaultValues(); 1077 if (!defaultValues.isEmpty()) 1078 { 1079 defaultCipher = defaultValues.iterator().next().toString(); 1080 } 1081 } 1082 1083 if (defaultCipher != null) 1084 { 1085 // Check that the default cipher is supported by the JVM. 1086 try 1087 { 1088 Cipher.getInstance(defaultCipher); 1089 } 1090 catch (final GeneralSecurityException ex) 1091 { 1092 // The cipher is not supported: try to find an alternative one. 1093 final String alternativeCipher = getAlternativeCipher(); 1094 if (alternativeCipher != null) 1095 { 1096 try 1097 { 1098 updateConfigEntryWithAttribute( 1099 DN_CRYPTO_MANAGER, 1100 ATTR_CRYPTO_CIPHER_KEY_WRAPPING_TRANSFORMATION, 1101 CoreSchema.getDirectoryStringSyntax(), 1102 alternativeCipher); 1103 } 1104 catch (final Exception e) 1105 { 1106 throw new ConfigureDSException(e, ERR_CONFIGDS_CANNOT_UPDATE_CRYPTO_MANAGER.get(e)); 1107 } 1108 } 1109 } 1110 } 1111 } 1112 1113 /** Update a config entry with the provided attribute parameters. */ 1114 private void updateConfigEntryWithAttribute(String entryDn, String attributeName, Syntax syntax, Object...values) 1115 throws DirectoryException, ConfigException 1116 { 1117 org.forgerock.opendj.ldap.Entry configEntry = configHandler.getEntry(DN.valueOf(entryDn)); 1118 final org.forgerock.opendj.ldap.Entry newEntry = putAttribute(configEntry, attributeName, syntax, values); 1119 configHandler.replaceEntry(configEntry, newEntry); 1120 } 1121 1122 /** Update a config entry by removing the provided attribute. */ 1123 private void updateConfigEntryByRemovingAttribute(String entryDn, String attributeName) 1124 throws DirectoryException, ConfigException 1125 { 1126 final org.forgerock.opendj.ldap.Entry configEntry = configHandler.getEntry(DN.valueOf(entryDn)); 1127 final Entry newEntry = removeAttribute(Converters.to(configEntry), attributeName); 1128 configHandler.replaceEntry(configEntry, Converters.from(newEntry)); 1129 } 1130 1131 /** 1132 * Duplicate the provided entry, and put an attribute to the duplicated entry. 1133 * <p> 1134 * Provided entry is not modified. 1135 * 1136 * @return the duplicate entry, modified with the attribute 1137 */ 1138 private org.forgerock.opendj.ldap.Entry putAttribute( 1139 org.forgerock.opendj.ldap.Entry configEntry, String attrName, Syntax syntax, Object...values) 1140 { 1141 org.forgerock.opendj.ldap.Entry newEntry = LinkedHashMapEntry.deepCopyOfEntry(configEntry); 1142 AttributeType attrType = DirectoryServer.getSchema().getAttributeType(attrName, syntax); 1143 newEntry.replaceAttribute(new LinkedAttribute(AttributeDescription.create(attrType), values)); 1144 return newEntry; 1145 } 1146 1147 /** 1148 * Duplicate the provided entry, and remove an attribute to the duplicated entry. 1149 * <p> 1150 * Provided entry is not modified. 1151 * 1152 * @return the duplicate entry, with removed attribute 1153 */ 1154 private Entry removeAttribute(Entry entry, String attrName) 1155 { 1156 Entry duplicateEntry = entry.duplicate(false); 1157 for (AttributeType t : entry.getUserAttributes().keySet()) 1158 { 1159 if (t.hasNameOrOID(attrName)) 1160 { 1161 entry.getUserAttributes().remove(t); 1162 return duplicateEntry; 1163 } 1164 } 1165 1166 for (AttributeType t : entry.getOperationalAttributes().keySet()) 1167 { 1168 if (t.hasNameOrOID(attrName)) 1169 { 1170 entry.getOperationalAttributes().remove(t); 1171 return duplicateEntry; 1172 } 1173 } 1174 return duplicateEntry; 1175 } 1176 1177 /** 1178 * Returns a cipher that is supported by the JVM we are running at. 1179 * Returns <CODE>null</CODE> if no alternative cipher could be found. 1180 * @return a cipher that is supported by the JVM we are running at. 1181 */ 1182 private static String getAlternativeCipher() 1183 { 1184 final String[] preferredAlternativeCiphers = 1185 { 1186 "RSA/ECB/OAEPWITHSHA1ANDMGF1PADDING", 1187 "RSA/ECB/PKCS1Padding" 1188 }; 1189 for (final String cipher : preferredAlternativeCiphers) 1190 { 1191 try 1192 { 1193 Cipher.getInstance(cipher); 1194 return cipher; 1195 } 1196 catch (final Throwable ignored) 1197 { 1198 // ignored 1199 } 1200 } 1201 return null; 1202 } 1203}