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