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.Closeable;
  29 import java.io.IOException;
  30 import java.io.InputStream;
  31 import java.io.OutputStream;
  32 import java.nio.BufferUnderflowException;
  33 import java.nio.ByteBuffer;
  34 import javax.crypto.BadPaddingException;
  35 import sun.security.ssl.SSLCipher.SSLReadCipher;
  36 
  37 /**
  38  * {@code InputRecord} takes care of the management of SSL/TLS/DTLS input
  39  * records, including buffering, decryption, handshake messages marshal, etc.
  40  *
  41  * @author David Brownell
  42  */
  43 abstract class InputRecord implements Record, Closeable {
  44     SSLReadCipher       readCipher;
  45     // Needed for KeyUpdate, used after Handshake.Finished
  46     TransportContext            tc;
  47 
  48     final HandshakeHash handshakeHash;
  49     boolean             isClosed;
  50 
  51     // The ClientHello version to accept. If set to ProtocolVersion.SSL20Hello
  52     // and the first message we read is a ClientHello in V2 format, we convert
  53     // it to V3. Otherwise we throw an exception when encountering a V2 hello.
  54     ProtocolVersion     helloVersion;
  55 
  56     // fragment size
  57     int                 fragmentSize;
  58 
  59     InputRecord(HandshakeHash handshakeHash, SSLReadCipher readCipher) {
  60         this.readCipher = readCipher;
  61         this.helloVersion = ProtocolVersion.TLS10;
  62         this.handshakeHash = handshakeHash;
  63         this.isClosed = false;
  64         this.fragmentSize = Record.maxDataSize;
  65     }
  66 
  67     void setHelloVersion(ProtocolVersion helloVersion) {
  68         this.helloVersion = helloVersion;
  69     }
  70 
  71     boolean seqNumIsHuge() {
  72         return (readCipher.authenticator != null) &&
  73                         readCipher.authenticator.seqNumIsHuge();
  74     }
  75 
  76     boolean isEmpty() {
  77         return false;
  78     }
  79 
  80     // apply to DTLS SSLEngine
  81     void expectingFinishFlight() {
  82         // blank
  83     }
  84 
  85     // apply to DTLS SSLEngine
  86     void finishHandshake() {
  87         // blank
  88     }
  89 
  90     /**
  91      * Prevent any more data from being read into this record,
  92      * and flag the record as holding no data.
  93      */
  94     @Override
  95     public synchronized void close() throws IOException {
  96         if (!isClosed) {
  97             isClosed = true;
  98             readCipher.dispose();
  99         }
 100     }
 101 
 102     synchronized boolean isClosed() {
 103         return isClosed;
 104     }
 105 
 106     // apply to SSLSocket and SSLEngine
 107     void changeReadCiphers(SSLReadCipher readCipher) {
 108 
 109         /*
 110          * Dispose of any intermediate state in the underlying cipher.
 111          * For PKCS11 ciphers, this will release any attached sessions,
 112          * and thus make finalization faster.
 113          *
 114          * Since MAC's doFinal() is called for every SSL/TLS packet, it's
 115          * not necessary to do the same with MAC's.
 116          */
 117         readCipher.dispose();
 118 
 119         this.readCipher = readCipher;
 120     }
 121 
 122     // change fragment size
 123     void changeFragmentSize(int fragmentSize) {
 124         this.fragmentSize = fragmentSize;
 125     }
 126 
 127     /*
 128      * Check if there is enough inbound data in the ByteBuffer to make
 129      * a inbound packet.
 130      *
 131      * @return -1 if there are not enough bytes to tell (small header),
 132      */
 133     // apply to SSLEngine only
 134     int bytesInCompletePacket(
 135         ByteBuffer[] srcs, int srcsOffset, int srcsLength) throws IOException {
 136 
 137         throw new UnsupportedOperationException("Not supported yet.");
 138     }
 139 
 140     // apply to SSLSocket only
 141     int bytesInCompletePacket() throws IOException {
 142         throw new UnsupportedOperationException();
 143     }
 144 
 145     // apply to SSLSocket only
 146     void setReceiverStream(InputStream inputStream) {
 147         throw new UnsupportedOperationException();
 148     }
 149 
 150     // apply to DTLS SSLEngine only
 151     Plaintext acquirePlaintext()
 152             throws IOException, BadPaddingException {
 153         throw new UnsupportedOperationException();
 154     }
 155 
 156     // read, decrypt and decompress the network record.
 157     //
 158     abstract Plaintext[] decode(ByteBuffer[] srcs, int srcsOffset,
 159             int srcsLength) throws IOException, BadPaddingException;
 160 
 161     // apply to SSLSocket only
 162     void setDeliverStream(OutputStream outputStream) {
 163         throw new UnsupportedOperationException();
 164     }
 165 
 166     // calculate plaintext fragment size
 167     //
 168     // apply to SSLEngine only
 169     int estimateFragmentSize(int packetSize) {
 170         throw new UnsupportedOperationException();
 171     }
 172 
 173     //
 174     // shared helpers
 175     //
 176 
 177     // Not apply to DTLS
 178     static ByteBuffer convertToClientHello(ByteBuffer packet) {
 179         int srcPos = packet.position();
 180 
 181         byte firstByte = packet.get();
 182         byte secondByte = packet.get();
 183         int recordLen = (((firstByte & 0x7F) << 8) | (secondByte & 0xFF)) + 2;
 184 
 185         packet.position(srcPos + 3);        // the V2ClientHello record header
 186 
 187         byte majorVersion = packet.get();
 188         byte minorVersion = packet.get();
 189 
 190         int cipherSpecLen = ((packet.get() & 0xFF) << 8) +
 191                              (packet.get() & 0xFF);
 192         int sessionIdLen  = ((packet.get() & 0xFF) << 8) +
 193                              (packet.get() & 0xFF);
 194         int nonceLen      = ((packet.get() & 0xFF) << 8) +
 195                              (packet.get() & 0xFF);
 196 
 197         // Required space for the target SSLv3 ClientHello message.
 198         //  5: record header size
 199         //  4: handshake header size
 200         //  2: ClientHello.client_version
 201         // 32: ClientHello.random
 202         //  1: length byte of ClientHello.session_id
 203         //  2: length bytes of ClientHello.cipher_suites
 204         //  2: empty ClientHello.compression_methods
 205         int requiredSize = 48 + sessionIdLen + ((cipherSpecLen * 2 ) / 3);
 206         byte[] converted = new byte[requiredSize];
 207 
 208         /*
 209          * Build the first part of the V3 record header from the V2 one
 210          * that's now buffered up.  (Lengths are fixed up later).
 211          */
 212         // Note: need not to set the header actually.
 213         converted[0] = ContentType.HANDSHAKE.id;
 214         converted[1] = majorVersion;
 215         converted[2] = minorVersion;
 216         // header [3..4] for handshake message length
 217         // required size is 5;
 218 
 219         /*
 220          * Store the generic V3 handshake header:  4 bytes
 221          */
 222         converted[5] = 1;    // HandshakeMessage.ht_client_hello
 223         // buf [6..8] for length of ClientHello (int24)
 224         // required size += 4;
 225 
 226         /*
 227          * ClientHello header starts with SSL version
 228          */
 229         converted[9] = majorVersion;
 230         converted[10] = minorVersion;
 231         // required size += 2;
 232         int pointer = 11;
 233 
 234         /*
 235          * Copy Random value/nonce ... if less than the 32 bytes of
 236          * a V3 "Random", right justify and zero pad to the left.  Else
 237          * just take the last 32 bytes.
 238          */
 239         int offset = srcPos + 11 + cipherSpecLen + sessionIdLen;
 240 
 241         if (nonceLen < 32) {
 242             for (int i = 0; i < (32 - nonceLen); i++) {
 243                 converted[pointer++] = 0;
 244             }
 245             packet.position(offset);
 246             packet.get(converted, pointer, nonceLen);
 247 
 248             pointer += nonceLen;
 249         } else {
 250             packet.position(offset + nonceLen - 32);
 251             packet.get(converted, pointer, 32);
 252 
 253             pointer += 32;
 254         }
 255 
 256         /*
 257          * Copy session ID (only one byte length!)
 258          */
 259         offset -= sessionIdLen;
 260         converted[pointer++] = (byte)(sessionIdLen & 0xFF);
 261         packet.position(offset);
 262         packet.get(converted, pointer, sessionIdLen);
 263 
 264         /*
 265          * Copy and translate cipher suites ... V2 specs with first byte zero
 266          * are really V3 specs (in the last 2 bytes), just copy those and drop
 267          * the other ones.  Preference order remains unchanged.
 268          *
 269          * Example:  Netscape Navigator 3.0 (exportable) says:
 270          *
 271          * 0/3,     SSL_RSA_EXPORT_WITH_RC4_40_MD5
 272          * 0/6,     SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5
 273          *
 274          * Microsoft Internet Explorer 3.0 (exportable) supports only
 275          *
 276          * 0/3,     SSL_RSA_EXPORT_WITH_RC4_40_MD5
 277          */
 278         int j;
 279 
 280         offset -= cipherSpecLen;
 281         packet.position(offset);
 282 
 283         j = pointer + 2;
 284         for (int i = 0; i < cipherSpecLen; i += 3) {
 285             if (packet.get() != 0) {
 286                 // Ignore version 2.0 specific cipher suite.  Clients
 287                 // should also include the version 3.0 equivalent in
 288                 // the V2ClientHello message.
 289                 packet.get();           // ignore the 2nd byte
 290                 packet.get();           // ignore the 3rd byte
 291                 continue;
 292             }
 293 
 294             converted[j++] = packet.get();
 295             converted[j++] = packet.get();
 296         }
 297 
 298         j -= pointer + 2;
 299         converted[pointer++] = (byte)((j >>> 8) & 0xFF);
 300         converted[pointer++] = (byte)(j & 0xFF);
 301         pointer += j;
 302 
 303         /*
 304          * Append compression methods (default/null only)
 305          */
 306         converted[pointer++] = 1;
 307         converted[pointer++] = 0;      // Session.compression_null
 308 
 309         /*
 310          * Fill in lengths of the messages we synthesized (nested:
 311          * V3 handshake message within V3 record).
 312          */
 313         // Note: need not to set the header actually.
 314         int fragLen = pointer - 5;                      // TLSPlaintext.length
 315         converted[3] = (byte)((fragLen >>> 8) & 0xFF);
 316         converted[4] = (byte)(fragLen & 0xFF);
 317 
 318         /*
 319          * Handshake.length, length of ClientHello message
 320          */
 321         fragLen = pointer - 9;                          // Handshake.length
 322         converted[6] = (byte)((fragLen >>> 16) & 0xFF);
 323         converted[7] = (byte)((fragLen >>> 8) & 0xFF);
 324         converted[8] = (byte)(fragLen & 0xFF);
 325 
 326         // consume the full record
 327         packet.position(srcPos + recordLen);
 328 
 329         // Need no header bytes.
 330         return ByteBuffer.wrap(converted, 5, pointer - 5);  // 5: header size
 331     }
 332 
 333     // Extract an SSL/(D)TLS record from the specified source buffers.
 334     static ByteBuffer extract(
 335             ByteBuffer[] buffers, int offset, int length, int headerSize) {
 336 
 337         boolean hasFullHeader = false;
 338         int contentLen = -1;
 339         for (int i = offset, j = 0;
 340                 i < (offset + length) && j < headerSize; i++) {
 341             int remains = buffers[i].remaining();
 342             int pos = buffers[i].position();
 343             for (int k = 0; k < remains && j < headerSize; j++, k++) {
 344                 byte b = buffers[i].get(pos + k);
 345                 if (j == (headerSize - 2)) {
 346                     contentLen = ((b & 0xFF) << 8);
 347                 } else if (j == (headerSize -1)) {
 348                     contentLen |= (b & 0xFF);
 349                     hasFullHeader = true;
 350                     break;
 351                 }
 352             }
 353         }
 354 
 355         if (!hasFullHeader) {
 356             throw new BufferUnderflowException();
 357         }
 358 
 359         int packetLen = headerSize + contentLen;
 360         int remains = 0;
 361         for (int i = offset; i < offset + length; i++) {
 362             remains += buffers[i].remaining();
 363             if (remains >= packetLen) {
 364                 break;
 365             }
 366         }
 367 
 368         if (remains < packetLen) {
 369             throw new BufferUnderflowException();
 370         }
 371 
 372         byte[] packet = new byte[packetLen];
 373         int packetOffset = 0;
 374         int packetSpaces = packetLen;
 375         for (int i = offset; i < offset + length; i++) {
 376             if (buffers[i].hasRemaining()) {
 377                 int len = Math.min(packetSpaces, buffers[i].remaining());
 378                 buffers[i].get(packet, packetOffset, len);
 379                 packetOffset += len;
 380                 packetSpaces -= len;
 381             }
 382 
 383             if (packetSpaces <= 0) {
 384                 break;
 385             }
 386         }
 387 
 388         return ByteBuffer.wrap(packet);
 389     }
 390 }