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