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 }