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-2009 Sun Microsystems, Inc. 015 * Portions Copyright 2012-2016 ForgeRock AS. 016 */ 017package org.opends.server.extensions; 018 019import java.io.IOException; 020import java.nio.ByteBuffer; 021import java.nio.channels.ByteChannel; 022import java.nio.channels.ClosedChannelException; 023import java.security.cert.Certificate; 024import java.util.Collections; 025import java.util.LinkedHashMap; 026import java.util.Map; 027 028import javax.net.ssl.SSLEngine; 029import javax.net.ssl.SSLEngineResult; 030import javax.net.ssl.SSLEngineResult.HandshakeStatus; 031import javax.net.ssl.SSLException; 032import javax.net.ssl.SSLPeerUnverifiedException; 033import javax.net.ssl.SSLSession; 034 035import org.forgerock.i18n.slf4j.LocalizedLogger; 036 037/** A class that provides a TLS byte channel implementation. */ 038public final class TLSByteChannel implements ConnectionSecurityProvider 039{ 040 /** Private implementation. */ 041 private final class ByteChannelImpl implements ByteChannel 042 { 043 @Override 044 public void close() throws IOException 045 { 046 synchronized (readLock) 047 { 048 synchronized (writeLock) 049 { 050 final boolean isInitiator = !sslEngine.isInboundDone(); 051 052 try 053 { 054 if (!sslEngine.isOutboundDone()) 055 { 056 sslEngine.closeOutbound(); 057 while (doWrapAndSend(EMPTY_BUFFER) > 0) 058 { 059 // Write out any remaining SSL close notifications. 060 } 061 } 062 } 063 catch (final ClosedChannelException e) 064 { 065 // Ignore this so that close is idempotent. 066 } 067 finally 068 { 069 try 070 { 071 sslEngine.closeInbound(); 072 } 073 catch (final SSLException e) 074 { 075 // Not yet received peer's close notification. Ignore this if we 076 // are the initiator. 077 if (!isInitiator) 078 { 079 throw e; 080 } 081 } 082 finally 083 { 084 channel.close(); 085 } 086 } 087 } 088 } 089 } 090 091 @Override 092 public boolean isOpen() 093 { 094 return !sslEngine.isOutboundDone() || !sslEngine.isInboundDone(); 095 } 096 097 @Override 098 public int read(final ByteBuffer unwrappedData) throws IOException 099 { 100 synchronized (readLock) 101 { 102 // Only read and unwrap new data if needed. 103 if (!recvUnwrappedBuffer.hasRemaining()) 104 { 105 final int read = doRecvAndUnwrap(); 106 if (read <= 0) 107 { 108 // No data read or end of stream. 109 return read; 110 } 111 } 112 113 // Copy available data. 114 final int startPos = unwrappedData.position(); 115 if (recvUnwrappedBuffer.remaining() > unwrappedData.remaining()) 116 { 117 // Unwrapped data does not fit in client buffer so copy one byte at a 118 // time: it's annoying that there is no easy way to do this with 119 // ByteBuffers. 120 while (unwrappedData.hasRemaining()) 121 { 122 unwrappedData.put(recvUnwrappedBuffer.get()); 123 } 124 } 125 else 126 { 127 // Unwrapped data fits client buffer so block copy. 128 unwrappedData.put(recvUnwrappedBuffer); 129 } 130 return unwrappedData.position() - startPos; 131 } 132 } 133 134 @Override 135 public int write(final ByteBuffer unwrappedData) throws IOException 136 { 137 // This method will block until the entire message is sent. 138 final int bytesWritten = unwrappedData.remaining(); 139 140 // Synchronized in order to prevent interleaving and reordering. 141 synchronized (writeLock) 142 { 143 // Repeat until the entire input data is written. 144 while (unwrappedData.hasRemaining()) 145 { 146 // Wrap and send the data. 147 doWrapAndSend(unwrappedData); 148 149 // Perform handshake if needed. 150 if (isHandshaking(sslEngine.getHandshakeStatus())) 151 { 152 doHandshake(false /* isReading */); 153 } 154 } 155 } 156 157 return bytesWritten; 158 } 159 160 /** 161 * It seems that the SSL engine does not remember if an error has already 162 * occurred so we must cache it here and rethrow. See OPENDJ-652. 163 */ 164 private void abortOnSSLException() throws IOException 165 { 166 if (sslException != null) 167 { 168 throw sslException; 169 } 170 } 171 172 private void doHandshake(final boolean isReading) throws IOException 173 { 174 // This lock is probably unnecessary since tasks can be run in parallel, 175 // but it adds no additional overhead so there's little harm in having 176 // it. 177 synchronized (handshakeLock) 178 { 179 while (true) 180 { 181 switch (sslEngine.getHandshakeStatus()) 182 { 183 case NEED_TASK: 184 Runnable runnable; 185 while ((runnable = sslEngine.getDelegatedTask()) != null) 186 { 187 runnable.run(); 188 } 189 break; 190 case NEED_UNWRAP: 191 // Block for writes, but be non-blocking for reads. 192 if (isReading) 193 { 194 // Let doRecvAndUnwrap() deal with this. 195 return; 196 } 197 198 // Need to do an unwrap (read) while writing. 199 if (doRecvAndUnwrap() < 0) 200 { 201 throw new ClosedChannelException(); 202 } 203 break; 204 case NEED_WRAP: 205 doWrapAndSend(EMPTY_BUFFER); 206 break; 207 default: // NOT_HANDSHAKING, FINISHED. 208 return; 209 } 210 } 211 } 212 } 213 214 /** Attempt to read and unwrap the next SSL packet. */ 215 private int doRecvAndUnwrap() throws IOException 216 { 217 // Synchronize SSL unwrap with channel reads. 218 synchronized (unwrapLock) 219 { 220 // Read SSL packets until some unwrapped data is produced or no more 221 // data is available on the underlying channel. 222 while (true) 223 { 224 // Unwrap any remaining data in the buffer. 225 abortOnSSLException(); 226 recvUnwrappedBuffer.compact(); // Prepare for append. 227 final SSLEngineResult result; 228 try 229 { 230 result = sslEngine.unwrap(recvWrappedBuffer, recvUnwrappedBuffer); 231 } 232 catch (final SSLException e) 233 { 234 // Save the error - see abortOnSSLException(). 235 sslException = e; 236 throw e; 237 } 238 finally 239 { 240 recvUnwrappedBuffer.flip(); // Restore for read. 241 } 242 243 switch (result.getStatus()) 244 { 245 case BUFFER_OVERFLOW: 246 // The unwrapped buffer is not big enough: resize and repeat. 247 final int newAppSize = sslEngine.getSession() 248 .getApplicationBufferSize(); 249 final ByteBuffer newRecvUnwrappedBuffer = ByteBuffer 250 .allocate(recvUnwrappedBuffer.limit() + newAppSize); 251 newRecvUnwrappedBuffer.put(recvUnwrappedBuffer); 252 newRecvUnwrappedBuffer.flip(); 253 recvUnwrappedBuffer = newRecvUnwrappedBuffer; 254 break; // Retry unwrap. 255 case BUFFER_UNDERFLOW: 256 // Not enough data was read. This either means that the inbound 257 // buffer was too small, or not enough data was read. 258 final int newPktSize = sslEngine.getSession().getPacketBufferSize(); 259 if (newPktSize > recvWrappedBuffer.capacity()) 260 { 261 // Increase the buffer size. 262 final ByteBuffer newRecvWrappedBuffer = ByteBuffer 263 .allocate(newPktSize); 264 newRecvWrappedBuffer.put(recvWrappedBuffer); 265 newRecvWrappedBuffer.flip(); 266 recvWrappedBuffer = newRecvWrappedBuffer; 267 } 268 // Read wrapped data from underlying channel. 269 recvWrappedBuffer.compact(); // Prepare for append. 270 final int read = channel.read(recvWrappedBuffer); 271 recvWrappedBuffer.flip(); // Restore for read. 272 if (read <= 0) 273 { 274 // Not enough data is available to read a complete SSL packet, or 275 // channel closed. 276 return read; 277 } 278 // Loop and unwrap. 279 break; 280 case CLOSED: 281 // Peer sent SSL close notification. 282 return -1; 283 default: // OK 284 if (recvUnwrappedBuffer.hasRemaining()) 285 { 286 // Some application data was read so return it. 287 return recvUnwrappedBuffer.remaining(); 288 } 289 else if (isHandshaking(result.getHandshakeStatus())) 290 { 291 // No application data was read, but if we are handshaking then 292 // try to continue. 293 doHandshake(true /* isReading */); 294 } 295 break; 296 } 297 } 298 } 299 } 300 301 /** Attempt to wrap and send the next SSL packet. */ 302 private int doWrapAndSend(final ByteBuffer unwrappedData) 303 throws IOException 304 { 305 // Synchronize SSL wrap with channel writes. 306 synchronized (wrapLock) 307 { 308 // Repeat while there is overflow. 309 while (true) 310 { 311 abortOnSSLException(); 312 final SSLEngineResult result; 313 try 314 { 315 result = sslEngine.wrap(unwrappedData, sendWrappedBuffer); 316 } 317 catch (SSLException e) 318 { 319 // Save the error - see abortOnSSLException(). 320 sslException = e; 321 throw e; 322 } 323 324 switch (result.getStatus()) 325 { 326 case BUFFER_OVERFLOW: 327 // The wrapped buffer is not big enough: resize and repeat. 328 final int newSize = sslEngine.getSession().getPacketBufferSize(); 329 final ByteBuffer newSendWrappedBuffer = ByteBuffer 330 .allocate(sendWrappedBuffer.position() + newSize); 331 sendWrappedBuffer.flip(); 332 newSendWrappedBuffer.put(sendWrappedBuffer); 333 sendWrappedBuffer = newSendWrappedBuffer; 334 break; // Retry. 335 case BUFFER_UNDERFLOW: 336 // This should not happen for sends. 337 sslException = 338 new SSLException("Got unexpected underflow while wrapping"); 339 throw sslException; 340 case CLOSED: 341 throw new ClosedChannelException(); 342 default: // OK 343 // Write the SSL packet: our IO stack will block until all the 344 // data is written. 345 sendWrappedBuffer.flip(); 346 while (sendWrappedBuffer.hasRemaining()) 347 { 348 channel.write(sendWrappedBuffer); 349 } 350 final int written = sendWrappedBuffer.position(); 351 sendWrappedBuffer.clear(); 352 return written; 353 } 354 } 355 } 356 } 357 358 private boolean isHandshaking(final HandshakeStatus status) 359 { 360 return status != HandshakeStatus.NOT_HANDSHAKING; 361 } 362 } 363 364 /** 365 * Map of cipher phrases to effective key size (bits). Taken from the 366 * following RFCs: 5289, 4346, 3268,4132 and 4162 and the IANA Transport Layer 367 * Security (TLS) Parameters. 368 * 369 * @see <a 370 * href="http://www.iana.org/assignments/tls-parameters/tls-parameters.xml"> 371 * Transport Layer Security (TLS) Parameters, TLS Cipher Suite Registry</a> 372 */ 373 static final Map<String, Integer> CIPHER_MAP; 374 static 375 { 376 final Map<String, Integer> map = new LinkedHashMap<>(); 377 map.put("_WITH_AES_256_", 256); 378 map.put("_WITH_ARIA_256_", 256); 379 map.put("_WITH_CAMELLIA_256_", 256); 380 map.put("_WITH_AES_128_", 128); 381 map.put("_WITH_ARIA_128_", 128); 382 map.put("_WITH_SEED_", 128); 383 map.put("_WITH_CAMELLIA_128_", 128); 384 map.put("_WITH_IDEA_", 128); 385 map.put("_WITH_RC4_128_", 128); 386 map.put("_WITH_3DES_EDE_", 112); 387 map.put("_WITH_FORTEZZA_", 96); 388 map.put("_WITH_RC4_56_", 56); 389 map.put("_WITH_DES_CBC_40_", 40); 390 map.put("_WITH_RC2_CBC_40_", 40); 391 map.put("_WITH_RC4_40_", 40); 392 map.put("_WITH_DES40_", 40); 393 map.put("_WITH_DES_", 56); 394 map.put("_WITH_NULL_", 0); 395 CIPHER_MAP = Collections.unmodifiableMap(map); 396 } 397 398 private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0); 399 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 400 401 private final ByteChannelImpl pimpl = new ByteChannelImpl(); 402 private final ByteChannel channel; 403 private final SSLEngine sslEngine; 404 405 private volatile SSLException sslException; 406 private ByteBuffer recvWrappedBuffer; 407 private ByteBuffer recvUnwrappedBuffer; 408 private ByteBuffer sendWrappedBuffer; 409 410 private final Object handshakeLock = new Object(); 411 private final Object unwrapLock = new Object(); 412 private final Object wrapLock = new Object(); 413 private final Object readLock = new Object(); 414 private final Object writeLock = new Object(); 415 416 /** 417 * Creates an TLS byte channel instance using the specified LDAP connection 418 * configuration, client connection, SSL context and socket channel 419 * parameters. 420 * 421 * @param channel 422 * The underlying channel. 423 * @param sslEngine 424 * The SSL engine to use. 425 */ 426 public TLSByteChannel(final ByteChannel channel, final SSLEngine sslEngine) 427 { 428 this.channel = channel; 429 this.sslEngine = sslEngine; 430 431 // Allocate read/write buffers. 432 final SSLSession session = sslEngine.getSession(); 433 final int wrappedBufferSize = session.getPacketBufferSize(); 434 final int unwrappedBufferSize = session.getApplicationBufferSize(); 435 436 sendWrappedBuffer = ByteBuffer.allocate(wrappedBufferSize); 437 recvWrappedBuffer = ByteBuffer.allocate(wrappedBufferSize); 438 recvUnwrappedBuffer = ByteBuffer.allocate(unwrappedBufferSize); 439 440 // Initially nothing has been received. 441 recvWrappedBuffer.flip(); 442 recvUnwrappedBuffer.flip(); 443 } 444 445 @Override 446 public ByteChannel getChannel() 447 { 448 return pimpl; 449 } 450 451 @Override 452 public Certificate[] getClientCertificateChain() 453 { 454 try 455 { 456 return sslEngine.getSession().getPeerCertificates(); 457 } 458 catch (final SSLPeerUnverifiedException e) 459 { 460 logger.traceException(e); 461 return new Certificate[0]; 462 } 463 } 464 465 @Override 466 public String getName() 467 { 468 return "TLS"; 469 } 470 471 @Override 472 public int getSSF() 473 { 474 final Integer ssf = getSSF(sslEngine.getSession().getCipherSuite()); 475 if (ssf != null) 476 { 477 return ssf.intValue(); 478 } 479 return 0; 480 } 481 482 /** 483 * Returns the Security Strength Factor corresponding to the supplied cipher 484 * string. 485 * 486 * @param cipherString 487 * the cipher to test for SSF 488 * @return the Security Strength Factor corresponding to the supplied cipher 489 * string, null if the cipher cannot be recognized. 490 */ 491 static Integer getSSF(final String cipherString) 492 { 493 for (final Map.Entry<String, Integer> mapEntry : CIPHER_MAP.entrySet()) 494 { 495 if (cipherString.contains(mapEntry.getKey())) 496 { 497 return mapEntry.getValue(); 498 } 499 } 500 return null; 501 } 502 503 @Override 504 public boolean isSecure() 505 { 506 return true; 507 } 508}