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 }