mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

boli
02.48.2007 061e0172510debb6cd8920caa717c29a10e1b233
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.protocols.jmx;
 
import static org.opends.server.loggers.debug.DebugLogger.debugVerbose;
import static org.opends.server.loggers.debug.DebugLogger.debugCaught;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import org.opends.server.types.DebugLogLevel;
 
import java.io.IOException;
 
import java.io.Serializable;
import java.net.Socket;
import java.rmi.server.RMIClientSocketFactory;
 
import java.util.Map;
 
// JSSE
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
 
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
 
/**
 * A <code>DirectoryRMIClientSocketFactory</code> instance is used by the
 * RMI runtime in order to obtain client sockets for RMI calls via SSL.
 * <p>
 * This class implements <code>RMIClientSocketFactory</code> over the
 * Secure Sockets Layer (SSL) or Transport Layer Security (TLS) protocols.
 * </p>
 */
public class DirectoryRMIClientSocketFactory implements
    RMIClientSocketFactory, Serializable
{
 
 
  /**
   * The serial version identifier required to satisfy the compiler because
   * this class implements the <CODE>java.io.Serializable</CODE> interface.
   * This value was generated using the <CODE>serialver</CODE> command-line
   * utility included with the Java SDK.
   */
  private static final long serialVersionUID = -6701450600497520362L;
 
  /**
   * the static thread-local connection environment used by the RMI
   * client to configure this factory.
   */
  private static InheritableThreadLocal<Map> tlMapConnectionEnv =
    new InheritableThreadLocal<Map>();
 
  /**
   * The static thread-local target server hostname used by the RMI
   * client to configure this factory.
   */
  private static InheritableThreadLocal<String> tlStrServerHostname =
    new InheritableThreadLocal<String>();
 
  /**
   * the connection mode. <code>true</code> indicates that the client
   * will be authenticated by its certificate (SSL protocol).
   * <code>false</code> indicates that we have to perform an lDAP
   * authentication
   */
  private final boolean needClientCertificate;
 
  /**
   * the ssl socket factory (do not serialize).
   */
  private transient SSLSocketFactory sslSocketFactory = null;
 
  /**
   * the host to connect to (do not serialize).
   */
  private transient String serverHostname = null;
 
  /**
   * Constructs a new <code>DirectoryRMIClientSocketFactory</code>.
   *
   * @param wellknown
   *            <code>true</code> for wellknown, <code>false</code>
   *            otherwise
   */
  public DirectoryRMIClientSocketFactory(boolean wellknown)
  {
    this.needClientCertificate = wellknown;
 
    // We don't force the initialization of the SSLSocketFactory
    // at construction time - because the RMI client socket factory is
    // created on the server side, where that initialization is a
    // priori
    // meaningless, unless both server and client run in the same JVM.
    // So contrarily to what we do for the server side, the
    // initialization
    // of the SSLSocketFactory will be delayed until the first time
    // createSocket() is called.
  }
 
  /**
   * Sets the thread-local connection environment.
   *
   * @param connectionEnv the new connection env
   */
  public static void setConnectionEnv(Map connectionEnv)
  {
    tlMapConnectionEnv.set(connectionEnv);
  }
 
  /**
   * Returns the thread-local connection environment.
   *
   * @return Map the connection environment used by new connections
   */
  public static Map getConnectionEnv()
  {
    return tlMapConnectionEnv.get();
  }
 
  /**
   * Sets the thread-local target server hostname.
   *
   * @param serverHostname
   *            the target server hostname
   */
  public static void setServerHostname(String serverHostname)
  {
    tlStrServerHostname.set(serverHostname);
  }
 
  /**
   * Returns the thread-local target server hostname.
   *
   * @return String the target server hostname
   */
  public static String getServerHostname()
  {
    return tlStrServerHostname.get();
  }
 
  /**
   * Returns the connection mode as configured at construction time.
   *
   * @return boolean <code>true</code> for wellknown,
   *         <code>false</code> otherwise
   */
  public boolean getNeedClientCertificate()
  {
    return needClientCertificate;
  }
 
  /**
   * Creates an SSL socket configured with the right trust stores and the
   * right target host.
   * <p>
   * The keystore and truststore used come from client configuration and
   * depend on the connection mode specified at construction time.
   * <p>
   * The target host is the host specified except if a different host was
   * specified in the connection environment.
   *
   * @param host
   *            the target host as known by the RMI stack
   * @param port
   *            the target port number
   * @return an SSL socket
   *
   * @throws IOException
   *             if an error occurs while configuring the socket
   */
  public Socket createSocket(String host, int port) throws IOException
  {
    //
    // gets ssl socket factory
    SSLSocketFactory sslSocketFactory = getSSLSocketFactory();
    String realhost = getRealServerHostname(host);
 
    final SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(
        realhost,
        port);
 
    return sslSocket;
  }
 
  /**
   * Indicates whether some other object is "equal to" this one.
   * <p>
   * Because each RMI client might have its own configuration, we do not
   * try to optimize anything. Each RMI connector uses its own RMI client
   * socket factory. In other words, Directory RMI clients never share
   * the same client socket factory.
   * </p>
   *
   * @param obj
   *            the reference object with which to compare
   * @return <code>true</code> if this object is the same as the obj
   *         argument <code>false</code> otherwise
   */
  public boolean equals(Object obj)
  {
    return super.equals(obj);
  }
 
  /**
   * Returns a hash code value for this
   * <code>DirectoryRMIClientSocketFactory</code>.
   *
   * @return a hash code value for this
   *         <code>DirectoryRMIClientSocketFactory</code>
   */
  public int hashCode()
  {
    return super.hashCode();
  }
 
  /**
   * Returns the real server hostname to connect to.
   *
   * @param rmiHostname
   *            the target hostname as known by RMI stack
   * @return String the real hostname to connect to
   * @throws IOException
   *             if an error occurs
   */
  private synchronized String getRealServerHostname(String rmiHostname)
      throws IOException
  {
    if (serverHostname == null)
    {
      //
      // does the client specify another host by
      // using thread-local static parameter ?
      serverHostname = getServerHostname();
 
      //
      // if not found here, don't look for real host in static
      // anymore
      if (serverHostname == null)
      {
        serverHostname = "";
      }
    }
 
    if (serverHostname.length() > 0)
    {
      return serverHostname;
    }
    else
    {
      return rmiHostname;
    }
  }
 
  /**
   * Returns the ssl socket factory used by this RMI client socket
   * factory.
   *
   * @return SSLSocketFactory the ssl socket factory
   * @throws IOException
   *             if an error occurs
   */
  private synchronized SSLSocketFactory getSSLSocketFactory()
      throws IOException
  {
    if (sslSocketFactory == null)
    {
      if (debugEnabled())
      {
        debugVerbose("sslSocketFactory is null, get a new one");
      }
 
      // socket factory not yet initialized
      // initialize the trust
      Map connectionEnv = getConnectionEnv();
      TrustManager[] tms = null;
 
      //
      // Check if a trust manager array was provided in the
      // connection
      // Env. If yes, use it for this SSL Connection
      if ((connectionEnv != null)
          && (connectionEnv
          .containsKey(JmxConnectionHandler.TRUST_MANAGER_ARRAY_KEY)))
      {
        try
        {
          tms = (TrustManager[]) connectionEnv
              .get(JmxConnectionHandler.TRUST_MANAGER_ARRAY_KEY);
        }
        catch (Exception e)
        {
          if (debugEnabled())
          {
            debugCaught(DebugLogLevel.ERROR, e);
          }
          tms = null;
        }
 
        if (tms == null)
        {
          //
          // Why? The value is not invalid ?
          // Too bad: we raised an exception
          throw new IOException("invalid type or null value for key ["
              + JmxConnectionHandler.TRUST_MANAGER_ARRAY_KEY
              + "] in connection environment : "
              + connectionEnv
              .get(JmxConnectionHandler.TRUST_MANAGER_ARRAY_KEY));
        }
      }
 
      // now we have the array of trust Manager
      // we can initialize the ssl ctx
      SSLContext ctx = null;
      try
      {
        ctx = SSLContext.getInstance("TLSv1");
        ctx.init(null, tms, null);
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        throw new IOException("Unable to initialize SSL context : "
            + e.getMessage());
      }
 
      // create the SSLSocket
      sslSocketFactory = ctx.getSocketFactory();
    }
    return sslSocketFactory;
  }
}