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