/*
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions Copyright [year] [name of copyright owner]".
*
* Copyright 2006-2009 Sun Microsystems, Inc.
* Portions Copyright 2012-2016 ForgeRock AS.
*/
package org.opends.server.tools;
import static com.forgerock.opendj.cli.ArgumentConstants.*;
import static com.forgerock.opendj.cli.CliMessages.INFO_FILE_PLACEHOLDER;
import static com.forgerock.opendj.cli.CliMessages.INFO_JMXPORT_PLACEHOLDER;
import static com.forgerock.opendj.cli.CliMessages.INFO_PORT_PLACEHOLDER;
import static com.forgerock.opendj.cli.CommonArguments.*;
import static com.forgerock.opendj.cli.Utils.*;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.messages.ToolMessages.*;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import java.io.File;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.StringReader;
import java.net.InetAddress;
import java.security.GeneralSecurityException;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.crypto.Cipher;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizedIllegalArgumentException;
import org.forgerock.opendj.adapter.server3x.Converters;
import org.forgerock.opendj.config.DefaultBehaviorProvider;
import org.forgerock.opendj.config.DefinedDefaultBehaviorProvider;
import org.forgerock.opendj.config.ManagedObjectDefinition;
import org.forgerock.opendj.config.StringPropertyDefinition;
import org.forgerock.opendj.config.server.ConfigException;
import org.forgerock.opendj.ldap.AttributeDescription;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.LinkedAttribute;
import org.forgerock.opendj.ldap.LinkedHashMapEntry;
import org.forgerock.opendj.ldap.schema.AttributeType;
import org.forgerock.opendj.ldap.schema.CoreSchema;
import org.forgerock.opendj.ldap.schema.Syntax;
import org.forgerock.opendj.server.config.client.BackendCfgClient;
import org.forgerock.opendj.server.config.meta.CryptoManagerCfgDefn;
import org.forgerock.opendj.server.config.server.BackendCfg;
import org.opends.quicksetup.installer.Installer;
import org.opends.server.config.ConfigurationHandler;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.LockFileManager;
import org.opends.server.extensions.SaltedSHA512PasswordStorageScheme;
import org.opends.server.protocols.ldap.LDAPResultCode;
import org.opends.server.types.DirectoryEnvironmentConfig;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.InitializationException;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.NullOutputStream;
import org.opends.server.util.LDIFReader;
import org.opends.server.util.ServerConstants;
import com.forgerock.opendj.cli.Argument;
import com.forgerock.opendj.cli.ArgumentException;
import com.forgerock.opendj.cli.ArgumentParser;
import com.forgerock.opendj.cli.BooleanArgument;
import com.forgerock.opendj.cli.CliConstants;
import com.forgerock.opendj.cli.FileBasedArgument;
import com.forgerock.opendj.cli.IntegerArgument;
import com.forgerock.opendj.cli.StringArgument;
/**
* This class provides a very basic tool that can be used to configure some of
* the most important settings in the Directory Server. This configuration is
* performed by editing the server's configuration files and therefore the
* Directory Server must be offline. This utility will be used during the
* Directory Server installation process.
*
* The options that this tool can currently set include:
*
*
configMain method
* for processing.
*
* @param args The set of command-line arguments provided to this program.
*/
public static void main(String[] args)
{
final int exitCode = configMain(args, System.out, System.err);
if (exitCode != SUCCESS)
{
System.exit(filterExitCode(exitCode));
}
}
/**
* Parses the provided command-line arguments and makes the appropriate
* changes to the Directory Server configuration.
*
* @param args The command-line arguments provided to this program.
*
* @param outStream Output stream.
* @param errStream Error stream.
* @return The exit code from the configuration processing. A nonzero value
* indicates that there was some kind of problem during the
* configuration processing.
*/
public static int configMain(final String[] args, final OutputStream outStream, final OutputStream errStream)
{
final ConfigureDS tool = new ConfigureDS(args, outStream, errStream);
return tool.run();
}
private final String[] arguments;
private final PrintStream out;
private final PrintStream err;
private final ArgumentParser argParser;
private BooleanArgument showUsage;
private BooleanArgument enableStartTLS;
private FileBasedArgument rootPasswordFile;
private StringArgument hostName;
private IntegerArgument ldapPort;
private IntegerArgument adminConnectorPort;
private IntegerArgument ldapsPort;
private IntegerArgument jmxPort;
private StringArgument baseDNString;
private StringArgument configFile;
private StringArgument rootDNString;
private StringArgument rootPassword;
private StringArgument keyManagerProviderDN;
private StringArgument trustManagerProviderDN;
private StringArgument certNickNames;
private StringArgument keyManagerPath;
private StringArgument serverRoot;
private StringArgument backendType;
private final String serverLockFileName = LockFileManager.getServerLockFileName();
private final StringBuilder failureReason = new StringBuilder();
private ConfigurationHandler configHandler;
private ConfigureDS(final String[] args, final OutputStream outStream, final OutputStream errStream)
{
arguments = args;
out = NullOutputStream.wrapOrNullStream(outStream);
err = NullOutputStream.wrapOrNullStream(errStream);
argParser = new ArgumentParser(ConfigureDS.class.getName(), INFO_CONFIGDS_TOOL_DESCRIPTION.get(), false);
}
private int run()
{
try
{
initializeArguments();
parseArguments();
if (argParser.usageOrVersionDisplayed())
{
return SUCCESS;
}
checkArgumentsConsistency();
checkPortArguments();
tryAcquireExclusiveLocks();
updateBaseDNs(parseProvidedBaseDNs());
initializeDirectoryServer();
final DN rootDN = parseRootDN();
final String rootPW = parseRootDNPassword();
configHandler = DirectoryServer.getConfigurationHandler();
checkManagerProvider(keyManagerProviderDN, JCKES_KEY_MANAGER_DN, JCKES_KEY_MANAGER_LDIF_ENTRY, true);
checkManagerProvider(trustManagerProviderDN, JCKES_TRUST_MANAGER_DN, JCKES_TRUST_MANAGER_LDIF_ENTRY, false);
// Check that the keystore path values are valid.
if (keyManagerPath.isPresent() && !keyManagerProviderDN.isPresent())
{
final LocalizableMessage message = ERR_CONFIGDS_KEYMANAGER_PROVIDER_DN_REQUIRED.get(
keyManagerProviderDN.getLongIdentifier(), keyManagerPath.getLongIdentifier());
throw new ConfigureDSException(message);
}
updateLdapPort();
updateAdminConnectorPort();
updateLdapSecurePort();
updateJMXport();
updateStartTLS();
updateKeyManager();
updateTrustManager();
updateRootUser(rootDN, rootPW);
addFQDNDigestMD5();
updateCryptoCipher();
printWrappedText(out, INFO_CONFIGDS_WROTE_UPDATED_CONFIG.get());
return SUCCESS;
}
catch (final ConfigureDSException e)
{
if (e.isWrongUsage())
{
argParser.displayMessageAndUsageReference(err, e.getErrorMessage());
}
else
{
printWrappedText(err, e.getErrorMessage());
}
return e.getErrorCode();
}
finally
{
LockFileManager.releaseLock(serverLockFileName, failureReason);
}
}
private void initializeArguments() throws ConfigureDSException
{
try
{
configFile =
StringArgument.builder("configFile")
.shortIdentifier('c')
.description(INFO_DESCRIPTION_CONFIG_FILE.get())
.hidden()
.required()
.valuePlaceholder(INFO_CONFIGFILE_PLACEHOLDER.get())
.buildAndAddToParser(argParser);
String defaultHostName;
try
{
defaultHostName = InetAddress.getLocalHost().getHostName();
}
catch (final Exception e)
{
// Not much we can do here.
defaultHostName = "localhost";
}
hostName =
StringArgument.builder(OPTION_LONG_HOST)
.shortIdentifier(OPTION_SHORT_HOST)
.description(INFO_INSTALLDS_DESCRIPTION_HOST_NAME.get())
.defaultValue(defaultHostName)
.valuePlaceholder(INFO_HOST_PLACEHOLDER.get())
.buildAndAddToParser(argParser);
ldapPort =
IntegerArgument.builder("ldapPort")
.shortIdentifier(OPTION_SHORT_PORT)
.description(INFO_CONFIGDS_DESCRIPTION_LDAP_PORT.get())
.range(1, 65535)
.defaultValue(389)
.valuePlaceholder(INFO_LDAPPORT_PLACEHOLDER.get())
.buildAndAddToParser(argParser);
adminConnectorPort =
IntegerArgument.builder("adminConnectorPort")
.description(INFO_INSTALLDS_DESCRIPTION_ADMINCONNECTORPORT.get())
.range(1, 65535)
.defaultValue(4444)
.valuePlaceholder(INFO_PORT_PLACEHOLDER.get())
.buildAndAddToParser(argParser);
ldapsPort =
IntegerArgument.builder("ldapsPort")
.shortIdentifier('P')
.description(INFO_CONFIGDS_DESCRIPTION_LDAPS_PORT.get())
.range(1, 65535)
.defaultValue(636)
.valuePlaceholder(INFO_LDAPPORT_PLACEHOLDER.get())
.buildAndAddToParser(argParser);
enableStartTLS =
BooleanArgument.builder("enableStartTLS")
.shortIdentifier(OPTION_SHORT_START_TLS)
.description(INFO_CONFIGDS_DESCRIPTION_ENABLE_START_TLS.get())
.buildAndAddToParser(argParser);
jmxPort =
IntegerArgument.builder("jmxPort")
.shortIdentifier('x')
.description(INFO_CONFIGDS_DESCRIPTION_JMX_PORT.get())
.range(1, 65535)
.defaultValue(CliConstants.DEFAULT_JMX_PORT)
.valuePlaceholder(INFO_JMXPORT_PLACEHOLDER.get())
.buildAndAddToParser(argParser);
keyManagerProviderDN =
StringArgument.builder("keyManagerProviderDN")
.shortIdentifier('k')
.description(INFO_CONFIGDS_DESCRIPTION_KEYMANAGER_PROVIDER_DN.get())
.valuePlaceholder(INFO_KEY_MANAGER_PROVIDER_DN_PLACEHOLDER.get())
.buildAndAddToParser(argParser);
trustManagerProviderDN =
StringArgument.builder("trustManagerProviderDN")
.shortIdentifier('t')
.description(INFO_CONFIGDS_DESCRIPTION_TRUSTMANAGER_PROVIDER_DN.get())
.valuePlaceholder(INFO_TRUST_MANAGER_PROVIDER_DN_PLACEHOLDER.get())
.buildAndAddToParser(argParser);
keyManagerPath =
StringArgument.builder("keyManagerPath")
.shortIdentifier('m')
.description(INFO_CONFIGDS_DESCRIPTION_KEYMANAGER_PATH.get())
.valuePlaceholder(INFO_KEY_MANAGER_PATH_PLACEHOLDER.get())
.buildAndAddToParser(argParser);
certNickNames =
StringArgument.builder("certNickName")
.shortIdentifier('a')
.description(INFO_CONFIGDS_DESCRIPTION_CERTNICKNAME.get())
.multiValued()
.valuePlaceholder(INFO_NICKNAME_PLACEHOLDER.get())
.buildAndAddToParser(argParser);
baseDNString =
StringArgument.builder(OPTION_LONG_BASEDN)
.shortIdentifier(OPTION_SHORT_BASEDN)
.description(INFO_CONFIGDS_DESCRIPTION_BASE_DN.get())
.multiValued()
.defaultValue("dc=example,dc=com")
.valuePlaceholder(INFO_BASEDN_PLACEHOLDER.get())
.buildAndAddToParser(argParser);
rootDNString =
StringArgument.builder(OPTION_LONG_ROOT_USER_DN)
.shortIdentifier(OPTION_SHORT_ROOT_USER_DN)
.description(INFO_CONFIGDS_DESCRIPTION_ROOT_DN.get())
.defaultValue("cn=Directory Manager")
.valuePlaceholder(INFO_ROOT_USER_DN_PLACEHOLDER.get())
.buildAndAddToParser(argParser);
rootPassword =
StringArgument.builder("rootPassword")
.shortIdentifier(OPTION_SHORT_BINDPWD)
.description(INFO_CONFIGDS_DESCRIPTION_ROOT_PW.get())
.valuePlaceholder(INFO_ROOT_USER_PWD_PLACEHOLDER.get())
.buildAndAddToParser(argParser);
rootPasswordFile =
FileBasedArgument.builder("rootPasswordFile")
.shortIdentifier(OPTION_SHORT_BINDPWD_FILE)
.description(INFO_CONFIGDS_DESCRIPTION_ROOT_PW_FILE.get())
.valuePlaceholder(INFO_FILE_PLACEHOLDER.get())
.buildAndAddToParser(argParser);
showUsage = showUsageArgument();
argParser.addArgument(showUsage);
argParser.setUsageArgument(showUsage);
serverRoot =
StringArgument.builder(OPTION_LONG_SERVER_ROOT)
.shortIdentifier(OPTION_SHORT_SERVER_ROOT)
.hidden()
.valuePlaceholder(INFO_SERVER_ROOT_DIR_PLACEHOLDER.get())
.buildAndAddToParser(argParser);
backendType =
StringArgument.builder(OPTION_LONG_BACKEND_TYPE)
.description(INFO_INSTALLDS_DESCRIPTION_BACKEND_TYPE.get())
.valuePlaceholder(INFO_INSTALLDS_BACKEND_TYPE_PLACEHOLDER.get())
.buildAndAddToParser(argParser);
}
catch (final ArgumentException ae)
{
throw new ConfigureDSException(ae, ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()));
}
}
private int parseArguments() throws ConfigureDSException
{
try
{
argParser.parseArguments(arguments);
return SUCCESS;
}
catch (final ArgumentException ae)
{
throw new ConfigureDSException(ae, ERR_ERROR_PARSING_ARGS.get(ae.getMessage()),
WRONG_USAGE, LDAPResultCode.CLIENT_SIDE_PARAM_ERROR);
}
}
/** Make sure that the user actually tried to configure something. */
private void checkArgumentsConsistency() throws ConfigureDSException
{
if (!baseDNString.isPresent()
&& !ldapPort.isPresent()
&& !jmxPort.isPresent()
&& !rootDNString.isPresent())
{
throw new ConfigureDSException(ERR_CONFIGDS_NO_CONFIG_CHANGES.get(), WRONG_USAGE);
}
}
private void checkPortArguments() throws ConfigureDSException
{
try
{
final IntegerArgument[] portArgs = {ldapPort, adminConnectorPort, ldapsPort, jmxPort};
final Set* Provided entry is not modified. * * @return the duplicate entry, modified with the attribute */ private org.forgerock.opendj.ldap.Entry putAttribute( org.forgerock.opendj.ldap.Entry configEntry, String attrName, Syntax syntax, Object...values) { org.forgerock.opendj.ldap.Entry newEntry = LinkedHashMapEntry.deepCopyOfEntry(configEntry); AttributeType attrType = DirectoryServer.getInstance().getServerContext().getSchema().getAttributeType(attrName, syntax); newEntry.replaceAttribute(new LinkedAttribute(AttributeDescription.create(attrType), values)); return newEntry; } /** * Duplicate the provided entry, and remove an attribute to the duplicated entry. *
* Provided entry is not modified.
*
* @return the duplicate entry, with removed attribute
*/
private Entry removeAttribute(Entry entry, String attrName)
{
Entry duplicateEntry = entry.duplicate(false);
for (AttributeType t : entry.getUserAttributes().keySet())
{
if (t.hasNameOrOID(attrName))
{
entry.getUserAttributes().remove(t);
return duplicateEntry;
}
}
for (AttributeType t : entry.getOperationalAttributes().keySet())
{
if (t.hasNameOrOID(attrName))
{
entry.getOperationalAttributes().remove(t);
return duplicateEntry;
}
}
return duplicateEntry;
}
/**
* Returns a cipher that is supported by the JVM we are running at.
* Returns null if no alternative cipher could be found.
* @return a cipher that is supported by the JVM we are running at.
*/
private static String getAlternativeCipher()
{
final String[] preferredAlternativeCiphers =
{
"RSA/ECB/OAEPWITHSHA1ANDMGF1PADDING",
"RSA/ECB/PKCS1Padding"
};
for (final String cipher : preferredAlternativeCiphers)
{
try
{
Cipher.getInstance(cipher);
return cipher;
}
catch (final Throwable ignored)
{
// ignored
}
}
return null;
}
}