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