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