Home | History | Annotate | Download | only in ssl
      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 
     32 import javax.net.ssl.SSLException;
     33 import sun.misc.HexDumpEncoder;
     34 
     35 
     36 /**
     37  * A OutputRecord class extension which uses external ByteBuffers
     38  * or the internal ByteArrayOutputStream for data manipulations.
     39  * <P>
     40  * Instead of rewriting this entire class
     41  * to use ByteBuffers, we leave things intact, so handshake, CCS,
     42  * and alerts will continue to use the internal buffers, but application
     43  * data will use external buffers.
     44  *
     45  * @author Brad Wetmore
     46  */
     47 final class EngineOutputRecord extends OutputRecord {
     48 
     49     private SSLEngineImpl engine;
     50     private EngineWriter writer;
     51 
     52     private boolean finishedMsg = false;
     53 
     54     /*
     55      * All handshake hashing is done by the superclass
     56      */
     57 
     58     /*
     59      * Default constructor makes a record supporting the maximum
     60      * SSL record size.  It allocates the header bytes directly.
     61      *
     62      * @param type the content type for the record
     63      */
     64     EngineOutputRecord(byte type, SSLEngineImpl engine) {
     65         super(type, recordSize(type));
     66         this.engine = engine;
     67         writer = engine.writer;
     68     }
     69 
     70     /**
     71      * Get the size of the buffer we need for records of the specified
     72      * type.
     73      * <P>
     74      * Application data buffers will provide their own byte buffers,
     75      * and will not use the internal byte caching.
     76      */
     77     private static int recordSize(byte type) {
     78         switch (type) {
     79 
     80         case ct_change_cipher_spec:
     81         case ct_alert:
     82             return maxAlertRecordSize;
     83 
     84         case ct_handshake:
     85             return maxRecordSize;
     86 
     87         case ct_application_data:
     88             return 0;
     89         }
     90 
     91         throw new RuntimeException("Unknown record type: " + type);
     92     }
     93 
     94     void setFinishedMsg() {
     95         finishedMsg = true;
     96     }
     97 
     98     public void flush() throws IOException {
     99         finishedMsg = false;
    100     }
    101 
    102     boolean isFinishedMsg() {
    103         return finishedMsg;
    104     }
    105 
    106 
    107     /**
    108      * Calculate the MAC value, storing the result either in
    109      * the internal buffer, or at the end of the destination
    110      * ByteBuffer.
    111      * <P>
    112      * We assume that the higher levels have assured us enough
    113      * room, otherwise we'll indirectly throw a
    114      * BufferOverFlowException runtime exception.
    115      *
    116      * position should equal limit, and points to the next
    117      * free spot.
    118      */
    119     private void addMAC(MAC signer, ByteBuffer bb)
    120             throws IOException {
    121 
    122         if (signer.MAClen() != 0) {
    123             byte[] hash = signer.compute(contentType(), bb, false);
    124 
    125             /*
    126              * position was advanced to limit in compute above.
    127              *
    128              * Mark next area as writable (above layers should have
    129              * established that we have plenty of room), then write
    130              * out the hash.
    131              */
    132             bb.limit(bb.limit() + hash.length);
    133             bb.put(hash);
    134         }
    135     }
    136 
    137     /*
    138      * Encrypt a ByteBuffer.
    139      *
    140      * We assume that the higher levels have assured us enough
    141      * room for the encryption (plus padding), otherwise we'll
    142      * indirectly throw a BufferOverFlowException runtime exception.
    143      *
    144      * position and limit will be the same, and points to the
    145      * next free spot.
    146      */
    147     void encrypt(CipherBox box, ByteBuffer bb) {
    148         box.encrypt(bb);
    149     }
    150 
    151     /*
    152      * Override the actual write below.  We do things this way to be
    153      * consistent with InputRecord.  InputRecord may try to write out
    154      * data to the peer, and *then* throw an Exception.  This forces
    155      * data to be generated/output before the exception is ever
    156      * generated.
    157      */
    158     @Override
    159     void writeBuffer(OutputStream s, byte [] buf, int off, int len,
    160             int debugOffset) throws IOException {
    161         /*
    162          * Copy data out of buffer, it's ready to go.
    163          */
    164         ByteBuffer netBB = (ByteBuffer)
    165             ByteBuffer.allocate(len).put(buf, 0, len).flip();
    166         writer.putOutboundData(netBB);
    167     }
    168 
    169     /*
    170      * Main method for writing non-application data.
    171      * We MAC/encrypt, then send down for processing.
    172      */
    173     void write(MAC writeMAC, CipherBox writeCipher) throws IOException {
    174         /*
    175          * Sanity check.
    176          */
    177         switch (contentType()) {
    178         case ct_change_cipher_spec:
    179         case ct_alert:
    180         case ct_handshake:
    181             break;
    182         default:
    183             throw new RuntimeException("unexpected byte buffers");
    184         }
    185 
    186         /*
    187          * Don't bother to really write empty records.  We went this
    188          * far to drive the handshake machinery, for correctness; not
    189          * writing empty records improves performance by cutting CPU
    190          * time and network resource usage.  Also, some protocol
    191          * implementations are fragile and don't like to see empty
    192          * records, so this increases robustness.
    193          *
    194          * (Even change cipher spec messages have a byte of data!)
    195          */
    196         if (!isEmpty()) {
    197             // compress();              // eventually
    198             addMAC(writeMAC);
    199             encrypt(writeCipher);
    200             write((OutputStream)null, false,   // send down for processing
    201                 (ByteArrayOutputStream)null);
    202         }
    203         return;
    204     }
    205 
    206     /**
    207      * Main wrap/write driver.
    208      */
    209     void write(EngineArgs ea, MAC writeMAC, CipherBox writeCipher)
    210             throws IOException {
    211         /*
    212          * sanity check to make sure someone didn't inadvertantly
    213          * send us an impossible combination we don't know how
    214          * to process.
    215          */
    216         assert(contentType() == ct_application_data);
    217 
    218         /*
    219          * Have we set the MAC's yet?  If not, we're not ready
    220          * to process application data yet.
    221          */
    222         if (writeMAC == MAC.NULL) {
    223             return;
    224         }
    225 
    226         /*
    227          * Don't bother to really write empty records.  We went this
    228          * far to drive the handshake machinery, for correctness; not
    229          * writing empty records improves performance by cutting CPU
    230          * time and network resource usage.  Also, some protocol
    231          * implementations are fragile and don't like to see empty
    232          * records, so this increases robustness.
    233          */
    234         if (ea.getAppRemaining() == 0) {
    235             return;
    236         }
    237 
    238         /*
    239          * By default, we counter chosen plaintext issues on CBC mode
    240          * ciphersuites in SSLv3/TLS1.0 by sending one byte of application
    241          * data in the first record of every payload, and the rest in
    242          * subsequent record(s). Note that the issues have been solved in
    243          * TLS 1.1 or later.
    244          *
    245          * It is not necessary to split the very first application record of
    246          * a freshly negotiated TLS session, as there is no previous
    247          * application data to guess.  To improve compatibility, we will not
    248          * split such records.
    249          *
    250          * Because of the compatibility, we'd better produce no more than
    251          * SSLSession.getPacketBufferSize() net data for each wrap. As we
    252          * need a one-byte record at first, the 2nd record size should be
    253          * equal to or less than Record.maxDataSizeMinusOneByteRecord.
    254          *
    255          * This avoids issues in the outbound direction.  For a full fix,
    256          * the peer must have similar protections.
    257          */
    258         int length;
    259         if (engine.needToSplitPayload(writeCipher, protocolVersion)) {
    260             write(ea, writeMAC, writeCipher, 0x01);
    261             ea.resetLim();      // reset application data buffer limit
    262             length = Math.min(ea.getAppRemaining(),
    263                         maxDataSizeMinusOneByteRecord);
    264         } else {
    265             length = Math.min(ea.getAppRemaining(), maxDataSize);
    266         }
    267 
    268         // Don't bother to really write empty records.
    269         if (length > 0) {
    270             write(ea, writeMAC, writeCipher, length);
    271         }
    272 
    273         return;
    274     }
    275 
    276     void write(EngineArgs ea, MAC writeMAC, CipherBox writeCipher,
    277             int length) throws IOException {
    278         /*
    279          * Copy out existing buffer values.
    280          */
    281         ByteBuffer dstBB = ea.netData;
    282         int dstPos = dstBB.position();
    283         int dstLim = dstBB.limit();
    284 
    285         /*
    286          * Where to put the data.  Jump over the header.
    287          *
    288          * Don't need to worry about SSLv2 rewrites, if we're here,
    289          * that's long since done.
    290          */
    291         int dstData = dstPos + headerSize;
    292         dstBB.position(dstData);
    293 
    294         ea.gather(length);
    295 
    296         /*
    297          * "flip" but skip over header again, add MAC & encrypt
    298          * addMAC will expand the limit to reflect the new
    299          * data.
    300          */
    301         dstBB.limit(dstBB.position());
    302         dstBB.position(dstData);
    303         addMAC(writeMAC, dstBB);
    304 
    305         /*
    306          * Encrypt may pad, so again the limit may have changed.
    307          */
    308         dstBB.limit(dstBB.position());
    309         dstBB.position(dstData);
    310         encrypt(writeCipher, dstBB);
    311 
    312         if (debug != null
    313                 && (Debug.isOn("record") || Debug.isOn("handshake"))) {
    314             if ((debug != null && Debug.isOn("record"))
    315                     || contentType() == ct_change_cipher_spec)
    316                 System.out.println(Thread.currentThread().getName()
    317                     // v3.0/v3.1 ...
    318                     + ", WRITE: " + protocolVersion
    319                     + " " + InputRecord.contentName(contentType())
    320                     + ", length = " + length);
    321         }
    322 
    323         int packetLength = dstBB.limit() - dstData;
    324 
    325         /*
    326          * Finish out the record header.
    327          */
    328         dstBB.put(dstPos, contentType());
    329         dstBB.put(dstPos + 1, protocolVersion.major);
    330         dstBB.put(dstPos + 2, protocolVersion.minor);
    331         dstBB.put(dstPos + 3, (byte)(packetLength >> 8));
    332         dstBB.put(dstPos + 4, (byte)packetLength);
    333 
    334         /*
    335          * Position was already set by encrypt() above.
    336          */
    337         dstBB.limit(dstLim);
    338 
    339         return;
    340     }
    341 }
    342