1 /*
   2  * Copyright (c) 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.text.MessageFormat;
  31 import java.util.Locale;
  32 import javax.net.ssl.SSLProtocolException;
  33 
  34 import sun.security.ssl.ClientHello.ClientHelloMessage;
  35 import sun.security.ssl.SSLExtension.ExtensionConsumer;
  36 import sun.security.ssl.SSLHandshake.HandshakeMessage;
  37 import sun.security.ssl.SSLExtension.SSLExtensionSpec;
  38 import sun.security.ssl.ServerHello.ServerHelloMessage;
  39 import sun.security.util.HexDumpEncoder;
  40 
  41 public class CookieExtension {
  42     static final HandshakeProducer chNetworkProducer =
  43             new CHCookieProducer();
  44     static final ExtensionConsumer chOnLoadConcumer =
  45             new CHCookieConsumer();
  46     static final HandshakeConsumer chOnTradeConsumer =
  47             new CHCookieUpdate();
  48 
  49     static final HandshakeProducer hrrNetworkProducer =
  50             new HRRCookieProducer();
  51     static final ExtensionConsumer hrrOnLoadConcumer =
  52             new HRRCookieConsumer();
  53 
  54     static final HandshakeProducer hrrNetworkReproducer =
  55             new HRRCookieReproducer();
  56 
  57     static final CookieStringize cookieStringize =
  58             new CookieStringize();
  59 
  60     /**
  61      * The "cookie" extension.
  62      */
  63     static class CookieSpec implements SSLExtensionSpec {
  64         final byte[] cookie;
  65 
  66         CookieSpec(byte[] cookie) {
  67             this.cookie = cookie;
  68         }
  69 
  70         private CookieSpec(ByteBuffer m) throws IOException {
  71             // opaque cookie<1..2^16-1>;
  72             if (m.remaining() < 3) {
  73                 throw new SSLProtocolException(
  74                     "Invalid cookie extension: insufficient data");
  75             }
  76 
  77             this.cookie = Record.getBytes16(m);
  78         }
  79 
  80         @Override
  81         public String toString() {
  82             MessageFormat messageFormat = new MessageFormat(
  83                     "\"cookie\": '{'\n" +
  84                     "{0}\n" +
  85                     "'}',", Locale.ENGLISH);
  86             HexDumpEncoder hexEncoder = new HexDumpEncoder();
  87             Object[] messageFields = {
  88                 Utilities.indent(hexEncoder.encode(cookie))
  89             };
  90 
  91             return messageFormat.format(messageFields);
  92         }
  93     }
  94 
  95     private static final class CookieStringize implements SSLStringize {
  96         @Override
  97         public String toString(ByteBuffer buffer) {
  98             try {
  99                 return (new CookieSpec(buffer)).toString();
 100             } catch (IOException ioe) {
 101                 // For debug logging only, so please swallow exceptions.
 102                 return ioe.getMessage();
 103             }
 104         }
 105     }
 106 
 107     private static final
 108             class CHCookieProducer implements HandshakeProducer {
 109         // Prevent instantiation of this class.
 110         private CHCookieProducer() {
 111             // blank
 112         }
 113 
 114         @Override
 115         public byte[] produce(ConnectionContext context,
 116                 HandshakeMessage message) throws IOException {
 117             ClientHandshakeContext chc = (ClientHandshakeContext) context;
 118 
 119             // Is it a supported and enabled extension?
 120             if (!chc.sslConfig.isAvailable(SSLExtension.CH_COOKIE)) {
 121                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 122                     SSLLogger.fine(
 123                             "Ignore unavailable cookie extension");
 124                 }
 125                 return null;
 126             }
 127 
 128             // response to an HelloRetryRequest cookie
 129             CookieSpec spec = (CookieSpec)chc.handshakeExtensions.get(
 130                     SSLExtension.HRR_COOKIE);
 131 
 132             if (spec != null &&
 133                     spec.cookie != null && spec.cookie.length != 0) {
 134                 byte[] extData = new byte[spec.cookie.length + 2];
 135                 ByteBuffer m = ByteBuffer.wrap(extData);
 136                 Record.putBytes16(m, spec.cookie);
 137                 return extData;
 138             }
 139 
 140             return null;
 141         }
 142     }
 143 
 144     private static final
 145             class CHCookieConsumer implements ExtensionConsumer {
 146         // Prevent instantiation of this class.
 147         private CHCookieConsumer() {
 148             // blank
 149         }
 150 
 151         @Override
 152         public void consume(ConnectionContext context,
 153             HandshakeMessage message, ByteBuffer buffer) throws IOException {
 154             // The comsuming happens in server side only.
 155             ServerHandshakeContext shc = (ServerHandshakeContext)context;
 156 
 157             // Is it a supported and enabled extension?
 158             if (!shc.sslConfig.isAvailable(SSLExtension.CH_COOKIE)) {
 159                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 160                     SSLLogger.fine(
 161                             "Ignore unavailable cookie extension");
 162                 }
 163                 return;     // ignore the extension
 164             }
 165 
 166             CookieSpec spec;
 167             try {
 168                 spec = new CookieSpec(buffer);
 169             } catch (IOException ioe) {
 170                 shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
 171                 return;     // fatal() always throws, make the compiler happy.
 172             }
 173 
 174             shc.handshakeExtensions.put(SSLExtension.CH_COOKIE, spec);
 175 
 176             // No impact on session resumption.
 177             //
 178             // Note that the protocol version negotiation happens before the
 179             // session resumption negotiation.  And the session resumption
 180             // negotiation depends on the negotiated protocol version.
 181         }
 182     }
 183 
 184     private static final
 185             class CHCookieUpdate implements HandshakeConsumer {
 186         // Prevent instantiation of this class.
 187         private CHCookieUpdate() {
 188             // blank
 189         }
 190 
 191         @Override
 192         public void consume(ConnectionContext context,
 193                 HandshakeMessage message) throws IOException {
 194             // The comsuming happens in server side only.
 195             ServerHandshakeContext shc = (ServerHandshakeContext)context;
 196             ClientHelloMessage clientHello = (ClientHelloMessage)message;
 197 
 198             CookieSpec spec = (CookieSpec)
 199                     shc.handshakeExtensions.get(SSLExtension.CH_COOKIE);
 200             if (spec == null) {
 201                 // Ignore, no "cookie" extension requested.
 202                 return;
 203             }
 204 
 205             HelloCookieManager hcm =
 206                 shc.sslContext.getHelloCookieManager(shc.negotiatedProtocol);
 207             if (!hcm.isCookieValid(shc, clientHello, spec.cookie)) {
 208                 shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
 209                         "unrecognized cookie");
 210                 return;     // fatal() always throws, make the compiler happy.
 211             }
 212         }
 213     }
 214 
 215     private static final
 216             class HRRCookieProducer implements HandshakeProducer {
 217         // Prevent instantiation of this class.
 218         private HRRCookieProducer() {
 219             // blank
 220         }
 221 
 222         @Override
 223         public byte[] produce(ConnectionContext context,
 224                 HandshakeMessage message) throws IOException {
 225             // The producing happens in server side only.
 226             ServerHandshakeContext shc = (ServerHandshakeContext)context;
 227             ServerHelloMessage hrrm = (ServerHelloMessage)message;
 228 
 229             // Is it a supported and enabled extension?
 230             if (!shc.sslConfig.isAvailable(SSLExtension.HRR_COOKIE)) {
 231                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 232                     SSLLogger.fine(
 233                             "Ignore unavailable cookie extension");
 234                 }
 235                 return null;
 236             }
 237 
 238             HelloCookieManager hcm =
 239                 shc.sslContext.getHelloCookieManager(shc.negotiatedProtocol);
 240 
 241             byte[] cookie = hcm.createCookie(shc, hrrm.clientHello);
 242 
 243             byte[] extData = new byte[cookie.length + 2];
 244             ByteBuffer m = ByteBuffer.wrap(extData);
 245             Record.putBytes16(m, cookie);
 246 
 247             return extData;
 248         }
 249     }
 250 
 251     private static final
 252             class HRRCookieConsumer implements ExtensionConsumer {
 253         // Prevent instantiation of this class.
 254         private HRRCookieConsumer() {
 255             // blank
 256         }
 257 
 258         @Override
 259         public void consume(ConnectionContext context,
 260             HandshakeMessage message, ByteBuffer buffer) throws IOException {
 261             // The comsuming happens in client side only.
 262             ClientHandshakeContext chc = (ClientHandshakeContext)context;
 263 
 264             // Is it a supported and enabled extension?
 265             if (!chc.sslConfig.isAvailable(SSLExtension.HRR_COOKIE)) {
 266                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 267                     SSLLogger.fine(
 268                             "Ignore unavailable cookie extension");
 269                 }
 270                 return;     // ignore the extension
 271             }
 272 
 273             CookieSpec spec;
 274             try {
 275                 spec = new CookieSpec(buffer);
 276             } catch (IOException ioe) {
 277                 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
 278                 return;     // fatal() always throws, make the compiler happy.
 279             }
 280 
 281             chc.handshakeExtensions.put(SSLExtension.HRR_COOKIE, spec);
 282         }
 283     }
 284 
 285     private static final
 286             class HRRCookieReproducer implements HandshakeProducer {
 287         // Prevent instantiation of this class.
 288         private HRRCookieReproducer() {
 289             // blank
 290         }
 291 
 292         @Override
 293         public byte[] produce(ConnectionContext context,
 294                 HandshakeMessage message) throws IOException {
 295             // The producing happens in server side only.
 296             ServerHandshakeContext shc = (ServerHandshakeContext) context;
 297 
 298             // Is it a supported and enabled extension?
 299             if (!shc.sslConfig.isAvailable(SSLExtension.HRR_COOKIE)) {
 300                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 301                     SSLLogger.fine(
 302                             "Ignore unavailable cookie extension");
 303                 }
 304                 return null;
 305             }
 306 
 307             // copy of the ClientHello cookie
 308             CookieSpec spec = (CookieSpec)shc.handshakeExtensions.get(
 309                     SSLExtension.CH_COOKIE);
 310 
 311             if (spec != null &&
 312                     spec.cookie != null && spec.cookie.length != 0) {
 313                 byte[] extData = new byte[spec.cookie.length + 2];
 314                 ByteBuffer m = ByteBuffer.wrap(extData);
 315                 Record.putBytes16(m, spec.cookie);
 316                 return extData;
 317             }
 318 
 319             return null;
 320         }
 321     }
 322 }