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 }