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