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}