1 /* 2 * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. 3 * Copyright (c) 2020, Azul Systems, Inc. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27 package sun.security.ssl; 28 29 import java.io.EOFException; 30 import java.io.InterruptedIOException; 31 import java.io.IOException; 32 import java.io.InputStream; 33 import java.io.OutputStream; 34 import java.nio.ByteBuffer; 35 import java.security.GeneralSecurityException; 36 import java.util.ArrayList; 37 import javax.crypto.BadPaddingException; 38 import javax.net.ssl.SSLException; 39 import javax.net.ssl.SSLHandshakeException; 40 import javax.net.ssl.SSLProtocolException; 41 42 import sun.security.ssl.SSLCipher.SSLReadCipher; 43 44 /** 45 * {@code InputRecord} implementation for {@code SSLSocket}. 46 * 47 * @author David Brownell 48 */ 49 final class SSLSocketInputRecord extends InputRecord implements SSLRecord { 50 private InputStream is = null; 51 private OutputStream os = null; 52 private final byte[] header = new byte[headerSize]; 53 private int headerOff = 0; 54 // Cache for incomplete record body. 55 private ByteBuffer recordBody = ByteBuffer.allocate(1024); 56 57 private boolean formatVerified = false; // SSLv2 ruled out? 58 59 // Cache for incomplete handshake messages. 60 private ByteBuffer handshakeBuffer = null; 61 SSLSocketInputRecord(HandshakeHash handshakeHash)62 SSLSocketInputRecord(HandshakeHash handshakeHash) { 63 super(handshakeHash, SSLReadCipher.nullTlsReadCipher()); 64 } 65 66 @Override bytesInCompletePacket()67 int bytesInCompletePacket() throws IOException { 68 // read header 69 try { 70 readHeader(); 71 } catch (EOFException eofe) { 72 // The caller will handle EOF. 73 return -1; 74 } 75 76 byte byteZero = header[0]; 77 int len = 0; 78 79 /* 80 * If we have already verified previous packets, we can 81 * ignore the verifications steps, and jump right to the 82 * determination. Otherwise, try one last heuristic to 83 * see if it's SSL/TLS. 84 */ 85 if (formatVerified || 86 (byteZero == ContentType.HANDSHAKE.id) || 87 (byteZero == ContentType.ALERT.id)) { 88 /* 89 * Last sanity check that it's not a wild record 90 */ 91 if (!ProtocolVersion.isNegotiable( 92 header[1], header[2], false, false)) { 93 throw new SSLException("Unrecognized record version " + 94 ProtocolVersion.nameOf(header[1], header[2]) + 95 " , plaintext connection?"); 96 } 97 98 /* 99 * Reasonably sure this is a V3, disable further checks. 100 * We can't do the same in the v2 check below, because 101 * read still needs to parse/handle the v2 clientHello. 102 */ 103 formatVerified = true; 104 105 /* 106 * One of the SSLv3/TLS message types. 107 */ 108 len = ((header[3] & 0xFF) << 8) + 109 (header[4] & 0xFF) + headerSize; 110 } else { 111 /* 112 * Must be SSLv2 or something unknown. 113 * Check if it's short (2 bytes) or 114 * long (3) header. 115 * 116 * Internals can warn about unsupported SSLv2 117 */ 118 boolean isShort = ((byteZero & 0x80) != 0); 119 120 if (isShort && ((header[2] == 1) || (header[2] == 4))) { 121 if (!ProtocolVersion.isNegotiable( 122 header[3], header[4], false, false)) { 123 throw new SSLException("Unrecognized record version " + 124 ProtocolVersion.nameOf(header[3], header[4]) + 125 " , plaintext connection?"); 126 } 127 128 /* 129 * Client or Server Hello 130 */ 131 // 132 // Short header is using here. We reverse the code here 133 // in case it is used in the future. 134 // 135 // int mask = (isShort ? 0x7F : 0x3F); 136 // len = ((byteZero & mask) << 8) + 137 // (header[1] & 0xFF) + (isShort ? 2 : 3); 138 // 139 len = ((byteZero & 0x7F) << 8) + (header[1] & 0xFF) + 2; 140 } else { 141 // Gobblygook! 142 throw new SSLException( 143 "Unrecognized SSL message, plaintext connection?"); 144 } 145 } 146 147 return len; 148 } 149 150 // Note that the input arguments are not used actually. 151 @Override decode(ByteBuffer[] srcs, int srcsOffset, int srcsLength)152 Plaintext[] decode(ByteBuffer[] srcs, int srcsOffset, 153 int srcsLength) throws IOException, BadPaddingException { 154 155 if (isClosed) { 156 return null; 157 } 158 159 // read header 160 readHeader(); 161 162 Plaintext[] plaintext = null; 163 boolean cleanInBuffer = true; 164 try { 165 if (!formatVerified) { 166 formatVerified = true; 167 168 /* 169 * The first record must either be a handshake record or an 170 * alert message. If it's not, it is either invalid or an 171 * SSLv2 message. 172 */ 173 if ((header[0] != ContentType.HANDSHAKE.id) && 174 (header[0] != ContentType.ALERT.id)) { 175 plaintext = handleUnknownRecord(); 176 } 177 } 178 179 // The record header should has consumed. 180 if (plaintext == null) { 181 plaintext = decodeInputRecord(); 182 } 183 } catch(InterruptedIOException e) { 184 // do not clean header and recordBody in case of Socket Timeout 185 cleanInBuffer = false; 186 throw e; 187 } finally { 188 if (cleanInBuffer) { 189 headerOff = 0; 190 recordBody.clear(); 191 } 192 } 193 return plaintext; 194 } 195 196 @Override setReceiverStream(InputStream inputStream)197 void setReceiverStream(InputStream inputStream) { 198 this.is = inputStream; 199 } 200 201 @Override setDeliverStream(OutputStream outputStream)202 void setDeliverStream(OutputStream outputStream) { 203 this.os = outputStream; 204 } 205 decodeInputRecord()206 private Plaintext[] decodeInputRecord() throws IOException, BadPaddingException { 207 byte contentType = header[0]; // pos: 0 208 byte majorVersion = header[1]; // pos: 1 209 byte minorVersion = header[2]; // pos: 2 210 int contentLen = ((header[3] & 0xFF) << 8) + 211 (header[4] & 0xFF); // pos: 3, 4 212 213 if (SSLLogger.isOn && SSLLogger.isOn("record")) { 214 SSLLogger.fine( 215 "READ: " + 216 ProtocolVersion.nameOf(majorVersion, minorVersion) + 217 " " + ContentType.nameOf(contentType) + ", length = " + 218 contentLen); 219 } 220 221 // 222 // Check for upper bound. 223 // 224 // Note: May check packetSize limit in the future. 225 if (contentLen < 0 || contentLen > maxLargeRecordSize - headerSize) { 226 throw new SSLProtocolException( 227 "Bad input record size, TLSCiphertext.length = " + contentLen); 228 } 229 230 // 231 // Read a complete record and store in the recordBody 232 // recordBody is used to cache incoming record and restore in case of 233 // read operation timedout 234 // 235 if (recordBody.position() == 0) { 236 if (recordBody.capacity() < contentLen) { 237 recordBody = ByteBuffer.allocate(contentLen); 238 } 239 recordBody.limit(contentLen); 240 } else { 241 contentLen = recordBody.remaining(); 242 } 243 readFully(contentLen); 244 recordBody.flip(); 245 246 if (SSLLogger.isOn && SSLLogger.isOn("record")) { 247 SSLLogger.fine( 248 "READ: " + 249 ProtocolVersion.nameOf(majorVersion, minorVersion) + 250 " " + ContentType.nameOf(contentType) + ", length = " + 251 recordBody.remaining()); 252 } 253 254 // 255 // Decrypt the fragment 256 // 257 ByteBuffer fragment; 258 try { 259 Plaintext plaintext = 260 readCipher.decrypt(contentType, recordBody, null); 261 fragment = plaintext.fragment; 262 contentType = plaintext.contentType; 263 } catch (BadPaddingException bpe) { 264 throw bpe; 265 } catch (GeneralSecurityException gse) { 266 throw (SSLProtocolException)(new SSLProtocolException( 267 "Unexpected exception")).initCause(gse); 268 } 269 270 if (contentType != ContentType.HANDSHAKE.id && 271 handshakeBuffer != null && handshakeBuffer.hasRemaining()) { 272 throw new SSLProtocolException( 273 "Expecting a handshake fragment, but received " + 274 ContentType.nameOf(contentType)); 275 } 276 277 // 278 // parse handshake messages 279 // 280 if (contentType == ContentType.HANDSHAKE.id) { 281 ByteBuffer handshakeFrag = fragment; 282 if ((handshakeBuffer != null) && 283 (handshakeBuffer.remaining() != 0)) { 284 ByteBuffer bb = ByteBuffer.wrap(new byte[ 285 handshakeBuffer.remaining() + fragment.remaining()]); 286 bb.put(handshakeBuffer); 287 bb.put(fragment); 288 handshakeFrag = bb.rewind(); 289 handshakeBuffer = null; 290 } 291 292 ArrayList<Plaintext> plaintexts = new ArrayList<>(5); 293 while (handshakeFrag.hasRemaining()) { 294 int remaining = handshakeFrag.remaining(); 295 if (remaining < handshakeHeaderSize) { 296 handshakeBuffer = ByteBuffer.wrap(new byte[remaining]); 297 handshakeBuffer.put(handshakeFrag); 298 handshakeBuffer.rewind(); 299 break; 300 } 301 302 handshakeFrag.mark(); 303 304 // Fail fast for unknown handshake message. 305 byte handshakeType = handshakeFrag.get(); 306 if (!SSLHandshake.isKnown(handshakeType)) { 307 throw new SSLProtocolException( 308 "Unknown handshake type size, Handshake.msg_type = " + 309 (handshakeType & 0xFF)); 310 } 311 312 int handshakeBodyLen = Record.getInt24(handshakeFrag); 313 if (handshakeBodyLen > SSLConfiguration.maxHandshakeMessageSize) { 314 throw new SSLProtocolException( 315 "The size of the handshake message (" 316 + handshakeBodyLen 317 + ") exceeds the maximum allowed size (" 318 + SSLConfiguration.maxHandshakeMessageSize 319 + ")"); 320 } 321 322 handshakeFrag.reset(); 323 int handshakeMessageLen = 324 handshakeHeaderSize + handshakeBodyLen; 325 if (remaining < handshakeMessageLen) { 326 handshakeBuffer = ByteBuffer.wrap(new byte[remaining]); 327 handshakeBuffer.put(handshakeFrag); 328 handshakeBuffer.rewind(); 329 break; 330 } 331 332 if (remaining == handshakeMessageLen) { 333 if (handshakeHash.isHashable(handshakeType)) { 334 handshakeHash.receive(handshakeFrag); 335 } 336 337 plaintexts.add( 338 new Plaintext(contentType, 339 majorVersion, minorVersion, -1, -1L, handshakeFrag) 340 ); 341 break; 342 } else { 343 int fragPos = handshakeFrag.position(); 344 int fragLim = handshakeFrag.limit(); 345 int nextPos = fragPos + handshakeMessageLen; 346 handshakeFrag.limit(nextPos); 347 348 if (handshakeHash.isHashable(handshakeType)) { 349 handshakeHash.receive(handshakeFrag); 350 } 351 352 plaintexts.add( 353 new Plaintext(contentType, majorVersion, minorVersion, 354 -1, -1L, handshakeFrag.slice()) 355 ); 356 357 handshakeFrag.position(nextPos); 358 handshakeFrag.limit(fragLim); 359 } 360 } 361 362 return plaintexts.toArray(new Plaintext[0]); 363 } 364 365 return new Plaintext[] { 366 new Plaintext(contentType, 367 majorVersion, minorVersion, -1, -1L, fragment) 368 }; 369 } 370 handleUnknownRecord()371 private Plaintext[] handleUnknownRecord() throws IOException, BadPaddingException { 372 byte firstByte = header[0]; 373 byte thirdByte = header[2]; 374 375 // Does it look like a Version 2 client hello (V2ClientHello)? 376 if (((firstByte & 0x80) != 0) && (thirdByte == 1)) { 377 /* 378 * If SSLv2Hello is not enabled, throw an exception. 379 */ 380 if (helloVersion != ProtocolVersion.SSL20Hello) { 381 throw new SSLHandshakeException("SSLv2Hello is not enabled"); 382 } 383 384 byte majorVersion = header[3]; 385 byte minorVersion = header[4]; 386 387 if ((majorVersion == ProtocolVersion.SSL20Hello.major) && 388 (minorVersion == ProtocolVersion.SSL20Hello.minor)) { 389 390 /* 391 * Looks like a V2 client hello, but not one saying 392 * "let's talk SSLv3". So we need to send an SSLv2 393 * error message, one that's treated as fatal by 394 * clients (Otherwise we'll hang.) 395 */ 396 os.write(SSLRecord.v2NoCipher); // SSLv2Hello 397 398 if (SSLLogger.isOn) { 399 if (SSLLogger.isOn("record")) { 400 SSLLogger.fine( 401 "Requested to negotiate unsupported SSLv2!"); 402 } 403 404 if (SSLLogger.isOn("packet")) { 405 SSLLogger.fine("Raw write", SSLRecord.v2NoCipher); 406 } 407 } 408 409 throw new SSLException("Unsupported SSL v2.0 ClientHello"); 410 } 411 412 int msgLen = ((header[0] & 0x7F) << 8) | (header[1] & 0xFF); 413 if (recordBody.position() == 0) { 414 if (recordBody.capacity() < (headerSize + msgLen)) { 415 recordBody = ByteBuffer.allocate(headerSize + msgLen); 416 } 417 recordBody.limit(headerSize + msgLen); 418 recordBody.put(header, 0, headerSize); 419 } else { 420 msgLen = recordBody.remaining(); 421 } 422 msgLen -= 3; // had read 3 bytes of content as header 423 readFully(msgLen); 424 recordBody.flip(); 425 426 /* 427 * If we can map this into a V3 ClientHello, read and 428 * hash the rest of the V2 handshake, turn it into a 429 * V3 ClientHello message, and pass it up. 430 */ 431 recordBody.position(2); // exclude the header 432 handshakeHash.receive(recordBody); 433 recordBody.position(0); 434 435 ByteBuffer converted = convertToClientHello(recordBody); 436 437 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { 438 SSLLogger.fine( 439 "[Converted] ClientHello", converted); 440 } 441 442 return new Plaintext[] { 443 new Plaintext(ContentType.HANDSHAKE.id, 444 majorVersion, minorVersion, -1, -1L, converted) 445 }; 446 } else { 447 if (((firstByte & 0x80) != 0) && (thirdByte == 4)) { 448 throw new SSLException("SSL V2.0 servers are not supported."); 449 } 450 451 throw new SSLException("Unsupported or unrecognized SSL message"); 452 } 453 } 454 455 // Read the exact bytes of data, otherwise, throw IOException. readFully(int len)456 private int readFully(int len) throws IOException { 457 int end = len + recordBody.position(); 458 int off = recordBody.position(); 459 try { 460 while (off < end) { 461 off += read(is, recordBody.array(), off, end - off); 462 } 463 } finally { 464 recordBody.position(off); 465 } 466 return len; 467 } 468 469 // Read SSE record header, otherwise, throw IOException. readHeader()470 private int readHeader() throws IOException { 471 while (headerOff < headerSize) { 472 headerOff += read(is, header, headerOff, headerSize - headerOff); 473 } 474 return headerSize; 475 } 476 read(InputStream is, byte[] buf, int off, int len)477 private static int read(InputStream is, byte[] buf, int off, int len) throws IOException { 478 int readLen = is.read(buf, off, len); 479 if (readLen < 0) { 480 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { 481 SSLLogger.fine("Raw read: EOF"); 482 } 483 throw new EOFException("SSL peer shut down incorrectly"); 484 } 485 486 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { 487 ByteBuffer bb = ByteBuffer.wrap(buf, off, readLen); 488 SSLLogger.fine("Raw read", bb); 489 } 490 return readLen; 491 } 492 493 // Try to use up the input stream without impact the performance too much. deplete(boolean tryToRead)494 void deplete(boolean tryToRead) throws IOException { 495 int remaining = is.available(); 496 if (tryToRead && (remaining == 0)) { 497 // try to wait and read one byte if no buffered input 498 is.read(); 499 } 500 501 while ((remaining = is.available()) != 0) { 502 is.skip(remaining); 503 } 504 } 505 } 506