1 /* 2 * Copyright (c) 1996, 2019, 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.util.LinkedList; 31 import javax.net.ssl.SSLHandshakeException; 32 33 import sun.security.ssl.SSLCipher.SSLWriteCipher; 34 35 /** 36 * {@code OutputRecord} implementation for {@code SSLEngine}. 37 */ 38 final class SSLEngineOutputRecord extends OutputRecord implements SSLRecord { 39 40 private HandshakeFragment fragmenter = null; 41 private boolean isTalkingToV2 = false; // SSLv2Hello 42 private ByteBuffer v2ClientHello = null; // SSLv2Hello 43 44 private volatile boolean isCloseWaiting = false; 45 SSLEngineOutputRecord(HandshakeHash handshakeHash)46 SSLEngineOutputRecord(HandshakeHash handshakeHash) { 47 super(handshakeHash, SSLWriteCipher.nullTlsWriteCipher()); 48 49 this.packetSize = SSLRecord.maxRecordSize; 50 this.protocolVersion = ProtocolVersion.NONE; 51 } 52 53 @Override close()54 public void close() throws IOException { 55 recordLock.lock(); 56 try { 57 if (!isClosed) { 58 if (fragmenter != null && fragmenter.hasAlert()) { 59 isCloseWaiting = true; 60 } else { 61 super.close(); 62 } 63 } 64 } finally { 65 recordLock.unlock(); 66 } 67 } 68 isClosed()69 boolean isClosed() { 70 return isClosed || isCloseWaiting; 71 } 72 73 @Override encodeAlert(byte level, byte description)74 void encodeAlert(byte level, byte description) throws IOException { 75 if (isClosed()) { 76 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { 77 SSLLogger.warning("outbound has closed, ignore outbound " + 78 "alert message: " + Alert.nameOf(description)); 79 } 80 return; 81 } 82 83 if (fragmenter == null) { 84 fragmenter = new HandshakeFragment(); 85 } 86 87 fragmenter.queueUpAlert(level, description); 88 } 89 90 @Override encodeHandshake(byte[] source, int offset, int length)91 void encodeHandshake(byte[] source, 92 int offset, int length) throws IOException { 93 if (isClosed()) { 94 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { 95 SSLLogger.warning("outbound has closed, ignore outbound " + 96 "handshake message", 97 ByteBuffer.wrap(source, offset, length)); 98 } 99 return; 100 } 101 102 if (fragmenter == null) { 103 fragmenter = new HandshakeFragment(); 104 } 105 106 if (firstMessage) { 107 firstMessage = false; 108 109 if ((helloVersion == ProtocolVersion.SSL20Hello) && 110 (source[offset] == SSLHandshake.CLIENT_HELLO.id) && 111 // 5: recode header size 112 (source[offset + 4 + 2 + 32] == 0)) { 113 // V3 session ID is empty 114 // 4: handshake header size 115 // 2: client_version in ClientHello 116 // 32: random in ClientHello 117 118 // Double space should be big enough for the converted message. 119 v2ClientHello = encodeV2ClientHello( 120 source, (offset + 4), (length - 4)); 121 122 v2ClientHello.position(2); // exclude the header 123 handshakeHash.deliver(v2ClientHello); 124 v2ClientHello.position(0); 125 126 return; 127 } 128 } 129 130 byte handshakeType = source[offset]; 131 if (handshakeHash.isHashable(handshakeType)) { 132 handshakeHash.deliver(source, offset, length); 133 } 134 135 fragmenter.queueUpFragment(source, offset, length); 136 } 137 138 @Override encodeChangeCipherSpec()139 void encodeChangeCipherSpec() throws IOException { 140 if (isClosed()) { 141 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { 142 SSLLogger.warning("outbound has closed, ignore outbound " + 143 "change_cipher_spec message"); 144 } 145 return; 146 } 147 148 if (fragmenter == null) { 149 fragmenter = new HandshakeFragment(); 150 } 151 fragmenter.queueUpChangeCipherSpec(); 152 } 153 154 @Override encodeV2NoCipher()155 void encodeV2NoCipher() throws IOException { 156 isTalkingToV2 = true; 157 } 158 159 @Override encode( ByteBuffer[] srcs, int srcsOffset, int srcsLength, ByteBuffer[] dsts, int dstsOffset, int dstsLength)160 Ciphertext encode( 161 ByteBuffer[] srcs, int srcsOffset, int srcsLength, 162 ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException { 163 164 if (isClosed) { 165 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { 166 SSLLogger.warning("outbound has closed, ignore outbound " + 167 "application data or cached messages"); 168 } 169 170 return null; 171 } else if (isCloseWaiting) { 172 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { 173 SSLLogger.warning("outbound has closed, ignore outbound " + 174 "application data"); 175 } 176 177 srcs = null; // use no application data. 178 } 179 180 return encode(srcs, srcsOffset, srcsLength, dsts[0]); 181 } 182 encode(ByteBuffer[] sources, int offset, int length, ByteBuffer destination)183 private Ciphertext encode(ByteBuffer[] sources, int offset, int length, 184 ByteBuffer destination) throws IOException { 185 186 if (writeCipher.authenticator.seqNumOverflow()) { 187 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { 188 SSLLogger.fine( 189 "sequence number extremely close to overflow " + 190 "(2^64-1 packets). Closing connection."); 191 } 192 193 throw new SSLHandshakeException("sequence number overflow"); 194 } 195 196 // Don't process the incoming record until all of the 197 // buffered records get handled. 198 Ciphertext ct = acquireCiphertext(destination); 199 if (ct != null) { 200 return ct; 201 } 202 203 if (sources == null || sources.length == 0) { 204 return null; 205 } 206 207 int srcsRemains = 0; 208 for (int i = offset; i < offset + length; i++) { 209 srcsRemains += sources[i].remaining(); 210 } 211 212 if (srcsRemains == 0) { 213 return null; 214 } 215 216 int dstLim = destination.limit(); 217 boolean isFirstRecordOfThePayload = true; 218 int packetLeftSize = Math.min(maxRecordSize, packetSize); 219 boolean needMorePayload = true; 220 long recordSN = 0L; 221 while (needMorePayload) { 222 int fragLen; 223 if (isFirstRecordOfThePayload && needToSplitPayload()) { 224 needMorePayload = true; 225 226 fragLen = 1; 227 isFirstRecordOfThePayload = false; 228 } else { 229 needMorePayload = false; 230 231 if (packetLeftSize > 0) { 232 fragLen = writeCipher.calculateFragmentSize( 233 packetLeftSize, headerSize); 234 235 fragLen = Math.min(fragLen, Record.maxDataSize); 236 } else { 237 fragLen = Record.maxDataSize; 238 } 239 240 // Calculate more impact, for example TLS 1.3 padding. 241 fragLen = calculateFragmentSize(fragLen); 242 } 243 244 int dstPos = destination.position(); 245 int dstContent = dstPos + headerSize + 246 writeCipher.getExplicitNonceSize(); 247 destination.position(dstContent); 248 249 int remains = Math.min(fragLen, destination.remaining()); 250 fragLen = 0; 251 int srcsLen = offset + length; 252 for (int i = offset; (i < srcsLen) && (remains > 0); i++) { 253 int amount = Math.min(sources[i].remaining(), remains); 254 int srcLimit = sources[i].limit(); 255 sources[i].limit(sources[i].position() + amount); 256 destination.put(sources[i]); 257 sources[i].limit(srcLimit); // restore the limit 258 remains -= amount; 259 fragLen += amount; 260 261 if (remains > 0) { 262 offset++; 263 length--; 264 } 265 } 266 267 destination.limit(destination.position()); 268 destination.position(dstContent); 269 270 if (SSLLogger.isOn && SSLLogger.isOn("record")) { 271 SSLLogger.fine( 272 "WRITE: " + protocolVersion + " " + 273 ContentType.APPLICATION_DATA.name + 274 ", length = " + destination.remaining()); 275 } 276 277 // Encrypt the fragment and wrap up a record. 278 recordSN = encrypt(writeCipher, 279 ContentType.APPLICATION_DATA.id, destination, 280 dstPos, dstLim, headerSize, 281 protocolVersion); 282 283 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { 284 ByteBuffer temporary = destination.duplicate(); 285 temporary.limit(temporary.position()); 286 temporary.position(dstPos); 287 SSLLogger.fine("Raw write", temporary); 288 } 289 290 packetLeftSize -= destination.position() - dstPos; 291 292 // remain the limit unchanged 293 destination.limit(dstLim); 294 295 if (isFirstAppOutputRecord) { 296 isFirstAppOutputRecord = false; 297 } 298 } 299 300 return new Ciphertext(ContentType.APPLICATION_DATA.id, 301 SSLHandshake.NOT_APPLICABLE.id, recordSN); 302 } 303 acquireCiphertext( ByteBuffer destination)304 private Ciphertext acquireCiphertext( 305 ByteBuffer destination) throws IOException { 306 if (isTalkingToV2) { // SSLv2Hello 307 // We don't support SSLv2. Send an SSLv2 error message 308 // so that the connection can be closed gracefully. 309 // 310 // Please don't change the limit of the destination buffer. 311 destination.put(SSLRecord.v2NoCipher); 312 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { 313 SSLLogger.fine("Raw write", SSLRecord.v2NoCipher); 314 } 315 316 isTalkingToV2 = false; 317 318 return new Ciphertext(ContentType.ALERT.id, 319 SSLHandshake.NOT_APPLICABLE.id, -1L); 320 } 321 322 if (v2ClientHello != null) { 323 // deliver the SSLv2 format ClientHello message 324 // 325 // Please don't change the limit of the destination buffer. 326 if (SSLLogger.isOn) { 327 if (SSLLogger.isOn("record")) { 328 SSLLogger.fine(Thread.currentThread().getName() + 329 ", WRITE: SSLv2 ClientHello message" + 330 ", length = " + v2ClientHello.remaining()); 331 } 332 333 if (SSLLogger.isOn("packet")) { 334 SSLLogger.fine("Raw write", v2ClientHello); 335 } 336 } 337 338 destination.put(v2ClientHello); 339 v2ClientHello = null; 340 341 return new Ciphertext(ContentType.HANDSHAKE.id, 342 SSLHandshake.CLIENT_HELLO.id, -1L); 343 } 344 345 if (fragmenter != null) { 346 return fragmenter.acquireCiphertext(destination); 347 } 348 349 return null; 350 } 351 352 @Override isEmpty()353 boolean isEmpty() { 354 return (!isTalkingToV2) && (v2ClientHello == null) && 355 ((fragmenter == null) || fragmenter.isEmpty()); 356 } 357 358 // buffered record fragment 359 private static class RecordMemo { 360 byte contentType; 361 byte majorVersion; 362 byte minorVersion; 363 SSLWriteCipher encodeCipher; 364 365 byte[] fragment; 366 } 367 368 private static class HandshakeMemo extends RecordMemo { 369 byte handshakeType; 370 int acquireOffset; 371 } 372 373 final class HandshakeFragment { 374 private LinkedList<RecordMemo> handshakeMemos = new LinkedList<>(); 375 queueUpFragment(byte[] source, int offset, int length)376 void queueUpFragment(byte[] source, 377 int offset, int length) throws IOException { 378 HandshakeMemo memo = new HandshakeMemo(); 379 380 memo.contentType = ContentType.HANDSHAKE.id; 381 memo.majorVersion = protocolVersion.major; // kick start version? 382 memo.minorVersion = protocolVersion.minor; 383 memo.encodeCipher = writeCipher; 384 385 memo.handshakeType = source[offset]; 386 memo.acquireOffset = 0; 387 memo.fragment = new byte[length - 4]; // 4: header size 388 // 1: HandshakeType 389 // 3: message length 390 System.arraycopy(source, offset + 4, memo.fragment, 0, length - 4); 391 392 handshakeMemos.add(memo); 393 } 394 queueUpChangeCipherSpec()395 void queueUpChangeCipherSpec() { 396 RecordMemo memo = new RecordMemo(); 397 398 memo.contentType = ContentType.CHANGE_CIPHER_SPEC.id; 399 memo.majorVersion = protocolVersion.major; 400 memo.minorVersion = protocolVersion.minor; 401 memo.encodeCipher = writeCipher; 402 403 memo.fragment = new byte[1]; 404 memo.fragment[0] = 1; 405 406 handshakeMemos.add(memo); 407 } 408 queueUpAlert(byte level, byte description)409 void queueUpAlert(byte level, byte description) { 410 RecordMemo memo = new RecordMemo(); 411 412 memo.contentType = ContentType.ALERT.id; 413 memo.majorVersion = protocolVersion.major; 414 memo.minorVersion = protocolVersion.minor; 415 memo.encodeCipher = writeCipher; 416 417 memo.fragment = new byte[2]; 418 memo.fragment[0] = level; 419 memo.fragment[1] = description; 420 421 handshakeMemos.add(memo); 422 } 423 acquireCiphertext(ByteBuffer dstBuf)424 Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException { 425 if (isEmpty()) { 426 return null; 427 } 428 429 RecordMemo memo = handshakeMemos.getFirst(); 430 HandshakeMemo hsMemo = null; 431 if (memo.contentType == ContentType.HANDSHAKE.id) { 432 hsMemo = (HandshakeMemo)memo; 433 } 434 435 // ChangeCipherSpec message is pretty small. Don't worry about 436 // the fragmentation of ChangeCipherSpec record. 437 int fragLen; 438 if (packetSize > 0) { 439 fragLen = Math.min(maxRecordSize, packetSize); 440 fragLen = memo.encodeCipher.calculateFragmentSize( 441 fragLen, headerSize); 442 } else { 443 fragLen = Record.maxDataSize; 444 } 445 446 // Calculate more impact, for example TLS 1.3 padding. 447 fragLen = calculateFragmentSize(fragLen); 448 449 int dstPos = dstBuf.position(); 450 int dstLim = dstBuf.limit(); 451 int dstContent = dstPos + headerSize + 452 memo.encodeCipher.getExplicitNonceSize(); 453 dstBuf.position(dstContent); 454 455 if (hsMemo != null) { 456 int remainingFragLen = fragLen; 457 while ((remainingFragLen > 0) && !handshakeMemos.isEmpty()) { 458 int memoFragLen = hsMemo.fragment.length; 459 if (hsMemo.acquireOffset == 0) { 460 // Don't fragment handshake message header 461 if (remainingFragLen <= 4) { 462 break; 463 } 464 465 dstBuf.put(hsMemo.handshakeType); 466 dstBuf.put((byte)((memoFragLen >> 16) & 0xFF)); 467 dstBuf.put((byte)((memoFragLen >> 8) & 0xFF)); 468 dstBuf.put((byte)(memoFragLen & 0xFF)); 469 470 remainingFragLen -= 4; 471 } // Otherwise, handshake message is fragmented. 472 473 int chipLen = Math.min(remainingFragLen, 474 (memoFragLen - hsMemo.acquireOffset)); 475 dstBuf.put(hsMemo.fragment, hsMemo.acquireOffset, chipLen); 476 477 hsMemo.acquireOffset += chipLen; 478 if (hsMemo.acquireOffset == memoFragLen) { 479 handshakeMemos.removeFirst(); 480 481 // still have space for more records? 482 if ((remainingFragLen > chipLen) && 483 !handshakeMemos.isEmpty()) { 484 485 // look for the next buffered record fragment 486 RecordMemo rm = handshakeMemos.getFirst(); 487 if (rm.contentType == ContentType.HANDSHAKE.id && 488 rm.encodeCipher == hsMemo.encodeCipher) { 489 hsMemo = (HandshakeMemo)rm; 490 } else { 491 // not of the flight, break the loop 492 break; 493 } 494 } 495 } 496 497 remainingFragLen -= chipLen; 498 } 499 } else { 500 fragLen = Math.min(fragLen, memo.fragment.length); 501 dstBuf.put(memo.fragment, 0, fragLen); 502 503 handshakeMemos.removeFirst(); 504 } 505 506 dstBuf.limit(dstBuf.position()); 507 dstBuf.position(dstContent); 508 509 if (SSLLogger.isOn && SSLLogger.isOn("record")) { 510 SSLLogger.fine( 511 "WRITE: " + protocolVersion + " " + 512 ContentType.nameOf(memo.contentType) + 513 ", length = " + dstBuf.remaining()); 514 } 515 516 // Encrypt the fragment and wrap up a record. 517 long recordSN = encrypt( 518 memo.encodeCipher, 519 memo.contentType, dstBuf, 520 dstPos, dstLim, headerSize, 521 ProtocolVersion.valueOf(memo.majorVersion, 522 memo.minorVersion)); 523 524 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { 525 ByteBuffer temporary = dstBuf.duplicate(); 526 temporary.limit(temporary.position()); 527 temporary.position(dstPos); 528 SSLLogger.fine("Raw write", temporary); 529 } 530 531 // remain the limit unchanged 532 dstBuf.limit(dstLim); 533 534 // Reset the fragmentation offset. 535 if (hsMemo != null) { 536 return new Ciphertext(hsMemo.contentType, 537 hsMemo.handshakeType, recordSN); 538 } else { 539 if (isCloseWaiting && 540 memo.contentType == ContentType.ALERT.id) { 541 close(); 542 } 543 544 return new Ciphertext(memo.contentType, 545 SSLHandshake.NOT_APPLICABLE.id, recordSN); 546 } 547 } 548 isEmpty()549 boolean isEmpty() { 550 return handshakeMemos.isEmpty(); 551 } 552 hasAlert()553 boolean hasAlert() { 554 for (RecordMemo memo : handshakeMemos) { 555 if (memo.contentType == ContentType.ALERT.id) { 556 return true; 557 } 558 } 559 560 return false; 561 } 562 } 563 564 /* 565 * Need to split the payload except the following cases: 566 * 567 * 1. protocol version is TLS 1.1 or later; 568 * 2. bulk cipher does not use CBC mode, including null bulk cipher suites. 569 * 3. the payload is the first application record of a freshly 570 * negotiated TLS session. 571 * 4. the CBC protection is disabled; 572 * 573 * By default, we counter chosen plaintext issues on CBC mode 574 * ciphersuites in SSLv3/TLS1.0 by sending one byte of application 575 * data in the first record of every payload, and the rest in 576 * subsequent record(s). Note that the issues have been solved in 577 * TLS 1.1 or later. 578 * 579 * It is not necessary to split the very first application record of 580 * a freshly negotiated TLS session, as there is no previous 581 * application data to guess. To improve compatibility, we will not 582 * split such records. 583 * 584 * This avoids issues in the outbound direction. For a full fix, 585 * the peer must have similar protections. 586 */ needToSplitPayload()587 boolean needToSplitPayload() { 588 return (!protocolVersion.useTLS11PlusSpec()) && 589 writeCipher.isCBCMode() && !isFirstAppOutputRecord && 590 Record.enableCBCProtection; 591 } 592 } 593