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