| opends/src/ads/org/opends/admin/ads/util/ApplicationTrustManager.java | ●●●●● patch | view | raw | blame | history | |
| opends/src/ads/org/opends/admin/ads/util/OpendsCertificationException.java | ●●●●● patch | view | raw | blame | history | |
| opends/src/messages/messages/utility.properties | ●●●●● patch | view | raw | blame | history | |
| opends/src/server/org/opends/server/tools/dsconfig/LDAPManagementContextFactory.java | ●●●●● patch | view | raw | blame | history | |
| opends/src/server/org/opends/server/util/cli/LDAPConnectionConsoleInteraction.java | ●●●●● patch | view | raw | blame | history |
opends/src/ads/org/opends/admin/ads/util/ApplicationTrustManager.java
@@ -163,7 +163,10 @@ lastRefusedChain = chain; lastRefusedAuthType = authType; lastRefusedCause = Cause.NOT_TRUSTED; throw ce; OpendsCertificationException e = new OpendsCertificationException( chain); e.initCause(ce); throw e; } if (!explicitlyAccepted) @@ -177,7 +180,10 @@ lastRefusedChain = chain; lastRefusedAuthType = authType; lastRefusedCause = Cause.HOST_NAME_MISMATCH; throw ce; OpendsCertificationException e = new OpendsCertificationException( chain); e.initCause(ce); throw e; } } } @@ -214,7 +220,9 @@ lastRefusedChain = chain; lastRefusedAuthType = authType; lastRefusedCause = Cause.NOT_TRUSTED; throw ce; OpendsCertificationException e = new OpendsCertificationException(chain); e.initCause(ce); throw e; } if (!explicitlyAccepted) @@ -228,7 +236,10 @@ lastRefusedChain = chain; lastRefusedAuthType = authType; lastRefusedCause = Cause.HOST_NAME_MISMATCH; throw ce; OpendsCertificationException e = new OpendsCertificationException( chain); e.initCause(ce); throw e; } } } @@ -334,8 +345,8 @@ } if (!found) { throw new CertificateException( "Certificate not in list of accepted certificates"); throw new OpendsCertificationException( "Certificate not in list of accepted certificates", chain); } } @@ -368,8 +379,10 @@ if (!matches) { throw new CertificateException("Hostname mismatch between host name "+ host+" and subject DN: "+chain[0].getSubjectX500Principal()); throw new OpendsCertificationException( "Hostname mismatch between host name " + host + " and subject DN: " + chain[0].getSubjectX500Principal(), chain); } } } opends/src/ads/org/opends/admin/ads/util/OpendsCertificationException.java
New file @@ -0,0 +1,96 @@ /* * 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.admin.ads.util; // // J2SE import java.security.cert.CertificateException; import java.security.cert.X509Certificate ; /** * When a remote client (dsconfig for instance) wants to establish a * remote connection with opends server through a secure connection, * and if the certificate is not known, the SSL handcheck fails and * this exception is thrown. This allows to get the certificate chain * which is unknown. */ public class OpendsCertificationException extends CertificateException { /** * The serial version UUID. */ private static final long serialVersionUID = 1151044344529478436L; // ------------------ // Private certificate chain // ------------------ private X509Certificate[] chain; // ------------------ // Constructor // ------------------ /** * Build a new OpendsCertificationException object. * * @param chain the certificate chain which is unknown and has caused * the SSL handcheck failure. */ public OpendsCertificationException(X509Certificate[] chain) { super(); this.chain = chain; } /** * Build a new OpendsCertificationException object. * * @param msg the detail message string of this exception. * * @param chain the certificate chain which is unknown and has caused * the SSL handcheck failure. */ public OpendsCertificationException(String msg, X509Certificate[] chain) { super(msg); this.chain = chain; } /** * Return the certificate chain which is unknown and has caused * the SSL handcheck failure. * * @return the certificate chain which is unknown and has caused * the SSL handcheck failure. */ public X509Certificate[] getChain() { return chain; } } opends/src/messages/messages/utility.properties
@@ -506,8 +506,8 @@ INFO_LDAP_CONN_PROMPT_SECURITY_LDAP_226=LDAP INFO_LDAP_CONN_PROMPT_SECURITY_USE_SSL_227=LDAP with SSL INFO_LDAP_CONN_PROMPT_SECURITY_USE_START_TSL_228=LDAP with StartTSL INFO_LDAP_CONN_PROMPT_SECURITY_USE_TRUST_ALL_229=Do you want to automatically \ trust the server certificate? INFO_LDAP_CONN_PROMPT_SECURITY_USE_TRUST_ALL_229=Automatically \ trust INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE_PATH_230=Truststore path: INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE_PASSWORD_231=Password for \ truststore '%s': @@ -531,3 +531,16 @@ INFO_LDAP_CONN_PROMPT_SECURITY_PROTOCOL_DEFAULT_CHOICE_243=%d SEVERE_ERR_LDAP_CONN_PROMPT_SECURITY_INVALID_FILE_PATH_244=The provided path \ is not valid INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_METHOD_245=How do you want to trust the server certificate? INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE_246=Use a truststore INFO_LDAP_CONN_PROMPT_SECURITY_MANUAL_CHECK_247=Manually validate INFO_LDAP_CONN_PROMPT_SECURITY_SERVER_CERTIFICATE_248=Server Certificate: INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_249=%s INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION_250=Do you trust this server certificate? INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION_NO_251=No INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION_SESSION_252=Yes, for this session only INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION_ALWAYS_253=Yes, also add it to a truststore INFO_LDAP_CONN_PROMPT_SECURITY_CERTIFICATE_DETAILS_254=View certificate details INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_USER_DN_255 =User DN : %s INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_VALIDITY_256=Validity : From '%s'%n To '%s' INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_ISSUER_257 =Issuer : %s opends/src/server/org/opends/server/tools/dsconfig/LDAPManagementContextFactory.java
@@ -26,8 +26,9 @@ */ package org.opends.server.tools.dsconfig; import org.opends.admin.ads.util.ConnectionUtils; import org.opends.admin.ads.util.OpendsCertificationException; import static org.opends.messages.DSConfigMessages.*; import org.opends.messages.Message; import org.opends.messages.MessageBuilder; @@ -101,38 +102,96 @@ { InitialLdapContext ctx; String ldapsUrl = "ldaps://" + hostName + ":" + portNumber; try while (true) { ctx = ConnectionUtils.createLdapsContext(ldapsUrl, bindDN, bindPassword, ConnectionUtils.getDefaultLDAPTimeout(), null, trustManager, keyManager); conn = JNDIDirContextAdaptor.adapt(ctx); } catch (NamingException e) { Message message = ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get( hostName, String.valueOf(portNumber)); throw new ClientException(LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message) ; try { ctx = ConnectionUtils.createLdapsContext(ldapsUrl, bindDN, bindPassword, ConnectionUtils.getDefaultLDAPTimeout(), null, trustManager, keyManager); conn = JNDIDirContextAdaptor.adapt(ctx); break; } catch (NamingException e) { if ( app.isInteractive() && ci.isTrustStoreInMemory()) { if ((e.getRootCause() != null) && (e.getRootCause().getCause() instanceof OpendsCertificationException)) { OpendsCertificationException oce = (OpendsCertificationException) e.getRootCause().getCause(); if (ci.checkServerCertificate(oce.getChain())) { // If the certificate is trusted, update the trust manager. trustManager = ci.getTrustManager(); // Try to connect again. continue ; } } else { Message message = ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get( hostName, String.valueOf(portNumber)); throw new ClientException( LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message); } } Message message = ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get( hostName, String.valueOf(portNumber)); throw new ClientException( LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message); } } } else if (ci.useStartTLS()) { InitialLdapContext ctx; String ldapUrl = "ldap://" + hostName + ":" + portNumber; try while (true) { ctx = ConnectionUtils.createStartTLSContext(ldapUrl, bindDN, bindPassword, ConnectionUtils.getDefaultLDAPTimeout(), null, trustManager, keyManager, null); conn = JNDIDirContextAdaptor.adapt(ctx); } catch (NamingException e) { Message message = ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get( hostName, String.valueOf(portNumber)); throw new ClientException(LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message) ; try { ctx = ConnectionUtils.createStartTLSContext(ldapUrl, bindDN, bindPassword, ConnectionUtils.getDefaultLDAPTimeout(), null, trustManager, keyManager, null); conn = JNDIDirContextAdaptor.adapt(ctx); break; } catch (NamingException e) { if ( app.isInteractive() && ci.isTrustStoreInMemory()) { if ((e.getRootCause() != null) && (e.getRootCause().getCause() instanceof OpendsCertificationException)) { OpendsCertificationException oce = (OpendsCertificationException) e.getRootCause().getCause(); if (ci.checkServerCertificate(oce.getChain())) { // If the certificate is trusted, update the trust manager. trustManager = ci.getTrustManager(); // Try to connect again. continue ; } } else { Message message = ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get( hostName, String.valueOf(portNumber)); throw new ClientException( LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message); } } Message message = ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get( hostName, String.valueOf(portNumber)); throw new ClientException( LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message); } } } else opends/src/server/org/opends/server/util/cli/LDAPConnectionConsoleInteraction.java
@@ -43,7 +43,11 @@ 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; /** * Supports interacting with a user through the command line to @@ -69,6 +73,12 @@ // the Console application private ConsoleApplication app; // Indicate if the truststore in in memory private boolean trustStoreInMemory = false; // The truststore to use for the SSL or STARTTLS connection private KeyStore truststore; /** * Enumeration description protocols for interactive CLI choices. */ @@ -118,6 +128,105 @@ } /** * 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 Message msg; /** * Private constructor. * * @param i * the menu return value. * @param s * the message message. */ private TrustMethod(int i, Message 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 Message 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 Message msg; /** * Private constructor. * * @param i * the menu return value. * @param s * the message message. */ private TrustOption(int i, Message 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 Message getMenuMessage() { return msg; } } /** * Constructs a parameterized instance. * * @param app console application @@ -480,8 +589,25 @@ private ApplicationTrustManager getTrustManagerInternal() throws ArgumentException { boolean trustAll = secureArgsList.trustAllArg.isPresent(); if (app.isInteractive() && !secureArgsList.trustAllArg.isPresent()) // If we have the trustALL flag, don't do anything // just return null if (secureArgsList.trustAllArg.isPresent()) { 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; if (app.isInteractive() && weDontKnowTheTrustMethod) { if (!isHeadingDisplayed) { @@ -491,29 +617,73 @@ isHeadingDisplayed = true; } 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 { app.println(); trustAll = app.confirmAction( INFO_LDAP_CONN_PROMPT_SECURITY_USE_TRUST_ALL.get(), false); MenuResult<Integer> result = menu.run(); if (result.isSuccess()) { if (result.getValue().equals(TrustMethod.TRUSTALL.getChoice())) { // 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 truststore info askForTrustStore = true; } else if (result.getValue().equals( TrustMethod.DISPLAY_CERTIFICATE.getChoice())) { // The certificate will be displayed to the user askForTrustStore = false; trustStoreInMemory = true; } else { // Should never happen. throw new RuntimeException(); } } else { // Should never happen. throw new RuntimeException(); } } catch (CLIException e) { // Should never happen. throw new RuntimeException(e); } } // Trust everything, so no trust manager if (trustAll) { return null; } } // If we not trust all server certificates, we have to get info // about truststore. First get the truststore path. String truststorePath = secureArgsList.trustStorePathArg.getValue(); if (app.isInteractive() && !secureArgsList.trustStorePathArg.isPresent()) if (app.isInteractive() && !secureArgsList.trustStorePathArg.isPresent() && askForTrustStore) { if (!isHeadingDisplayed) { @@ -610,17 +780,24 @@ // We'we got all the information to get the truststore manager try { FileInputStream fos = new FileInputStream(truststorePath); KeyStore truststore = KeyStore.getInstance(KeyStore.getDefaultType()); if (truststorePassword != null) truststore = KeyStore.getInstance(KeyStore.getDefaultType()); if (truststorePath != null) { truststore.load(fos, truststorePassword.toCharArray()); FileInputStream fos = new FileInputStream(truststorePath); if (truststorePassword != null) { truststore.load(fos, truststorePassword.toCharArray()); } else { truststore.load(fos, null); } fos.close(); } else { truststore.load(fos, null); truststore.load(null, null); } fos.close(); return new ApplicationTrustManager(truststore); } catch (Exception e) @@ -890,6 +1067,217 @@ return this.keyManager; } /** * Indicate if the truststore is in memory. * * @return true if the truststore is in memory. */ public boolean isTrustStoreInMemory() { return this.trustStoreInMemory; } /** * 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) { 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].toString())); } 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 should we do else? return false; } } // Update the trust manager 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(); Message 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); } } } }