1 /* 2 * Copyright (c) 2015, 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.security.MessageDigest; 31 import java.text.MessageFormat; 32 import java.util.Arrays; 33 import java.util.Locale; 34 import javax.net.ssl.SSLProtocolException; 35 import sun.security.ssl.ClientHello.ClientHelloMessage; 36 import static sun.security.ssl.SSLExtension.CH_RENEGOTIATION_INFO; 37 import sun.security.ssl.SSLExtension.ExtensionConsumer; 38 import static sun.security.ssl.SSLExtension.SH_RENEGOTIATION_INFO; 39 import sun.security.ssl.SSLExtension.SSLExtensionSpec; 40 import sun.security.ssl.SSLHandshake.HandshakeMessage; 41 import sun.security.util.ByteArrays; 42 43 /** 44 * Pack of the "renegotiation_info" extensions [RFC 5746]. 45 */ 46 final class RenegoInfoExtension { 47 static final HandshakeProducer chNetworkProducer = 48 new CHRenegotiationInfoProducer(); 49 static final ExtensionConsumer chOnLoadConsumer = 50 new CHRenegotiationInfoConsumer(); 51 static final HandshakeAbsence chOnLoadAbsence = 52 new CHRenegotiationInfoAbsence(); 53 54 static final HandshakeProducer shNetworkProducer = 55 new SHRenegotiationInfoProducer(); 56 static final ExtensionConsumer shOnLoadConsumer = 57 new SHRenegotiationInfoConsumer(); 58 static final HandshakeAbsence shOnLoadAbsence = 59 new SHRenegotiationInfoAbsence(); 60 61 static final SSLStringizer rniStringizer = 62 new RenegotiationInfoStringizer(); 63 64 /** 65 * The "renegotiation_info" extension. 66 */ 67 static final class RenegotiationInfoSpec implements SSLExtensionSpec { 68 // A nominal object that does not holding any real renegotiation info. 69 static final RenegotiationInfoSpec NOMINAL = 70 new RenegotiationInfoSpec(new byte[0]); 71 72 private final byte[] renegotiatedConnection; 73 RenegotiationInfoSpec(byte[] renegotiatedConnection)74 private RenegotiationInfoSpec(byte[] renegotiatedConnection) { 75 this.renegotiatedConnection = Arrays.copyOf( 76 renegotiatedConnection, renegotiatedConnection.length); 77 } 78 RenegotiationInfoSpec(ByteBuffer m)79 private RenegotiationInfoSpec(ByteBuffer m) throws IOException { 80 // Parse the extension. 81 if (!m.hasRemaining() || m.remaining() < 1) { 82 throw new SSLProtocolException( 83 "Invalid renegotiation_info extension data: " + 84 "insufficient data"); 85 } 86 this.renegotiatedConnection = Record.getBytes8(m); 87 } 88 89 @Override toString()90 public String toString() { 91 MessageFormat messageFormat = new MessageFormat( 92 "\"renegotiated connection\": '['{0}']'", Locale.ENGLISH); 93 if (renegotiatedConnection.length == 0) { 94 Object[] messageFields = { 95 "<no renegotiated connection>" 96 }; 97 return messageFormat.format(messageFields); 98 } else { 99 Object[] messageFields = { 100 Utilities.toHexString(renegotiatedConnection) 101 }; 102 return messageFormat.format(messageFields); 103 } 104 } 105 } 106 107 private static final 108 class RenegotiationInfoStringizer implements SSLStringizer { 109 @Override toString(ByteBuffer buffer)110 public String toString(ByteBuffer buffer) { 111 try { 112 return (new RenegotiationInfoSpec(buffer)).toString(); 113 } catch (IOException ioe) { 114 // For debug logging only, so please swallow exceptions. 115 return ioe.getMessage(); 116 } 117 } 118 } 119 120 /** 121 * Network data producer of a "renegotiation_info" extension in 122 * the ClientHello handshake message. 123 */ 124 private static final 125 class CHRenegotiationInfoProducer implements HandshakeProducer { 126 // Prevent instantiation of this class. CHRenegotiationInfoProducer()127 private CHRenegotiationInfoProducer() { 128 // blank 129 } 130 131 @Override produce(ConnectionContext context, HandshakeMessage message)132 public byte[] produce(ConnectionContext context, 133 HandshakeMessage message) throws IOException { 134 // The producing happens in client side only. 135 ClientHandshakeContext chc = (ClientHandshakeContext)context; 136 137 // Is it a supported and enabled extension? 138 if (!chc.sslConfig.isAvailable(CH_RENEGOTIATION_INFO)) { 139 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 140 SSLLogger.fine( 141 "Ignore unavailable renegotiation_info extension"); 142 } 143 144 return null; 145 } 146 147 if (!chc.conContext.isNegotiated) { 148 if (chc.activeCipherSuites.contains( 149 CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) { 150 // Using the the TLS_EMPTY_RENEGOTIATION_INFO_SCSV instead. 151 return null; 152 } 153 154 // initial handshaking. 155 // 156 // If this is the initial handshake for a connection, then the 157 // "renegotiated_connection" field is of zero length in both 158 // the ClientHello and the ServerHello. [RFC 5746] 159 byte[] extData = new byte[] { 0x00 }; 160 chc.handshakeExtensions.put( 161 CH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL); 162 163 return extData; 164 } else if (chc.conContext.secureRenegotiation) { 165 // secure renegotiation 166 // 167 // For ClientHello handshake message in renegotiation, this 168 // field contains the "client_verify_data". 169 byte[] extData = 170 new byte[chc.conContext.clientVerifyData.length + 1]; 171 ByteBuffer m = ByteBuffer.wrap(extData); 172 Record.putBytes8(m, chc.conContext.clientVerifyData); 173 174 // The conContext.clientVerifyData will be used for further 175 // processing, so it does not matter to save whatever in the 176 // RenegotiationInfoSpec object. 177 chc.handshakeExtensions.put( 178 CH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL); 179 180 return extData; 181 } else { // not secure renegotiation 182 if (HandshakeContext.allowUnsafeRenegotiation) { 183 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 184 SSLLogger.warning("Using insecure renegotiation"); 185 } 186 187 return null; 188 } else { 189 // terminate the session. 190 throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, 191 "insecure renegotiation is not allowed"); 192 } 193 } 194 } 195 } 196 197 /** 198 * Network data producer of a "renegotiation_info" extension in 199 * the ServerHello handshake message. 200 */ 201 private static final 202 class CHRenegotiationInfoConsumer implements ExtensionConsumer { 203 // Prevent instantiation of this class. CHRenegotiationInfoConsumer()204 private CHRenegotiationInfoConsumer() { 205 // blank 206 } 207 208 @Override consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer)209 public void consume(ConnectionContext context, 210 HandshakeMessage message, ByteBuffer buffer) throws IOException { 211 212 // The consuming happens in server side only. 213 ServerHandshakeContext shc = (ServerHandshakeContext)context; 214 215 // Is it a supported and enabled extension? 216 if (!shc.sslConfig.isAvailable(CH_RENEGOTIATION_INFO)) { 217 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 218 SSLLogger.fine("Ignore unavailable extension: " + 219 CH_RENEGOTIATION_INFO.name); 220 } 221 return; // ignore the extension 222 } 223 224 // Parse the extension. 225 RenegotiationInfoSpec spec; 226 try { 227 spec = new RenegotiationInfoSpec(buffer); 228 } catch (IOException ioe) { 229 throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); 230 } 231 232 if (!shc.conContext.isNegotiated) { 233 // initial handshaking. 234 if (spec.renegotiatedConnection.length != 0) { 235 throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 236 "Invalid renegotiation_info extension data: not empty"); 237 } 238 shc.conContext.secureRenegotiation = true; 239 } else { 240 if (!shc.conContext.secureRenegotiation) { 241 // Unexpected RI extension for insecure renegotiation, 242 // abort the handshake with a fatal handshake_failure alert. 243 throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, 244 "The renegotiation_info is present in a insecure " + 245 "renegotiation"); 246 } else { 247 // verify the client_verify_data value 248 if (!MessageDigest.isEqual(shc.conContext.clientVerifyData, 249 spec.renegotiatedConnection)) { 250 throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 251 "Invalid renegotiation_info extension data: " + 252 "incorrect verify data in ClientHello"); 253 } 254 } 255 } 256 257 // Update the context. 258 // 259 // The conContext.clientVerifyData will be used for further 260 // processing, so it does not matter to save whatever in the 261 // RenegotiationInfoSpec object. 262 shc.handshakeExtensions.put( 263 CH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL); 264 265 // No impact on session resumption. 266 } 267 } 268 269 /** 270 * The absence processing if a "renegotiation_info" extension is 271 * not present in the ClientHello handshake message. 272 */ 273 private static final 274 class CHRenegotiationInfoAbsence implements HandshakeAbsence { 275 @Override absent(ConnectionContext context, HandshakeMessage message)276 public void absent(ConnectionContext context, 277 HandshakeMessage message) throws IOException { 278 // The producing happens in server side only. 279 ServerHandshakeContext shc = (ServerHandshakeContext)context; 280 ClientHelloMessage clientHello = (ClientHelloMessage)message; 281 282 if (!shc.conContext.isNegotiated) { 283 // initial handshaking. 284 for (int id : clientHello.cipherSuiteIds) { 285 if (id == 286 CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV.id) { 287 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 288 SSLLogger.finest( 289 "Safe renegotiation, using the SCSV signgling"); 290 } 291 shc.conContext.secureRenegotiation = true; 292 return; 293 } 294 } 295 296 if (!HandshakeContext.allowLegacyHelloMessages) { 297 throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, 298 "Failed to negotiate the use of secure renegotiation"); 299 } // otherwise, allow legacy hello message 300 301 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 302 SSLLogger.warning("Warning: No renegotiation " + 303 "indication in ClientHello, allow legacy ClientHello"); 304 } 305 306 shc.conContext.secureRenegotiation = false; 307 } else if (shc.conContext.secureRenegotiation) { 308 // Require secure renegotiation, terminate the connection. 309 throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, 310 "Inconsistent secure renegotiation indication"); 311 } else { // renegotiation, not secure 312 if (HandshakeContext.allowUnsafeRenegotiation) { 313 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 314 SSLLogger.warning("Using insecure renegotiation"); 315 } 316 } else { 317 // Unsafe renegotiation should have been aborted in 318 // ealier processes. 319 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 320 SSLLogger.fine("Terminate insecure renegotiation"); 321 } 322 throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, 323 "Unsafe renegotiation is not allowed"); 324 } 325 } 326 } 327 } 328 329 /** 330 * Network data producer of a "renegotiation_info" extension in 331 * the ServerHello handshake message. 332 */ 333 private static final 334 class SHRenegotiationInfoProducer implements HandshakeProducer { 335 // Prevent instantiation of this class. SHRenegotiationInfoProducer()336 private SHRenegotiationInfoProducer() { 337 // blank 338 } 339 340 @Override produce(ConnectionContext context, HandshakeMessage message)341 public byte[] produce(ConnectionContext context, 342 HandshakeMessage message) throws IOException { 343 // The producing happens in server side only. 344 ServerHandshakeContext shc = (ServerHandshakeContext)context; 345 346 // In response to "renegotiation_info" extension request only. 347 RenegotiationInfoSpec requestedSpec = (RenegotiationInfoSpec) 348 shc.handshakeExtensions.get(CH_RENEGOTIATION_INFO); 349 if (requestedSpec == null && !shc.conContext.secureRenegotiation) { 350 // Ignore, no renegotiation_info extension or SCSV signgling 351 // requested. 352 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 353 SSLLogger.finest( 354 "Ignore unavailable renegotiation_info extension"); 355 } 356 return null; // ignore the extension 357 } 358 359 if (!shc.conContext.secureRenegotiation) { 360 // Ignore, no secure renegotiation is negotiated. 361 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 362 SSLLogger.finest( 363 "No secure renegotiation has been negotiated"); 364 } 365 return null; // ignore the extension 366 } 367 368 if (!shc.conContext.isNegotiated) { 369 // initial handshaking. 370 // 371 // If this is the initial handshake for a connection, then the 372 // "renegotiated_connection" field is of zero length in both 373 // the ClientHello and the ServerHello. [RFC 5746] 374 byte[] extData = new byte[] { 0x00 }; 375 376 // The conContext.client/serverVerifyData will be used for 377 // further processing, so it does not matter to save whatever 378 // in the RenegotiationInfoSpec object. 379 shc.handshakeExtensions.put( 380 SH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL); 381 382 return extData; 383 } else { 384 // secure renegotiation 385 // 386 // For secure renegotiation, the server MUST include a 387 // "renegotiation_info" extension containing the saved 388 // client_verify_data and server_verify_data in the ServerHello. 389 int infoLen = shc.conContext.clientVerifyData.length + 390 shc.conContext.serverVerifyData.length; 391 byte[] extData = new byte[infoLen + 1]; 392 ByteBuffer m = ByteBuffer.wrap(extData); 393 Record.putInt8(m, infoLen); 394 m.put(shc.conContext.clientVerifyData); 395 m.put(shc.conContext.serverVerifyData); 396 397 // The conContext.client/serverVerifyData will be used for 398 // further processing, so it does not matter to save whatever 399 // in the RenegotiationInfoSpec object. 400 shc.handshakeExtensions.put( 401 SH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL); 402 403 return extData; 404 } 405 } 406 } 407 408 /** 409 * Network data consumer of a "renegotiation_info" extension in 410 * the ServerHello handshake message. 411 */ 412 private static final 413 class SHRenegotiationInfoConsumer implements ExtensionConsumer { 414 // Prevent instantiation of this class. SHRenegotiationInfoConsumer()415 private SHRenegotiationInfoConsumer() { 416 // blank 417 } 418 419 @Override consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer)420 public void consume(ConnectionContext context, 421 HandshakeMessage message, ByteBuffer buffer) throws IOException { 422 // The producing happens in client side only. 423 ClientHandshakeContext chc = (ClientHandshakeContext)context; 424 425 // In response to the client renegotiation_info extension request 426 // or SCSV signling, which is mandatory for ClientHello message. 427 RenegotiationInfoSpec requestedSpec = (RenegotiationInfoSpec) 428 chc.handshakeExtensions.get(CH_RENEGOTIATION_INFO); 429 if (requestedSpec == null && 430 !chc.activeCipherSuites.contains( 431 CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) { 432 throw chc.conContext.fatal(Alert.INTERNAL_ERROR, 433 "Missing renegotiation_info and SCSV detected in " + 434 "ClientHello"); 435 } 436 437 // Parse the extension. 438 RenegotiationInfoSpec spec; 439 try { 440 spec = new RenegotiationInfoSpec(buffer); 441 } catch (IOException ioe) { 442 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); 443 } 444 445 446 if (!chc.conContext.isNegotiated) { // initial handshake 447 // If the extension is present, set the secure_renegotiation 448 // flag to TRUE. The client MUST then verify that the 449 // length of the "renegotiated_connection" field is zero, 450 // and if it is not, MUST abort the handshake (by sending 451 // a fatal handshake_failure alert). [RFC 5746] 452 if (spec.renegotiatedConnection.length != 0) { 453 throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, 454 "Invalid renegotiation_info in ServerHello: " + 455 "not empty renegotiated_connection"); 456 } 457 458 chc.conContext.secureRenegotiation = true; 459 } else { // renegotiation 460 // The client MUST then verify that the first half of the 461 // "renegotiated_connection" field is equal to the saved 462 // client_verify_data value, and the second half is equal to the 463 // saved server_verify_data value. If they are not, the client 464 // MUST abort the handshake. [RFC 5746] 465 int infoLen = chc.conContext.clientVerifyData.length + 466 chc.conContext.serverVerifyData.length; 467 if (spec.renegotiatedConnection.length != infoLen) { 468 throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, 469 "Invalid renegotiation_info in ServerHello: " + 470 "invalid renegotiated_connection length (" + 471 spec.renegotiatedConnection.length + ")"); 472 } 473 474 byte[] cvd = chc.conContext.clientVerifyData; 475 if (!ByteArrays.isEqual(spec.renegotiatedConnection, 476 0, cvd.length, cvd, 0, cvd.length)) { 477 throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, 478 "Invalid renegotiation_info in ServerHello: " + 479 "unmatched client_verify_data value"); 480 } 481 byte[] svd = chc.conContext.serverVerifyData; 482 if (!ByteArrays.isEqual(spec.renegotiatedConnection, 483 cvd.length, infoLen, svd, 0, svd.length)) { 484 throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, 485 "Invalid renegotiation_info in ServerHello: " + 486 "unmatched server_verify_data value"); 487 } 488 } 489 490 // Update the context. 491 chc.handshakeExtensions.put( 492 SH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL); 493 494 // No impact on session resumption. 495 } 496 } 497 498 /** 499 * The absence processing if a "renegotiation_info" extension is 500 * not present in the ServerHello handshake message. 501 */ 502 private static final 503 class SHRenegotiationInfoAbsence implements HandshakeAbsence { 504 @Override absent(ConnectionContext context, HandshakeMessage message)505 public void absent(ConnectionContext context, 506 HandshakeMessage message) throws IOException { 507 // The producing happens in client side only. 508 ClientHandshakeContext chc = (ClientHandshakeContext)context; 509 510 // In response to the client renegotiation_info extension request 511 // or SCSV signling, which is mandatory for ClientHello message. 512 RenegotiationInfoSpec requestedSpec = (RenegotiationInfoSpec) 513 chc.handshakeExtensions.get(CH_RENEGOTIATION_INFO); 514 if (requestedSpec == null && 515 !chc.activeCipherSuites.contains( 516 CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) { 517 throw chc.conContext.fatal(Alert.INTERNAL_ERROR, 518 "Missing renegotiation_info and SCSV detected in " + 519 "ClientHello"); 520 } 521 522 if (!chc.conContext.isNegotiated) { 523 // initial handshaking. 524 if (!HandshakeContext.allowLegacyHelloMessages) { 525 throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, 526 "Failed to negotiate the use of secure renegotiation"); 527 } // otherwise, allow legacy hello message 528 529 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 530 SSLLogger.warning("Warning: No renegotiation " + 531 "indication in ServerHello, allow legacy ServerHello"); 532 } 533 534 chc.conContext.secureRenegotiation = false; 535 } else if (chc.conContext.secureRenegotiation) { 536 // Require secure renegotiation, terminate the connection. 537 throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, 538 "Inconsistent secure renegotiation indication"); 539 } else { // renegotiation, not secure 540 if (HandshakeContext.allowUnsafeRenegotiation) { 541 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 542 SSLLogger.warning("Using insecure renegotiation"); 543 } 544 } else { 545 // Unsafe renegotiation should have been aborted in 546 // ealier processes. 547 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 548 SSLLogger.fine("Terminate insecure renegotiation"); 549 } 550 throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, 551 "Unsafe renegotiation is not allowed"); 552 } 553 } 554 } 555 } 556 } 557