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