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