src/java.base/share/classes/sun/security/ssl/AlpnExtension.java
changeset 50768 68fa3d4026ea
parent 47216 71c04702a3d5
child 53064 103ed9569fc8
equal deleted inserted replaced
50767:356eaea05bf0 50768:68fa3d4026ea
       
     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 chOnLoadConsumer = new CHAlpnConsumer();
       
    48     static final HandshakeAbsence chOnLoadAbsence = new CHAlpnAbsence();
       
    49 
       
    50     static final HandshakeProducer shNetworkProducer = new SHAlpnProducer();
       
    51     static final ExtensionConsumer shOnLoadConsumer = 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 eeOnLoadConsumer = new SHAlpnConsumer();
       
    58     static final HandshakeAbsence eeOnLoadAbsence = new SHAlpnAbsence();
       
    59 
       
    60     static final SSLStringizer alpnStringizer = new AlpnStringizer();
       
    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 AlpnStringizer implements SSLStringizer {
       
   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 
       
   153                 chc.applicationProtocol = "";
       
   154                 chc.conContext.applicationProtocol = "";
       
   155                 return null;
       
   156             }
       
   157 
       
   158             String[] laps = chc.sslConfig.applicationProtocols;
       
   159             if ((laps == null) || (laps.length == 0)) {
       
   160                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   161                     SSLLogger.info(
       
   162                             "No available application protocols");
       
   163                 }
       
   164                 return null;
       
   165             }
       
   166 
       
   167             // Produce the extension.
       
   168             int listLength = 0;     // ProtocolNameList length
       
   169             for (String ap : laps) {
       
   170                 int length = ap.getBytes(StandardCharsets.UTF_8).length;
       
   171                 if (length == 0) {
       
   172                     // log the configuration problem
       
   173                     if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   174                         SSLLogger.severe(
       
   175                                 "Application protocol name cannot be empty");
       
   176                     }
       
   177                     chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
       
   178                             "Application protocol name cannot be empty");
       
   179                 }
       
   180 
       
   181                 if (length <= MAX_AP_LENGTH) {
       
   182                     // opaque ProtocolName<1..2^8-1>, RFC 7301.
       
   183                     listLength += (length + 1);
       
   184                 } else {
       
   185                     // log the configuration problem
       
   186                     if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   187                         SSLLogger.severe(
       
   188                                 "Application protocol name (" + ap +
       
   189                                 ") exceeds the size limit (" +
       
   190                                 MAX_AP_LENGTH + " bytes)");
       
   191                     }
       
   192                     chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
       
   193                                 "Application protocol name (" + ap +
       
   194                                 ") exceeds the size limit (" +
       
   195                                 MAX_AP_LENGTH + " bytes)");
       
   196                 }
       
   197 
       
   198                 if (listLength > MAX_AP_LIST_LENGTH) {
       
   199                     // log the configuration problem
       
   200                     if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   201                         SSLLogger.severe(
       
   202                                 "The configured application protocols (" +
       
   203                                 Arrays.toString(laps) +
       
   204                                 ") exceed the size limit (" +
       
   205                                 MAX_AP_LIST_LENGTH + " bytes)");
       
   206                     }
       
   207                     chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
       
   208                                 "The configured application protocols (" +
       
   209                                 Arrays.toString(laps) +
       
   210                                 ") exceed the size limit (" +
       
   211                                 MAX_AP_LIST_LENGTH + " bytes)");
       
   212                 }
       
   213             }
       
   214 
       
   215             // ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.
       
   216             byte[] extData = new byte[listLength + 2];
       
   217             ByteBuffer m = ByteBuffer.wrap(extData);
       
   218             Record.putInt16(m, listLength);
       
   219             for (String ap : laps) {
       
   220                 Record.putBytes8(m, ap.getBytes(StandardCharsets.UTF_8));
       
   221             }
       
   222 
       
   223             // Update the context.
       
   224             chc.handshakeExtensions.put(SSLExtension.CH_ALPN,
       
   225                     new AlpnSpec(chc.sslConfig.applicationProtocols));
       
   226 
       
   227             return extData;
       
   228         }
       
   229     }
       
   230 
       
   231     /**
       
   232      * Network data consumer of the extension in a ClientHello
       
   233      * handshake message.
       
   234      */
       
   235     private static final class CHAlpnConsumer implements ExtensionConsumer {
       
   236         // Prevent instantiation of this class.
       
   237         private CHAlpnConsumer() {
       
   238             // blank
       
   239         }
       
   240 
       
   241         @Override
       
   242         public void consume(ConnectionContext context,
       
   243             HandshakeMessage message, ByteBuffer buffer) throws IOException {
       
   244             // The consuming happens in server side only.
       
   245             ServerHandshakeContext shc = (ServerHandshakeContext)context;
       
   246 
       
   247             // Is it a supported and enabled extension?
       
   248             if (!shc.sslConfig.isAvailable(SSLExtension.CH_ALPN)) {
       
   249                 shc.applicationProtocol = "";
       
   250                 shc.conContext.applicationProtocol = "";
       
   251                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   252                     SSLLogger.info(
       
   253                             "Ignore server unavailable extension: " +
       
   254                             SSLExtension.CH_ALPN.name);
       
   255                 }
       
   256                 return;     // ignore the extension
       
   257             }
       
   258 
       
   259             // Is the extension enabled?
       
   260             boolean noAPSelector;
       
   261             if (shc.conContext.transport instanceof SSLEngine) {
       
   262                 noAPSelector = (shc.sslConfig.engineAPSelector == null);
       
   263             } else {
       
   264                 noAPSelector = (shc.sslConfig.socketAPSelector == null);
       
   265             }
       
   266 
       
   267             boolean noAlpnProtocols =
       
   268                     shc.sslConfig.applicationProtocols == null ||
       
   269                     shc.sslConfig.applicationProtocols.length == 0;
       
   270             if (noAPSelector && noAlpnProtocols) {
       
   271                 shc.applicationProtocol = "";
       
   272                 shc.conContext.applicationProtocol = "";
       
   273                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   274                     SSLLogger.fine(
       
   275                             "Ignore server unenabled extension: " +
       
   276                             SSLExtension.CH_ALPN.name);
       
   277                 }
       
   278                 return;     // ignore the extension
       
   279             }
       
   280 
       
   281             // Parse the extension.
       
   282             AlpnSpec spec;
       
   283             try {
       
   284                 spec = new AlpnSpec(buffer);
       
   285             } catch (IOException ioe) {
       
   286                 shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
       
   287                 return;     // fatal() always throws, make the compiler happy.
       
   288             }
       
   289 
       
   290             // Update the context.
       
   291             if (noAPSelector) {     // noAlpnProtocols is false
       
   292                 List<String> protocolNames = spec.applicationProtocols;
       
   293                 boolean matched = false;
       
   294                 // Use server application protocol preference order.
       
   295                 for (String ap : shc.sslConfig.applicationProtocols) {
       
   296                     if (protocolNames.contains(ap)) {
       
   297                         shc.applicationProtocol = ap;
       
   298                         shc.conContext.applicationProtocol = ap;
       
   299                         matched = true;
       
   300                         break;
       
   301                     }
       
   302                 }
       
   303 
       
   304                 if (!matched) {
       
   305                     shc.conContext.fatal(Alert.NO_APPLICATION_PROTOCOL,
       
   306                             "No matching application layer protocol values");
       
   307                 }
       
   308             }   // Otherwise, applicationProtocol will be set by the
       
   309                 // application selector callback later.
       
   310 
       
   311             shc.handshakeExtensions.put(SSLExtension.CH_ALPN, spec);
       
   312 
       
   313             // No impact on session resumption.
       
   314             //
       
   315             // [RFC 7301] Unlike many other TLS extensions, this extension
       
   316             // does not establish properties of the session, only of the
       
   317             // connection.  When session resumption or session tickets are
       
   318             // used, the previous contents of this extension are irrelevant,
       
   319             // and only the values in the new handshake messages are
       
   320             // considered.
       
   321         }
       
   322     }
       
   323 
       
   324     /**
       
   325      * The absence processing if the extension is not present in
       
   326      * a ClientHello handshake message.
       
   327      */
       
   328     private static final class CHAlpnAbsence implements HandshakeAbsence {
       
   329         @Override
       
   330         public void absent(ConnectionContext context,
       
   331                 HandshakeMessage message) throws IOException {
       
   332             // The producing happens in server side only.
       
   333             ServerHandshakeContext shc = (ServerHandshakeContext)context;
       
   334 
       
   335             // Please don't use the previous negotiated application protocol.
       
   336             shc.applicationProtocol = "";
       
   337             shc.conContext.applicationProtocol = "";
       
   338         }
       
   339     }
       
   340 
       
   341     /**
       
   342      * Network data producer of the extension in the ServerHello
       
   343      * handshake message.
       
   344      */
       
   345     private static final class SHAlpnProducer implements HandshakeProducer {
       
   346         // Prevent instantiation of this class.
       
   347         private SHAlpnProducer() {
       
   348             // blank
       
   349         }
       
   350 
       
   351         @Override
       
   352         public byte[] produce(ConnectionContext context,
       
   353                 HandshakeMessage message) throws IOException {
       
   354             // The producing happens in client side only.
       
   355             ServerHandshakeContext shc = (ServerHandshakeContext)context;
       
   356 
       
   357             // In response to ALPN request only
       
   358             AlpnSpec requestedAlps =
       
   359                     (AlpnSpec)shc.handshakeExtensions.get(SSLExtension.CH_ALPN);
       
   360             if (requestedAlps == null) {
       
   361                 // Ignore, this extension was not requested and accepted.
       
   362                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   363                     SSLLogger.fine(
       
   364                             "Ignore unavailable extension: " +
       
   365                             SSLExtension.SH_ALPN.name);
       
   366                 }
       
   367 
       
   368                 shc.applicationProtocol = "";
       
   369                 shc.conContext.applicationProtocol = "";
       
   370                 return null;
       
   371             }
       
   372 
       
   373             List<String> alps = requestedAlps.applicationProtocols;
       
   374             if (shc.conContext.transport instanceof SSLEngine) {
       
   375                 if (shc.sslConfig.engineAPSelector != null) {
       
   376                     SSLEngine engine = (SSLEngine)shc.conContext.transport;
       
   377                     shc.applicationProtocol =
       
   378                         shc.sslConfig.engineAPSelector.apply(engine, alps);
       
   379                     if ((shc.applicationProtocol == null) ||
       
   380                             (!shc.applicationProtocol.isEmpty() &&
       
   381                             !alps.contains(shc.applicationProtocol))) {
       
   382                         shc.conContext.fatal(Alert.NO_APPLICATION_PROTOCOL,
       
   383                             "No matching application layer protocol values");
       
   384                     }
       
   385                 }
       
   386             } else {
       
   387                 if (shc.sslConfig.socketAPSelector != null) {
       
   388                     SSLSocket socket = (SSLSocket)shc.conContext.transport;
       
   389                     shc.applicationProtocol =
       
   390                         shc.sslConfig.socketAPSelector.apply(socket, alps);
       
   391                     if ((shc.applicationProtocol == null) ||
       
   392                             (!shc.applicationProtocol.isEmpty() &&
       
   393                             !alps.contains(shc.applicationProtocol))) {
       
   394                         shc.conContext.fatal(Alert.NO_APPLICATION_PROTOCOL,
       
   395                             "No matching application layer protocol values");
       
   396                     }
       
   397                 }
       
   398             }
       
   399 
       
   400             if ((shc.applicationProtocol == null) ||
       
   401                     (shc.applicationProtocol.isEmpty())) {
       
   402                 // Ignore, no negotiated application layer protocol.
       
   403                 shc.applicationProtocol = "";
       
   404                 shc.conContext.applicationProtocol = "";
       
   405                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   406                     SSLLogger.warning(
       
   407                         "Ignore, no negotiated application layer protocol");
       
   408                 }
       
   409 
       
   410                 return null;
       
   411             }
       
   412 
       
   413             // opaque ProtocolName<1..2^8-1>, RFC 7301.
       
   414             int listLen = shc.applicationProtocol.length() + 1;
       
   415                                                         // 1: length byte
       
   416             // ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.
       
   417             byte[] extData = new byte[listLen + 2];     // 2: list length
       
   418             ByteBuffer m = ByteBuffer.wrap(extData);
       
   419             Record.putInt16(m, listLen);
       
   420             Record.putBytes8(m,
       
   421                     shc.applicationProtocol.getBytes(StandardCharsets.UTF_8));
       
   422 
       
   423             // Update the context.
       
   424             shc.conContext.applicationProtocol = shc.applicationProtocol;
       
   425 
       
   426             // Clean or register the extension
       
   427             //
       
   428             // No further use of the request and respond extension any more.
       
   429             shc.handshakeExtensions.remove(SSLExtension.CH_ALPN);
       
   430 
       
   431             return extData;
       
   432         }
       
   433     }
       
   434 
       
   435     /**
       
   436      * Network data consumer of the extension in the ServerHello
       
   437      * handshake message.
       
   438      */
       
   439     private static final class SHAlpnConsumer implements ExtensionConsumer {
       
   440         // Prevent instantiation of this class.
       
   441         private SHAlpnConsumer() {
       
   442             // blank
       
   443         }
       
   444 
       
   445         @Override
       
   446         public void consume(ConnectionContext context,
       
   447             HandshakeMessage message, ByteBuffer buffer) throws IOException {
       
   448             // The producing happens in client side only.
       
   449             ClientHandshakeContext chc = (ClientHandshakeContext)context;
       
   450 
       
   451             // In response to ALPN request only
       
   452             AlpnSpec requestedAlps =
       
   453                     (AlpnSpec)chc.handshakeExtensions.get(SSLExtension.CH_ALPN);
       
   454             if (requestedAlps == null ||
       
   455                     requestedAlps.applicationProtocols == null ||
       
   456                     requestedAlps.applicationProtocols.isEmpty()) {
       
   457                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   458                     "Unexpected " + SSLExtension.CH_ALPN.name + " extension");
       
   459             }
       
   460 
       
   461             // Parse the extension.
       
   462             AlpnSpec spec;
       
   463             try {
       
   464                 spec = new AlpnSpec(buffer);
       
   465             } catch (IOException ioe) {
       
   466                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
       
   467                 return;     // fatal() always throws, make the compiler happy.
       
   468             }
       
   469 
       
   470             // Only one application protocol is allowed.
       
   471             if (spec.applicationProtocols.size() != 1) {
       
   472                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   473                     "Invalid " + SSLExtension.CH_ALPN.name + " extension: " +
       
   474                     "Only one application protocol name " +
       
   475                     "is allowed in ServerHello message");
       
   476             }
       
   477 
       
   478             // The respond application protocol must be one of the requested.
       
   479             if (!requestedAlps.applicationProtocols.containsAll(
       
   480                     spec.applicationProtocols)) {
       
   481                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
       
   482                     "Invalid " + SSLExtension.CH_ALPN.name + " extension: " +
       
   483                     "Only client specified application protocol " +
       
   484                     "is allowed in ServerHello message");
       
   485             }
       
   486 
       
   487             // Update the context.
       
   488             chc.applicationProtocol = spec.applicationProtocols.get(0);
       
   489             chc.conContext.applicationProtocol = chc.applicationProtocol;
       
   490 
       
   491             // Clean or register the extension
       
   492             //
       
   493             // No further use of the request and respond extension any more.
       
   494             chc.handshakeExtensions.remove(SSLExtension.CH_ALPN);
       
   495         }
       
   496     }
       
   497 
       
   498     /**
       
   499      * The absence processing if the extension is not present in
       
   500      * the ServerHello handshake message.
       
   501      */
       
   502     private static final class SHAlpnAbsence implements HandshakeAbsence {
       
   503         @Override
       
   504         public void absent(ConnectionContext context,
       
   505                 HandshakeMessage message) throws IOException {
       
   506             // The producing happens in client side only.
       
   507             ClientHandshakeContext chc = (ClientHandshakeContext)context;
       
   508 
       
   509             // Please don't use the previous negotiated application protocol.
       
   510             chc.applicationProtocol = "";
       
   511             chc.conContext.applicationProtocol = "";
       
   512         }
       
   513     }
       
   514 }