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}