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 }