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 static org.opends.server.loggers.debug.DebugLogger.*; 030 import org.opends.server.loggers.debug.DebugTracer; 031 import org.opends.server.types.DebugLogLevel; 032 033 import java.io.IOException; 034 035 import java.io.Serializable; 036 import java.net.Socket; 037 import java.rmi.server.RMIClientSocketFactory; 038 039 import java.util.Map; 040 041 // JSSE 042 import javax.net.ssl.SSLContext; 043 import javax.net.ssl.TrustManager; 044 045 import javax.net.ssl.SSLSocket; 046 import javax.net.ssl.SSLSocketFactory; 047 048 /** 049 * A <code>DirectoryRMIClientSocketFactory</code> instance is used by the 050 * RMI runtime in order to obtain client sockets for RMI calls via SSL. 051 * <p> 052 * This class implements <code>RMIClientSocketFactory</code> over the 053 * Secure Sockets Layer (SSL) or Transport Layer Security (TLS) protocols. 054 * </p> 055 */ 056 public class DirectoryRMIClientSocketFactory implements 057 RMIClientSocketFactory, Serializable 058 { 059 /** 060 * The tracer object for the debug logger. 061 */ 062 private static final DebugTracer TRACER = getTracer(); 063 064 065 066 /** 067 * The serial version identifier required to satisfy the compiler because 068 * this class implements the <CODE>java.io.Serializable</CODE> interface. 069 * This value was generated using the <CODE>serialver</CODE> command-line 070 * utility included with the Java SDK. 071 */ 072 private static final long serialVersionUID = -6701450600497520362L; 073 074 /** 075 * the static thread-local connection environment used by the RMI 076 * client to configure this factory. 077 */ 078 private static InheritableThreadLocal<Map> tlMapConnectionEnv = 079 new InheritableThreadLocal<Map>(); 080 081 /** 082 * The static thread-local target server hostname used by the RMI 083 * client to configure this factory. 084 */ 085 private static InheritableThreadLocal<String> tlStrServerHostname = 086 new InheritableThreadLocal<String>(); 087 088 /** 089 * the connection mode. <code>true</code> indicates that the client 090 * will be authenticated by its certificate (SSL protocol). 091 * <code>false</code> indicates that we have to perform an lDAP 092 * authentication 093 */ 094 private final boolean needClientCertificate; 095 096 /** 097 * the ssl socket factory (do not serialize). 098 */ 099 private transient SSLSocketFactory sslSocketFactory = null; 100 101 /** 102 * the host to connect to (do not serialize). 103 */ 104 private transient String serverHostname = null; 105 106 /** 107 * Constructs a new <code>DirectoryRMIClientSocketFactory</code>. 108 * 109 * @param wellknown 110 * <code>true</code> for wellknown, <code>false</code> 111 * otherwise 112 */ 113 public DirectoryRMIClientSocketFactory(boolean wellknown) 114 { 115 this.needClientCertificate = wellknown; 116 117 // We don't force the initialization of the SSLSocketFactory 118 // at construction time - because the RMI client socket factory is 119 // created on the server side, where that initialization is a 120 // priori 121 // meaningless, unless both server and client run in the same JVM. 122 // So contrarily to what we do for the server side, the 123 // initialization 124 // of the SSLSocketFactory will be delayed until the first time 125 // createSocket() is called. 126 } 127 128 /** 129 * Sets the thread-local connection environment. 130 * 131 * @param connectionEnv the new connection env 132 */ 133 public static void setConnectionEnv(Map connectionEnv) 134 { 135 tlMapConnectionEnv.set(connectionEnv); 136 } 137 138 /** 139 * Returns the thread-local connection environment. 140 * 141 * @return Map the connection environment used by new connections 142 */ 143 public static Map getConnectionEnv() 144 { 145 return tlMapConnectionEnv.get(); 146 } 147 148 /** 149 * Sets the thread-local target server hostname. 150 * 151 * @param serverHostname 152 * the target server hostname 153 */ 154 public static void setServerHostname(String serverHostname) 155 { 156 tlStrServerHostname.set(serverHostname); 157 } 158 159 /** 160 * Returns the thread-local target server hostname. 161 * 162 * @return String the target server hostname 163 */ 164 public static String getServerHostname() 165 { 166 return tlStrServerHostname.get(); 167 } 168 169 /** 170 * Returns the connection mode as configured at construction time. 171 * 172 * @return boolean <code>true</code> for wellknown, 173 * <code>false</code> otherwise 174 */ 175 public boolean getNeedClientCertificate() 176 { 177 return needClientCertificate; 178 } 179 180 /** 181 * Creates an SSL socket configured with the right trust stores and the 182 * right target host. 183 * <p> 184 * The keystore and truststore used come from client configuration and 185 * depend on the connection mode specified at construction time. 186 * <p> 187 * The target host is the host specified except if a different host was 188 * specified in the connection environment. 189 * 190 * @param host 191 * the target host as known by the RMI stack 192 * @param port 193 * the target port number 194 * @return an SSL socket 195 * 196 * @throws IOException 197 * if an error occurs while configuring the socket 198 */ 199 public Socket createSocket(String host, int port) throws IOException 200 { 201 // 202 // gets ssl socket factory 203 SSLSocketFactory sslSocketFactory = getSSLSocketFactory(); 204 String realhost = getRealServerHostname(host); 205 206 final SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket( 207 realhost, 208 port); 209 210 return sslSocket; 211 } 212 213 /** 214 * Indicates whether some other object is "equal to" this one. 215 * <p> 216 * Because each RMI client might have its own configuration, we do not 217 * try to optimize anything. Each RMI connector uses its own RMI client 218 * socket factory. In other words, Directory RMI clients never share 219 * the same client socket factory. 220 * </p> 221 * 222 * @param obj 223 * the reference object with which to compare 224 * @return <code>true</code> if this object is the same as the obj 225 * argument <code>false</code> otherwise 226 */ 227 public boolean equals(Object obj) 228 { 229 return super.equals(obj); 230 } 231 232 /** 233 * Returns a hash code value for this 234 * <code>DirectoryRMIClientSocketFactory</code>. 235 * 236 * @return a hash code value for this 237 * <code>DirectoryRMIClientSocketFactory</code> 238 */ 239 public int hashCode() 240 { 241 return super.hashCode(); 242 } 243 244 /** 245 * Returns the real server hostname to connect to. 246 * 247 * @param rmiHostname 248 * the target hostname as known by RMI stack 249 * @return String the real hostname to connect to 250 * @throws IOException 251 * if an error occurs 252 */ 253 private synchronized String getRealServerHostname(String rmiHostname) 254 throws IOException 255 { 256 if (serverHostname == null) 257 { 258 // 259 // does the client specify another host by 260 // using thread-local static parameter ? 261 serverHostname = getServerHostname(); 262 263 // 264 // if not found here, don't look for real host in static 265 // anymore 266 if (serverHostname == null) 267 { 268 serverHostname = ""; 269 } 270 } 271 272 if (serverHostname.length() > 0) 273 { 274 return serverHostname; 275 } 276 else 277 { 278 return rmiHostname; 279 } 280 } 281 282 /** 283 * Returns the ssl socket factory used by this RMI client socket 284 * factory. 285 * 286 * @return SSLSocketFactory the ssl socket factory 287 * @throws IOException 288 * if an error occurs 289 */ 290 private synchronized SSLSocketFactory getSSLSocketFactory() 291 throws IOException 292 { 293 if (sslSocketFactory == null) 294 { 295 if (debugEnabled()) 296 { 297 TRACER.debugVerbose("sslSocketFactory is null, get a new one"); 298 } 299 300 // socket factory not yet initialized 301 // initialize the trust 302 Map connectionEnv = getConnectionEnv(); 303 TrustManager[] tms = null; 304 305 // 306 // Check if a trust manager array was provided in the 307 // connection 308 // Env. If yes, use it for this SSL Connection 309 if ((connectionEnv != null) 310 && (connectionEnv 311 .containsKey(JmxConnectionHandler.TRUST_MANAGER_ARRAY_KEY))) 312 { 313 try 314 { 315 tms = (TrustManager[]) connectionEnv 316 .get(JmxConnectionHandler.TRUST_MANAGER_ARRAY_KEY); 317 } 318 catch (Exception e) 319 { 320 if (debugEnabled()) 321 { 322 TRACER.debugCaught(DebugLogLevel.ERROR, e); 323 } 324 tms = null; 325 } 326 327 if (tms == null) 328 { 329 // 330 // Why? The value is not invalid ? 331 // Too bad: we raised an exception 332 throw new IOException("invalid type or null value for key [" 333 + JmxConnectionHandler.TRUST_MANAGER_ARRAY_KEY 334 + "] in connection environment : " 335 + connectionEnv 336 .get(JmxConnectionHandler.TRUST_MANAGER_ARRAY_KEY)); 337 } 338 } 339 340 // now we have the array of trust Manager 341 // we can initialize the ssl ctx 342 SSLContext ctx = null; 343 try 344 { 345 ctx = SSLContext.getInstance("TLSv1"); 346 ctx.init(null, tms, null); 347 } 348 catch (Exception e) 349 { 350 if (debugEnabled()) 351 { 352 TRACER.debugCaught(DebugLogLevel.ERROR, e); 353 } 354 throw new IOException("Unable to initialize SSL context : " 355 + e.getMessage()); 356 } 357 358 // create the SSLSocket 359 sslSocketFactory = ctx.getSocketFactory(); 360 } 361 return sslSocketFactory; 362 } 363 }