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}