Path: blob/master/src/java.base/share/classes/sun/security/ssl/AlpnExtension.java
41159 views
/*1* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.ssl;2627import java.io.IOException;28import java.nio.ByteBuffer;29import java.nio.charset.Charset;30import java.security.AccessController;31import java.security.PrivilegedAction;32import java.security.Security;33import java.util.Arrays;34import java.util.Collections;35import java.util.LinkedList;36import java.util.List;37import javax.net.ssl.SSLEngine;38import javax.net.ssl.SSLProtocolException;39import javax.net.ssl.SSLSocket;40import sun.security.ssl.SSLExtension.ExtensionConsumer;41import sun.security.ssl.SSLExtension.SSLExtensionSpec;42import sun.security.ssl.SSLHandshake.HandshakeMessage;4344/**45* Pack of the "application_layer_protocol_negotiation" extensions [RFC 7301].46*/47final class AlpnExtension {48static final HandshakeProducer chNetworkProducer = new CHAlpnProducer();49static final ExtensionConsumer chOnLoadConsumer = new CHAlpnConsumer();50static final HandshakeAbsence chOnLoadAbsence = new CHAlpnAbsence();5152static final HandshakeProducer shNetworkProducer = new SHAlpnProducer();53static final ExtensionConsumer shOnLoadConsumer = new SHAlpnConsumer();54static final HandshakeAbsence shOnLoadAbsence = new SHAlpnAbsence();5556// Note: we reuse ServerHello operations for EncryptedExtensions for now.57// Please be careful about any code or specification changes in the future.58static final HandshakeProducer eeNetworkProducer = new SHAlpnProducer();59static final ExtensionConsumer eeOnLoadConsumer = new SHAlpnConsumer();60static final HandshakeAbsence eeOnLoadAbsence = new SHAlpnAbsence();6162static final SSLStringizer alpnStringizer = new AlpnStringizer();6364// Encoding Charset to convert between String and byte[]65static final Charset alpnCharset;6667static {68@SuppressWarnings("removal")69String alpnCharsetString = AccessController.doPrivileged(70(PrivilegedAction<String>) ()71-> Security.getProperty("jdk.tls.alpnCharset"));72if ((alpnCharsetString == null)73|| (alpnCharsetString.length() == 0)) {74alpnCharsetString = "ISO_8859_1";75}76alpnCharset = Charset.forName(alpnCharsetString);77}7879/**80* The "application_layer_protocol_negotiation" extension.81*82* See RFC 7301 for the specification of this extension.83*/84static final class AlpnSpec implements SSLExtensionSpec {85final List<String> applicationProtocols;8687private AlpnSpec(String[] applicationProtocols) {88this.applicationProtocols = List.of(applicationProtocols);89}9091private AlpnSpec(HandshakeContext hc,92ByteBuffer buffer) throws IOException {93// ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.94if (buffer.remaining() < 2) {95throw hc.conContext.fatal(Alert.DECODE_ERROR,96new SSLProtocolException(97"Invalid application_layer_protocol_negotiation: " +98"insufficient data (length=" + buffer.remaining() + ")"));99}100101int listLen = Record.getInt16(buffer);102if (listLen < 2 || listLen != buffer.remaining()) {103throw hc.conContext.fatal(Alert.DECODE_ERROR,104new SSLProtocolException(105"Invalid application_layer_protocol_negotiation: " +106"incorrect list length (length=" + listLen + ")"));107}108109List<String> protocolNames = new LinkedList<>();110while (buffer.hasRemaining()) {111// opaque ProtocolName<1..2^8-1>, RFC 7301.112byte[] bytes = Record.getBytes8(buffer);113if (bytes.length == 0) {114throw hc.conContext.fatal(Alert.DECODE_ERROR,115new SSLProtocolException(116"Invalid application_layer_protocol_negotiation " +117"extension: empty application protocol name"));118}119120String appProtocol = new String(bytes, alpnCharset);121protocolNames.add(appProtocol);122}123124this.applicationProtocols =125Collections.unmodifiableList(protocolNames);126}127128@Override129public String toString() {130return applicationProtocols.toString();131}132}133134private static final class AlpnStringizer implements SSLStringizer {135@Override136public String toString(HandshakeContext hc, ByteBuffer buffer) {137try {138return (new AlpnSpec(hc, buffer)).toString();139} catch (IOException ioe) {140// For debug logging only, so please swallow exceptions.141return ioe.getMessage();142}143}144}145146/**147* Network data producer of the extension in a ClientHello148* handshake message.149*/150private static final class CHAlpnProducer implements HandshakeProducer {151static final int MAX_AP_LENGTH = 255;152static final int MAX_AP_LIST_LENGTH = 65535;153154// Prevent instantiation of this class.155private CHAlpnProducer() {156// blank157}158159@Override160public byte[] produce(ConnectionContext context,161HandshakeMessage message) throws IOException {162// The producing happens in client side only.163ClientHandshakeContext chc = (ClientHandshakeContext)context;164165// Is it a supported and enabled extension?166if (!chc.sslConfig.isAvailable(SSLExtension.CH_ALPN)) {167if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {168SSLLogger.info(169"Ignore client unavailable extension: " +170SSLExtension.CH_ALPN.name);171}172173chc.applicationProtocol = "";174chc.conContext.applicationProtocol = "";175return null;176}177178String[] laps = chc.sslConfig.applicationProtocols;179if ((laps == null) || (laps.length == 0)) {180if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {181SSLLogger.info(182"No available application protocols");183}184return null;185}186187// Produce the extension: first find the overall length188int listLength = 0; // ProtocolNameList length189for (String ap : laps) {190int length = ap.getBytes(alpnCharset).length;191if (length == 0) {192// log the configuration problem193if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {194SSLLogger.severe(195"Application protocol name cannot be empty");196}197198throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,199"Application protocol name cannot be empty");200}201202if (length <= MAX_AP_LENGTH) {203// opaque ProtocolName<1..2^8-1>, RFC 7301.204listLength += (length + 1);205} else {206// log the configuration problem207if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {208SSLLogger.severe(209"Application protocol name (" + ap +210") exceeds the size limit (" +211MAX_AP_LENGTH + " bytes)");212}213214throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,215"Application protocol name (" + ap +216") exceeds the size limit (" +217MAX_AP_LENGTH + " bytes)");218}219220if (listLength > MAX_AP_LIST_LENGTH) {221// log the configuration problem222if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {223SSLLogger.severe(224"The configured application protocols (" +225Arrays.toString(laps) +226") exceed the size limit (" +227MAX_AP_LIST_LENGTH + " bytes)");228}229230throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,231"The configured application protocols (" +232Arrays.toString(laps) +233") exceed the size limit (" +234MAX_AP_LIST_LENGTH + " bytes)");235}236}237238// ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.239byte[] extData = new byte[listLength + 2];240ByteBuffer m = ByteBuffer.wrap(extData);241Record.putInt16(m, listLength);242243// opaque ProtocolName<1..2^8-1>;244for (String ap : laps) {245Record.putBytes8(m, ap.getBytes(alpnCharset));246}247248// Update the context.249chc.handshakeExtensions.put(SSLExtension.CH_ALPN,250new AlpnSpec(chc.sslConfig.applicationProtocols));251252return extData;253}254}255256/**257* Network data consumer of the extension in a ClientHello258* handshake message.259*/260private static final class CHAlpnConsumer implements ExtensionConsumer {261// Prevent instantiation of this class.262private CHAlpnConsumer() {263// blank264}265266@Override267public void consume(ConnectionContext context,268HandshakeMessage message, ByteBuffer buffer) throws IOException {269// The consuming happens in server side only.270ServerHandshakeContext shc = (ServerHandshakeContext)context;271272// Is it a supported and enabled extension?273if (!shc.sslConfig.isAvailable(SSLExtension.CH_ALPN)) {274shc.applicationProtocol = "";275shc.conContext.applicationProtocol = "";276if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {277SSLLogger.info(278"Ignore server unavailable extension: " +279SSLExtension.CH_ALPN.name);280}281return; // ignore the extension282}283284// Is the extension enabled?285boolean noAPSelector;286if (shc.conContext.transport instanceof SSLEngine) {287noAPSelector = (shc.sslConfig.engineAPSelector == null);288} else {289noAPSelector = (shc.sslConfig.socketAPSelector == null);290}291292boolean noAlpnProtocols =293shc.sslConfig.applicationProtocols == null ||294shc.sslConfig.applicationProtocols.length == 0;295if (noAPSelector && noAlpnProtocols) {296shc.applicationProtocol = "";297shc.conContext.applicationProtocol = "";298if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {299SSLLogger.fine(300"Ignore server unenabled extension: " +301SSLExtension.CH_ALPN.name);302}303return; // ignore the extension304}305306// Parse the extension.307AlpnSpec spec = new AlpnSpec(shc, buffer);308309// Update the context.310if (noAPSelector) { // noAlpnProtocols is false311List<String> protocolNames = spec.applicationProtocols;312boolean matched = false;313// Use server application protocol preference order.314for (String ap : shc.sslConfig.applicationProtocols) {315if (protocolNames.contains(ap)) {316shc.applicationProtocol = ap;317shc.conContext.applicationProtocol = ap;318matched = true;319break;320}321}322323if (!matched) {324throw shc.conContext.fatal(Alert.NO_APPLICATION_PROTOCOL,325"No matching application layer protocol values");326}327} // Otherwise, applicationProtocol will be set by the328// application selector callback later.329330shc.handshakeExtensions.put(SSLExtension.CH_ALPN, spec);331332// No impact on session resumption.333//334// [RFC 7301] Unlike many other TLS extensions, this extension335// does not establish properties of the session, only of the336// connection. When session resumption or session tickets are337// used, the previous contents of this extension are irrelevant,338// and only the values in the new handshake messages are339// considered.340}341}342343/**344* The absence processing if the extension is not present in345* a ClientHello handshake message.346*/347private static final class CHAlpnAbsence implements HandshakeAbsence {348@Override349public void absent(ConnectionContext context,350HandshakeMessage message) throws IOException {351// The producing happens in server side only.352ServerHandshakeContext shc = (ServerHandshakeContext)context;353354// Please don't use the previous negotiated application protocol.355shc.applicationProtocol = "";356shc.conContext.applicationProtocol = "";357}358}359360/**361* Network data producer of the extension in the ServerHello362* handshake message.363*/364private static final class SHAlpnProducer implements HandshakeProducer {365// Prevent instantiation of this class.366private SHAlpnProducer() {367// blank368}369370@Override371public byte[] produce(ConnectionContext context,372HandshakeMessage message) throws IOException {373// The producing happens in client side only.374ServerHandshakeContext shc = (ServerHandshakeContext)context;375376// In response to ALPN request only377AlpnSpec requestedAlps =378(AlpnSpec)shc.handshakeExtensions.get(SSLExtension.CH_ALPN);379if (requestedAlps == null) {380// Ignore, this extension was not requested and accepted.381if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {382SSLLogger.fine(383"Ignore unavailable extension: " +384SSLExtension.SH_ALPN.name);385}386387shc.applicationProtocol = "";388shc.conContext.applicationProtocol = "";389return null;390}391392List<String> alps = requestedAlps.applicationProtocols;393if (shc.conContext.transport instanceof SSLEngine) {394if (shc.sslConfig.engineAPSelector != null) {395SSLEngine engine = (SSLEngine)shc.conContext.transport;396shc.applicationProtocol =397shc.sslConfig.engineAPSelector.apply(engine, alps);398if ((shc.applicationProtocol == null) ||399(!shc.applicationProtocol.isEmpty() &&400!alps.contains(shc.applicationProtocol))) {401throw shc.conContext.fatal(402Alert.NO_APPLICATION_PROTOCOL,403"No matching application layer protocol values");404}405}406} else {407if (shc.sslConfig.socketAPSelector != null) {408SSLSocket socket = (SSLSocket)shc.conContext.transport;409shc.applicationProtocol =410shc.sslConfig.socketAPSelector.apply(socket, alps);411if ((shc.applicationProtocol == null) ||412(!shc.applicationProtocol.isEmpty() &&413!alps.contains(shc.applicationProtocol))) {414throw shc.conContext.fatal(415Alert.NO_APPLICATION_PROTOCOL,416"No matching application layer protocol values");417}418}419}420421if ((shc.applicationProtocol == null) ||422(shc.applicationProtocol.isEmpty())) {423// Ignore, no negotiated application layer protocol.424shc.applicationProtocol = "";425shc.conContext.applicationProtocol = "";426if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {427SSLLogger.warning(428"Ignore, no negotiated application layer protocol");429}430431return null;432}433434// opaque ProtocolName<1..2^8-1>, RFC 7301.435byte[] bytes = shc.applicationProtocol.getBytes(alpnCharset);436int listLen = bytes.length + 1; // 1: length byte437438// ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.439byte[] extData = new byte[listLen + 2]; // 2: list length440ByteBuffer m = ByteBuffer.wrap(extData);441Record.putInt16(m, listLen);442Record.putBytes8(m, bytes);443444// Update the context.445shc.conContext.applicationProtocol = shc.applicationProtocol;446447// Clean or register the extension448//449// No further use of the request and respond extension any more.450shc.handshakeExtensions.remove(SSLExtension.CH_ALPN);451452return extData;453}454}455456/**457* Network data consumer of the extension in the ServerHello458* handshake message.459*/460private static final class SHAlpnConsumer implements ExtensionConsumer {461// Prevent instantiation of this class.462private SHAlpnConsumer() {463// blank464}465466@Override467public void consume(ConnectionContext context,468HandshakeMessage message, ByteBuffer buffer) throws IOException {469// The producing happens in client side only.470ClientHandshakeContext chc = (ClientHandshakeContext)context;471472// In response to ALPN request only473AlpnSpec requestedAlps =474(AlpnSpec)chc.handshakeExtensions.get(SSLExtension.CH_ALPN);475if (requestedAlps == null ||476requestedAlps.applicationProtocols == null ||477requestedAlps.applicationProtocols.isEmpty()) {478throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,479"Unexpected " + SSLExtension.CH_ALPN.name + " extension");480}481482// Parse the extension.483AlpnSpec spec = new AlpnSpec(chc, buffer);484485// Only one application protocol is allowed.486if (spec.applicationProtocols.size() != 1) {487throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,488"Invalid " + SSLExtension.CH_ALPN.name + " extension: " +489"Only one application protocol name " +490"is allowed in ServerHello message");491}492493// The respond application protocol must be one of the requested.494if (!requestedAlps.applicationProtocols.containsAll(495spec.applicationProtocols)) {496throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,497"Invalid " + SSLExtension.CH_ALPN.name + " extension: " +498"Only client specified application protocol " +499"is allowed in ServerHello message");500}501502// Update the context.503chc.applicationProtocol = spec.applicationProtocols.get(0);504chc.conContext.applicationProtocol = chc.applicationProtocol;505506// Clean or register the extension507//508// No further use of the request and respond extension any more.509chc.handshakeExtensions.remove(SSLExtension.CH_ALPN);510}511}512513/**514* The absence processing if the extension is not present in515* the ServerHello handshake message.516*/517private static final class SHAlpnAbsence implements HandshakeAbsence {518@Override519public void absent(ConnectionContext context,520HandshakeMessage message) throws IOException {521// The producing happens in client side only.522ClientHandshakeContext chc = (ClientHandshakeContext)context;523524// Please don't use the previous negotiated application protocol.525chc.applicationProtocol = "";526chc.conContext.applicationProtocol = "";527}528}529}530531532