/* * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 * * * Portions Copyright 2007 Sun Microsystems, Inc. */ package org.opends.server.admin.client.cli; import static org.opends.server.admin.client.cli.DsFrameworkCliReturnCode.*; import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; import static org.opends.server.loggers.debug.DebugLogger.getTracer; import static org.opends.server.messages.MessageHandler.getMessage; import static org.opends.server.messages.ToolMessages.*; import static org.opends.server.tools.ToolConstants.*; import static org.opends.server.util.ServerConstants.MAX_LINE_WIDTH; import static org.opends.server.util.StaticUtils.wrapText; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; import javax.net.ssl.KeyManager; import org.opends.admin.ads.util.ApplicationKeyManager; import org.opends.admin.ads.util.ApplicationTrustManager; import org.opends.server.loggers.debug.DebugTracer; import org.opends.server.types.DebugLogLevel; import org.opends.server.util.PasswordReader; import org.opends.server.util.SelectableCertificateKeyManager; import org.opends.server.util.args.ArgumentException; import org.opends.server.util.args.BooleanArgument; import org.opends.server.util.args.FileBasedArgument; import org.opends.server.util.args.IntegerArgument; import org.opends.server.util.args.StringArgument; import org.opends.server.util.args.SubCommandArgumentParser; /** * This is a commodity class that can be used to check the arguments required * to stablish a secure connection in the command line. It can be used * to generate an ApplicationTrustManager object based on the options provided * by the user in the command line. * */ public abstract class SecureConnectionCliParser extends SubCommandArgumentParser { /** * The showUsage' global argument. */ protected BooleanArgument showUsageArg = null; /** * The 'hostName' global argument. */ protected StringArgument hostNameArg = null; /** * The 'port' global argument. */ protected IntegerArgument portArg = null; /** * The 'binDN' global argument. */ protected StringArgument bindDnArg = null; /** * The 'bindPasswordFile' global argument. */ protected FileBasedArgument bindPasswordFileArg = null; /** * The 'bindPassword' global argument. */ protected StringArgument bindPasswordArg = null; /** * The 'verbose' global argument. */ protected BooleanArgument verboseArg = null; /** * The 'trustAllArg' global argument. */ protected BooleanArgument trustAllArg = null; /** * The 'trustStore' global argument. */ protected StringArgument trustStorePathArg = null; /** * The 'trustStorePassword' global argument. */ protected StringArgument trustStorePasswordArg = null; /** * The 'trustStorePasswordFile' global argument. */ protected FileBasedArgument trustStorePasswordFileArg = null; /** * The 'keyStore' global argument. */ protected StringArgument keyStorePathArg = null; /** * The 'keyStorePassword' global argument. */ protected StringArgument keyStorePasswordArg = null; /** * The 'keyStorePasswordFile' global argument. */ protected FileBasedArgument keyStorePasswordFileArg = null; /** * The 'certNicknameArg' global argument. */ protected StringArgument certNicknameArg = null; /** * The 'useSSLArg' global argument. */ protected BooleanArgument useSSLArg = null; /** * The 'startTLSArg' global argument. */ protected BooleanArgument startTLSArg = null; /** * The tracer object for the debug logger. */ private static final DebugTracer TRACER = getTracer(); /** * End Of Line. */ protected static String EOL = System.getProperty("line.separator"); /** * The Logger. */ static private final Logger LOG = Logger.getLogger(SecureConnectionCliParser.class.getName()); /** * Creates a new instance of this subcommand argument parser with no * arguments. * * @param mainClassName * The fully-qualified name of the Java class that should * be invoked to launch the program with which this * argument parser is associated. * @param toolDescription * A human-readable description for the tool, which will be * included when displaying usage information. * @param longArgumentsCaseSensitive * Indicates whether subcommand and long argument names * should be treated in a case-sensitive manner. */ protected SecureConnectionCliParser(String mainClassName, String toolDescription, boolean longArgumentsCaseSensitive) { super(mainClassName, toolDescription, longArgumentsCaseSensitive); } /** * Get the bindDN which has to be used for the command. * * @return The bindDN specified by the command line argument, or the * default value, if not specified. */ public String getBindDN() { if (bindDnArg.isPresent()) { return bindDnArg.getValue(); } else { return bindDnArg.getDefaultValue(); } } /** * Get the password which has to be used for the command. * * @param dn * The user DN for which to password could be asked. * @param out * The input stream to used if we have to prompt to the * user. * @param err * The error stream to used if we have to prompt to the * user. * @return The password stored into the specified file on by the * command line argument, or prompts it if not specified. */ public String getBindPassword(String dn, OutputStream out, OutputStream err) { if (bindPasswordArg.isPresent()) { String bindPasswordValue = bindPasswordArg.getValue(); if(bindPasswordValue != null && bindPasswordValue.equals("-")) { // read the password from the stdin. try { out.write(getMessage(MSGID_LDAPAUTH_PASSWORD_PROMPT, dn).getBytes()); char[] pwChars = PasswordReader.readPassword(); bindPasswordValue = new String(pwChars); } catch(Exception ex) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, ex); } try { err.write(wrapText(ex.getMessage(), MAX_LINE_WIDTH).getBytes()); err.write(EOL.getBytes()); } catch (IOException e) { } return null; } } return bindPasswordValue; } else if (bindPasswordFileArg.isPresent()) { return bindPasswordFileArg.getValue(); } else { // read the password from the stdin. try { out.write(getMessage(MSGID_LDAPAUTH_PASSWORD_PROMPT, dn).getBytes()); char[] pwChars = PasswordReader.readPassword(); return new String(pwChars); } catch (Exception ex) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, ex); } try { err.write(wrapText(ex.getMessage(), MAX_LINE_WIDTH).getBytes()); err.write(EOL.getBytes()); } catch (IOException e) { } return null; } } } /** * Initialize Global option. * * @param outStream * The output stream used for the usage. * @throws ArgumentException * If there is a problem with any of the parameters used * to create this argument. */ protected void initializeGlobalOption(OutputStream outStream) throws ArgumentException { showUsageArg = new BooleanArgument("showUsage", OPTION_SHORT_HELP, OPTION_LONG_HELP, MSGID_DESCRIPTION_SHOWUSAGE); addGlobalArgument(showUsageArg); setUsageArgument(showUsageArg, outStream); useSSLArg = new BooleanArgument("useSSL", OPTION_SHORT_USE_SSL, OPTION_LONG_USE_SSL, MSGID_DESCRIPTION_USE_SSL); addGlobalArgument(useSSLArg); startTLSArg = new BooleanArgument("startTLS", OPTION_SHORT_START_TLS, OPTION_LONG_START_TLS, MSGID_DESCRIPTION_START_TLS); addGlobalArgument(startTLSArg); hostNameArg = new StringArgument("host", OPTION_SHORT_HOST, OPTION_LONG_HOST, false, false, true, OPTION_VALUE_HOST, "localhost", null, MSGID_DESCRIPTION_HOST); addGlobalArgument(hostNameArg); portArg = new IntegerArgument("port", OPTION_SHORT_PORT, OPTION_LONG_PORT, false, false, true, OPTION_VALUE_PORT, 389, null, MSGID_DESCRIPTION_PORT); addGlobalArgument(portArg); bindDnArg = new StringArgument("bindDN", OPTION_SHORT_BINDDN, OPTION_LONG_BINDDN, false, false, true, OPTION_VALUE_BINDDN, "cn=Directory Manager", null, MSGID_DESCRIPTION_BINDDN); addGlobalArgument(bindDnArg); bindPasswordArg = new StringArgument("bindPassword", OPTION_SHORT_BINDPWD, OPTION_LONG_BINDPWD, false, false, true, OPTION_VALUE_BINDPWD, null, null, MSGID_DESCRIPTION_BINDPASSWORD); addGlobalArgument(bindPasswordArg); bindPasswordFileArg = new FileBasedArgument("bindPasswordFile", OPTION_SHORT_BINDPWD_FILE, OPTION_LONG_BINDPWD_FILE, false, false, OPTION_VALUE_BINDPWD_FILE, null, null, MSGID_DESCRIPTION_BINDPASSWORDFILE); addGlobalArgument(bindPasswordFileArg); trustAllArg = new BooleanArgument("trustAll", 'X', "trustAll", MSGID_DESCRIPTION_TRUSTALL); addGlobalArgument(trustAllArg); trustStorePathArg = new StringArgument("trustStorePath", OPTION_SHORT_TRUSTSTOREPATH, OPTION_LONG_TRUSTSTOREPATH, false, false, true, OPTION_VALUE_TRUSTSTOREPATH, null, null, MSGID_DESCRIPTION_TRUSTSTOREPATH); addGlobalArgument(trustStorePathArg); trustStorePasswordArg = new StringArgument("trustStorePassword", null, OPTION_LONG_TRUSTSTORE_PWD, false, false, true, OPTION_VALUE_TRUSTSTORE_PWD, null, null, MSGID_DESCRIPTION_TRUSTSTOREPASSWORD); addGlobalArgument(trustStorePasswordArg); trustStorePasswordFileArg = new FileBasedArgument("truststorepasswordfile", OPTION_SHORT_TRUSTSTORE_PWD_FILE, OPTION_LONG_TRUSTSTORE_PWD_FILE, false, false, OPTION_VALUE_TRUSTSTORE_PWD_FILE, null, null, MSGID_DESCRIPTION_TRUSTSTOREPASSWORD_FILE); addGlobalArgument(trustStorePasswordFileArg); keyStorePathArg = new StringArgument("keyStorePath", OPTION_SHORT_KEYSTOREPATH, OPTION_LONG_KEYSTOREPATH, false, false, true, OPTION_VALUE_KEYSTOREPATH, null, null, MSGID_DESCRIPTION_KEYSTOREPATH); addGlobalArgument(keyStorePathArg); keyStorePasswordArg = new StringArgument("keyStorePassword", null, OPTION_LONG_KEYSTORE_PWD, false, false, true, OPTION_VALUE_KEYSTORE_PWD, null, null, MSGID_DESCRIPTION_KEYSTOREPASSWORD); addGlobalArgument(keyStorePasswordArg); keyStorePasswordFileArg = new FileBasedArgument("keystorepasswordfile", OPTION_SHORT_KEYSTORE_PWD_FILE, OPTION_LONG_KEYSTORE_PWD_FILE, false, false, OPTION_VALUE_KEYSTORE_PWD_FILE, null, null, MSGID_DESCRIPTION_KEYSTOREPASSWORD_FILE); addGlobalArgument(keyStorePasswordFileArg); certNicknameArg = new StringArgument("certnickname", 'N', "certNickname", false, false, true, "{nickname}", null, null, MSGID_DESCRIPTION_CERT_NICKNAME); addGlobalArgument(certNicknameArg); verboseArg = new BooleanArgument("verbose", 'v', "verbose", MSGID_DESCRIPTION_VERBOSE); addGlobalArgument(verboseArg); } /** * Get the host name which has to be used for the command. * * @return The host name specified by the command line argument, or * the default value, if not specified. */ public String getHostName() { if (hostNameArg.isPresent()) { return hostNameArg.getValue(); } else { return hostNameArg.getDefaultValue(); } } /** * Get the port which has to be used for the command. * * @return The port specified by the command line argument, or the * default value, if not specified. */ public String getPort() { if (portArg.isPresent()) { return portArg.getValue(); } else { return portArg.getDefaultValue(); } } /** * Indication if provided global options are validate. * * @param err the stream to be used to print error message. * @return return code. */ public int validateGlobalOption(PrintStream err) { ArrayList errors = new ArrayList(); // Couldn't have at the same time bindPassword and bindPasswordFile if(bindPasswordArg.isPresent() && bindPasswordFileArg.isPresent()) { int msgID = MSGID_TOOL_CONFLICTING_ARGS; String message = getMessage(msgID, bindPasswordArg.getLongIdentifier(), bindPasswordFileArg.getLongIdentifier()); errors.add(message); } // Couldn't have at the same time trustAll and // trustStore related arg if (trustAllArg.isPresent() && trustStorePathArg.isPresent()) { int msgID = MSGID_TOOL_CONFLICTING_ARGS; String message = getMessage(msgID, trustAllArg.getLongIdentifier(), trustStorePathArg.getLongIdentifier()); errors.add(message); } if (trustAllArg.isPresent() && trustStorePasswordArg.isPresent()) { int msgID = MSGID_TOOL_CONFLICTING_ARGS; String message = getMessage(msgID, trustAllArg.getLongIdentifier(), trustStorePasswordArg.getLongIdentifier()); errors.add(message); } if (trustAllArg.isPresent() && trustStorePasswordFileArg.isPresent()) { int msgID = MSGID_TOOL_CONFLICTING_ARGS; String message = getMessage(msgID, trustAllArg.getLongIdentifier(), trustStorePasswordFileArg.getLongIdentifier()); errors.add(message); } // Couldn't have at the same time trustStorePasswordArg and // trustStorePasswordFileArg if (trustStorePasswordArg.isPresent() && trustStorePasswordFileArg.isPresent()) { int msgID = MSGID_TOOL_CONFLICTING_ARGS; String message = getMessage(msgID, trustStorePasswordArg .getLongIdentifier(), trustStorePasswordFileArg.getLongIdentifier()); errors.add(message); } // Couldn't have at the same time startTLSArg and // useSSLArg if (startTLSArg.isPresent() && useSSLArg.isPresent()) { int msgID = MSGID_TOOL_CONFLICTING_ARGS; String message = getMessage(msgID, startTLSArg .getLongIdentifier(), useSSLArg.getLongIdentifier()); errors.add(message); } if (errors.size() > 0) { StringBuffer buf = new StringBuffer(); for (String error : errors) { if (buf.length() > 0) { buf.append(EOL); } buf.append(error); } err.println(wrapText(buf.toString(), MAX_LINE_WIDTH)); return CONFLICTING_ARGS.getReturnCode(); } return SUCCESSFUL_NOP.getReturnCode(); } /** * Indicate if the verbose mode is required. * * @return True if verbose mode is required */ public boolean isVerbose() { if (verboseArg.isPresent()) { return true; } else { return false ; } } /** * Indicate if the SSL mode is required. * * @return True if SSL mode is required */ public boolean useSSL() { if (useSSLArg.isPresent()) { return true; } else { return false ; } } /** * Indicate if the startTLS mode is required. * * @return True if startTLS mode is required */ public boolean startTLS() { if (startTLSArg.isPresent()) { return true; } else { return false ; } } /** * Handle TrustStore. * * @return The trustStore manager to be used for the command. */ public ApplicationTrustManager getTrustManager() { ApplicationTrustManager truststoreManager = null ; KeyStore truststore = null ; if (trustAllArg.isPresent()) { // Running a null TrustManager will force createLdapsContext and // createStartTLSContext to use a bindTrustManager. return null ; } else if (trustStorePathArg.isPresent()) { try { FileInputStream fos = new FileInputStream(trustStorePathArg.getValue()); String trustStorePasswordStringValue = null; char[] trustStorePasswordValue = null; if (trustStorePasswordArg.isPresent()) { trustStorePasswordStringValue = trustStorePasswordArg.getValue(); } else if (trustStorePasswordFileArg.isPresent()) { trustStorePasswordStringValue = trustStorePasswordFileArg.getValue(); } if (trustStorePasswordStringValue != null) { trustStorePasswordStringValue = System .getProperty("javax.net.ssl.trustStorePassword"); } if (trustStorePasswordStringValue != null) { trustStorePasswordValue = trustStorePasswordStringValue.toCharArray(); } truststore = KeyStore.getInstance(KeyStore.getDefaultType()); truststore.load(fos, trustStorePasswordValue); fos.close(); } catch (KeyStoreException e) { // Nothing to do: if this occurs we will systematically refuse the // certificates. Maybe we should avoid this and be strict, but we are // in a best effort mode. LOG.log(Level.WARNING, "Error with the truststore", e); } catch (NoSuchAlgorithmException e) { // Nothing to do: if this occurs we will systematically refuse the // certificates. Maybe we should avoid this and be strict, but we are // in a best effort mode. LOG.log(Level.WARNING, "Error with the truststore", e); } catch (CertificateException e) { // Nothing to do: if this occurs we will systematically refuse the // certificates. Maybe we should avoid this and be strict, but we are // in a best effort mode. LOG.log(Level.WARNING, "Error with the truststore", e); } catch (IOException e) { // Nothing to do: if this occurs we will systematically refuse the // certificates. Maybe we should avoid this and be strict, but we are // in a best effort mode. LOG.log(Level.WARNING, "Error with the truststore", e); } } truststoreManager = new ApplicationTrustManager(truststore); return truststoreManager; } /** * Handle KeyStore. * * @return The keyStore manager to be used for the command. */ public KeyManager getKeyManager() { KeyStore keyStore = null; String keyStorePasswordStringValue = null; char[] keyStorePasswordValue = null; if (keyStorePathArg.isPresent()) { try { FileInputStream fos = new FileInputStream(keyStorePathArg.getValue()); if (keyStorePasswordArg.isPresent()) { keyStorePasswordStringValue = keyStorePasswordArg.getValue(); } else if (keyStorePasswordFileArg.isPresent()) { keyStorePasswordStringValue = keyStorePasswordFileArg.getValue(); } if (keyStorePasswordStringValue != null) { keyStorePasswordValue = keyStorePasswordStringValue.toCharArray(); } keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(fos,keyStorePasswordValue); fos.close(); } catch (KeyStoreException e) { // Nothing to do: if this occurs we will systematically refuse // the // certificates. Maybe we should avoid this and be strict, but // we are in a best effort mode. LOG.log(Level.WARNING, "Error with the keystore", e); } catch (NoSuchAlgorithmException e) { // Nothing to do: if this occurs we will systematically refuse // the // certificates. Maybe we should avoid this and be strict, but // we are // in a best effort mode. LOG.log(Level.WARNING, "Error with the keystore", e); } catch (CertificateException e) { // Nothing to do: if this occurs we will systematically refuse // the // certificates. Maybe we should avoid this and be strict, but // we are // in a best effort mode. LOG.log(Level.WARNING, "Error with the keystore", e); } catch (IOException e) { // Nothing to do: if this occurs we will systematically refuse // the // certificates. Maybe we should avoid this and be strict, but // we are // in a best effort mode. LOG.log(Level.WARNING, "Error with the keystore", e); } char[] password = null; if (keyStorePasswordStringValue != null) { password = keyStorePasswordStringValue.toCharArray(); } ApplicationKeyManager akm = new ApplicationKeyManager(keyStore,password); if (certNicknameArg.isPresent()) { return new SelectableCertificateKeyManager(akm, certNicknameArg .getValue()); } else { return akm; } } else { return null; } } }