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
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2008 Sun Microsystems, Inc.
026     */
027    
028    package org.opends.server.replication.protocol;
029    
030    import static org.opends.server.loggers.ErrorLogger.logError;
031    import static org.opends.messages.ReplicationMessages.*;
032    
033    import org.opends.messages.Message;
034    import org.opends.server.admin.std.server.ReplicationServerCfg;
035    import org.opends.server.admin.std.server.ReplicationDomainCfg;
036    import org.opends.server.types.DirectoryConfig;
037    import org.opends.server.types.CryptoManager;
038    import org.opends.server.config.ConfigException;
039    
040    import javax.net.ssl.SSLException;
041    import javax.net.ssl.SSLSocket;
042    import javax.net.ssl.SSLContext;
043    import javax.net.ssl.SSLSocketFactory;
044    import java.util.SortedSet;
045    import java.net.Socket;
046    import java.net.InetAddress;
047    import java.io.IOException;
048    
049    /**
050     * This class represents the security configuration for replication protocol
051     * sessions. It contains all the configuration required to use SSL, and it
052     * determines whether encryption should be enabled for a session to a given
053     * replication server.
054     *
055     */
056    public class ReplSessionSecurity
057    {
058      /**
059       * Whether the replication server should listen on a secure port.
060       * Set false for test purposes only.
061       */
062      private static boolean useSSL = true;
063    
064      /**
065       * Whether replication sessions use SSL encryption.
066       */
067      private boolean sslEncryption;
068    
069      /**
070       * The name of the local certificate to use, or null if none is specified.
071       */
072      private String sslCertNickname;
073    
074      /**
075       * The set of enabled SSL protocols, or null for the default set.
076       */
077      private String sslProtocols[];
078    
079      /**
080       * The set of enabled SSL cipher suites, or null for the default set.
081       */
082      private String sslCipherSuites[];
083    
084      /**
085       * Create a ReplSessionSecurity instance from the supplied configuration
086       * values.
087       *
088       * @param sslCertNickname The name of the local certificate to use, or null
089       *                        if none is specified.
090       * @param sslProtocols    The protocols that should be enabled, or null if
091       *                        the default protocols should be used.
092       * @param sslCipherSuites The cipher suites that should be enabled, or null
093       *                        if the default cipher suites should be used.
094       * @param sslEncryption   Whether replication sessions use SSL encryption.
095       *
096       * @throws ConfigException    If the supplied configuration was not valid.
097       */
098      public ReplSessionSecurity(String sslCertNickname,
099        SortedSet<String> sslProtocols,
100        SortedSet<String> sslCipherSuites,
101        boolean sslEncryption)
102        throws ConfigException
103      {
104        if (sslProtocols == null || sslProtocols.size() == 0)
105        {
106          this.sslProtocols = null;
107        }
108        else
109        {
110          this.sslProtocols = new String[sslProtocols.size()];
111          sslProtocols.toArray(this.sslProtocols);
112        }
113    
114        if (sslCipherSuites == null || sslCipherSuites.size() == 0)
115        {
116          this.sslCipherSuites = null;
117        }
118        else
119        {
120          this.sslCipherSuites = new String[sslProtocols.size()];
121          sslProtocols.toArray(this.sslCipherSuites);
122        }
123    
124        this.sslEncryption = sslEncryption;
125        this.sslCertNickname = sslCertNickname;
126      }
127    
128      /**
129       * Create a ReplSessionSecurity instance from a provided replication server
130       * configuration.
131       *
132       * @param replServerCfg The replication server configuration.
133       *
134       * @throws ConfigException If the supplied configuration was not valid.
135       */
136      public ReplSessionSecurity(ReplicationServerCfg replServerCfg)
137        throws ConfigException
138      {
139        // Currently use global settings from the crypto manager.
140        this(DirectoryConfig.getCryptoManager().getSslCertNickname(),
141          DirectoryConfig.getCryptoManager().getSslProtocols(),
142          DirectoryConfig.getCryptoManager().getSslCipherSuites(),
143          DirectoryConfig.getCryptoManager().isSslEncryption());
144      }
145    
146      /**
147       * Create a ReplSessionSecurity instance from a provided multimaster domain
148       * configuration.
149       *
150       * @param multimasterDomainCfg The multimaster domain configuration.
151       *
152       * @throws ConfigException If the supplied configuration was not valid.
153       */
154      public ReplSessionSecurity(ReplicationDomainCfg multimasterDomainCfg)
155        throws ConfigException
156      {
157        // Currently use global settings from the crypto manager.
158        this(DirectoryConfig.getCryptoManager().getSslCertNickname(),
159          DirectoryConfig.getCryptoManager().getSslProtocols(),
160          DirectoryConfig.getCryptoManager().getSslCipherSuites(),
161          DirectoryConfig.getCryptoManager().isSslEncryption());
162      }
163    
164      /**
165       * Determine whether a given replication server is listening on a secure
166       * port.
167       * @param serverURL The replication server URL.
168       * @return true if the given replication server is listening on a secure
169       *         port, or false if it is listening on a non-secure port.
170       */
171      private boolean isSecurePort(String serverURL)
172      {
173        // Always true unless changed for test purposes.
174        return useSSL;
175      }
176    
177      /**
178       * Determine whether sessions to a given replication server should be
179       * encrypted.
180       * @param serverURL The replication server URL.
181       * @return true if sessions to the given replication server should be
182       *         encrypted, or false if they should not be encrypted.
183       */
184      public boolean isSslEncryption(String serverURL)
185      {
186        // Currently use global settings from the crypto manager.
187        return sslEncryption;
188      }
189    
190      /**
191       * Create a new protocol session in the client role on the provided socket.
192       * @param serverURL The remote replication server to which the socket is
193       *                  connected.
194       * @param socket The connected socket.
195       * @return The new protocol session.
196       * @throws ConfigException If the protocol session could not be established
197       *                         due to a configuration problem.
198       * @throws IOException     If the protocol session could not be established
199       *                         for some other reason.
200       */
201      public ProtocolSession createClientSession(String serverURL, Socket socket)
202        throws ConfigException, IOException
203      {
204        boolean useSSL = isSecurePort(serverURL);
205        if (useSSL)
206        {
207          // Create a new SSL context every time to make sure we pick up the
208          // latest contents of the trust store.
209          CryptoManager cryptoManager = DirectoryConfig.getCryptoManager();
210          SSLContext sslContext = cryptoManager.getSslContext(sslCertNickname);
211          SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
212    
213          SSLSocket secureSocket = (SSLSocket) sslSocketFactory.createSocket(socket,
214            socket.getInetAddress().getHostName(),
215            socket.getPort(), false);
216          secureSocket.setUseClientMode(true);
217    
218          if (sslProtocols != null)
219          {
220            secureSocket.setEnabledProtocols(sslProtocols);
221          }
222    
223          if (sslCipherSuites != null)
224          {
225            secureSocket.setEnabledCipherSuites(sslCipherSuites);
226          }
227    
228          // Force TLS negotiation now.
229          secureSocket.startHandshake();
230    
231          return new TLSSocketSession(socket, secureSocket);
232        }
233        else
234        {
235          return new SocketSession(socket);
236        }
237      }
238    
239      /**
240       * Create a new protocol session in the server role on the provided socket.
241       * @param socket The connected socket.
242       * @return The new protocol session.
243       * @throws ConfigException If the protocol session could not be established
244       *                         due to a configuration problem.
245       * @throws IOException     If the protocol session could not be established
246       *                         for some other reason.
247       */
248      public ProtocolSession createServerSession(Socket socket)
249        throws ConfigException, IOException
250      {
251        if (useSSL)
252        {
253          try
254          {
255            // Create a new SSL context every time to make sure we pick up the
256            // latest contents of the trust store.
257            CryptoManager cryptoManager = DirectoryConfig.getCryptoManager();
258            SSLContext sslContext = cryptoManager.getSslContext(sslCertNickname);
259            SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
260    
261            SSLSocket secureSocket = (SSLSocket)
262              sslSocketFactory.createSocket(socket,
263              socket.getInetAddress().getHostName(),
264              socket.getPort(), false);
265            secureSocket.setUseClientMode(false);
266            secureSocket.setNeedClientAuth(true);
267    
268            if (sslProtocols != null)
269            {
270              secureSocket.setEnabledProtocols(sslProtocols);
271            }
272    
273            if (sslCipherSuites != null)
274            {
275              secureSocket.setEnabledCipherSuites(sslCipherSuites);
276            }
277    
278            // Force TLS negotiation now.
279            secureSocket.startHandshake();
280    
281    //      SSLSession sslSession = secureSocket.getSession();
282    //      System.out.println("Peer      = " + sslSession.getPeerHost() + ":" +
283    //           sslSession.getPeerPort());
284    //      System.out.println("Principal = " + sslSession.getPeerPrincipal());
285    
286            return new TLSSocketSession(socket, secureSocket);
287          } catch (SSLException e)
288          {
289            // This is probably a connection attempt from an unexpected client
290            // log that to warn the administrator.
291            InetAddress remHost = socket.getInetAddress();
292            Message message = NOTE_SSL_SERVER_CON_ATTEMPT_ERROR.get(remHost.
293              getHostName(), remHost.getHostAddress(), e.getLocalizedMessage());
294            logError(message);
295            return null;
296          }
297        } else
298        {
299          return new SocketSession(socket);
300        }
301      }
302    
303    }