/*
|
* CDDL HEADER START
|
*
|
* The contents of this file are subject to the terms of the
|
* Common Development and Distribution License, Version 1.0 only
|
* (the "License"). You may not use this file except in compliance
|
* with the License.
|
*
|
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
|
* or http://forgerock.org/license/CDDLv1.0.html.
|
* See the License for the specific language governing permissions
|
* and limitations under the License.
|
*
|
* When distributing Covered Code, include this CDDL HEADER in each
|
* file and include the License file at legal-notices/CDDLv1_0.txt.
|
* If applicable, add the following below this CDDL HEADER, with the
|
* fields enclosed by brackets "[]" replaced with your own identifying
|
* information:
|
* Portions Copyright [yyyy] [name of copyright owner]
|
*
|
* CDDL HEADER END
|
*
|
*
|
* Copyright 2008-2010 Sun Microsystems, Inc.
|
* Portions Copyright 2011-2014 ForgeRock AS
|
*/
|
|
package org.opends.server.util.cli;
|
|
import org.forgerock.i18n.LocalizableMessage;
|
import static org.opends.messages.UtilityMessages.*;
|
import static org.opends.messages.QuickSetupMessages.*;
|
import static org.opends.messages.ToolMessages.*;
|
|
import org.opends.quicksetup.Step;
|
import org.opends.quicksetup.UserDataCertificateException;
|
import org.opends.quicksetup.util.Utils;
|
import org.opends.server.tools.dsconfig.ArgumentExceptionFactory;
|
import org.opends.server.tools.LDAPConnectionOptions;
|
import org.opends.server.tools.SSLConnectionFactory;
|
import org.opends.server.tools.SSLConnectionException;
|
import org.opends.server.admin.client.cli.SecureConnectionCliArgs;
|
import org.opends.server.util.args.ArgumentException;
|
import org.opends.server.util.SelectableCertificateKeyManager;
|
import org.opends.admin.ads.ADSContext;
|
import org.opends.admin.ads.util.ApplicationTrustManager;
|
import org.opends.admin.ads.util.ApplicationKeyManager;
|
|
import javax.net.ssl.KeyManager;
|
import java.net.InetAddress;
|
import java.net.URI;
|
import java.net.UnknownHostException;
|
import java.io.File;
|
import java.io.FileInputStream;
|
import java.io.FileNotFoundException;
|
import java.io.FileOutputStream;
|
import java.security.KeyStore;
|
import java.security.KeyStoreException;
|
import java.security.cert.X509Certificate;
|
import java.util.Enumeration;
|
import java.util.LinkedHashMap;
|
|
import org.forgerock.i18n.slf4j.LocalizedLogger;
|
|
/**
|
* Supports interacting with a user through the command line to
|
* prompt for information necessary to create an LDAP connection.
|
*/
|
public class LDAPConnectionConsoleInteraction {
|
|
private boolean useSSL;
|
private boolean useStartTLS;
|
private String hostName;
|
private int portNumber;
|
private String bindDN;
|
private String providedBindDN;
|
private String adminUID;
|
private String providedAdminUID;
|
private String bindPassword;
|
private KeyManager keyManager;
|
private ApplicationTrustManager trustManager;
|
// Boolean that tells if we ask for bind DN or admin UID in the same prompt.
|
private boolean useAdminOrBindDn = false;
|
// Boolean that tells if we must propose LDAP if it is available even if the
|
// user provided certificate parameters.
|
private boolean displayLdapIfSecureParameters = false;
|
|
// The SecureConnectionCliArgsList object.
|
private SecureConnectionCliArgs secureArgsList = null;
|
|
// Indicate if we need to display the heading
|
private boolean isHeadingDisplayed = false;
|
|
// the Console application
|
private ConsoleApplication app;
|
|
// Indicate if the trust store in in memory
|
private boolean trustStoreInMemory = false;
|
|
// Indicate if the all certificates are accepted
|
private boolean trustAll = false;
|
|
// Indicate that the trust manager was created with the parameters provided
|
private boolean trustManagerInitialized;
|
|
// The trust store to use for the SSL or STARTTLS connection
|
private KeyStore truststore;
|
|
private String keystorePath;
|
|
private String keystorePassword;
|
|
private String certifNickname;
|
|
private String truststorePath;
|
|
private String truststorePassword;
|
|
// The timeout to be used to connect
|
private int connectTimeout;
|
|
private LocalizableMessage heading = INFO_LDAP_CONN_HEADING_CONNECTION_PARAMETERS.get();
|
|
// A copy of the secureArgList for convenience.
|
private SecureConnectionCliArgs copySecureArgsList = null;
|
|
// The command builder that we can return with the connection information.
|
private CommandBuilder commandBuilder;
|
|
|
/**
|
* Enumeration description protocols for interactive CLI choices.
|
*/
|
private enum Protocols
|
{
|
LDAP(1, INFO_LDAP_CONN_PROMPT_SECURITY_LDAP.get()), SSL(2,
|
INFO_LDAP_CONN_PROMPT_SECURITY_USE_SSL.get()), START_TLS(3,
|
INFO_LDAP_CONN_PROMPT_SECURITY_USE_START_TLS.get());
|
|
private Integer choice;
|
|
private LocalizableMessage msg;
|
|
/**
|
* Private constructor.
|
*
|
* @param i
|
* the menu return value.
|
* @param msg
|
* the message message.
|
*/
|
private Protocols(int i, LocalizableMessage msg)
|
{
|
choice = i;
|
this.msg = msg;
|
}
|
|
/**
|
* Returns the choice number.
|
*
|
* @return the attribute name.
|
*/
|
public Integer getChoice()
|
{
|
return choice;
|
}
|
|
/**
|
* Return the menu message.
|
*
|
* @return the menu message.
|
*/
|
public LocalizableMessage getMenuMessage()
|
{
|
return msg;
|
}
|
}
|
|
/**
|
* Enumeration description protocols for interactive CLI choices.
|
*/
|
private enum TrustMethod
|
{
|
TRUSTALL(1, INFO_LDAP_CONN_PROMPT_SECURITY_USE_TRUST_ALL.get()),
|
|
TRUSTSTORE(2,INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE.get()),
|
|
DISPLAY_CERTIFICATE(3,INFO_LDAP_CONN_PROMPT_SECURITY_MANUAL_CHECK.get());
|
|
private Integer choice;
|
|
private LocalizableMessage msg;
|
|
/**
|
* Private constructor.
|
*
|
* @param i
|
* the menu return value.
|
* @param msg
|
* the message message.
|
*/
|
private TrustMethod(int i, LocalizableMessage msg)
|
{
|
choice = new Integer(i);
|
this.msg = msg;
|
}
|
|
/**
|
* Returns the choice number.
|
*
|
* @return the attribute name.
|
*/
|
public Integer getChoice()
|
{
|
return choice;
|
}
|
|
/**
|
* Return the menu message.
|
*
|
* @return the menu message.
|
*/
|
public LocalizableMessage getMenuMessage()
|
{
|
return msg;
|
}
|
}
|
|
/**
|
* Enumeration description server certificate trust option.
|
*/
|
private enum TrustOption
|
{
|
UNTRUSTED(1, INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION_NO.get()),
|
SESSION(2,INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION_SESSION.get()),
|
PERMAMENT(3,INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION_ALWAYS.get()),
|
CERTIFICATE_DETAILS(4,
|
INFO_LDAP_CONN_PROMPT_SECURITY_CERTIFICATE_DETAILS.get());
|
|
private Integer choice;
|
|
private LocalizableMessage msg;
|
|
/**
|
* Private constructor.
|
*
|
* @param i
|
* the menu return value.
|
* @param msg
|
* the message message.
|
*/
|
private TrustOption(int i, LocalizableMessage msg)
|
{
|
choice = new Integer(i);
|
this.msg = msg;
|
}
|
|
/**
|
* Returns the choice number.
|
*
|
* @return the attribute name.
|
*/
|
public Integer getChoice()
|
{
|
return choice;
|
}
|
|
/**
|
* Return the menu message.
|
*
|
* @return the menu message.
|
*/
|
public LocalizableMessage getMenuMessage()
|
{
|
return msg;
|
}
|
}
|
/**
|
* Constructs a parameterized instance.
|
*
|
* @param app console application
|
* @param secureArgs existing set of arguments that have already
|
* been parsed and contain some potential command line specified
|
* LDAP arguments
|
*/
|
public LDAPConnectionConsoleInteraction(ConsoleApplication app,
|
SecureConnectionCliArgs secureArgs) {
|
this.app = app;
|
this.secureArgsList = secureArgs;
|
this.commandBuilder = new CommandBuilder(null);
|
this.useSSL = secureArgs.useSSL();
|
this.useStartTLS = secureArgs.useStartTLS();
|
this.trustAll = secureArgs.trustAllArg.isPresent();
|
copySecureArgsList = new SecureConnectionCliArgs(secureArgs.alwaysSSL());
|
try
|
{
|
copySecureArgsList.createGlobalArguments();
|
}
|
catch (Throwable t)
|
{
|
// This is a bug: we should always be able to create the global arguments
|
// no need to localize this one.
|
throw new RuntimeException("Unexpected error: "+t, t);
|
}
|
}
|
|
/**
|
* Interact with the user though the console to get information
|
* necessary to establish an LDAP connection.
|
*
|
* @throws ArgumentException if there is a problem with the arguments
|
*/
|
public void run()
|
throws ArgumentException
|
{
|
run(true, true);
|
}
|
|
|
/**
|
* Interact with the user though the console to get information
|
* necessary to establish an LDAP connection.
|
* @param canUseSSL whether we can propose to connect using SSL or not.
|
* @param canUseStartTLS whether we can propose to connect using Start TLS or
|
* not.
|
*
|
* @throws ArgumentException if there is a problem with the arguments
|
*/
|
public void run(boolean canUseSSL, boolean canUseStartTLS)
|
throws ArgumentException
|
{
|
// Reset everything
|
commandBuilder.clearArguments();
|
copySecureArgsList.createGlobalArguments();
|
boolean secureConnection = (canUseSSL || canUseStartTLS) &&
|
(
|
secureArgsList.useSSLArg.isPresent()
|
||
|
secureArgsList.useStartTLSArg.isPresent()
|
||
|
secureArgsList.trustAllArg.isPresent()
|
||
|
secureArgsList.trustStorePathArg.isPresent()
|
||
|
secureArgsList.trustStorePasswordArg.isPresent()
|
||
|
secureArgsList.trustStorePasswordFileArg.isPresent()
|
||
|
secureArgsList.keyStorePathArg.isPresent()
|
||
|
secureArgsList.keyStorePasswordArg.isPresent()
|
||
|
secureArgsList.keyStorePasswordFileArg.isPresent()
|
);
|
// Get the LDAP host.
|
hostName = secureArgsList.hostNameArg.getValue();
|
final String tmpHostName = hostName;
|
if (app.isInteractive() && !secureArgsList.hostNameArg.isPresent())
|
{
|
checkHeadingDisplayed();
|
|
ValidationCallback<String> callback = new ValidationCallback<String>()
|
{
|
|
public String validate(ConsoleApplication app, String input)
|
throws CLIException
|
{
|
String ninput = input.trim();
|
if (ninput.length() == 0)
|
{
|
return tmpHostName;
|
}
|
else
|
{
|
try
|
{
|
InetAddress.getByName(ninput);
|
return ninput;
|
}
|
catch (UnknownHostException e)
|
{
|
// Try again...
|
app.println();
|
app.println(ERR_LDAP_CONN_BAD_HOST_NAME.get(ninput));
|
app.println();
|
return null;
|
}
|
}
|
}
|
|
};
|
|
try
|
{
|
app.println();
|
hostName = app.readValidatedInput(INFO_LDAP_CONN_PROMPT_HOST_NAME
|
.get(hostName), callback);
|
}
|
catch (CLIException e)
|
{
|
throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
|
}
|
}
|
|
copySecureArgsList.hostNameArg.clearValues();
|
copySecureArgsList.hostNameArg.addValue(hostName);
|
commandBuilder.addArgument(copySecureArgsList.hostNameArg);
|
|
// Connection type
|
useSSL = secureArgsList.useSSL();
|
useStartTLS = secureArgsList.useStartTLS();
|
boolean connectionTypeIsSet =
|
(
|
secureArgsList.alwaysSSL()
|
||
|
secureArgsList.useSSLArg.isPresent()
|
||
|
secureArgsList.useStartTLSArg.isPresent()
|
||
|
(
|
secureArgsList.useSSLArg.isValueSetByProperty()
|
&&
|
secureArgsList.useStartTLSArg.isValueSetByProperty()
|
)
|
);
|
if (app.isInteractive() && !connectionTypeIsSet)
|
{
|
checkHeadingDisplayed();
|
|
MenuBuilder<Integer> builder = new MenuBuilder<Integer>(app);
|
builder.setPrompt(INFO_LDAP_CONN_PROMPT_SECURITY_USE_SECURE_CTX.get());
|
|
Protocols defaultProtocol ;
|
if (secureConnection)
|
{
|
defaultProtocol = Protocols.SSL;
|
}
|
else
|
{
|
defaultProtocol = Protocols.LDAP;
|
}
|
for (Protocols p : Protocols.values())
|
{
|
if (secureConnection && p.equals(Protocols.LDAP) &&
|
!displayLdapIfSecureParameters)
|
{
|
continue ;
|
}
|
if (!canUseSSL && p.equals(Protocols.SSL))
|
{
|
continue;
|
}
|
if (!canUseStartTLS && p.equals(Protocols.START_TLS))
|
{
|
continue;
|
}
|
int i = builder.addNumberedOption(p.getMenuMessage(), MenuResult
|
.success(p.getChoice()));
|
if (p.equals(defaultProtocol))
|
{
|
builder.setDefault(
|
INFO_LDAP_CONN_PROMPT_SECURITY_PROTOCOL_DEFAULT_CHOICE
|
.get(i), MenuResult.success(p.getChoice()));
|
}
|
}
|
|
Menu<Integer> menu = builder.toMenu();
|
try
|
{
|
MenuResult<Integer> result = menu.run();
|
if (result.isSuccess())
|
{
|
if (result.getValue().equals(Protocols.SSL.getChoice()))
|
{
|
useSSL = true;
|
}
|
else if (result.getValue()
|
.equals(Protocols.START_TLS.getChoice()))
|
{
|
useStartTLS = true;
|
}
|
}
|
else
|
{
|
// Should never happen.
|
throw new RuntimeException();
|
}
|
}
|
catch (CLIException e)
|
{
|
throw new RuntimeException(e);
|
}
|
}
|
|
if (useSSL)
|
{
|
commandBuilder.addArgument(copySecureArgsList.useSSLArg);
|
}
|
else if (useStartTLS)
|
{
|
commandBuilder.addArgument(copySecureArgsList.useStartTLSArg);
|
}
|
|
// Get the LDAP port.
|
if (!useSSL)
|
{
|
portNumber = secureArgsList.portArg.getIntValue();
|
}
|
else
|
{
|
if (secureArgsList.portArg.isPresent())
|
{
|
portNumber = secureArgsList.portArg.getIntValue();
|
}
|
else
|
{
|
portNumber = secureArgsList.getPortFromConfig();
|
}
|
}
|
final int tmpPortNumber = portNumber;
|
if (app.isInteractive() && !secureArgsList.portArg.isPresent())
|
{
|
checkHeadingDisplayed();
|
|
ValidationCallback<Integer> callback = new ValidationCallback<Integer>()
|
{
|
|
public Integer validate(ConsoleApplication app, String input)
|
throws CLIException
|
{
|
String ninput = input.trim();
|
if (ninput.length() == 0)
|
{
|
return tmpPortNumber;
|
}
|
else
|
{
|
try
|
{
|
int i = Integer.parseInt(ninput);
|
if (i < 1 || i > 65535)
|
{
|
throw new NumberFormatException();
|
}
|
return i;
|
}
|
catch (NumberFormatException e)
|
{
|
// Try again...
|
app.println();
|
app.println(ERR_LDAP_CONN_BAD_PORT_NUMBER.get(ninput));
|
app.println();
|
return null;
|
}
|
}
|
}
|
|
};
|
|
try
|
{
|
app.println();
|
LocalizableMessage askPortNumber = null;
|
if (secureArgsList.alwaysSSL()) {
|
askPortNumber = INFO_ADMIN_CONN_PROMPT_PORT_NUMBER.get(portNumber);
|
} else {
|
askPortNumber = INFO_LDAP_CONN_PROMPT_PORT_NUMBER.get(portNumber);
|
}
|
portNumber = app.readValidatedInput(askPortNumber, callback);
|
}
|
catch (CLIException e)
|
{
|
throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
|
}
|
}
|
|
copySecureArgsList.portArg.clearValues();
|
copySecureArgsList.portArg.addValue(String.valueOf(portNumber));
|
commandBuilder.addArgument(copySecureArgsList.portArg);
|
|
// Handle certificate
|
if ((useSSL || useStartTLS) && (trustManager == null))
|
{
|
initializeTrustManager();
|
}
|
|
// Get the LDAP bind credentials.
|
bindDN = secureArgsList.bindDnArg.getValue();
|
adminUID = secureArgsList.adminUidArg.getValue();
|
final boolean useAdmin = secureArgsList.useAdminUID();
|
if (useAdmin && secureArgsList.adminUidArg.isPresent())
|
{
|
providedAdminUID = adminUID;
|
}
|
else
|
{
|
providedAdminUID = null;
|
}
|
if ((!useAdmin || useAdminOrBindDn) &&
|
secureArgsList.bindDnArg.isPresent())
|
{
|
providedBindDN = bindDN;
|
}
|
else
|
{
|
providedBindDN = null;
|
}
|
boolean argIsPresent = (providedAdminUID != null) ||
|
(providedBindDN != null);
|
final String tmpBindDN = bindDN;
|
final String tmpAdminUID = adminUID;
|
if (keyManager == null)
|
{
|
if (app.isInteractive() && !argIsPresent)
|
{
|
checkHeadingDisplayed();
|
|
ValidationCallback<String> callback = new ValidationCallback<String>()
|
{
|
|
public String validate(ConsoleApplication app, String input)
|
throws CLIException
|
{
|
String ninput = input.trim();
|
if (ninput.length() == 0)
|
{
|
if (useAdmin)
|
{
|
return tmpAdminUID;
|
}
|
else
|
{
|
return tmpBindDN;
|
}
|
}
|
else
|
{
|
return ninput;
|
}
|
}
|
|
};
|
|
try
|
{
|
app.println();
|
if (useAdminOrBindDn)
|
{
|
String def = (adminUID != null) ? adminUID : bindDN;
|
String v = app.readValidatedInput(
|
INFO_LDAP_CONN_GLOBAL_ADMINISTRATOR_OR_BINDDN_PROMPT.get(def),
|
callback);
|
if (Utils.isDn(v))
|
{
|
bindDN = v;
|
providedBindDN = v;
|
adminUID = null;
|
providedAdminUID = null;
|
}
|
else
|
{
|
bindDN = null;
|
providedBindDN = null;
|
adminUID = v;
|
providedAdminUID = v;
|
}
|
}
|
else if (useAdmin)
|
{
|
adminUID = app.readValidatedInput(
|
INFO_LDAP_CONN_PROMPT_ADMINISTRATOR_UID.get(adminUID),
|
callback);
|
providedAdminUID = adminUID;
|
}
|
else
|
{
|
bindDN = app.readValidatedInput(INFO_LDAP_CONN_PROMPT_BIND_DN
|
.get(bindDN), callback);
|
providedBindDN = bindDN;
|
}
|
}
|
catch (CLIException e)
|
{
|
throw ArgumentExceptionFactory
|
.unableToReadConnectionParameters(e);
|
}
|
}
|
if (useAdminOrBindDn)
|
{
|
boolean addAdmin = providedAdminUID != null;
|
boolean addBindDN = providedBindDN != null;
|
if (!addAdmin && !addBindDN)
|
{
|
addAdmin = getAdministratorUID() != null;
|
addBindDN = getBindDN() != null;
|
}
|
if (addAdmin)
|
{
|
copySecureArgsList.adminUidArg.clearValues();
|
copySecureArgsList.adminUidArg.addValue(getAdministratorUID());
|
commandBuilder.addArgument(copySecureArgsList.adminUidArg);
|
}
|
else if (addBindDN)
|
{
|
copySecureArgsList.bindDnArg.clearValues();
|
copySecureArgsList.bindDnArg.addValue(getBindDN());
|
commandBuilder.addArgument(copySecureArgsList.bindDnArg);
|
}
|
}
|
else if (useAdmin)
|
{
|
copySecureArgsList.adminUidArg.clearValues();
|
copySecureArgsList.adminUidArg.addValue(getAdministratorUID());
|
commandBuilder.addArgument(copySecureArgsList.adminUidArg);
|
}
|
else
|
{
|
copySecureArgsList.bindDnArg.clearValues();
|
copySecureArgsList.bindDnArg.addValue(getBindDN());
|
commandBuilder.addArgument(copySecureArgsList.bindDnArg);
|
}
|
}
|
else
|
{
|
bindDN = null;
|
adminUID = null;
|
}
|
|
boolean addedPasswordFileArgument = false;
|
bindPassword = secureArgsList.bindPasswordArg.getValue();
|
if (keyManager == null)
|
{
|
if (secureArgsList.bindPasswordFileArg.isPresent())
|
{
|
// Read from file if it exists.
|
bindPassword = secureArgsList.bindPasswordFileArg.getValue();
|
|
if (bindPassword == null)
|
{
|
if (useAdmin)
|
{
|
throw ArgumentExceptionFactory.missingBindPassword(adminUID);
|
}
|
else
|
{
|
throw ArgumentExceptionFactory.missingBindPassword(bindDN);
|
}
|
}
|
copySecureArgsList.bindPasswordFileArg.clearValues();
|
copySecureArgsList.bindPasswordFileArg.getNameToValueMap().putAll(
|
secureArgsList.bindPasswordFileArg.getNameToValueMap());
|
commandBuilder.addArgument(copySecureArgsList.bindPasswordFileArg);
|
addedPasswordFileArgument = true;
|
}
|
else if (bindPassword == null || bindPassword.equals("-"))
|
{
|
// Read the password from the stdin.
|
if (!app.isInteractive())
|
{
|
throw ArgumentExceptionFactory
|
.unableToReadBindPasswordInteractively();
|
}
|
|
checkHeadingDisplayed();
|
|
try
|
{
|
app.println();
|
LocalizableMessage prompt;
|
if (providedAdminUID != null)
|
{
|
prompt = INFO_LDAPAUTH_PASSWORD_PROMPT.get(providedAdminUID);
|
}
|
else if (providedBindDN != null)
|
{
|
prompt = INFO_LDAPAUTH_PASSWORD_PROMPT.get(providedBindDN);
|
}
|
else if (bindDN != null)
|
{
|
prompt = INFO_LDAPAUTH_PASSWORD_PROMPT.get(bindDN);
|
}
|
else
|
{
|
prompt = INFO_LDAPAUTH_PASSWORD_PROMPT.get(adminUID);
|
}
|
bindPassword = app.readPassword(prompt);
|
}
|
catch (Exception e)
|
{
|
throw ArgumentExceptionFactory
|
.unableToReadConnectionParameters(e);
|
}
|
}
|
copySecureArgsList.bindPasswordArg.clearValues();
|
copySecureArgsList.bindPasswordArg.addValue(bindPassword);
|
if (!addedPasswordFileArgument)
|
{
|
commandBuilder.addObfuscatedArgument(
|
copySecureArgsList.bindPasswordArg);
|
}
|
}
|
connectTimeout = secureArgsList.connectTimeoutArg.getIntValue();
|
}
|
|
/**
|
* Get the trust manager.
|
*
|
* @return The trust manager based on CLI args on interactive prompt.
|
* @throws ArgumentException If an error occurs when getting args values.
|
*/
|
private ApplicationTrustManager getTrustManagerInternal()
|
throws ArgumentException
|
{
|
// Remove these arguments since this method might be called several times.
|
commandBuilder.removeArgument(copySecureArgsList.trustAllArg);
|
commandBuilder.removeArgument(copySecureArgsList.trustStorePathArg);
|
commandBuilder.removeArgument(copySecureArgsList.trustStorePasswordArg);
|
commandBuilder.removeArgument(copySecureArgsList.trustStorePasswordFileArg);
|
|
// If we have the trustALL flag, don't do anything
|
// just return null
|
if (secureArgsList.trustAllArg.isPresent())
|
{
|
commandBuilder.addArgument(copySecureArgsList.trustAllArg);
|
return null;
|
}
|
|
// Check if some trust manager info are set
|
boolean weDontKnowTheTrustMethod =
|
!( secureArgsList.trustAllArg.isPresent()
|
||
|
secureArgsList.trustStorePathArg.isPresent()
|
||
|
secureArgsList.trustStorePasswordArg.isPresent()
|
||
|
secureArgsList.trustStorePasswordFileArg.isPresent()
|
);
|
boolean askForTrustStore = false;
|
|
trustAll = secureArgsList.trustAllArg.isPresent();
|
|
// Try to use the local instance trust store, to avoid certificate
|
// validation when both the CLI and the server are in the same instance.
|
if (weDontKnowTheTrustMethod) {
|
if (addLocalTrustStore()) {
|
weDontKnowTheTrustMethod = false;
|
|
}
|
}
|
|
if (app.isInteractive() && weDontKnowTheTrustMethod)
|
{
|
checkHeadingDisplayed();
|
|
app.println();
|
MenuBuilder<Integer> builder = new MenuBuilder<Integer>(app);
|
builder.setPrompt(INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_METHOD.get());
|
|
TrustMethod defaultTrustMethod = TrustMethod.DISPLAY_CERTIFICATE;
|
for (TrustMethod t : TrustMethod.values())
|
{
|
int i = builder.addNumberedOption(t.getMenuMessage(), MenuResult
|
.success(t.getChoice()));
|
if (t.equals(defaultTrustMethod))
|
{
|
builder.setDefault(
|
INFO_LDAP_CONN_PROMPT_SECURITY_PROTOCOL_DEFAULT_CHOICE
|
.get(new Integer(i)), MenuResult.success(t.getChoice()));
|
}
|
}
|
|
Menu<Integer> menu = builder.toMenu();
|
trustStoreInMemory = false;
|
try
|
{
|
MenuResult<Integer> result = menu.run();
|
if (result.isSuccess())
|
{
|
if (result.getValue().equals(TrustMethod.TRUSTALL.getChoice()))
|
{
|
commandBuilder.addArgument(copySecureArgsList.trustAllArg);
|
trustAll = true;
|
// If we have the trustALL flag, don't do anything
|
// just return null
|
return null;
|
}
|
else if (result.getValue().equals(
|
TrustMethod.TRUSTSTORE.getChoice()))
|
{
|
// We have to ask for trust store info
|
askForTrustStore = true;
|
}
|
else if (result.getValue().equals(
|
TrustMethod.DISPLAY_CERTIFICATE.getChoice()))
|
{
|
// The certificate will be displayed to the user
|
askForTrustStore = false;
|
trustStoreInMemory = true;
|
|
// There is no direct equivalent for this option, so propose the
|
// trust all option as command-line argument.
|
commandBuilder.addArgument(copySecureArgsList.trustAllArg);
|
}
|
else
|
{
|
// Should never happen.
|
throw new RuntimeException();
|
}
|
}
|
else
|
{
|
// Should never happen.
|
throw new RuntimeException();
|
}
|
}
|
catch (CLIException e)
|
{
|
throw new RuntimeException(e);
|
|
}
|
}
|
|
// If we do not trust all server certificates, we have to get info
|
// about trust store. First get the trust store path.
|
truststorePath = secureArgsList.trustStorePathArg.getValue();
|
|
if (app.isInteractive() && !secureArgsList.trustStorePathArg.isPresent()
|
&& askForTrustStore)
|
{
|
checkHeadingDisplayed();
|
|
ValidationCallback<String> callback = new ValidationCallback<String>()
|
{
|
public String validate(ConsoleApplication app, String input)
|
throws CLIException
|
{
|
String ninput = input.trim();
|
if (ninput.length() == 0)
|
{
|
app.println();
|
app.println(ERR_LDAP_CONN_PROMPT_SECURITY_INVALID_FILE_PATH
|
.get());
|
app.println();
|
return null;
|
}
|
File f = new File(ninput);
|
if (f.exists() && f.canRead() && !f.isDirectory())
|
{
|
return ninput;
|
}
|
else
|
{
|
app.println();
|
app.println(ERR_LDAP_CONN_PROMPT_SECURITY_INVALID_FILE_PATH
|
.get());
|
app.println();
|
return null;
|
}
|
}
|
};
|
|
try
|
{
|
app.println();
|
truststorePath = app.readValidatedInput(
|
INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE_PATH.get(), callback);
|
}
|
catch (CLIException e)
|
{
|
throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
|
}
|
}
|
|
if (truststorePath != null)
|
{
|
copySecureArgsList.trustStorePathArg.clearValues();
|
copySecureArgsList.trustStorePathArg.addValue(truststorePath);
|
commandBuilder.addArgument(copySecureArgsList.trustStorePathArg);
|
}
|
|
// Then the truststore password.
|
// As the most common case is to have no password for truststore,
|
// we don't ask it in the interactive mode.
|
truststorePassword = secureArgsList.trustStorePasswordArg
|
.getValue();
|
|
if (secureArgsList.trustStorePasswordFileArg.isPresent())
|
{
|
// Read from file if it exists.
|
truststorePassword = secureArgsList.trustStorePasswordFileArg
|
.getValue();
|
}
|
if ((truststorePassword != null) && (truststorePassword.equals("-")))
|
{
|
// Read the password from the stdin.
|
if (!app.isInteractive())
|
{
|
truststorePassword = null;
|
}
|
else
|
{
|
checkHeadingDisplayed();
|
|
try
|
{
|
app.println();
|
LocalizableMessage prompt = INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE_PASSWORD
|
.get(truststorePath);
|
truststorePassword = app.readPassword(prompt);
|
}
|
catch (Exception e)
|
{
|
throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
|
}
|
}
|
}
|
|
// We've got all the information to get the truststore manager
|
try
|
{
|
truststore = KeyStore.getInstance(KeyStore.getDefaultType());
|
if (truststorePath != null)
|
{
|
FileInputStream fos = new FileInputStream(truststorePath);
|
if (truststorePassword != null)
|
{
|
truststore.load(fos, truststorePassword.toCharArray());
|
}
|
else
|
{
|
truststore.load(fos, null);
|
}
|
fos.close();
|
}
|
else
|
{
|
truststore.load(null, null);
|
}
|
|
if (secureArgsList.trustStorePasswordFileArg.isPresent() &&
|
(truststorePath != null))
|
{
|
copySecureArgsList.trustStorePasswordFileArg.clearValues();
|
copySecureArgsList.trustStorePasswordFileArg.getNameToValueMap().putAll(
|
secureArgsList.trustStorePasswordFileArg.getNameToValueMap());
|
commandBuilder.addArgument(
|
copySecureArgsList.trustStorePasswordFileArg);
|
}
|
else if ((truststorePassword != null) && (truststorePath != null))
|
{
|
// Only add the trust store password if there is one AND if the user
|
// specified a trust store path.
|
copySecureArgsList.trustStorePasswordArg.clearValues();
|
copySecureArgsList.trustStorePasswordArg.addValue(truststorePassword);
|
commandBuilder.addObfuscatedArgument(
|
copySecureArgsList.trustStorePasswordArg);
|
}
|
|
return new ApplicationTrustManager(truststore);
|
}
|
catch (Exception e)
|
{
|
throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
|
}
|
}
|
|
/**
|
* Get the key manager.
|
*
|
* @return The key manager based on CLI args on interactive prompt.
|
* @throws ArgumentException If an error occurs when getting args values.
|
*/
|
private KeyManager getKeyManagerInternal()
|
throws ArgumentException
|
{
|
// Remove these arguments since this method might be called several times.
|
commandBuilder.removeArgument(copySecureArgsList.certNicknameArg);
|
commandBuilder.removeArgument(copySecureArgsList.keyStorePathArg);
|
commandBuilder.removeArgument(copySecureArgsList.keyStorePasswordArg);
|
commandBuilder.removeArgument(copySecureArgsList.keyStorePasswordFileArg);
|
|
// Do we need client side authentication ?
|
// If one of the client side authentication args is set, we assume
|
// that we
|
// need client side authentication.
|
boolean weDontKnowIfWeNeedKeystore = !(secureArgsList.keyStorePathArg
|
.isPresent()
|
|| secureArgsList.keyStorePasswordArg.isPresent()
|
|| secureArgsList.keyStorePasswordFileArg.isPresent()
|
|| secureArgsList.certNicknameArg
|
.isPresent());
|
|
// We don't have specific key manager parameter.
|
// We assume that no client side authentication is required
|
// Client side authentication is not the common use case. As a
|
// consequence, interactive mode doesn't add an extra question
|
// which will be in most cases useless.
|
if (weDontKnowIfWeNeedKeystore)
|
{
|
return null;
|
}
|
|
// Get info about keystore. First get the keystore path.
|
keystorePath = secureArgsList.keyStorePathArg.getValue();
|
if (app.isInteractive() && !secureArgsList.keyStorePathArg.isPresent())
|
{
|
checkHeadingDisplayed();
|
|
ValidationCallback<String> callback = new ValidationCallback<String>()
|
{
|
public String validate(ConsoleApplication app, String input)
|
throws CLIException
|
{
|
String ninput = input.trim();
|
if (ninput.length() == 0)
|
{
|
return ninput;
|
}
|
File f = new File(ninput);
|
if (f.exists() && f.canRead() && !f.isDirectory())
|
{
|
return ninput;
|
}
|
else
|
{
|
app.println();
|
app.println(ERR_LDAP_CONN_PROMPT_SECURITY_INVALID_FILE_PATH
|
.get());
|
app.println();
|
return null;
|
}
|
}
|
};
|
|
try
|
{
|
app.println();
|
keystorePath = app.readValidatedInput(
|
INFO_LDAP_CONN_PROMPT_SECURITY_KEYSTORE_PATH.get(), callback);
|
}
|
catch (CLIException e)
|
{
|
throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
|
}
|
}
|
|
if (keystorePath != null)
|
{
|
copySecureArgsList.keyStorePathArg.clearValues();
|
copySecureArgsList.keyStorePathArg.addValue(keystorePath);
|
commandBuilder.addArgument(copySecureArgsList.keyStorePathArg);
|
}
|
else
|
{
|
// KeystorePath is null. Either it's unspecified or there's a pb
|
// We should throw an exception here, anyway since code below will
|
// anyway
|
throw ArgumentExceptionFactory
|
.incompatiblePropertyModification("null keystorePath");
|
}
|
|
// Then the keystore password.
|
keystorePassword = secureArgsList.keyStorePasswordArg.getValue();
|
|
if (secureArgsList.keyStorePasswordFileArg.isPresent())
|
{
|
// Read from file if it exists.
|
keystorePassword = secureArgsList.keyStorePasswordFileArg.getValue();
|
|
if (keystorePassword == null)
|
{
|
throw ArgumentExceptionFactory.missingBindPassword(keystorePassword);
|
}
|
}
|
else if (keystorePassword == null || keystorePassword.equals("-"))
|
{
|
// Read the password from the stdin.
|
if (!app.isInteractive())
|
{
|
throw ArgumentExceptionFactory
|
.unableToReadBindPasswordInteractively();
|
}
|
|
checkHeadingDisplayed();
|
|
try
|
{
|
app.println();
|
LocalizableMessage prompt = INFO_LDAP_CONN_PROMPT_SECURITY_KEYSTORE_PASSWORD
|
.get(keystorePath);
|
keystorePassword = app.readPassword(prompt);
|
}
|
catch (Exception e)
|
{
|
throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
|
}
|
}
|
|
// finally the certificate name, if needed.
|
KeyStore keystore = null;
|
Enumeration<String> aliasesEnum = null;
|
try
|
{
|
FileInputStream fos = new FileInputStream(keystorePath);
|
keystore = KeyStore.getInstance(KeyStore.getDefaultType());
|
keystore.load(fos, keystorePassword.toCharArray());
|
fos.close();
|
aliasesEnum = keystore.aliases();
|
}
|
catch (Exception e)
|
{
|
throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
|
}
|
|
certifNickname = secureArgsList.certNicknameArg.getValue();
|
if (app.isInteractive() && !secureArgsList.certNicknameArg.isPresent()
|
&& aliasesEnum.hasMoreElements())
|
{
|
checkHeadingDisplayed();
|
|
try
|
{
|
MenuBuilder<String> builder = new MenuBuilder<String>(app);
|
builder.setPrompt(INFO_LDAP_CONN_PROMPT_SECURITY_CERTIFICATE_ALIASES
|
.get());
|
int certificateNumber = 0;
|
for (; aliasesEnum.hasMoreElements();)
|
{
|
String alias = aliasesEnum.nextElement();
|
if (keystore.isKeyEntry(alias))
|
{
|
X509Certificate certif = (X509Certificate) keystore
|
.getCertificate(alias);
|
certificateNumber++;
|
builder.addNumberedOption(
|
INFO_LDAP_CONN_PROMPT_SECURITY_CERTIFICATE_ALIAS.get(alias,
|
certif.getSubjectDN().getName()), MenuResult
|
.success(alias));
|
}
|
}
|
|
if (certificateNumber > 1)
|
{
|
app.println();
|
Menu<String> menu = builder.toMenu();
|
MenuResult<String> result = menu.run();
|
if (result.isSuccess())
|
{
|
certifNickname = result.getValue();
|
}
|
else
|
{
|
// Should never happen.
|
throw new RuntimeException();
|
}
|
}
|
else
|
{
|
certifNickname = null;
|
}
|
}
|
catch (KeyStoreException e)
|
{
|
throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
|
}
|
catch (CLIException e)
|
{
|
throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
|
}
|
}
|
|
// We'we got all the information to get the keys manager
|
ApplicationKeyManager akm = new ApplicationKeyManager(keystore,
|
keystorePassword.toCharArray());
|
|
|
if (secureArgsList.keyStorePasswordFileArg.isPresent())
|
{
|
copySecureArgsList.keyStorePasswordFileArg.clearValues();
|
copySecureArgsList.keyStorePasswordFileArg.getNameToValueMap().putAll(
|
secureArgsList.keyStorePasswordFileArg.getNameToValueMap());
|
commandBuilder.addArgument(
|
copySecureArgsList.keyStorePasswordFileArg);
|
}
|
else if (keystorePassword != null)
|
{
|
copySecureArgsList.keyStorePasswordArg.clearValues();
|
copySecureArgsList.keyStorePasswordArg.addValue(keystorePassword);
|
commandBuilder.addObfuscatedArgument(
|
copySecureArgsList.keyStorePasswordArg);
|
}
|
|
if (certifNickname != null)
|
{
|
copySecureArgsList.certNicknameArg.clearValues();
|
copySecureArgsList.certNicknameArg.addValue(certifNickname);
|
}
|
|
if (certifNickname != null)
|
{
|
return new SelectableCertificateKeyManager(akm, certifNickname);
|
}
|
else
|
{
|
return akm;
|
}
|
}
|
|
/**
|
* Indicates whether or not a connection should use SSL based on
|
* this interaction.
|
*
|
* @return boolean where true means use SSL
|
*/
|
public boolean useSSL() {
|
return useSSL;
|
}
|
|
/**
|
* Indicates whether or not a connection should use StartTLS based on
|
* this interaction.
|
*
|
* @return boolean where true means use StartTLS
|
*/
|
public boolean useStartTLS() {
|
return useStartTLS;
|
}
|
|
/**
|
* Gets the host name that should be used for connections based on
|
* this interaction.
|
*
|
* @return host name for connections
|
*/
|
public String getHostName() {
|
return hostName;
|
}
|
|
/**
|
* Gets the port number name that should be used for connections based on
|
* this interaction.
|
*
|
* @return port number for connections
|
*/
|
public int getPortNumber() {
|
return portNumber;
|
}
|
|
/**
|
* Sets the port number name that should be used for connections based on
|
* this interaction.
|
*
|
* @param portNumber port number for connections
|
*/
|
public void setPortNumber(int portNumber) {
|
this.portNumber = portNumber;
|
}
|
|
/**
|
* Gets the bind DN name that should be used for connections based on
|
* this interaction.
|
*
|
* @return bind DN for connections
|
*/
|
public String getBindDN() {
|
String dn;
|
if (useAdminOrBindDn)
|
{
|
if (providedBindDN != null)
|
{
|
dn = providedBindDN;
|
}
|
else if (providedAdminUID != null)
|
{
|
dn = ADSContext.getAdministratorDN(providedAdminUID);
|
}
|
else if (this.bindDN != null)
|
{
|
dn = this.bindDN;
|
}
|
else if (this.adminUID != null)
|
{
|
dn = ADSContext.getAdministratorDN(this.adminUID);
|
}
|
else
|
{
|
dn = null;
|
}
|
}
|
else if (secureArgsList.useAdminUID())
|
{
|
dn = ADSContext.getAdministratorDN(this.adminUID);
|
}
|
else
|
{
|
dn = this.bindDN;
|
}
|
return dn;
|
}
|
|
/**
|
* Gets the administrator UID name that should be used for connections based
|
* on this interaction.
|
*
|
* @return administrator UID for connections
|
*/
|
public String getAdministratorUID() {
|
return this.adminUID;
|
}
|
|
/**
|
* Gets the bind password that should be used for connections based on
|
* this interaction.
|
*
|
* @return bind password for connections
|
*/
|
public String getBindPassword() {
|
return this.bindPassword;
|
}
|
|
/**
|
* Gets the trust manager that should be used for connections based on
|
* this interaction.
|
*
|
* @return trust manager for connections
|
*/
|
public ApplicationTrustManager getTrustManager() {
|
return this.trustManager;
|
}
|
|
/**
|
* Gets the key store that should be used for connections based on
|
* this interaction.
|
*
|
* @return key store for connections
|
*/
|
public KeyStore getKeyStore() {
|
return this.truststore;
|
}
|
|
/**
|
* Gets the key manager that should be used for connections based on
|
* this interaction.
|
*
|
* @return key manager for connections
|
*/
|
public KeyManager getKeyManager() {
|
return this.keyManager;
|
}
|
|
/**
|
* Indicate if the trust store is in memory.
|
*
|
* @return true if the trust store is in memory.
|
*/
|
public boolean isTrustStoreInMemory() {
|
return this.trustStoreInMemory;
|
}
|
|
/**
|
* Indicate if all certificates must be accepted.
|
*
|
* @return true all certificates must be accepted.
|
*/
|
public boolean isTrustAll() {
|
return this.trustAll;
|
}
|
|
/**
|
* Returns the timeout to be used to connect with the server.
|
* @return the timeout to be used to connect with the server.
|
*/
|
public int getConnectTimeout()
|
{
|
return connectTimeout;
|
}
|
|
/**
|
* Indicate if the certificate chain can be trusted.
|
*
|
* @param chain The certificate chain to validate
|
* @return true if the server certificate is trusted.
|
*/
|
public boolean checkServerCertificate(X509Certificate[] chain)
|
{
|
return checkServerCertificate(chain, null, null);
|
}
|
|
/**
|
* Indicate if the certificate chain can be trusted.
|
*
|
* @param chain The certificate chain to validate
|
* @param authType the authentication type.
|
* @param host the host we tried to connect and that presented the
|
* certificate.
|
* @return true if the server certificate is trusted.
|
*/
|
public boolean checkServerCertificate(X509Certificate[] chain,
|
String authType, String host)
|
{
|
if (trustManager == null)
|
{
|
try
|
{
|
initializeTrustManager();
|
}
|
catch (ArgumentException ae)
|
{
|
// Should not occur
|
throw new RuntimeException(ae);
|
}
|
}
|
app.println();
|
app.println(INFO_LDAP_CONN_PROMPT_SECURITY_SERVER_CERTIFICATE.get());
|
app.println();
|
for (int i = 0; i < chain.length; i++)
|
{
|
// Certificate DN
|
app.println(INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_USER_DN.get(
|
chain[i].getSubjectDN().toString()));
|
|
// certificate validity
|
app.println(
|
INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_VALIDITY.get(
|
chain[i].getNotBefore().toString(),
|
chain[i].getNotAfter().toString()));
|
|
// certificate Issuer
|
app.println(
|
INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_ISSUER.get(
|
chain[i].getIssuerDN().toString()));
|
|
if (i+1 <chain.length)
|
{
|
app.println();
|
app.println();
|
}
|
}
|
MenuBuilder<Integer> builder = new MenuBuilder<Integer>(app);
|
builder.setPrompt(INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION.get());
|
|
TrustOption defaultTrustMethod = TrustOption.SESSION ;
|
for (TrustOption t : TrustOption.values())
|
{
|
int i = builder.addNumberedOption(t.getMenuMessage(), MenuResult
|
.success(t.getChoice()));
|
if (t.equals(defaultTrustMethod))
|
{
|
builder.setDefault(
|
INFO_LDAP_CONN_PROMPT_SECURITY_PROTOCOL_DEFAULT_CHOICE
|
.get(new Integer(i)), MenuResult.success(t.getChoice()));
|
}
|
}
|
|
app.println();
|
app.println();
|
|
Menu<Integer> menu = builder.toMenu();
|
while (true)
|
{
|
try
|
{
|
MenuResult<Integer> result = menu.run();
|
if (result.isSuccess())
|
{
|
if (result.getValue().equals(TrustOption.UNTRUSTED.getChoice()))
|
{
|
return false;
|
}
|
|
if ((result.getValue().equals(TrustOption.CERTIFICATE_DETAILS
|
.getChoice())))
|
{
|
for (int i = 0; i < chain.length; i++)
|
{
|
app.println();
|
app.println(INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE.get(chain[i]));
|
}
|
continue;
|
}
|
|
// We should add it in the memory truststore
|
for (int i = 0; i < chain.length; i++)
|
{
|
String alias = chain[i].getSubjectDN().getName();
|
try
|
{
|
truststore.setCertificateEntry(alias, chain[i]);
|
}
|
catch (KeyStoreException e1)
|
{
|
// What else should we do?
|
return false;
|
}
|
}
|
|
// Update the trust manager
|
if (trustManager == null)
|
{
|
trustManager = new ApplicationTrustManager(truststore);
|
}
|
if ((authType != null) && (host != null))
|
{
|
// Update the trust manager with the new certificate
|
trustManager.acceptCertificate(chain, authType, host);
|
}
|
else
|
{
|
// Do a full reset of the contents of the keystore.
|
trustManager = new ApplicationTrustManager(truststore);
|
}
|
if (result.getValue().equals(TrustOption.PERMAMENT.getChoice()))
|
{
|
ValidationCallback<String> callback =
|
new ValidationCallback<String>()
|
{
|
public String validate(ConsoleApplication app, String input)
|
throws CLIException
|
{
|
String ninput = input.trim();
|
if (ninput.length() == 0)
|
{
|
app.println();
|
app.println(ERR_LDAP_CONN_PROMPT_SECURITY_INVALID_FILE_PATH
|
.get());
|
app.println();
|
return null;
|
}
|
File f = new File(ninput);
|
if (!f.isDirectory())
|
{
|
return ninput;
|
}
|
else
|
{
|
app.println();
|
app.println(ERR_LDAP_CONN_PROMPT_SECURITY_INVALID_FILE_PATH
|
.get());
|
app.println();
|
return null;
|
}
|
}
|
};
|
|
String truststorePath;
|
try
|
{
|
app.println();
|
truststorePath = app.readValidatedInput(
|
INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE_PATH.get(),
|
callback);
|
}
|
catch (CLIException e)
|
{
|
return true;
|
}
|
|
// Read the password from the stdin.
|
String truststorePassword;
|
try
|
{
|
app.println();
|
LocalizableMessage prompt = INFO_LDAP_CONN_PROMPT_SECURITY_KEYSTORE_PASSWORD
|
.get(truststorePath);
|
truststorePassword = app.readPassword(prompt);
|
}
|
catch (Exception e)
|
{
|
return true;
|
}
|
try
|
{
|
KeyStore ts = KeyStore.getInstance("JKS");
|
FileInputStream fis;
|
try
|
{
|
fis = new FileInputStream(truststorePath);
|
}
|
catch (FileNotFoundException e)
|
{
|
fis = null;
|
}
|
ts.load(fis, truststorePassword.toCharArray());
|
if (fis != null)
|
{
|
fis.close();
|
}
|
for (int i = 0; i < chain.length; i++)
|
{
|
String alias = chain[i].getSubjectDN().getName();
|
ts.setCertificateEntry(alias, chain[i]);
|
}
|
FileOutputStream fos = new FileOutputStream(truststorePath);
|
ts.store(fos, truststorePassword.toCharArray());
|
if (fos != null)
|
{
|
fos.close();
|
}
|
}
|
catch (Exception e)
|
{
|
return true;
|
}
|
}
|
return true;
|
}
|
else
|
{
|
// Should never happen.
|
throw new RuntimeException();
|
}
|
}
|
catch (CLIException cliE)
|
{
|
throw new RuntimeException(cliE);
|
}
|
}
|
}
|
|
/**
|
* Populates a set of LDAP options with state from this interaction.
|
*
|
* @param options existing set of options; may be null in which case this
|
* method will create a new set of <code>LDAPConnectionOptions</code>
|
* to be returned
|
* @return used during this interaction
|
* @throws SSLConnectionException if this interaction has specified the use
|
* of SSL and there is a problem initializing the SSL connection
|
* factory
|
*/
|
public LDAPConnectionOptions populateLDAPOptions(
|
LDAPConnectionOptions options)
|
throws SSLConnectionException
|
{
|
if (options == null) {
|
options = new LDAPConnectionOptions();
|
}
|
if (this.useSSL) {
|
options.setUseSSL(true);
|
SSLConnectionFactory sslConnectionFactory = new SSLConnectionFactory();
|
sslConnectionFactory.init(getTrustManager() == null, keystorePath,
|
keystorePassword, certifNickname,
|
truststorePath, truststorePassword);
|
options.setSSLConnectionFactory(sslConnectionFactory);
|
} else {
|
options.setUseSSL(false);
|
}
|
options.setStartTLS(this.useStartTLS);
|
return options;
|
}
|
|
/**
|
* Prompts the user to accept the certificate.
|
* @param t the throwable that was generated because the certificate was
|
* not trusted.
|
* @param usedTrustManager the trustManager used when trying to establish the
|
* connection.
|
* @param usedUrl the LDAP URL used to connect to the server.
|
* @param displayErrorMessage whether to display an error message before
|
* asking to accept the certificate or not.
|
* @param logger the Logger used to log messages.
|
* @return <CODE>true</CODE> if the user accepted the certificate and
|
* <CODE>false</CODE> otherwise.
|
*/
|
public boolean promptForCertificateConfirmation(Throwable t,
|
ApplicationTrustManager usedTrustManager, String usedUrl,
|
boolean displayErrorMessage, LocalizedLogger logger)
|
{
|
boolean returnValue = false;
|
ApplicationTrustManager.Cause cause;
|
if (usedTrustManager != null)
|
{
|
cause = usedTrustManager.getLastRefusedCause();
|
}
|
else
|
{
|
cause = null;
|
}
|
if (logger != null)
|
{
|
logger.debug(LocalizableMessage.raw("Certificate exception cause: "+cause));
|
}
|
UserDataCertificateException.Type excType = null;
|
if (cause == ApplicationTrustManager.Cause.NOT_TRUSTED)
|
{
|
excType = UserDataCertificateException.Type.NOT_TRUSTED;
|
}
|
else if (cause ==
|
ApplicationTrustManager.Cause.HOST_NAME_MISMATCH)
|
{
|
excType = UserDataCertificateException.Type.HOST_NAME_MISMATCH;
|
}
|
else
|
{
|
LocalizableMessage msg = Utils.getThrowableMsg(INFO_ERROR_CONNECTING_TO_LOCAL.get(),
|
t);
|
app.println(msg);
|
}
|
|
if (excType != null)
|
{
|
String h;
|
int p;
|
try
|
{
|
URI uri = new URI(usedUrl);
|
h = uri.getHost();
|
p = uri.getPort();
|
}
|
catch (Throwable t1)
|
{
|
if (logger != null)
|
{
|
logger.warn(LocalizableMessage.raw("Error parsing ldap url of ldap url.", t1));
|
}
|
h = INFO_NOT_AVAILABLE_LABEL.get().toString();
|
p = -1;
|
}
|
|
|
|
UserDataCertificateException udce =
|
new UserDataCertificateException(Step.REPLICATION_OPTIONS,
|
INFO_CERTIFICATE_EXCEPTION.get(h, String.valueOf(p)), t, h, p,
|
usedTrustManager.getLastRefusedChain(),
|
usedTrustManager.getLastRefusedAuthType(), excType);
|
|
LocalizableMessage msg;
|
if (udce.getType() == UserDataCertificateException.Type.NOT_TRUSTED)
|
{
|
msg = INFO_CERTIFICATE_NOT_TRUSTED_TEXT_CLI.get(
|
udce.getHost(), String.valueOf(udce.getPort()));
|
}
|
else
|
{
|
msg = INFO_CERTIFICATE_NAME_MISMATCH_TEXT_CLI.get(
|
udce.getHost(), String.valueOf(udce.getPort()),
|
udce.getHost(),
|
udce.getHost(), String.valueOf(udce.getPort()));
|
}
|
if (displayErrorMessage)
|
{
|
app.println(msg);
|
}
|
X509Certificate[] chain = udce.getChain();
|
String authType = udce.getAuthType();
|
String host = udce.getHost();
|
if (logger != null)
|
{
|
if (chain == null)
|
{
|
logger.warn(LocalizableMessage.raw(
|
"The chain is null for the UserDataCertificateException"));
|
}
|
if (authType == null)
|
{
|
logger.warn(LocalizableMessage.raw(
|
"The auth type is null for the UserDataCertificateException"));
|
}
|
if (host == null)
|
{
|
logger.warn(LocalizableMessage.raw(
|
"The host is null for the UserDataCertificateException"));
|
}
|
}
|
if (chain != null)
|
{
|
returnValue = checkServerCertificate(chain, authType, host);
|
}
|
}
|
return returnValue;
|
}
|
|
/**
|
* Sets the heading that is displayed in interactive mode.
|
* @param heading the heading that is displayed in interactive mode.
|
*/
|
public void setHeadingMessage(LocalizableMessage heading)
|
{
|
this.heading = heading;
|
}
|
|
/**
|
* Returns the command builder with the equivalent arguments on the
|
* non-interactive mode.
|
* @return the command builder with the equivalent arguments on the
|
* non-interactive mode.
|
*/
|
public CommandBuilder getCommandBuilder()
|
{
|
return commandBuilder;
|
}
|
|
/**
|
* Displays the heading if it was not displayed before.
|
*
|
*/
|
private void checkHeadingDisplayed()
|
{
|
if (!isHeadingDisplayed)
|
{
|
app.println();
|
app.println();
|
app.println(heading);
|
isHeadingDisplayed = true;
|
}
|
}
|
|
/**
|
* Tells whether during interaction we can ask for both the DN or the admin
|
* UID.
|
* @return <CODE>true</CODE> if during interaction we can ask for both the DN
|
* and the admin UID and <CODE>false</CODE> otherwise.
|
*/
|
public boolean isUseAdminOrBindDn()
|
{
|
return useAdminOrBindDn;
|
}
|
|
/**
|
* Tells whether we can ask during interaction for both the DN and the admin
|
* UID or not.
|
* @param useAdminOrBindDn whether we can ask for both the DN and the admin UID
|
* during interaction or not.
|
*/
|
public void setUseAdminOrBindDn(boolean useAdminOrBindDn)
|
{
|
this.useAdminOrBindDn = useAdminOrBindDn;
|
}
|
|
/**
|
* Tells whether we propose LDAP as protocol even if the user provided security
|
* parameters. This is required in command-lines that access multiple servers
|
* (like dsreplication).
|
* @param displayLdapIfSecureParameters whether propose LDAP as protocol even
|
* if the user provided security parameters or not.
|
*/
|
public void setDisplayLdapIfSecureParameters(
|
boolean displayLdapIfSecureParameters)
|
{
|
this.displayLdapIfSecureParameters = displayLdapIfSecureParameters;
|
}
|
|
/**
|
* Resets the heading displayed flag, so that next time we call run the heading
|
* is displayed.
|
*/
|
public void resetHeadingDisplayed()
|
{
|
isHeadingDisplayed = false;
|
}
|
|
/**
|
* Resets the trust manager, so that next time we call the run() method
|
* the trust manager takes into account the local trust store.
|
*/
|
public void resetTrustManager()
|
{
|
trustManager = null;
|
}
|
|
/**
|
* Forces the initialization of the trust manager with the arguments provided
|
* by the user.
|
* @throws ArgumentException if there is an error with the arguments provided
|
* by the user.
|
*/
|
public void initializeTrustManagerIfRequired() throws ArgumentException
|
{
|
if (!trustManagerInitialized)
|
{
|
initializeTrustManager();
|
}
|
}
|
|
/**
|
* Initializes the global arguments in the parser with the provided values.
|
* This is useful when we want to call LDAPConnectionConsoleInteraction.run()
|
* with some default values.
|
* @param hostName the host name.
|
* @param port the port to connect to the server.
|
* @param adminUid the administrator UID.
|
* @param bindDn the bind DN to bind to the server.
|
* @param bindPwd the password to bind.
|
* @param pwdFile the Map containing the file and the password to bind.
|
*/
|
public void initializeGlobalArguments(String hostName, int port,
|
String adminUid, String bindDn,
|
String bindPwd, LinkedHashMap<String, String> pwdFile)
|
{
|
resetConnectionArguments();
|
if (hostName != null)
|
{
|
secureArgsList.hostNameArg.addValue(hostName);
|
secureArgsList.hostNameArg.setPresent(true);
|
}
|
// resetConnectionArguments does not clear the values for the port
|
secureArgsList.portArg.clearValues();
|
if (port != -1)
|
{
|
secureArgsList.portArg.addValue(String.valueOf(port));
|
secureArgsList.portArg.setPresent(true);
|
}
|
else
|
{
|
// This is done to be able to call IntegerArgument.getIntValue()
|
secureArgsList.portArg.addValue(secureArgsList.portArg.getDefaultValue());
|
}
|
secureArgsList.useSSLArg.setPresent(useSSL);
|
secureArgsList.useStartTLSArg.setPresent(useStartTLS);
|
if (adminUid != null)
|
{
|
secureArgsList.adminUidArg.addValue(adminUid);
|
secureArgsList.adminUidArg.setPresent(true);
|
}
|
if (bindDn != null)
|
{
|
secureArgsList.bindDnArg.addValue(bindDn);
|
secureArgsList.bindDnArg.setPresent(true);
|
}
|
if (pwdFile != null)
|
{
|
secureArgsList.bindPasswordFileArg.getNameToValueMap().putAll(pwdFile);
|
for (String value : pwdFile.keySet())
|
{
|
secureArgsList.bindPasswordFileArg.addValue(value);
|
}
|
secureArgsList.bindPasswordFileArg.setPresent(true);
|
}
|
else if (bindPwd != null)
|
{
|
secureArgsList.bindPasswordArg.addValue(bindPwd);
|
secureArgsList.bindPasswordArg.setPresent(true);
|
}
|
}
|
|
/**
|
* Resets the connection parameters for the LDAPConsoleInteraction object.
|
* The reset does not apply to the certificate parameters. This is called
|
* in order the LDAPConnectionConsoleInteraction object to ask for all this
|
* connection parameters next time we call
|
* LDAPConnectionConsoleInteraction.run().
|
*/
|
public void resetConnectionArguments()
|
{
|
secureArgsList.hostNameArg.clearValues();
|
secureArgsList.hostNameArg.setPresent(false);
|
secureArgsList.portArg.clearValues();
|
secureArgsList.portArg.setPresent(false);
|
// This is done to be able to call IntegerArgument.getIntValue()
|
secureArgsList.portArg.addValue(secureArgsList.portArg.getDefaultValue());
|
secureArgsList.bindDnArg.clearValues();
|
secureArgsList.bindDnArg.setPresent(false);
|
secureArgsList.bindPasswordArg.clearValues();
|
secureArgsList.bindPasswordArg.setPresent(false);
|
secureArgsList.bindPasswordFileArg.clearValues();
|
secureArgsList.bindPasswordFileArg.getNameToValueMap().clear();
|
secureArgsList.bindPasswordFileArg.setPresent(false);
|
secureArgsList.adminUidArg.clearValues();
|
secureArgsList.adminUidArg.setPresent(false);
|
}
|
|
private void initializeTrustManager() throws ArgumentException
|
{
|
// Get trust store info
|
trustManager = getTrustManagerInternal();
|
|
// Check if we need client side authentication
|
keyManager = getKeyManagerInternal();
|
|
trustManagerInitialized = true;
|
}
|
/**
|
* Returns the explicitly provided Admin UID from the user (interactively
|
* or through the argument).
|
* @return the explicitly provided Admin UID from the user (interactively
|
* or through the argument).
|
*/
|
public String getProvidedAdminUID()
|
{
|
return providedAdminUID;
|
}
|
|
/**
|
* Returns the explicitly provided bind DN from the user (interactively
|
* or through the argument).
|
* @return the explicitly provided bind DN from the user (interactively
|
* or through the argument).
|
*/
|
public String getProvidedBindDN()
|
{
|
return providedBindDN;
|
}
|
|
/**
|
* Add the TrustStore of the administration connector of the local instance.
|
*
|
* @return true if the local trust store has been added.
|
*/
|
private boolean addLocalTrustStore()
|
{
|
try {
|
// If remote host, return
|
if (!InetAddress.getLocalHost().getHostName().equals(hostName)) {
|
return false;
|
}
|
// check if we are in a local instance. Already checked the host,
|
// now check the port
|
if (secureArgsList.getAdminPortFromConfig() != portNumber) {
|
return false;
|
}
|
|
String truststoreFileAbsolute =
|
secureArgsList.getTruststoreFileFromConfig();
|
if (truststoreFileAbsolute != null)
|
{
|
secureArgsList.trustStorePathArg.addValue(truststoreFileAbsolute);
|
return true;
|
}
|
else
|
{
|
return false;
|
}
|
} catch (Exception ex) {
|
// do nothing
|
return false;
|
}
|
}
|
}
|