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 2008 Sun Microsystems, Inc.
015 * Portions Copyright 2011-2017 ForgeRock AS.
016 */
017package org.opends.server.replication.protocol;
018
019import java.io.IOException;
020import org.forgerock.i18n.slf4j.LocalizedLogger;
021import java.net.Socket;
022import java.util.SortedSet;
023
024import javax.net.ssl.SSLContext;
025import javax.net.ssl.SSLException;
026import javax.net.ssl.SSLSocket;
027import javax.net.ssl.SSLSocketFactory;
028
029import org.forgerock.opendj.config.server.ConfigException;
030import org.opends.server.types.CryptoManager;
031import org.opends.server.types.DirectoryConfig;
032
033import static org.opends.messages.ReplicationMessages.*;
034import static org.opends.server.util.StaticUtils.*;
035
036/**
037 * This class represents the security configuration for replication protocol
038 * sessions. It contains all the configuration required to use SSL, and it
039 * determines whether encryption should be enabled for a session to a given
040 * replication server.
041 */
042public final class ReplSessionSecurity
043{
044
045  private static final String REPLICATION_SERVER_NAME = "Replication Server";
046
047  private static final String REPLICATION_CLIENT_NAME = "Replication Client";
048
049  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
050
051  /**
052   * Whether replication sessions use SSL encryption.
053   */
054  private final boolean sslEncryption;
055
056  /**
057   * The names of the local certificates to use, or null if none is specified.
058   */
059  private final SortedSet<String> sslCertNicknames;
060
061  /**
062   * The set of enabled SSL protocols, or null for the default set.
063   */
064  private final String sslProtocols[];
065
066  /**
067   * The set of enabled SSL cipher suites, or null for the default set.
068   */
069  private final String sslCipherSuites[];
070
071
072
073  /**
074   * Create a ReplSessionSecurity instance from a provided multimaster domain
075   * configuration.
076   *
077   * @throws ConfigException
078   *           If the supplied configuration was not valid.
079   */
080  public ReplSessionSecurity() throws ConfigException
081  {
082    // Currently use global settings from the crypto manager.
083    this(DirectoryConfig.getCryptoManager().getSslCertNicknames(),
084        DirectoryConfig.getCryptoManager().getSslProtocols(),
085        DirectoryConfig.getCryptoManager().getSslCipherSuites(),
086        DirectoryConfig.getCryptoManager().isSslEncryption());
087  }
088
089
090
091  /**
092   * Create a ReplSessionSecurity instance from the supplied configuration
093   * values.
094   *
095   * @param sslCertNicknames
096   *          The names of the local certificates to use, or null if none is
097   *          specified.
098   * @param sslProtocols
099   *          The protocols that should be enabled, or null if the default
100   *          protocols should be used.
101   * @param sslCipherSuites
102   *          The cipher suites that should be enabled, or null if the default
103   *          cipher suites should be used.
104   * @param sslEncryption
105   *          Whether replication sessions use SSL encryption.
106   * @throws ConfigException
107   *           If the supplied configuration was not valid.
108   */
109  public ReplSessionSecurity(final SortedSet<String> sslCertNicknames,
110      final SortedSet<String> sslProtocols,
111      final SortedSet<String> sslCipherSuites,
112      final boolean sslEncryption) throws ConfigException
113  {
114    if (sslProtocols == null || sslProtocols.isEmpty())
115    {
116      this.sslProtocols = null;
117    }
118    else
119    {
120      this.sslProtocols = new String[sslProtocols.size()];
121      sslProtocols.toArray(this.sslProtocols);
122    }
123
124    if (sslCipherSuites == null || sslCipherSuites.isEmpty())
125    {
126      this.sslCipherSuites = null;
127    }
128    else
129    {
130      this.sslCipherSuites = new String[sslCipherSuites.size()];
131      sslCipherSuites.toArray(this.sslCipherSuites);
132    }
133
134    this.sslEncryption = sslEncryption;
135    this.sslCertNicknames = sslCertNicknames;
136  }
137
138
139
140  /**
141   * Create a new protocol session in the client role on the provided socket.
142   *
143   * @param socket
144   *          The connected socket.
145   * @param soTimeout
146   *          The socket timeout option to use for the protocol session.
147   * @return The new protocol session.
148   * @throws ConfigException
149   *           If the protocol session could not be established due to a
150   *           configuration problem.
151   * @throws IOException
152   *           If the protocol session could not be established for some other
153   *           reason.
154   */
155  public Session createClientSession(final Socket socket,
156      final int soTimeout) throws ConfigException, IOException
157  {
158    boolean hasCompleted = false;
159    SSLSocket secureSocket = null;
160
161    try
162    {
163      // Create a new SSL context every time to make sure we pick up the
164      // latest contents of the trust store.
165      final CryptoManager cryptoManager = DirectoryConfig.getCryptoManager();
166      final SSLContext sslContext = cryptoManager.getSslContext(REPLICATION_CLIENT_NAME, sslCertNicknames);
167      final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
168
169      secureSocket = (SSLSocket) sslSocketFactory.createSocket(
170          socket, socket.getInetAddress().getHostName(),
171          socket.getPort(), false);
172      secureSocket.setUseClientMode(true);
173      secureSocket.setSoTimeout(soTimeout);
174
175      if (sslProtocols != null)
176      {
177        secureSocket.setEnabledProtocols(sslProtocols);
178      }
179
180      if (sslCipherSuites != null)
181      {
182        secureSocket.setEnabledCipherSuites(sslCipherSuites);
183      }
184
185      // Force TLS negotiation now.
186      secureSocket.startHandshake();
187      hasCompleted = true;
188      return new Session(socket, secureSocket);
189    }
190    finally
191    {
192      if (!hasCompleted)
193      {
194        close(socket);
195        close(secureSocket);
196      }
197    }
198  }
199
200
201
202  /**
203   * Create a new protocol session in the server role on the provided socket.
204   *
205   * @param socket
206   *          The connected socket.
207   * @param soTimeout
208   *          The socket timeout option to use for the protocol session.
209   * @return The new protocol session.
210   * @throws ConfigException
211   *           If the protocol session could not be established due to a
212   *           configuration problem.
213   * @throws IOException
214   *           If the protocol session could not be established for some other
215   *           reason.
216   */
217  public Session createServerSession(final Socket socket,
218      final int soTimeout) throws ConfigException, IOException
219  {
220    boolean hasCompleted = false;
221    SSLSocket secureSocket = null;
222
223    try
224    {
225      // Create a new SSL context every time to make sure we pick up the
226      // latest contents of the trust store.
227      final CryptoManager cryptoManager = DirectoryConfig.getCryptoManager();
228      final SSLContext sslContext = cryptoManager.getSslContext(REPLICATION_SERVER_NAME, sslCertNicknames);
229      final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
230
231      secureSocket = (SSLSocket) sslSocketFactory.createSocket(
232          socket, socket.getInetAddress().getHostName(),
233          socket.getPort(), false);
234      secureSocket.setUseClientMode(false);
235      secureSocket.setNeedClientAuth(true);
236      secureSocket.setSoTimeout(soTimeout);
237
238      if (sslProtocols != null)
239      {
240        secureSocket.setEnabledProtocols(sslProtocols);
241      }
242
243      if (sslCipherSuites != null)
244      {
245        secureSocket.setEnabledCipherSuites(sslCipherSuites);
246      }
247
248      // Force TLS negotiation now.
249      secureSocket.startHandshake();
250      hasCompleted = true;
251      return new Session(socket, secureSocket);
252    }
253    catch (final SSLException e)
254    {
255      // This is probably a connection attempt from an unexpected client
256      // log that to warn the administrator.
257      logger.debug(INFO_SSL_SERVER_CON_ATTEMPT_ERROR, socket.getRemoteSocketAddress(),
258          socket.getLocalSocketAddress(), e.getLocalizedMessage());
259      return null;
260    }
261    finally
262    {
263      if (!hasCompleted)
264      {
265        close(socket);
266        close(secureSocket);
267      }
268    }
269  }
270
271
272
273  /**
274   * Determine whether sessions to a given replication server should be
275   * encrypted.
276   *
277   * @return true if sessions to the given replication server should be
278   *         encrypted, or false if they should not be encrypted.
279   */
280  public boolean isSslEncryption()
281  {
282    // Currently use global settings from the crypto manager.
283    return sslEncryption;
284  }
285
286  /** {@inheritDoc} */
287  @Override
288  public String toString()
289  {
290    return getClass().getSimpleName() + " " + (sslEncryption ? "with SSL" : "");
291  }
292}