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 2006-2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.protocols.jmx;
028
029 import java.io.IOException;
030 import java.rmi.RemoteException;
031 import java.rmi.registry.LocateRegistry;
032 import java.rmi.registry.Registry;
033 import java.util.HashMap;
034
035 import javax.net.ssl.KeyManager;
036 import javax.net.ssl.SSLSocketFactory;
037 import javax.net.ssl.SSLContext;
038
039 import javax.management.MBeanServer;
040 import javax.management.ObjectName;
041 import javax.management.remote.JMXConnectorServer;
042 import javax.management.remote.JMXServiceURL;
043 import javax.management.remote.rmi.RMIConnectorServer;
044
045 import org.opends.server.api.KeyManagerProvider;
046 import org.opends.server.config.JMXMBean;
047 import org.opends.server.core.DirectoryServer;
048 import org.opends.server.extensions.NullKeyManagerProvider;
049
050 import static org.opends.server.loggers.debug.DebugLogger.*;
051 import org.opends.server.loggers.debug.DebugTracer;
052 import org.opends.server.types.DebugLogLevel;
053
054 import org.opends.server.util.SelectableCertificateKeyManager;
055
056 /**
057 * The RMI connector class starts and stops the JMX RMI connector server.
058 * There are 2 different connector servers
059 * <ul>
060 * <li> the RMI Client connector server, supporting TLS-encrypted.
061 * communication, server authentication by certificate and client
062 * authentication by providing appropriate LDAP credentials through
063 * SASL/PLAIN.
064 * <li> the RMI client connector server, supporting TLS-encrypted
065 * communication, server authentication by certificate, client
066 * authentication by certificate and identity assertion through SASL/PLAIN.
067 * </ul>
068 * <p>
069 * Each connector is registered into the JMX MBean server.
070 */
071 public class RmiConnector
072 {
073 /**
074 * The tracer object for the debug logger.
075 */
076 private static final DebugTracer TRACER = getTracer();
077
078
079 /**
080 * The MBean server used to handle JMX interaction.
081 */
082 private MBeanServer mbs = null;
083
084
085 /**
086 * the client address to connect to the common registry. Note that a
087 * remote client should use the correct IP address.
088 */
089 private String registryClientAddress = "0.0.0.0";
090
091 /**
092 * The associated JMX Connection Handler.
093 */
094 private JmxConnectionHandler jmxConnectionHandler;
095
096 /**
097 * The name of the JMX connector with no SSL client
098 * authentication.
099 */
100 private String jmxRmiConnectorNoClientCertificateName;
101
102 /**
103 * The name of the JMX connector with SSL client
104 * authentication.
105 */
106 private String jmxRmiConnectorClientCertificateName;
107
108 /**
109 * The reference to the JMX connector client with no SSL client
110 * authentication.
111 */
112 protected JMXConnectorServer jmxRmiConnectorNoClientCertificate;
113
114 /**
115 * The reference to the JMX connector client with SSL client
116 * authentication.
117 */
118 private JMXConnectorServer jmxRmiConnectorClientCertificate;
119
120 /**
121 * The reference to authenticator.
122 */
123 private RmiAuthenticator rmiAuthenticator;
124
125 /**
126 * The reference to the created RMI registry.
127 */
128 private Registry registry = null;
129
130 /**
131 * The Underlying Socket factory.
132 */
133 private OpendsRmiServerSocketFactory rmiSsf;
134
135 /**
136 * The RMI protocol verison used by this connector.
137 */
138 private String rmiVersion;
139
140 // ===================================================================
141 // CONSTRUCTOR
142 // ===================================================================
143 /**
144 * Create a new instance of RmiConnector .
145 *
146 * @param mbs
147 * The MBean server.
148 * @param jmxConnectionHandler
149 * The associated JMX Connection Handler
150 */
151 public RmiConnector(MBeanServer mbs,
152 JmxConnectionHandler jmxConnectionHandler)
153 {
154 this.mbs = mbs;
155 this.jmxConnectionHandler = jmxConnectionHandler;
156
157 String baseName = JMXMBean.getJmxName(jmxConnectionHandler
158 .getComponentEntryDN());
159
160 jmxRmiConnectorNoClientCertificateName = baseName + ","
161 + "Type=jmxRmiConnectorNoClientCertificateName";
162
163 jmxRmiConnectorClientCertificateName = baseName + ","
164 + "Type=jmxRmiConnectorClientCertificateName";
165 }
166
167 // ===================================================================
168 // Initialization
169 // ===================================================================
170 /**
171 * Activates the RMI Connectors. It starts the secure connectors.
172 */
173 public void initialize()
174 {
175 try
176 {
177 //
178 // start the common registry
179 startCommonRegistry();
180
181 //
182 // start the RMI connector (SSL + server authentication)
183 startConnectorNoClientCertificate();
184
185 //
186 // start the RMI connector (SSL + server authentication +
187 // client authentication + identity given part SASL/PLAIN)
188 // TODO startConnectorClientCertificate(clientConnection);
189
190 }
191 catch (Exception e)
192 {
193 if (debugEnabled())
194 {
195 TRACER.debugCaught(DebugLogLevel.ERROR, e);
196 }
197
198 throw new RuntimeException("Error while starting the RMI module : "
199 + e.getMessage());
200 }
201
202 if (debugEnabled())
203 {
204 TRACER.debugVerbose("RMI module started");
205 }
206 }
207
208 /**
209 * Starts the common RMI registry. In order to provide RMI stub for
210 * remote client, the JMX RMI connector should be register into an RMI
211 * registry. Each server will maintain its own private one.
212 *
213 * @throws Exception
214 * if the registry cannot be started
215 */
216 private void startCommonRegistry() throws Exception
217 {
218 int registryPort = jmxConnectionHandler.getListenPort();
219
220 //
221 // create our local RMI registry if it does not exist already
222 if (debugEnabled())
223 {
224 TRACER.debugVerbose("start or reach an RMI registry on port %d",
225 registryPort);
226 }
227 try
228 {
229 //
230 // TODO Not yet implemented: If the host has several interfaces
231 if (registry == null)
232 {
233 rmiSsf = new OpendsRmiServerSocketFactory();
234 registry = LocateRegistry.createRegistry(registryPort, null, rmiSsf);
235 }
236 }
237 catch (RemoteException re)
238 {
239 //
240 // is the registry already created ?
241 if (debugEnabled())
242 {
243 TRACER.debugWarning("cannot create the RMI registry -> already done ?");
244 }
245 try
246 {
247 //
248 // get a 'remote' reference on the registry
249 Registry reg = LocateRegistry.getRegistry(registryPort);
250
251 //
252 // 'ping' the registry
253 reg.list();
254 registry = reg;
255 }
256 catch (Exception e)
257 {
258 if (debugEnabled())
259 {
260 //
261 // no 'valid' registry found on the specified port
262 TRACER.debugError("exception thrown while pinging the RMI registry");
263
264 //
265 // throw the original exception
266 TRACER.debugCaught(DebugLogLevel.ERROR, re);
267 }
268 throw re;
269 }
270
271 //
272 // here the registry is ok even though
273 // it was not created by this call
274 if (debugEnabled())
275 {
276 TRACER.debugWarning("RMI was registry already started");
277 }
278 }
279 }
280
281 /**
282 * Starts a secure RMI connector, with a client that doesn't have to
283 * present a certificate, on the local mbean server.
284 * This method assumes that the common registry was successfully
285 * started.
286 * <p>
287 * If the connector is already started, this method simply returns
288 * without doing anything.
289 *
290 * @throws Exception
291 * if an error occurs
292 */
293 private void startConnectorNoClientCertificate() throws Exception
294 {
295 try
296 {
297 //
298 // Environment map
299 HashMap<String, Object> env = new HashMap<String, Object>();
300
301 // ---------------------
302 // init an ssl context
303 // ---------------------
304 DirectoryRMIClientSocketFactory rmiClientSockeyFactory = null;
305 DirectoryRMIServerSocketFactory rmiServerSockeyFactory = null;
306 if (jmxConnectionHandler.isUseSSL())
307 {
308 if (debugEnabled())
309 {
310 TRACER.debugVerbose("SSL connection");
311 }
312
313 // ---------------------
314 // SERVER SIDE
315 // ---------------------
316 //
317 // Get a Server socket factory
318 KeyManager[] keyManagers;
319 KeyManagerProvider provider = DirectoryServer
320 .getKeyManagerProvider(jmxConnectionHandler
321 .getKeyManagerProviderDN());
322 if (provider == null) {
323 keyManagers = new NullKeyManagerProvider().getKeyManagers();
324 }
325 else
326 {
327 String nickname = jmxConnectionHandler.getSSLServerCertNickname();
328 if (nickname == null)
329 {
330 keyManagers = provider.getKeyManagers();
331 }
332 else
333 {
334 keyManagers =
335 SelectableCertificateKeyManager.wrap(provider.getKeyManagers(),
336 nickname);
337 }
338 }
339
340 SSLContext ctx = SSLContext.getInstance("TLSv1");
341 ctx.init(
342 keyManagers,
343 null,
344 null);
345 SSLSocketFactory ssf = ctx.getSocketFactory();
346
347 //
348 // set the Server socket factory in the JMX map
349 rmiServerSockeyFactory = new DirectoryRMIServerSocketFactory(ssf,
350 false);
351 env.put(
352 "jmx.remote.rmi.server.socket.factory",
353 rmiServerSockeyFactory);
354
355 // ---------------------
356 // CLIENT SIDE : Rmi stores the client stub in the
357 // registry
358 // ---------------------
359 // Set the Client socket factory in the JMX map
360 rmiClientSockeyFactory = new DirectoryRMIClientSocketFactory(false);
361 env.put(
362 "jmx.remote.rmi.client.socket.factory",
363 rmiClientSockeyFactory);
364 }
365 else
366 {
367 if (debugEnabled())
368 {
369 TRACER.debugVerbose("UNSECURE CONNECTION");
370 }
371 }
372
373 //
374 // specify the rmi JMX authenticator to be used
375 if (debugEnabled())
376 {
377 TRACER.debugVerbose("Add RmiAuthenticator into JMX map");
378 }
379 rmiAuthenticator = new RmiAuthenticator(jmxConnectionHandler);
380
381 env.put(JMXConnectorServer.AUTHENTICATOR, rmiAuthenticator);
382
383 //
384 // Create the JMX Service URL
385 String uri = "org.opends.server.protocols.jmx.client-unknown";
386 String serviceUrl = "service:jmx:rmi:///jndi/rmi://"
387 + registryClientAddress + ":" + jmxConnectionHandler.getListenPort()
388 + "/" + uri;
389 JMXServiceURL url = new JMXServiceURL(serviceUrl);
390
391 //
392 // Create and start the connector
393 if (debugEnabled())
394 {
395 TRACER.debugVerbose("Create and start the JMX RMI connector");
396 }
397 OpendsRMIJRMPServerImpl opendsRmiConnectorServer =
398 new OpendsRMIJRMPServerImpl(
399 0, rmiClientSockeyFactory, rmiServerSockeyFactory, env);
400 jmxRmiConnectorNoClientCertificate = new RMIConnectorServer(url, env,
401 opendsRmiConnectorServer, mbs);
402 jmxRmiConnectorNoClientCertificate.start();
403
404 //
405 // Register the connector into the RMI registry
406 // TODO Should we do that?
407 ObjectName name = new ObjectName(jmxRmiConnectorNoClientCertificateName);
408 mbs.registerMBean(jmxRmiConnectorNoClientCertificate, name);
409 rmiVersion = opendsRmiConnectorServer.getVersion();
410
411 if (debugEnabled())
412 {
413 TRACER.debugVerbose("JMX RMI connector Started");
414 }
415
416 }
417 catch (Exception e)
418 {
419 if (debugEnabled())
420 {
421 TRACER.debugCaught(DebugLogLevel.ERROR, e);
422 }
423 throw e;
424 }
425
426 }
427
428 /**
429 * Closes this connection handler so that it will no longer accept new
430 * client connections. It may or may not disconnect existing client
431 * connections based on the provided flag.
432 *
433 * @param closeConnections
434 * Indicates whether any established client connections
435 * associated with the connection handler should also be
436 * closed.
437 * @param stopRegistry Indicates if the RMI registry should be stopped
438 */
439 public void finalizeConnectionHandler(
440 boolean closeConnections, boolean stopRegistry)
441 {
442 if (closeConnections)
443 {
444 try
445 {
446 if (jmxRmiConnectorNoClientCertificate != null)
447 {
448 jmxRmiConnectorNoClientCertificate.stop();
449 }
450 if (jmxRmiConnectorClientCertificate != null)
451 {
452 jmxRmiConnectorClientCertificate.stop();
453 }
454 }
455 catch (Exception e)
456 {
457 }
458 jmxRmiConnectorNoClientCertificate = null;
459 jmxRmiConnectorClientCertificate = null;
460 }
461 else
462 {
463 rmiAuthenticator.setFinalizedPhase(true);
464 }
465
466 //
467 // Unregister connectors and stop them.
468 try
469 {
470 ObjectName name = new ObjectName(jmxRmiConnectorNoClientCertificateName);
471 if (mbs.isRegistered(name))
472 {
473 mbs.unregisterMBean(name);
474 }
475 if (jmxRmiConnectorNoClientCertificate != null)
476 {
477 jmxRmiConnectorNoClientCertificate.stop();
478 }
479
480 // TODO: unregister the connector with SSL client authen
481 // name = new ObjectName(jmxRmiConnectorClientCertificateName);
482 // if (mbs.isRegistered(name))
483 // {
484 // mbs.unregisterMBean(name);
485 // }
486 // jmxRmiConnectorClientCertificate.stop() ;
487 }
488 catch (Exception e)
489 {
490 // TODO Log an error message
491 if (debugEnabled())
492 {
493 TRACER.debugCaught(DebugLogLevel.ERROR, e);
494 }
495 }
496
497 if (stopRegistry)
498 {
499 //
500 // Close the socket
501 try
502 {
503 rmiSsf.close();
504 }
505 catch (IOException e)
506 {
507 // TODO Log an error message
508 if (debugEnabled())
509 {
510 TRACER.debugCaught(DebugLogLevel.ERROR, e);
511 }
512 }
513 registry = null;
514 }
515
516 }
517
518
519
520 /**
521 * Retrieves the RMI protocol version string in use for this connector.
522 *
523 * @return The RMI protocol version string in use for this connector.
524 */
525 public String getProtocolVersion()
526 {
527 return rmiVersion;
528 }
529 }