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 legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2006-2008 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.server.protocols.jmx;
028
029import org.forgerock.i18n.slf4j.LocalizedLogger;
030
031import java.io.IOException;
032import java.net.InetAddress;
033import java.net.ServerSocket;
034import java.net.Socket;
035import java.rmi.server.RMIServerSocketFactory;
036
037import javax.net.ssl.SSLSocket;
038import javax.net.ssl.SSLSocketFactory;
039
040/**
041 * A <code>DirectoryRMIServerSocketFactory</code> instance is used by the RMI
042 * runtime in order to obtain server sockets for RMI calls via SSL.
043 *
044 * <p>
045 * This class implements <code>RMIServerSocketFactory</code> over the Secure
046 * Sockets Layer (SSL) or Transport Layer Security (TLS) protocols.
047 * </p>
048 */
049public class DirectoryRMIServerSocketFactory implements
050    RMIServerSocketFactory
051{
052  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
053
054  /**
055   *  The SSL socket factory associated with the connector.
056   */
057  private SSLSocketFactory sslSocketFactory;
058
059  /**
060   * Indicate if we required the client authentication via SSL.
061   */
062  private final boolean needClientCertificate;
063
064
065  /**
066   * Constructs a new <code>DirectoryRMIServerSocketFactory</code> with the
067   * specified SSL socket configuration.
068   *
069   * @param sslSocketFactory
070   *            the SSL socket factory to be used by this factory
071   *
072   * @param needClientCertificate
073   *            <code>true</code> to require client authentication on SSL
074   *            connections accepted by server sockets created by this
075   *            factory; <code>false</code> to not require client
076   *            authentication.
077   */
078  public DirectoryRMIServerSocketFactory(SSLSocketFactory sslSocketFactory,
079      boolean needClientCertificate)
080  {
081    // Initialize the configuration parameters.
082    this.needClientCertificate = needClientCertificate;
083    this.sslSocketFactory = sslSocketFactory;
084  }
085
086  /**
087   * <p>
088   * Returns <code>true</code> if client authentication is required on SSL
089   * connections accepted by server sockets created by this factory.
090   * </p>
091   *
092   * @return <code>true</code> if client authentication is required
093   *
094   * @see SSLSocket#setNeedClientAuth
095   */
096  public final boolean getNeedClientCertificate()
097  {
098    return needClientCertificate;
099  }
100
101  /**
102   * Creates a server socket that accepts SSL connections configured according
103   * to this factory's SSL socket configuration parameters.
104   *
105   * @param port
106   *            the port number the socket listens to
107   *
108   * @return a server socket
109   *
110   * @throws IOException
111   *             if the socket cannot be created
112   */
113  public ServerSocket createServerSocket(int port) throws IOException
114  {
115    return new ServerSocket(port, 0, InetAddress.getByName("0.0.0.0"))
116    {
117      @Override
118      public Socket accept() throws IOException
119      {
120        Socket socket = super.accept();
121        if (logger.isTraceEnabled())
122        {
123          logger.trace("host/port: %s/%d",
124                       socket.getInetAddress().getHostName(), socket.getPort());
125        }
126        SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(
127            socket,
128            socket.getInetAddress().getHostName(),
129            socket.getPort(),
130            true);
131
132        sslSocket.setUseClientMode(false);
133        sslSocket.setNeedClientAuth(needClientCertificate);
134        return sslSocket;
135      }
136    };
137
138  }
139
140  /**
141   * <p>
142   * Indicates whether some other object is "equal to" this one.
143   * </p>
144   *
145   * <p>
146   * Two <code>CacaoRMIServerSocketFactory</code> objects are equal if they
147   * have been constructed with the same SSL socket configuration parameters.
148   * </p>
149   *
150   * <p>
151   * A subclass should override this method (as well as {@link #hashCode()})
152   * if it adds instance state that affects equality.
153   * </p>
154   *
155   * @param obj the reference object with which to compare.
156   *
157   * @return <code>true</code> if this object is the same as the obj
158   *         argument <code>false</code> otherwise.
159   */
160  public boolean equals(Object obj)
161  {
162    if (obj == this)
163    {
164      return true;
165    }
166    if (!(obj instanceof DirectoryRMIServerSocketFactory))
167    {
168      return false;
169    }
170    DirectoryRMIServerSocketFactory that =
171      (DirectoryRMIServerSocketFactory) obj;
172    return getClass().equals(that.getClass()) && checkParameters(that);
173  }
174
175  /**
176   * Checks if inputs parameters are OK.
177   * @param that the input parameter
178   * @return true or false.
179   */
180  private boolean checkParameters(DirectoryRMIServerSocketFactory that)
181  {
182    return needClientCertificate == that.needClientCertificate
183        && sslSocketFactory.equals(that.sslSocketFactory);
184  }
185
186  /**
187   * <p>Returns a hash code value for this
188   * <code>CacaoRMIServerSocketFactory</code>.</p>
189   *
190   * @return a hash code value for this
191   * <code>CacaoRMIServerSocketFactory</code>.
192   */
193  public int hashCode()
194  {
195    return getClass().hashCode()
196        + Boolean.valueOf(needClientCertificate).hashCode()
197        + sslSocketFactory.hashCode();
198  }
199}