001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2007-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.tools.dsconfig; 028 029 import org.opends.admin.ads.util.ApplicationTrustManager; 030 import org.opends.admin.ads.util.ConnectionUtils; 031 import org.opends.admin.ads.util.OpendsCertificateException; 032 033 import static org.opends.messages.DSConfigMessages.*; 034 import org.opends.messages.Message; 035 import org.opends.messages.MessageBuilder; 036 import org.opends.server.admin.client.AuthenticationException; 037 import org.opends.server.admin.client.AuthenticationNotSupportedException; 038 import org.opends.server.admin.client.CommunicationException; 039 import org.opends.server.admin.client.ManagementContext; 040 import org.opends.server.admin.client.cli.DsFrameworkCliReturnCode; 041 import org.opends.server.admin.client.cli.SecureConnectionCliArgs; 042 import org.opends.server.admin.client.ldap.JNDIDirContextAdaptor; 043 import org.opends.server.admin.client.ldap.LDAPConnection; 044 import org.opends.server.admin.client.ldap.LDAPManagementContext; 045 import org.opends.server.protocols.ldap.LDAPResultCode; 046 import org.opends.server.tools.ClientException; 047 import org.opends.server.util.args.Argument; 048 import org.opends.server.util.args.ArgumentException; 049 import org.opends.server.util.args.SubCommandArgumentParser; 050 import org.opends.server.util.cli.CommandBuilder; 051 import org.opends.server.util.cli.LDAPConnectionConsoleInteraction; 052 import org.opends.server.util.cli.ConsoleApplication; 053 054 import javax.naming.NamingException; 055 import javax.naming.ldap.InitialLdapContext; 056 import javax.net.ssl.KeyManager; 057 import javax.net.ssl.TrustManager; 058 import java.util.LinkedHashSet; 059 060 061 /** 062 * An LDAP management context factory. 063 */ 064 public final class LDAPManagementContextFactory implements 065 ManagementContextFactory { 066 067 // The SecureConnectionCliArgsList object. 068 private SecureConnectionCliArgs secureArgsList = null; 069 070 // The management context. 071 private ManagementContext context = null; 072 073 // The connection parameters command builder. 074 private CommandBuilder contextCommandBuilder; 075 076 /** 077 * Creates a new LDAP management context factory. 078 */ 079 public LDAPManagementContextFactory() { 080 // No implementation required. 081 } 082 083 /** 084 * {@inheritDoc} 085 */ 086 public ManagementContext getManagementContext(ConsoleApplication app) 087 throws ArgumentException, ClientException 088 { 089 // Lazily create the LDAP management context. 090 if (context == null) 091 { 092 LDAPConnectionConsoleInteraction ci = 093 new LDAPConnectionConsoleInteraction(app, secureArgsList); 094 ci.run(); 095 context = getManagementContext(app, ci); 096 contextCommandBuilder = ci.getCommandBuilder(); 097 } 098 return context; 099 } 100 101 /** 102 * {@inheritDoc} 103 */ 104 public void close() 105 { 106 if (context != null) 107 { 108 context.close(); 109 } 110 } 111 112 /** 113 * {@inheritDoc} 114 */ 115 public CommandBuilder getContextCommandBuilder() 116 { 117 return contextCommandBuilder; 118 } 119 120 /** 121 * Gets the management context which sub-commands should use in 122 * order to manage the directory server. Implementations can use the 123 * application instance for retrieving passwords interactively. 124 * 125 * @param app 126 * The application instance. 127 * @param ci the LDAPConsoleInteraction object to be used. The code assumes 128 * that the LDAPConsoleInteraction has already been run. 129 * @return Returns the management context which sub-commands should 130 * use in order to manage the directory server. 131 * @throws ArgumentException 132 * If a management context related argument could not be 133 * parsed successfully. 134 * @throws ClientException 135 * If the management context could not be created. 136 */ 137 public ManagementContext getManagementContext(ConsoleApplication app, 138 LDAPConnectionConsoleInteraction ci) 139 throws ArgumentException, ClientException 140 { 141 // Lazily create the LDAP management context. 142 if (context == null) 143 { 144 // Interact with the user though the console to get 145 // LDAP connection information 146 String hostName = ConnectionUtils.getHostNameForLdapUrl(ci.getHostName()); 147 Integer portNumber = ci.getPortNumber(); 148 String bindDN = ci.getBindDN(); 149 String bindPassword = ci.getBindPassword(); 150 TrustManager trustManager = ci.getTrustManager(); 151 KeyManager keyManager = ci.getKeyManager(); 152 153 // Do we have a secure connection ? 154 LDAPConnection conn ; 155 if (ci.useSSL()) 156 { 157 InitialLdapContext ctx; 158 String ldapsUrl = "ldaps://" + hostName + ":" + portNumber; 159 while (true) 160 { 161 try 162 { 163 ctx = ConnectionUtils.createLdapsContext(ldapsUrl, bindDN, 164 bindPassword, ConnectionUtils.getDefaultLDAPTimeout(), null, 165 trustManager, keyManager); 166 ctx.reconnect(null); 167 conn = JNDIDirContextAdaptor.adapt(ctx); 168 break; 169 } 170 catch (NamingException e) 171 { 172 if ( app.isInteractive() && ci.isTrustStoreInMemory()) 173 { 174 if ((e.getRootCause() != null) 175 && (e.getRootCause().getCause() 176 instanceof OpendsCertificateException)) 177 { 178 OpendsCertificateException oce = 179 (OpendsCertificateException) e.getRootCause().getCause(); 180 String authType = null; 181 if (trustManager instanceof ApplicationTrustManager) 182 { 183 ApplicationTrustManager appTrustManager = 184 (ApplicationTrustManager)trustManager; 185 authType = appTrustManager.getLastRefusedAuthType(); 186 } 187 if (ci.checkServerCertificate(oce.getChain(), authType, 188 hostName)) 189 { 190 // If the certificate is trusted, update the trust manager. 191 trustManager = ci.getTrustManager(); 192 193 // Try to connect again. 194 continue ; 195 } 196 } 197 else 198 { 199 Message message = ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get( 200 hostName, String.valueOf(portNumber)); 201 throw new ClientException( 202 LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message); 203 } 204 } 205 Message message = ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get( 206 hostName, String.valueOf(portNumber)); 207 throw new ClientException( 208 LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message); 209 } 210 } 211 } 212 else if (ci.useStartTLS()) 213 { 214 InitialLdapContext ctx; 215 String ldapUrl = "ldap://" + hostName + ":" + portNumber; 216 while (true) 217 { 218 try 219 { 220 ctx = ConnectionUtils.createStartTLSContext(ldapUrl, bindDN, 221 bindPassword, ConnectionUtils.getDefaultLDAPTimeout(), null, 222 trustManager, keyManager, null); 223 ctx.reconnect(null); 224 conn = JNDIDirContextAdaptor.adapt(ctx); 225 break; 226 } 227 catch (NamingException e) 228 { 229 if ( app.isInteractive() && ci.isTrustStoreInMemory()) 230 { 231 if ((e.getRootCause() != null) 232 && (e.getRootCause().getCause() 233 instanceof OpendsCertificateException)) 234 { 235 String authType = null; 236 if (trustManager instanceof ApplicationTrustManager) 237 { 238 ApplicationTrustManager appTrustManager = 239 (ApplicationTrustManager)trustManager; 240 authType = appTrustManager.getLastRefusedAuthType(); 241 } 242 OpendsCertificateException oce = 243 (OpendsCertificateException) e.getRootCause().getCause(); 244 if (ci.checkServerCertificate(oce.getChain(), authType, 245 hostName)) 246 { 247 // If the certificate is trusted, update the trust manager. 248 trustManager = ci.getTrustManager(); 249 250 // Try to connect again. 251 continue ; 252 } 253 } 254 else 255 { 256 Message message = ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get( 257 hostName, String.valueOf(portNumber)); 258 throw new ClientException( 259 LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message); 260 } 261 } 262 Message message = ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get( 263 hostName, String.valueOf(portNumber)); 264 throw new ClientException( 265 LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, message); 266 } 267 } 268 } 269 else 270 { 271 // Create the management context. 272 try 273 { 274 conn = JNDIDirContextAdaptor.simpleBind(hostName, portNumber, 275 bindDN, bindPassword); 276 } 277 catch (AuthenticationNotSupportedException e) 278 { 279 Message message = ERR_DSCFG_ERROR_LDAP_SIMPLE_BIND_NOT_SUPPORTED 280 .get(); 281 throw new ClientException(LDAPResultCode.AUTH_METHOD_NOT_SUPPORTED, 282 message); 283 } 284 catch (AuthenticationException e) 285 { 286 Message message = ERR_DSCFG_ERROR_LDAP_SIMPLE_BIND_FAILED 287 .get(bindDN); 288 throw new ClientException(LDAPResultCode.INVALID_CREDENTIALS, 289 message); 290 } 291 catch (CommunicationException e) 292 { 293 Message message = ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get( 294 hostName, String.valueOf(portNumber)); 295 throw new ClientException(LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, 296 message); 297 } 298 } 299 context = LDAPManagementContext.createFromContext(conn); 300 } 301 return context; 302 } 303 304 305 306 /** 307 * {@inheritDoc} 308 */ 309 public void registerGlobalArguments(SubCommandArgumentParser parser) 310 throws ArgumentException { 311 // Create the global arguments. 312 secureArgsList = new SecureConnectionCliArgs(); 313 LinkedHashSet<Argument> args = secureArgsList.createGlobalArguments(); 314 315 316 // Register the global arguments. 317 for (Argument arg : args) 318 { 319 parser.addGlobalArgument(arg); 320 } 321 322 } 323 324 325 326 /** 327 * {@inheritDoc} 328 */ 329 public void validateGlobalArguments() throws ArgumentException { 330 // Make sure that the user didn't specify any conflicting 331 // arguments. 332 MessageBuilder buf = new MessageBuilder(); 333 int v = secureArgsList.validateGlobalOptions(buf); 334 if (v != DsFrameworkCliReturnCode.SUCCESSFUL_NOP.getReturnCode()) 335 { 336 throw new ArgumentException(buf.toMessage()); 337 } 338 } 339 340 }