Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/ssl/AlpnExtension.java
38830 views
/*1* Copyright (c) 2015, 2018, 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.StandardCharsets;30import java.util.Arrays;31import java.util.Collections;32import java.util.LinkedList;33import java.util.List;34import javax.net.ssl.SSLEngine;35import javax.net.ssl.SSLProtocolException;36import javax.net.ssl.SSLSocket;37import sun.security.ssl.SSLExtension.ExtensionConsumer;38import sun.security.ssl.SSLExtension.SSLExtensionSpec;39import sun.security.ssl.SSLHandshake.HandshakeMessage;4041/**42* Pack of the "application_layer_protocol_negotiation" extensions [RFC 7301].43*/44final class AlpnExtension {45static final HandshakeProducer chNetworkProducer = new CHAlpnProducer();46static final ExtensionConsumer chOnLoadConsumer = new CHAlpnConsumer();47static final HandshakeAbsence chOnLoadAbsence = new CHAlpnAbsence();4849static final HandshakeProducer shNetworkProducer = new SHAlpnProducer();50static final ExtensionConsumer shOnLoadConsumer = new SHAlpnConsumer();51static final HandshakeAbsence shOnLoadAbsence = new SHAlpnAbsence();5253// Note: we reuse ServerHello operations for EncryptedExtensions for now.54// Please be careful about any code or specification changes in the future.55static final HandshakeProducer eeNetworkProducer = new SHAlpnProducer();56static final ExtensionConsumer eeOnLoadConsumer = new SHAlpnConsumer();57static final HandshakeAbsence eeOnLoadAbsence = new SHAlpnAbsence();5859static final SSLStringizer alpnStringizer = new AlpnStringizer();6061/**62* The "application_layer_protocol_negotiation" extension.63*64* See RFC 7301 for the specification of this extension.65*/66static final class AlpnSpec implements SSLExtensionSpec {67final List<String> applicationProtocols;6869private AlpnSpec(String[] applicationProtocols) {70this.applicationProtocols = Collections.unmodifiableList(71Arrays.asList(applicationProtocols));72}7374private AlpnSpec(ByteBuffer buffer) throws IOException {75// ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.76if (buffer.remaining() < 2) {77throw new SSLProtocolException(78"Invalid application_layer_protocol_negotiation: " +79"insufficient data (length=" + buffer.remaining() + ")");80}8182int listLen = Record.getInt16(buffer);83if (listLen < 2 || listLen != buffer.remaining()) {84throw new SSLProtocolException(85"Invalid application_layer_protocol_negotiation: " +86"incorrect list length (length=" + listLen + ")");87}8889List<String> protocolNames = new LinkedList<>();90while (buffer.hasRemaining()) {91// opaque ProtocolName<1..2^8-1>, RFC 7301.92byte[] bytes = Record.getBytes8(buffer);93if (bytes.length == 0) {94throw new SSLProtocolException(95"Invalid application_layer_protocol_negotiation " +96"extension: empty application protocol name");97}9899String appProtocol = new String(bytes, StandardCharsets.UTF_8);100protocolNames.add(appProtocol);101}102103this.applicationProtocols =104Collections.unmodifiableList(protocolNames);105}106107@Override108public String toString() {109return applicationProtocols.toString();110}111}112113private static final class AlpnStringizer implements SSLStringizer {114@Override115public String toString(ByteBuffer buffer) {116try {117return (new AlpnSpec(buffer)).toString();118} catch (IOException ioe) {119// For debug logging only, so please swallow exceptions.120return ioe.getMessage();121}122}123}124125/**126* Network data producer of the extension in a ClientHello127* handshake message.128*/129private static final class CHAlpnProducer implements HandshakeProducer {130static final int MAX_AP_LENGTH = 255;131static final int MAX_AP_LIST_LENGTH = 65535;132133// Prevent instantiation of this class.134private CHAlpnProducer() {135// blank136}137138@Override139public byte[] produce(ConnectionContext context,140HandshakeMessage message) throws IOException {141// The producing happens in client side only.142ClientHandshakeContext chc = (ClientHandshakeContext)context;143144// Is it a supported and enabled extension?145if (!chc.sslConfig.isAvailable(SSLExtension.CH_ALPN)) {146if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {147SSLLogger.info(148"Ignore client unavailable extension: " +149SSLExtension.CH_ALPN.name);150}151152chc.applicationProtocol = "";153chc.conContext.applicationProtocol = "";154return null;155}156157String[] laps = chc.sslConfig.applicationProtocols;158if ((laps == null) || (laps.length == 0)) {159if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {160SSLLogger.info(161"No available application protocols");162}163return null;164}165166// Produce the extension.167int listLength = 0; // ProtocolNameList length168for (String ap : laps) {169int length = ap.getBytes(StandardCharsets.UTF_8).length;170if (length == 0) {171// log the configuration problem172if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {173SSLLogger.severe(174"Application protocol name cannot be empty");175}176177throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,178"Application protocol name cannot be empty");179}180181if (length <= MAX_AP_LENGTH) {182// opaque ProtocolName<1..2^8-1>, RFC 7301.183listLength += (length + 1);184} else {185// log the configuration problem186if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {187SSLLogger.severe(188"Application protocol name (" + ap +189") exceeds the size limit (" +190MAX_AP_LENGTH + " bytes)");191}192193throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,194"Application protocol name (" + ap +195") exceeds the size limit (" +196MAX_AP_LENGTH + " bytes)");197}198199if (listLength > MAX_AP_LIST_LENGTH) {200// log the configuration problem201if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {202SSLLogger.severe(203"The configured application protocols (" +204Arrays.toString(laps) +205") exceed the size limit (" +206MAX_AP_LIST_LENGTH + " bytes)");207}208209throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,210"The configured application protocols (" +211Arrays.toString(laps) +212") exceed the size limit (" +213MAX_AP_LIST_LENGTH + " bytes)");214}215}216217// ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.218byte[] extData = new byte[listLength + 2];219ByteBuffer m = ByteBuffer.wrap(extData);220Record.putInt16(m, listLength);221for (String ap : laps) {222Record.putBytes8(m, ap.getBytes(StandardCharsets.UTF_8));223}224225// Update the context.226chc.handshakeExtensions.put(SSLExtension.CH_ALPN,227new AlpnSpec(chc.sslConfig.applicationProtocols));228229return extData;230}231}232233/**234* Network data consumer of the extension in a ClientHello235* handshake message.236*/237private static final class CHAlpnConsumer implements ExtensionConsumer {238// Prevent instantiation of this class.239private CHAlpnConsumer() {240// blank241}242243@Override244public void consume(ConnectionContext context,245HandshakeMessage message, ByteBuffer buffer) throws IOException {246// The consuming happens in server side only.247ServerHandshakeContext shc = (ServerHandshakeContext)context;248249// Is it a supported and enabled extension?250if (!shc.sslConfig.isAvailable(SSLExtension.CH_ALPN)) {251shc.applicationProtocol = "";252shc.conContext.applicationProtocol = "";253if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {254SSLLogger.info(255"Ignore server unavailable extension: " +256SSLExtension.CH_ALPN.name);257}258return; // ignore the extension259}260261// Is the extension enabled?262boolean noAPSelector;263if (shc.conContext.transport instanceof SSLEngine) {264noAPSelector = (shc.sslConfig.engineAPSelector == null);265} else {266noAPSelector = (shc.sslConfig.socketAPSelector == null);267}268269boolean noAlpnProtocols =270shc.sslConfig.applicationProtocols == null ||271shc.sslConfig.applicationProtocols.length == 0;272if (noAPSelector && noAlpnProtocols) {273shc.applicationProtocol = "";274shc.conContext.applicationProtocol = "";275if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {276SSLLogger.fine(277"Ignore server unenabled extension: " +278SSLExtension.CH_ALPN.name);279}280return; // ignore the extension281}282283// Parse the extension.284AlpnSpec spec;285try {286spec = new AlpnSpec(buffer);287} catch (IOException ioe) {288throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);289}290291// Update the context.292if (noAPSelector) { // noAlpnProtocols is false293List<String> protocolNames = spec.applicationProtocols;294boolean matched = false;295// Use server application protocol preference order.296for (String ap : shc.sslConfig.applicationProtocols) {297if (protocolNames.contains(ap)) {298shc.applicationProtocol = ap;299shc.conContext.applicationProtocol = ap;300matched = true;301break;302}303}304305if (!matched) {306throw shc.conContext.fatal(Alert.NO_APPLICATION_PROTOCOL,307"No matching application layer protocol values");308}309} // Otherwise, applicationProtocol will be set by the310// application selector callback later.311312shc.handshakeExtensions.put(SSLExtension.CH_ALPN, spec);313314// No impact on session resumption.315//316// [RFC 7301] Unlike many other TLS extensions, this extension317// does not establish properties of the session, only of the318// connection. When session resumption or session tickets are319// used, the previous contents of this extension are irrelevant,320// and only the values in the new handshake messages are321// considered.322}323}324325/**326* The absence processing if the extension is not present in327* a ClientHello handshake message.328*/329private static final class CHAlpnAbsence implements HandshakeAbsence {330@Override331public void absent(ConnectionContext context,332HandshakeMessage message) throws IOException {333// The producing happens in server side only.334ServerHandshakeContext shc = (ServerHandshakeContext)context;335336// Please don't use the previous negotiated application protocol.337shc.applicationProtocol = "";338shc.conContext.applicationProtocol = "";339}340}341342/**343* Network data producer of the extension in the ServerHello344* handshake message.345*/346private static final class SHAlpnProducer implements HandshakeProducer {347// Prevent instantiation of this class.348private SHAlpnProducer() {349// blank350}351352@Override353public byte[] produce(ConnectionContext context,354HandshakeMessage message) throws IOException {355// The producing happens in client side only.356ServerHandshakeContext shc = (ServerHandshakeContext)context;357358// In response to ALPN request only359AlpnSpec requestedAlps =360(AlpnSpec)shc.handshakeExtensions.get(SSLExtension.CH_ALPN);361if (requestedAlps == null) {362// Ignore, this extension was not requested and accepted.363if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {364SSLLogger.fine(365"Ignore unavailable extension: " +366SSLExtension.SH_ALPN.name);367}368369shc.applicationProtocol = "";370shc.conContext.applicationProtocol = "";371return null;372}373374List<String> alps = requestedAlps.applicationProtocols;375if (shc.conContext.transport instanceof SSLEngine) {376if (shc.sslConfig.engineAPSelector != null) {377SSLEngine engine = (SSLEngine)shc.conContext.transport;378shc.applicationProtocol =379shc.sslConfig.engineAPSelector.apply(engine, alps);380if ((shc.applicationProtocol == null) ||381(!shc.applicationProtocol.isEmpty() &&382!alps.contains(shc.applicationProtocol))) {383throw shc.conContext.fatal(384Alert.NO_APPLICATION_PROTOCOL,385"No matching application layer protocol values");386}387}388} else {389if (shc.sslConfig.socketAPSelector != null) {390SSLSocket socket = (SSLSocket)shc.conContext.transport;391shc.applicationProtocol =392shc.sslConfig.socketAPSelector.apply(socket, alps);393if ((shc.applicationProtocol == null) ||394(!shc.applicationProtocol.isEmpty() &&395!alps.contains(shc.applicationProtocol))) {396throw shc.conContext.fatal(397Alert.NO_APPLICATION_PROTOCOL,398"No matching application layer protocol values");399}400}401}402403if ((shc.applicationProtocol == null) ||404(shc.applicationProtocol.isEmpty())) {405// Ignore, no negotiated application layer protocol.406shc.applicationProtocol = "";407shc.conContext.applicationProtocol = "";408if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {409SSLLogger.warning(410"Ignore, no negotiated application layer protocol");411}412413return null;414}415416// opaque ProtocolName<1..2^8-1>, RFC 7301.417int listLen = shc.applicationProtocol.length() + 1;418// 1: length byte419// ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.420byte[] extData = new byte[listLen + 2]; // 2: list length421ByteBuffer m = ByteBuffer.wrap(extData);422Record.putInt16(m, listLen);423Record.putBytes8(m,424shc.applicationProtocol.getBytes(StandardCharsets.UTF_8));425426// Update the context.427shc.conContext.applicationProtocol = shc.applicationProtocol;428429// Clean or register the extension430//431// No further use of the request and respond extension any more.432shc.handshakeExtensions.remove(SSLExtension.CH_ALPN);433434return extData;435}436}437438/**439* Network data consumer of the extension in the ServerHello440* handshake message.441*/442private static final class SHAlpnConsumer implements ExtensionConsumer {443// Prevent instantiation of this class.444private SHAlpnConsumer() {445// blank446}447448@Override449public void consume(ConnectionContext context,450HandshakeMessage message, ByteBuffer buffer) throws IOException {451// The producing happens in client side only.452ClientHandshakeContext chc = (ClientHandshakeContext)context;453454// In response to ALPN request only455AlpnSpec requestedAlps =456(AlpnSpec)chc.handshakeExtensions.get(SSLExtension.CH_ALPN);457if (requestedAlps == null ||458requestedAlps.applicationProtocols == null ||459requestedAlps.applicationProtocols.isEmpty()) {460throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,461"Unexpected " + SSLExtension.CH_ALPN.name + " extension");462}463464// Parse the extension.465AlpnSpec spec;466try {467spec = new AlpnSpec(buffer);468} catch (IOException ioe) {469throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);470}471472// Only one application protocol is allowed.473if (spec.applicationProtocols.size() != 1) {474throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,475"Invalid " + SSLExtension.CH_ALPN.name + " extension: " +476"Only one application protocol name " +477"is allowed in ServerHello message");478}479480// The respond application protocol must be one of the requested.481if (!requestedAlps.applicationProtocols.containsAll(482spec.applicationProtocols)) {483throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,484"Invalid " + SSLExtension.CH_ALPN.name + " extension: " +485"Only client specified application protocol " +486"is allowed in ServerHello message");487}488489// Update the context.490chc.applicationProtocol = spec.applicationProtocols.get(0);491chc.conContext.applicationProtocol = chc.applicationProtocol;492493// Clean or register the extension494//495// No further use of the request and respond extension any more.496chc.handshakeExtensions.remove(SSLExtension.CH_ALPN);497}498}499500/**501* The absence processing if the extension is not present in502* the ServerHello handshake message.503*/504private static final class SHAlpnAbsence implements HandshakeAbsence {505@Override506public void absent(ConnectionContext context,507HandshakeMessage message) throws IOException {508// The producing happens in client side only.509ClientHandshakeContext chc = (ClientHandshakeContext)context;510511// Please don't use the previous negotiated application protocol.512chc.applicationProtocol = "";513chc.conContext.applicationProtocol = "";514}515}516}517518519