src/java.base/share/classes/sun/security/ssl/AlpnExtension.java
branchJDK-8145252-TLS13-branch
changeset 56542 56aaa6cb3693
parent 47216 71c04702a3d5
child 56566 a06a7dece503
equal deleted inserted replaced
56541:92cbbfc996f3 56542:56aaa6cb3693
       
     1 /*
       
     2  * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package sun.security.ssl;
       
    27 
       
    28 import java.io.IOException;
       
    29 import java.nio.ByteBuffer;
       
    30 import java.nio.charset.StandardCharsets;
       
    31 import java.util.Arrays;
       
    32 import java.util.Collections;
       
    33 import java.util.LinkedList;
       
    34 import java.util.List;
       
    35 import javax.net.ssl.SSLEngine;
       
    36 import javax.net.ssl.SSLProtocolException;
       
    37 import javax.net.ssl.SSLSocket;
       
    38 import sun.security.ssl.SSLExtension.ExtensionConsumer;
       
    39 import sun.security.ssl.SSLExtension.SSLExtensionSpec;
       
    40 import sun.security.ssl.SSLHandshake.HandshakeMessage;
       
    41 
       
    42 /**
       
    43  * Pack of the "application_layer_protocol_negotiation" extensions [RFC 7301].
       
    44  */
       
    45 final class AlpnExtension {
       
    46     static final HandshakeProducer chNetworkProducer = new CHAlpnProducer();
       
    47     static final ExtensionConsumer chOnLoadConcumer = new CHAlpnConsumer();
       
    48     static final HandshakeAbsence chOnLoadAbsence = new CHAlpnAbsence();
       
    49 
       
    50     static final HandshakeProducer shNetworkProducer = new SHAlpnProducer();
       
    51     static final ExtensionConsumer shOnLoadConcumer = new SHAlpnConsumer();
       
    52     static final HandshakeAbsence shOnLoadAbsence = new SHAlpnAbsence();
       
    53 
       
    54     // Note: we reuse ServerHello operations for EncryptedExtensions for now.
       
    55     // Please be careful about any code or specification changes in the future.
       
    56     static final HandshakeProducer eeNetworkProducer = new SHAlpnProducer();
       
    57     static final ExtensionConsumer eeOnLoadConcumer = new SHAlpnConsumer();
       
    58     static final HandshakeAbsence eeOnLoadAbsence = new SHAlpnAbsence();
       
    59 
       
    60     static final SSLStringize alpnStringize = new AlpnStringize();
       
    61 
       
    62     /**
       
    63      * The "application_layer_protocol_negotiation" extension.
       
    64      *
       
    65      * See RFC 7301 for the specification of this extension.
       
    66      */
       
    67     static final class AlpnSpec implements SSLExtensionSpec {
       
    68         final List<String> applicationProtocols;
       
    69 
       
    70         private AlpnSpec(String[] applicationProtocols) {
       
    71             this.applicationProtocols = Collections.unmodifiableList(
       
    72                     Arrays.asList(applicationProtocols));
       
    73         }
       
    74 
       
    75         private AlpnSpec(ByteBuffer buffer) throws IOException {
       
    76             // ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.
       
    77             if (buffer.remaining() < 2) {
       
    78                 throw new SSLProtocolException(
       
    79                     "Invalid application_layer_protocol_negotiation: " +
       
    80                     "insufficient data (length=" + buffer.remaining() + ")");
       
    81             }
       
    82 
       
    83             int listLen = Record.getInt16(buffer);
       
    84             if (listLen < 2 || listLen != buffer.remaining()) {
       
    85                 throw new SSLProtocolException(
       
    86                     "Invalid application_layer_protocol_negotiation: " +
       
    87                     "incorrect list length (length=" + listLen + ")");
       
    88             }
       
    89 
       
    90             List<String> protocolNames = new LinkedList<>();
       
    91             while (buffer.hasRemaining()) {
       
    92                 // opaque ProtocolName<1..2^8-1>, RFC 7301.
       
    93                 byte[] bytes = Record.getBytes8(buffer);
       
    94                 if (bytes.length == 0) {
       
    95                     throw new SSLProtocolException(
       
    96                         "Invalid application_layer_protocol_negotiation " +
       
    97                         "extension: empty application protocol name");
       
    98                 }
       
    99 
       
   100                 String appProtocol = new String(bytes, StandardCharsets.UTF_8);
       
   101                 protocolNames.add(appProtocol);
       
   102             }
       
   103 
       
   104             this.applicationProtocols =
       
   105                     Collections.unmodifiableList(protocolNames);
       
   106         }
       
   107 
       
   108         @Override
       
   109         public String toString() {
       
   110             return applicationProtocols.toString();
       
   111         }
       
   112     }
       
   113 
       
   114     private static final class AlpnStringize implements SSLStringize {
       
   115         @Override
       
   116         public String toString(ByteBuffer buffer) {
       
   117             try {
       
   118                 return (new AlpnSpec(buffer)).toString();
       
   119             } catch (IOException ioe) {
       
   120                 // For debug logging only, so please swallow exceptions.
       
   121                 return ioe.getMessage();
       
   122             }
       
   123         }
       
   124     }
       
   125 
       
   126     /**
       
   127      * Network data producer of the extension in a ClientHello
       
   128      * handshake message.
       
   129      */
       
   130     private static final class CHAlpnProducer implements HandshakeProducer {
       
   131         static final int MAX_AP_LENGTH = 255;
       
   132         static final int MAX_AP_LIST_LENGTH = 65535;
       
   133 
       
   134         // Prevent instantiation of this class.
       
   135         private CHAlpnProducer() {
       
   136             // blank
       
   137         }
       
   138 
       
   139         @Override
       
   140         public byte[] produce(ConnectionContext context,
       
   141                 HandshakeMessage message) throws IOException {
       
   142             // The producing happens in client side only.
       
   143             ClientHandshakeContext chc = (ClientHandshakeContext)context;
       
   144 
       
   145             // Is it a supported and enabled extension?
       
   146             if (!chc.sslConfig.isAvailable(SSLExtension.CH_ALPN)) {
       
   147                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   148                     SSLLogger.info(
       
   149                             "Ignore client unavailable extension: " +
       
   150                             SSLExtension.CH_ALPN.name);
       
   151                 }
       
   152                 return null;
       
   153             }
       
   154 
       
   155             String[] laps = chc.sslConfig.applicationProtocols;
       
   156             if ((laps == null) || (laps.length == 0)) {
       
   157                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   158                     SSLLogger.info(
       
   159                             "No available application protocols");
       
   160                 }
       
   161                 return null;
       
   162             }
       
   163 
       
   164             // Produce the extension.
       
   165             int listLength = 0;     // ProtocolNameList length
       
   166             for (String ap : laps) {
       
   167                 int length = ap.getBytes(StandardCharsets.UTF_8).length;
       
   168                 if (length == 0) {
       
   169                     // log the configuration problem
       
   170                     if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   171                         SSLLogger.severe(
       
   172                                 "Application protocol name cannot be empty");
       
   173                     }
       
   174                     chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
       
   175                             "Application protocol name cannot be empty");
       
   176                 }
       
   177 
       
   178                 if (length <= MAX_AP_LENGTH) {
       
   179                     // opaque ProtocolName<1..2^8-1>, RFC 7301.
       
   180                     listLength += (length + 1);
       
   181                 } else {
       
   182                     // log the configuration problem
       
   183                     if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   184                         SSLLogger.severe(
       
   185                                 "Application protocol name (" + ap +
       
   186                                 ") exceeds the size limit (" +
       
   187                                 MAX_AP_LENGTH + " bytes)");
       
   188                     }
       
   189                     chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
       
   190                                 "Application protocol name (" + ap +
       
   191                                 ") exceeds the size limit (" +
       
   192                                 MAX_AP_LENGTH + " bytes)");
       
   193                 }
       
   194 
       
   195                 if (listLength > MAX_AP_LIST_LENGTH) {
       
   196                     // log the configuration problem
       
   197                     if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   198                         SSLLogger.severe(
       
   199                                 "The configured application protocols (" +
       
   200                                 Arrays.toString(laps) +
       
   201                                 ") exceed the size limit (" +
       
   202                                 MAX_AP_LIST_LENGTH + " bytes)");
       
   203                     }
       
   204                     chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
       
   205                                 "The configured application protocols (" +
       
   206                                 Arrays.toString(laps) +
       
   207                                 ") exceed the size limit (" +
       
   208                                 MAX_AP_LIST_LENGTH + " bytes)");
       
   209                 }
       
   210             }
       
   211 
       
   212             // ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.
       
   213             byte[] extData = new byte[listLength + 2];
       
   214             ByteBuffer m = ByteBuffer.wrap(extData);
       
   215             Record.putInt16(m, listLength);
       
   216             for (String ap : laps) {
       
   217                 Record.putBytes8(m, ap.getBytes(StandardCharsets.UTF_8));
       
   218             }
       
   219 
       
   220             // Update the context.
       
   221             chc.handshakeExtensions.put(SSLExtension.CH_ALPN,
       
   222                     new AlpnSpec(chc.sslConfig.applicationProtocols));
       
   223 
       
   224             return extData;
       
   225         }
       
   226     }
       
   227 
       
   228     /**
       
   229      * Network data consumer of the extension in a ClientHello
       
   230      * handshake message.
       
   231      */
       
   232     private static final class CHAlpnConsumer implements ExtensionConsumer {
       
   233         // Prevent instantiation of this class.
       
   234         private CHAlpnConsumer() {
       
   235             // blank
       
   236         }
       
   237 
       
   238         @Override
       
   239         public void consume(ConnectionContext context,
       
   240             HandshakeMessage message, ByteBuffer buffer) throws IOException {
       
   241             // The comsuming happens in server side only.
       
   242             ServerHandshakeContext shc = (ServerHandshakeContext)context;
       
   243 
       
   244             // Is it a supported and enabled extension?
       
   245             if (!shc.sslConfig.isAvailable(SSLExtension.CH_ALPN)) {
       
   246                 shc.applicationProtocol = "";
       
   247                 shc.conContext.applicationProtocol = "";
       
   248                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   249                     SSLLogger.info(
       
   250                             "Ignore server unavailable extension: " +
       
   251                             SSLExtension.CH_ALPN.name);
       
   252                 }
       
   253                 return;     // ignore the extension
       
   254             }
       
   255 
       
   256             // Is the extension enabled?
       
   257             boolean noAPSelector;
       
   258             if (shc.conContext.transport instanceof SSLEngine) {
       
   259                 noAPSelector = (shc.sslConfig.engineAPSelector == null);
       
   260             } else {
       
   261                 noAPSelector = (shc.sslConfig.socketAPSelector == null);
       
   262             }
       
   263 
       
   264             boolean noAlpnProtocols =
       
   265                     shc.sslConfig.applicationProtocols == null ||
       
   266                     shc.sslConfig.applicationProtocols.length == 0;
       
   267             if (noAPSelector && noAlpnProtocols) {
       
   268                 shc.applicationProtocol = "";
       
   269                 shc.conContext.applicationProtocol = "";
       
   270                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   271                     SSLLogger.fine(
       
   272                             "Ignore server unenabled extension: " +
       
   273                             SSLExtension.CH_ALPN.name);
       
   274                 }
       
   275                 return;     // ignore the extension
       
   276             }
       
   277 
       
   278             // Parse the extension.
       
   279             AlpnSpec spec;
       
   280             try {
       
   281                 spec = new AlpnSpec(buffer);
       
   282             } catch (IOException ioe) {
       
   283                 shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
       
   284                 return;     // fatal() always throws, make the compiler happy.
       
   285             }
       
   286 
       
   287             // Update the context.
       
   288             if (noAPSelector) {     // noAlpnProtocols is false
       
   289                 List<String> protocolNames = spec.applicationProtocols;
       
   290                 boolean matched = false;
       
   291                 // Use server application protocol preference order.
       
   292                 for (String ap : shc.sslConfig.applicationProtocols) {
       
   293                     if (protocolNames.contains(ap)) {
       
   294                         shc.applicationProtocol = ap;
       
   295                         shc.conContext.applicationProtocol = ap;
       
   296                         matched = true;
       
   297                         break;
       
   298                     }
       
   299                 }
       
   300 
       
   301                 if (!matched) {
       
   302                     shc.conContext.fatal(Alert.NO_APPLICATION_PROTOCOL,
       
   303                             "No matching application layer protocol values");
       
   304                 }
       
   305             }   // Otherwise, applicationProtocol will be set by the
       
   306                 // application selector callback later.
       
   307 
       
   308             shc.handshakeExtensions.put(SSLExtension.CH_ALPN, spec);
       
   309 
       
   310             // No impact on session resumption.
       
   311             //
       
   312             // [RFC 7301] Unlike many other TLS extensions, this extension
       
   313             // does not establish properties of the session, only of the
       
   314             // connection.  When session resumption or session tickets are
       
   315             // used, the previous contents of this extension are irrelevant,
       
   316             // and only the values in the new handshake messages are
       
   317             // considered.
       
   318         }
       
   319     }
       
   320 
       
   321     /**
       
   322      * The absence processing if the extension is not present in
       
   323      * a ClientHello handshake message.
       
   324      */
       
   325     private static final class CHAlpnAbsence implements HandshakeAbsence {
       
   326         @Override
       
   327         public void absent(ConnectionContext context,
       
   328                 HandshakeMessage message) throws IOException {
       
   329             // The producing happens in server side only.
       
   330             ServerHandshakeContext shc = (ServerHandshakeContext)context;
       
   331 
       
   332             // Please don't use the previous negotiated application protocol.
       
   333             shc.applicationProtocol = "";
       
   334             shc.conContext.applicationProtocol = "";
       
   335         }
       
   336     }
       
   337 
       
   338     /**
       
   339      * Network data producer of the extension in the ServerHello
       
   340      * handshake message.
       
   341      */
       
   342     private static final class SHAlpnProducer implements HandshakeProducer {
       
   343         // Prevent instantiation of this class.
       
   344         private SHAlpnProducer() {
       
   345             // blank
       
   346         }
       
   347 
       
   348         @Override
       
   349         public byte[] produce(ConnectionContext context,
       
   350                 HandshakeMessage message) throws IOException {
       
   351             // The producing happens in client side only.
       
   352             ServerHandshakeContext shc = (ServerHandshakeContext)context;
       
   353 
       
   354             // In response to ALPN request only
       
   355             AlpnSpec requestedAlps =
       
   356                     (AlpnSpec)shc.handshakeExtensions.get(SSLExtension.CH_ALPN);
       
   357             if (requestedAlps == null) {
       
   358                 // Ignore, this extension was not requested and accepted.
       
   359                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   360                     SSLLogger.fine(
       
   361                             "Ignore unavailable extension: " +
       
   362                             SSLExtension.SH_ALPN.name);
       
   363                 }
       
   364                 return null;
       
   365             }
       
   366 
       
   367             List<String> alps = requestedAlps.applicationProtocols;
       
   368             if (shc.conContext.transport instanceof SSLEngine) {
       
   369                 if (shc.sslConfig.engineAPSelector != null) {
       
   370                     SSLEngine engine = (SSLEngine)shc.conContext.transport;
       
   371                     shc.applicationProtocol =
       
   372                         shc.sslConfig.engineAPSelector.apply(engine, alps);
       
   373                     if ((shc.applicationProtocol == null) ||
       
   374                             (!shc.applicationProtocol.isEmpty() &&
       
   375                             !alps.contains(shc.applicationProtocol))) {
       
   376                         shc.conContext.fatal(Alert.NO_APPLICATION_PROTOCOL,
       
   377                             "No matching application layer protocol values");
       
   378                     }
       
   379                 }
       
   380             } else {
       
   381                 if (shc.sslConfig.socketAPSelector != null) {
       
   382                     SSLSocket socket = (SSLSocket)shc.conContext.transport;
       
   383                     shc.applicationProtocol =
       
   384                         shc.sslConfig.socketAPSelector.apply(socket, alps);
       
   385                     if ((shc.applicationProtocol == null) ||
       
   386                             (!shc.applicationProtocol.isEmpty() &&
       
   387                             !alps.contains(shc.applicationProtocol))) {
       
   388                         shc.conContext.fatal(Alert.NO_APPLICATION_PROTOCOL,
       
   389                             "No matching application layer protocol values");
       
   390                     }
       
   391                 }
       
   392             }
       
   393 
       
   394             if ((shc.applicationProtocol == null) ||
       
   395                     (shc.applicationProtocol.isEmpty())) {
       
   396                 // Ignore, no negotiated application layer protocol.
       
   397                 shc.applicationProtocol = "";
       
   398                 shc.conContext.applicationProtocol = "";
       
   399                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   400                     SSLLogger.warning(
       
   401                         "Ignore, no negotiated application layer protocol");
       
   402                 }
       
   403 
       
   404                 return null;
       
   405             }
       
   406 
       
   407             // opaque ProtocolName<1..2^8-1>, RFC 7301.
       
   408             int listLen = shc.applicationProtocol.length() + 1;
       
   409                                                         // 1: length byte
       
   410             // ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.
       
   411             byte[] extData = new byte[listLen + 2];     // 2: list length
       
   412             ByteBuffer m = ByteBuffer.wrap(extData);
       
   413             Record.putInt16(m, listLen);
       
   414             Record.putBytes8(m,
       
   415                     shc.applicationProtocol.getBytes(StandardCharsets.UTF_8));
       
   416 
       
   417             // Update the context.
       
   418             shc.conContext.applicationProtocol = shc.applicationProtocol;
       
   419 
       
   420             // Clean or register the extension
       
   421             //
       
   422             // No further use of the request and respond extension any more.
       
   423             shc.handshakeExtensions.remove(SSLExtension.CH_ALPN);
       
   424 
       
   425             return extData;
       
   426         }
       
   427     }
       
   428 
       
   429     /**
       
   430      * Network data consumer of the extension in the ServerHello
       
   431      * handshake message.
       
   432      */
       
   433     private static final class SHAlpnConsumer implements ExtensionConsumer {
       
   434         // Prevent instantiation of this class.
       
   435         private SHAlpnConsumer() {
       
   436             // blank
       
   437         }
       
   438 
       
   439         @Override
       
   440         public void consume(ConnectionContext context,
       
   441             HandshakeMessage message, ByteBuffer buffer) throws IOException {
       
   442             // The producing happens in client side only.
       
   443             ClientHandshakeContext chc = (ClientHandshakeContext)context;
       
   444 
       
   445             // In response to ALPN request only
       
   446             AlpnSpec requestedAlps =
       
   447                     (AlpnSpec)chc.handshakeExtensions.get(SSLExtension.CH_ALPN);
       
   448             if (requestedAlps == null) {
       
   449                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   450                     "Unexpected " + SSLExtension.CH_ALPN.name + " extension");
       
   451             }
       
   452 
       
   453             // Parse the extension.
       
   454             AlpnSpec spec;
       
   455             try {
       
   456                 spec = new AlpnSpec(buffer);
       
   457             } catch (IOException ioe) {
       
   458                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
       
   459                 return;     // fatal() always throws, make the compiler happy.
       
   460             }
       
   461 
       
   462             // Only one application protocol is allowed.
       
   463             if (spec.applicationProtocols.size() != 1) {
       
   464                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   465                     "Invalid " + SSLExtension.CH_ALPN.name + " extension: " +
       
   466                     "Only one protocol name is allowed in ServerHello message");
       
   467             }
       
   468 
       
   469             // Update the context.
       
   470             chc.applicationProtocol = spec.applicationProtocols.get(0);
       
   471             chc.conContext.applicationProtocol = chc.applicationProtocol;
       
   472 
       
   473             // Clean or register the extension
       
   474             //
       
   475             // No further use of the request and respond extension any more.
       
   476             chc.handshakeExtensions.remove(SSLExtension.CH_ALPN);
       
   477         }
       
   478     }
       
   479 
       
   480     /**
       
   481      * The absence processing if the extension is not present in
       
   482      * the ServerHello handshake message.
       
   483      */
       
   484     private static final class SHAlpnAbsence implements HandshakeAbsence {
       
   485         @Override
       
   486         public void absent(ConnectionContext context,
       
   487                 HandshakeMessage message) throws IOException {
       
   488             // The producing happens in client side only.
       
   489             ClientHandshakeContext chc = (ClientHandshakeContext)context;
       
   490 
       
   491             // Please don't use the previous negotiated application protocol.
       
   492             chc.applicationProtocol = "";
       
   493             chc.conContext.applicationProtocol = "";
       
   494         }
       
   495     }
       
   496 }