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}