1 /*
   2  * Copyright (c) 2003, 2013, 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 
  27 package sun.security.ssl;
  28 
  29 import java.io.*;
  30 import java.nio.*;
  31 import javax.net.ssl.*;
  32 import javax.crypto.BadPaddingException;
  33 import sun.misc.HexDumpEncoder;
  34 
  35 
  36 /**
  37  * Wrapper class around InputRecord.
  38  *
  39  * Application data is kept external to the InputRecord,
  40  * but handshake data (alert/change_cipher_spec/handshake) will
  41  * be kept internally in the ByteArrayInputStream.
  42  *
  43  * @author Brad Wetmore
  44  */
  45 final class EngineInputRecord extends InputRecord {
  46 
  47     private SSLEngineImpl engine;
  48 
  49     /*
  50      * A dummy ByteBuffer we'll pass back even when the data
  51      * is stored internally.  It'll never actually be used.
  52      */
  53     static private ByteBuffer tmpBB = ByteBuffer.allocate(0);
  54 
  55     /*
  56      * Flag to tell whether the last read/parsed data resides
  57      * internal in the ByteArrayInputStream, or in the external
  58      * buffers.
  59      */
  60     private boolean internalData;
  61 
  62     EngineInputRecord(SSLEngineImpl engine) {
  63         super();
  64         this.engine = engine;
  65     }
  66 
  67     @Override
  68     byte contentType() {
  69         if (internalData) {
  70             return super.contentType();
  71         } else {
  72             return ct_application_data;
  73         }
  74     }
  75 
  76     /*
  77      * Check if there is enough inbound data in the ByteBuffer
  78      * to make a inbound packet.  Look for both SSLv2 and SSLv3.
  79      *
  80      * @return -1 if there are not enough bytes to tell (small header),
  81      */
  82     int bytesInCompletePacket(ByteBuffer buf) throws SSLException {
  83 
  84         /*
  85          * SSLv2 length field is in bytes 0/1
  86          * SSLv3/TLS length field is in bytes 3/4
  87          */
  88         if (buf.remaining() < 5) {
  89             return -1;
  90         }
  91 
  92         int pos = buf.position();
  93         byte byteZero = buf.get(pos);
  94 
  95         int len = 0;
  96 
  97         /*
  98          * If we have already verified previous packets, we can
  99          * ignore the verifications steps, and jump right to the
 100          * determination.  Otherwise, try one last hueristic to
 101          * see if it's SSL/TLS.
 102          */
 103         if (formatVerified ||
 104                 (byteZero == ct_handshake) ||
 105                 (byteZero == ct_alert)) {
 106             /*
 107              * Last sanity check that it's not a wild record
 108              */
 109             ProtocolVersion recordVersion =
 110                 ProtocolVersion.valueOf(buf.get(pos + 1), buf.get(pos + 2));
 111 
 112             // Check if too old (currently not possible)
 113             // or if the major version does not match.
 114             // The actual version negotiation is in the handshaker classes
 115             if ((recordVersion.v < ProtocolVersion.MIN.v)
 116                     || (recordVersion.major > ProtocolVersion.MAX.major)) {
 117                 throw new SSLException(
 118                     "Unsupported record version " + recordVersion);
 119             }
 120 
 121             /*
 122              * Reasonably sure this is a V3, disable further checks.
 123              * We can't do the same in the v2 check below, because
 124              * read still needs to parse/handle the v2 clientHello.
 125              */
 126             formatVerified = true;
 127 
 128             /*
 129              * One of the SSLv3/TLS message types.
 130              */
 131             len = ((buf.get(pos + 3) & 0xff) << 8) +
 132                 (buf.get(pos + 4) & 0xff) + headerSize;
 133 
 134         } else {
 135             /*
 136              * Must be SSLv2 or something unknown.
 137              * Check if it's short (2 bytes) or
 138              * long (3) header.
 139              *
 140              * Internals can warn about unsupported SSLv2
 141              */
 142             boolean isShort = ((byteZero & 0x80) != 0);
 143 
 144             if (isShort &&
 145                     ((buf.get(pos + 2) == 1) || buf.get(pos + 2) == 4)) {
 146 
 147                 ProtocolVersion recordVersion =
 148                     ProtocolVersion.valueOf(buf.get(pos + 3), buf.get(pos + 4));
 149 
 150                 // Check if too old (currently not possible)
 151                 // or if the major version does not match.
 152                 // The actual version negotiation is in the handshaker classes
 153                 if ((recordVersion.v < ProtocolVersion.MIN.v)
 154                         || (recordVersion.major > ProtocolVersion.MAX.major)) {
 155 
 156                     // if it's not SSLv2, we're out of here.
 157                     if (recordVersion.v != ProtocolVersion.SSL20Hello.v) {
 158                         throw new SSLException(
 159                             "Unsupported record version " + recordVersion);
 160                     }
 161                 }
 162 
 163                 /*
 164                  * Client or Server Hello
 165                  */
 166                 int mask = (isShort ? 0x7f : 0x3f);
 167                 len = ((byteZero & mask) << 8) + (buf.get(pos + 1) & 0xff) +
 168                     (isShort ? 2 : 3);
 169 
 170             } else {
 171                 // Gobblygook!
 172                 throw new SSLException(
 173                     "Unrecognized SSL message, plaintext connection?");
 174             }
 175         }
 176 
 177         return len;
 178     }
 179 
 180     /*
 181      * Pass the data down if it's internally cached, otherwise
 182      * do it here.
 183      *
 184      * If internal data, data is decrypted internally.
 185      *
 186      * If external data(app), return a new ByteBuffer with data to
 187      * process.
 188      */
 189     ByteBuffer decrypt(Authenticator authenticator,
 190             CipherBox box, ByteBuffer bb) throws BadPaddingException {
 191 
 192         if (internalData) {
 193             decrypt(authenticator, box);   // MAC is checked during decryption
 194             return tmpBB;
 195         }
 196 
 197         BadPaddingException reservedBPE = null;
 198         int tagLen =
 199             (authenticator instanceof MAC) ? ((MAC)authenticator).MAClen() : 0;
 200         int cipheredLength = bb.remaining();
 201 
 202         if (!box.isNullCipher()) {
 203             try {
 204                 // apply explicit nonce for AEAD/CBC cipher suites if needed
 205                 int nonceSize =
 206                     box.applyExplicitNonce(authenticator, contentType(), bb);
 207 
 208                 // decrypt the content
 209                 if (box.isAEADMode()) {
 210                     // DON'T encrypt the nonce_explicit for AEAD mode
 211                     bb.position(bb.position() + nonceSize);
 212                 }   // The explicit IV for CBC mode can be decrypted.
 213 
 214                 // Note that the CipherBox.decrypt() does not change
 215                 // the capacity of the buffer.
 216                 box.decrypt(bb, tagLen);
 217                 bb.position(nonceSize); // We don't actually remove the nonce.
 218             } catch (BadPaddingException bpe) {
 219                 // RFC 2246 states that decryption_failed should be used
 220                 // for this purpose. However, that allows certain attacks,
 221                 // so we just send bad record MAC. We also need to make
 222                 // sure to always check the MAC to avoid a timing attack
 223                 // for the same issue. See paper by Vaudenay et al and the
 224                 // update in RFC 4346/5246.
 225                 //
 226                 // Failover to message authentication code checking.
 227                 reservedBPE = bpe;
 228             }
 229         }
 230 
 231         // Requires message authentication code for null, stream and block
 232         // cipher suites.
 233         if ((authenticator instanceof MAC) && (tagLen != 0)) {
 234             MAC signer = (MAC)authenticator;
 235             int macOffset = bb.limit() - tagLen;
 236 
 237             // Note that although it is not necessary, we run the same MAC
 238             // computation and comparison on the payload for both stream
 239             // cipher and CBC block cipher.
 240             if (bb.remaining() < tagLen) {
 241                 // negative data length, something is wrong
 242                 if (reservedBPE == null) {
 243                     reservedBPE = new BadPaddingException("bad record");
 244                 }
 245 
 246                 // set offset of the dummy MAC
 247                 macOffset = cipheredLength - tagLen;
 248                 bb.limit(cipheredLength);
 249             }
 250 
 251             // Run MAC computation and comparison on the payload.
 252             if (checkMacTags(contentType(), bb, signer, false)) {
 253                 if (reservedBPE == null) {
 254                     reservedBPE = new BadPaddingException("bad record MAC");
 255                 }
 256             }
 257 
 258             // Run MAC computation and comparison on the remainder.
 259             //
 260             // It is only necessary for CBC block cipher.  It is used to get a
 261             // constant time of MAC computation and comparison on each record.
 262             if (box.isCBCMode()) {
 263                 int remainingLen = calculateRemainingLen(
 264                                         signer, cipheredLength, macOffset);
 265 
 266                 // NOTE: here we use the InputRecord.buf because I did not find
 267                 // an effective way to work on ByteBuffer when its capacity is
 268                 // less than remainingLen.
 269 
 270                 // NOTE: remainingLen may be bigger (less than 1 block of the
 271                 // hash algorithm of the MAC) than the cipheredLength. However,
 272                 // We won't need to worry about it because we always use a
 273                 // maximum buffer for every record.  We need a change here if
 274                 // we use small buffer size in the future.
 275                 if (remainingLen > buf.length) {
 276                     // unlikely to happen, just a placehold
 277                     throw new RuntimeException(
 278                         "Internal buffer capacity error");
 279                 }
 280 
 281                 // Won't need to worry about the result on the remainder. And
 282                 // then we won't need to worry about what's actual data to
 283                 // check MAC tag on.  We start the check from the header of the
 284                 // buffer so that we don't need to construct a new byte buffer.
 285                 checkMacTags(contentType(), buf, 0, remainingLen, signer, true);
 286             }
 287 
 288             bb.limit(macOffset);
 289         }
 290 
 291         // Is it a failover?
 292         if (reservedBPE != null) {
 293             throw reservedBPE;
 294         }
 295 
 296         return bb.slice();
 297     }
 298 
 299     /*
 300      * Run MAC computation and comparison
 301      *
 302      * Please DON'T change the content of the ByteBuffer parameter!
 303      */
 304     private static boolean checkMacTags(byte contentType, ByteBuffer bb,
 305             MAC signer, boolean isSimulated) {
 306 
 307         int position = bb.position();
 308         int tagLen = signer.MAClen();
 309         int lim = bb.limit();
 310         int macData = lim - tagLen;
 311 
 312         bb.limit(macData);
 313         byte[] hash = signer.compute(contentType, bb, isSimulated);
 314         if (hash == null || tagLen != hash.length) {
 315             // Something is wrong with MAC implementation.
 316             throw new RuntimeException("Internal MAC error");
 317         }
 318 
 319         bb.position(macData);
 320         bb.limit(lim);
 321         try {
 322             int[] results = compareMacTags(bb, hash);
 323             return (results[0] != 0);
 324         } finally {
 325             // reset to the data
 326             bb.position(position);
 327             bb.limit(macData);
 328         }
 329     }
 330 
 331     /*
 332      * A constant-time comparison of the MAC tags.
 333      *
 334      * Please DON'T change the content of the ByteBuffer parameter!
 335      */
 336     private static int[] compareMacTags(ByteBuffer bb, byte[] tag) {
 337 
 338         // An array of hits is used to prevent Hotspot optimization for
 339         // the purpose of a constant-time check.
 340         int[] results = {0, 0};     // {missed #, matched #}
 341 
 342         // The caller ensures there are enough bytes available in the buffer.
 343         // So we won't need to check the remaining of the buffer.
 344         for (int i = 0; i < tag.length; i++) {
 345             if (bb.get() != tag[i]) {
 346                 results[0]++;       // mismatched bytes
 347             } else {
 348                 results[1]++;       // matched bytes
 349             }
 350         }
 351 
 352         return results;
 353     }
 354 
 355     /*
 356      * Override the actual write below.  We do things this way to be
 357      * consistent with InputRecord.  InputRecord may try to write out
 358      * data to the peer, and *then* throw an Exception.  This forces
 359      * data to be generated/output before the exception is ever
 360      * generated.
 361      */
 362     @Override
 363     void writeBuffer(OutputStream s, byte [] buf, int off, int len)
 364             throws IOException {
 365         /*
 366          * Copy data out of buffer, it's ready to go.
 367          */
 368         ByteBuffer netBB = ByteBuffer.allocate(len).put(buf, 0, len).flip();
 369         engine.writer.putOutboundDataSync(netBB);
 370     }
 371 
 372     /*
 373      * Delineate or read a complete packet from src.
 374      *
 375      * If internal data (hs, alert, ccs), the data is read and
 376      * stored internally.
 377      *
 378      * If external data (app), return a new ByteBuffer which points
 379      * to the data to process.
 380      */
 381     ByteBuffer read(ByteBuffer srcBB) throws IOException {
 382         /*
 383          * Could have a src == null/dst == null check here,
 384          * but that was already checked by SSLEngine.unwrap before
 385          * ever attempting to read.
 386          */
 387 
 388         /*
 389          * If we have anything besides application data,
 390          * or if we haven't even done the initial v2 verification,
 391          * we send this down to be processed by the underlying
 392          * internal cache.
 393          */
 394         if (!formatVerified ||
 395                 (srcBB.get(srcBB.position()) != ct_application_data)) {
 396             internalData = true;
 397             read(new ByteBufferInputStream(srcBB), (OutputStream) null);
 398             return tmpBB;
 399         }
 400 
 401         internalData = false;
 402 
 403         int srcPos = srcBB.position();
 404         int srcLim = srcBB.limit();
 405 
 406         ProtocolVersion recordVersion = ProtocolVersion.valueOf(
 407                 srcBB.get(srcPos + 1), srcBB.get(srcPos + 2));
 408         // Check if too old (currently not possible)
 409         // or if the major version does not match.
 410         // The actual version negotiation is in the handshaker classes
 411         if ((recordVersion.v < ProtocolVersion.MIN.v)
 412                 || (recordVersion.major > ProtocolVersion.MAX.major)) {
 413             throw new SSLException(
 414                 "Unsupported record version " + recordVersion);
 415         }
 416 
 417         /*
 418          * It's really application data.  How much to consume?
 419          * Jump over the header.
 420          */
 421         int len = bytesInCompletePacket(srcBB);
 422         assert(len > 0);
 423 
 424         if (debug != null && Debug.isOn("packet")) {
 425             try {
 426                 HexDumpEncoder hd = new HexDumpEncoder();
 427                 ByteBuffer bb = srcBB.duplicate();  // Use copy of BB
 428                 bb.limit(srcPos + len);
 429 
 430                 System.out.println("[Raw read (bb)]: length = " + len);
 431                 hd.encodeBuffer(bb, System.out);
 432             } catch (IOException e) { }
 433         }
 434 
 435         // Demarcate past header to end of packet.
 436         srcBB.position(srcPos + headerSize);
 437         srcBB.limit(srcPos + len);
 438 
 439         // Protect remainder of buffer, create slice to actually
 440         // operate on.
 441         ByteBuffer bb = srcBB.slice();
 442 
 443         srcBB.position(srcBB.limit());
 444         srcBB.limit(srcLim);
 445 
 446         return bb;
 447     }
 448 }