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 }