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.*; 29 import java.nio.*; 30 import java.security.GeneralSecurityException; 31 import java.util.ArrayList; 32 import javax.crypto.BadPaddingException; 33 import javax.net.ssl.*; 34 import sun.security.ssl.SSLCipher.SSLReadCipher; 35 36 /** 37 * {@code InputRecord} implementation for {@code SSLSocket}. 38 * 39 * @author David Brownell 40 */ 41 final class SSLSocketInputRecord extends InputRecord implements SSLRecord { 42 private InputStream is = null; 43 private OutputStream os = null; 44 private final byte[] temporary = new byte[1024]; 45 46 // used by handshake hash computation for handshake fragment 47 private byte prevType = -1; 48 private int hsMsgOff = 0; 49 private int hsMsgLen = 0; 50 51 private boolean formatVerified = false; // SSLv2 ruled out? 52 53 // Cache for incomplete handshake messages. 54 private ByteBuffer handshakeBuffer = null; 55 56 private boolean hasHeader = false; // Had read the record header 57 58 SSLSocketInputRecord(HandshakeHash handshakeHash) { 59 super(handshakeHash, SSLReadCipher.nullTlsReadCipher()); 60 } 61 62 @Override 63 int bytesInCompletePacket() throws IOException { 64 65 if (!hasHeader) { 66 // read exactly one record 67 int really = read(is, temporary, 0, headerSize); 68 if (really < 0) { 69 // EOF: peer shut down incorrectly 70 return -1; 71 } 72 hasHeader = true; 73 } 74 75 byte byteZero = temporary[0]; 76 int len = 0; 77 78 /* 79 * If we have already verified previous packets, we can 80 * ignore the verifications steps, and jump right to the 81 * determination. Otherwise, try one last hueristic to 82 * see if it's SSL/TLS. 83 */ 84 if (formatVerified || 85 (byteZero == ContentType.HANDSHAKE.id) || 86 (byteZero == ContentType.ALERT.id)) { 87 /* 88 * Last sanity check that it's not a wild record 89 */ 90 if (!ProtocolVersion.isNegotiable( 91 temporary[1], temporary[2], false, false)) { 92 throw new SSLException("Unrecognized record version " + 93 ProtocolVersion.nameOf(temporary[1], temporary[2]) + 94 " , plaintext connection?"); 95 } 96 97 /* 98 * Reasonably sure this is a V3, disable further checks. 99 * We can't do the same in the v2 check below, because 100 * read still needs to parse/handle the v2 clientHello. 101 */ 102 formatVerified = true; 103 104 /* 105 * One of the SSLv3/TLS message types. 106 */ 107 len = ((temporary[3] & 0xFF) << 8) + 108 (temporary[4] & 0xFF) + headerSize; 109 } else { 110 /* 111 * Must be SSLv2 or something unknown. 112 * Check if it's short (2 bytes) or 113 * long (3) header. 114 * 115 * Internals can warn about unsupported SSLv2 116 */ 117 boolean isShort = ((byteZero & 0x80) != 0); 118 119 if (isShort && ((temporary[2] == 1) || (temporary[2] == 4))) { 120 if (!ProtocolVersion.isNegotiable( 121 temporary[3], temporary[4], false, false)) { 122 throw new SSLException("Unrecognized record version " + 123 ProtocolVersion.nameOf(temporary[3], temporary[4]) + 124 " , plaintext connection?"); 125 } 126 127 /* 128 * Client or Server Hello 129 */ 130 // 131 // Short header is using here. We reverse the code here 132 // in case it is used in the future. 133 // 134 // int mask = (isShort ? 0x7F : 0x3F); 135 // len = ((byteZero & mask) << 8) + 136 // (temporary[1] & 0xFF) + (isShort ? 2 : 3); 137 // 138 len = ((byteZero & 0x7F) << 8) + (temporary[1] & 0xFF) + 2; 139 } else { 140 // Gobblygook! 141 throw new SSLException( 142 "Unrecognized SSL message, plaintext connection?"); 143 } 144 } 145 146 return len; 147 } 148 149 // Note that the input arguments are not used actually. 150 @Override 151 Plaintext[] decode(ByteBuffer[] srcs, int srcsOffset, 152 int srcsLength) throws IOException, BadPaddingException { 153 154 if (isClosed) { 155 return null; 156 } 157 158 if (!hasHeader) { 159 // read exactly one record 160 int really = read(is, temporary, 0, headerSize); 161 if (really < 0) { 162 throw new EOFException("SSL peer shut down incorrectly"); 163 } 164 hasHeader = true; 165 } 166 167 Plaintext plaintext = null; 168 if (!formatVerified) { 169 formatVerified = true; 170 171 /* 172 * The first record must either be a handshake record or an 173 * alert message. If it's not, it is either invalid or an 174 * SSLv2 message. 175 */ 176 if ((temporary[0] != ContentType.HANDSHAKE.id) && 177 (temporary[0] != ContentType.ALERT.id)) { 178 hasHeader = false; 179 return handleUnknownRecord(temporary); 180 } 181 } 182 183 // The record header should has comsumed. 184 hasHeader = false; 185 return decodeInputRecord(temporary); 186 } 187 188 @Override 189 void setReceiverStream(InputStream inputStream) { 190 this.is = inputStream; 191 } 192 193 @Override 194 void setDeliverStream(OutputStream outputStream) { 195 this.os = outputStream; 196 } 197 198 // Note that destination may be null 199 private Plaintext[] decodeInputRecord( 200 byte[] header) throws IOException, BadPaddingException { 201 byte contentType = header[0]; // pos: 0 202 byte majorVersion = header[1]; // pos: 1 203 byte minorVersion = header[2]; // pos: 2 204 int contentLen = ((header[3] & 0xFF) << 8) + 205 (header[4] & 0xFF); // pos: 3, 4 206 207 if (SSLLogger.isOn && SSLLogger.isOn("record")) { 208 SSLLogger.fine( 209 "READ: " + 210 ProtocolVersion.nameOf(majorVersion, minorVersion) + 211 " " + ContentType.nameOf(contentType) + ", length = " + 212 contentLen); 213 } 214 215 // 216 // Check for upper bound. 217 // 218 // Note: May check packetSize limit in the future. 219 if (contentLen < 0 || contentLen > maxLargeRecordSize - headerSize) { 220 throw new SSLProtocolException( 221 "Bad input record size, TLSCiphertext.length = " + contentLen); 222 } 223 224 // 225 // Read a complete record. 226 // 227 ByteBuffer destination = ByteBuffer.allocate(headerSize + contentLen); 228 int dstPos = destination.position(); 229 destination.put(temporary, 0, headerSize); 230 while (contentLen > 0) { 231 int howmuch = Math.min(temporary.length, contentLen); 232 int really = read(is, temporary, 0, howmuch); 233 if (really < 0) { 234 throw new EOFException("SSL peer shut down incorrectly"); 235 } 236 237 destination.put(temporary, 0, howmuch); 238 contentLen -= howmuch; 239 } 240 destination.flip(); 241 destination.position(dstPos + headerSize); 242 243 if (SSLLogger.isOn && SSLLogger.isOn("record")) { 244 SSLLogger.fine( 245 "READ: " + 246 ProtocolVersion.nameOf(majorVersion, minorVersion) + 247 " " + ContentType.nameOf(contentType) + ", length = " + 248 destination.remaining()); 249 } 250 251 // 252 // Decrypt the fragment 253 // 254 ByteBuffer fragment; 255 try { 256 Plaintext plaintext = 257 readCipher.decrypt(contentType, destination, null); 258 fragment = plaintext.fragment; 259 contentType = plaintext.contentType; 260 } catch (BadPaddingException bpe) { 261 throw bpe; 262 } catch (GeneralSecurityException gse) { 263 throw (SSLProtocolException)(new SSLProtocolException( 264 "Unexpected exception")).initCause(gse); 265 } 266 if (contentType != ContentType.HANDSHAKE.id && hsMsgOff != hsMsgLen) { 267 throw new SSLProtocolException( 268 "Expected to get a handshake fragment"); 269 } 270 271 // 272 // parse handshake messages 273 // 274 if (contentType == ContentType.HANDSHAKE.id) { 275 ByteBuffer handshakeFrag = fragment; 276 if ((handshakeBuffer != null) && 277 (handshakeBuffer.remaining() != 0)) { 278 ByteBuffer bb = ByteBuffer.wrap(new byte[ 279 handshakeBuffer.remaining() + fragment.remaining()]); 280 bb.put(handshakeBuffer); 281 bb.put(fragment); 282 handshakeFrag = bb.rewind(); 283 handshakeBuffer = null; 284 } 285 286 ArrayList<Plaintext> plaintexts = new ArrayList<>(5); 287 while (handshakeFrag.hasRemaining()) { 288 int remaining = handshakeFrag.remaining(); 289 if (remaining < handshakeHeaderSize) { 290 handshakeBuffer = ByteBuffer.wrap(new byte[remaining]); 291 handshakeBuffer.put(handshakeFrag); 292 handshakeBuffer.rewind(); 293 break; 294 } 295 296 handshakeFrag.mark(); 297 // skip the first byte: handshake type 298 byte handshakeType = handshakeFrag.get(); 299 int handshakeBodyLen = Record.getInt24(handshakeFrag); 300 handshakeFrag.reset(); 301 int handshakeMessageLen = 302 handshakeHeaderSize + handshakeBodyLen; 303 if (remaining < handshakeMessageLen) { 304 handshakeBuffer = ByteBuffer.wrap(new byte[remaining]); 305 handshakeBuffer.put(handshakeFrag); 306 handshakeBuffer.rewind(); 307 break; 308 } if (remaining == handshakeMessageLen) { 309 if (handshakeHash.isHashable(handshakeType)) { 310 handshakeHash.receive(handshakeFrag); 311 } 312 313 plaintexts.add( 314 new Plaintext(contentType, 315 majorVersion, minorVersion, -1, -1L, handshakeFrag) 316 ); 317 break; 318 } else { 319 int fragPos = handshakeFrag.position(); 320 int fragLim = handshakeFrag.limit(); 321 int nextPos = fragPos + handshakeMessageLen; 322 handshakeFrag.limit(nextPos); 323 324 if (handshakeHash.isHashable(handshakeType)) { 325 handshakeHash.receive(handshakeFrag); 326 } 327 328 plaintexts.add( 329 new Plaintext(contentType, majorVersion, minorVersion, 330 -1, -1L, handshakeFrag.slice()) 331 ); 332 333 handshakeFrag.position(nextPos); 334 handshakeFrag.limit(fragLim); 335 } 336 } 337 338 return plaintexts.toArray(new Plaintext[0]); 339 } 340 341 return new Plaintext[] { 342 new Plaintext(contentType, 343 majorVersion, minorVersion, -1, -1L, fragment) 344 // recordEpoch, recordSeq, plaintext); 345 }; 346 } 347 348 private Plaintext[] handleUnknownRecord( 349 byte[] header) throws IOException, BadPaddingException { 350 byte firstByte = header[0]; 351 byte thirdByte = header[2]; 352 353 // Does it look like a Version 2 client hello (V2ClientHello)? 354 if (((firstByte & 0x80) != 0) && (thirdByte == 1)) { 355 /* 356 * If SSLv2Hello is not enabled, throw an exception. 357 */ 358 if (helloVersion != ProtocolVersion.SSL20Hello) { 359 throw new SSLHandshakeException("SSLv2Hello is not enabled"); 360 } 361 362 byte majorVersion = header[3]; 363 byte minorVersion = header[4]; 364 365 if ((majorVersion == ProtocolVersion.SSL20Hello.major) && 366 (minorVersion == ProtocolVersion.SSL20Hello.minor)) { 367 368 /* 369 * Looks like a V2 client hello, but not one saying 370 * "let's talk SSLv3". So we need to send an SSLv2 371 * error message, one that's treated as fatal by 372 * clients (Otherwise we'll hang.) 373 */ 374 os.write(SSLRecord.v2NoCipher); // SSLv2Hello 375 376 if (SSLLogger.isOn) { 377 if (SSLLogger.isOn("record")) { 378 SSLLogger.fine( 379 "Requested to negotiate unsupported SSLv2!"); 380 } 381 382 if (SSLLogger.isOn("packet")) { 383 SSLLogger.fine("Raw write", SSLRecord.v2NoCipher); 384 } 385 } 386 387 throw new SSLException("Unsupported SSL v2.0 ClientHello"); 388 } 389 390 int msgLen = ((header[0] & 0x7F) << 8) | (header[1] & 0xFF); 391 392 ByteBuffer destination = ByteBuffer.allocate(headerSize + msgLen); 393 destination.put(temporary, 0, headerSize); 394 msgLen -= 3; // had read 3 bytes of content as header 395 while (msgLen > 0) { 396 int howmuch = Math.min(temporary.length, msgLen); 397 int really = read(is, temporary, 0, howmuch); 398 if (really < 0) { 399 throw new EOFException("SSL peer shut down incorrectly"); 400 } 401 402 destination.put(temporary, 0, howmuch); 403 msgLen -= howmuch; 404 } 405 destination.flip(); 406 407 /* 408 * If we can map this into a V3 ClientHello, read and 409 * hash the rest of the V2 handshake, turn it into a 410 * V3 ClientHello message, and pass it up. 411 */ 412 destination.position(2); // exclude the header 413 handshakeHash.receive(destination); 414 destination.position(0); 415 416 ByteBuffer converted = convertToClientHello(destination); 417 418 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { 419 SSLLogger.fine( 420 "[Converted] ClientHello", converted); 421 } 422 423 return new Plaintext[] { 424 new Plaintext(ContentType.HANDSHAKE.id, 425 majorVersion, minorVersion, -1, -1L, converted) 426 }; 427 } else { 428 if (((firstByte & 0x80) != 0) && (thirdByte == 4)) { 429 throw new SSLException("SSL V2.0 servers are not supported."); 430 } 431 432 throw new SSLException("Unsupported or unrecognized SSL message"); 433 } 434 } 435 436 // Read the exact bytes of data, otherwise, return -1. 437 private static int read(InputStream is, 438 byte[] buffer, int offset, int len) throws IOException { 439 int n = 0; 440 while (n < len) { 441 int readLen = is.read(buffer, offset + n, len - n); 442 if (readLen < 0) { 443 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { 444 SSLLogger.fine("Raw read: EOF"); 445 } 446 return -1; 447 } 448 449 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { 450 ByteBuffer bb = ByteBuffer.wrap(buffer, offset + n, readLen); 451 SSLLogger.fine("Raw read", bb); 452 } 453 454 n += readLen; 455 } 456 457 return n; 458 } 459 }