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}