1 /* 2 * Copyright (c) 1996, 2019, 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.IOException; 29 import java.nio.ByteBuffer; 30 import java.security.GeneralSecurityException; 31 import java.util.ArrayList; 32 import javax.crypto.BadPaddingException; 33 import javax.net.ssl.SSLException; 34 import javax.net.ssl.SSLHandshakeException; 35 import javax.net.ssl.SSLProtocolException; 36 import sun.security.ssl.SSLCipher.SSLReadCipher; 37 38 /** 39 * {@code InputRecord} implementation for {@code SSLEngine}. 40 */ 41 final class SSLEngineInputRecord extends InputRecord implements SSLRecord { 42 private boolean formatVerified = false; // SSLv2 ruled out? 43 44 // Cache for incomplete handshake messages. 45 private ByteBuffer handshakeBuffer = null; 46 SSLEngineInputRecord(HandshakeHash handshakeHash)47 SSLEngineInputRecord(HandshakeHash handshakeHash) { 48 super(handshakeHash, SSLReadCipher.nullTlsReadCipher()); 49 } 50 51 @Override estimateFragmentSize(int packetSize)52 int estimateFragmentSize(int packetSize) { 53 if (packetSize > 0) { 54 return readCipher.estimateFragmentSize(packetSize, headerSize); 55 } else { 56 return Record.maxDataSize; 57 } 58 } 59 60 @Override bytesInCompletePacket( ByteBuffer[] srcs, int srcsOffset, int srcsLength)61 int bytesInCompletePacket( 62 ByteBuffer[] srcs, int srcsOffset, int srcsLength) throws IOException { 63 64 return bytesInCompletePacket(srcs[srcsOffset]); 65 } 66 bytesInCompletePacket(ByteBuffer packet)67 private int bytesInCompletePacket(ByteBuffer packet) throws SSLException { 68 /* 69 * SSLv2 length field is in bytes 0/1 70 * SSLv3/TLS length field is in bytes 3/4 71 */ 72 if (packet.remaining() < 5) { 73 return -1; 74 } 75 76 int pos = packet.position(); 77 byte byteZero = packet.get(pos); 78 79 int len = 0; 80 81 /* 82 * If we have already verified previous packets, we can 83 * ignore the verifications steps, and jump right to the 84 * determination. Otherwise, try one last heuristic to 85 * see if it's SSL/TLS. 86 */ 87 if (formatVerified || 88 (byteZero == ContentType.HANDSHAKE.id) || 89 (byteZero == ContentType.ALERT.id)) { 90 /* 91 * Last sanity check that it's not a wild record 92 */ 93 byte majorVersion = packet.get(pos + 1); 94 byte minorVersion = packet.get(pos + 2); 95 if (!ProtocolVersion.isNegotiable( 96 majorVersion, minorVersion, false, false)) { 97 throw new SSLException("Unrecognized record version " + 98 ProtocolVersion.nameOf(majorVersion, minorVersion) + 99 " , plaintext connection?"); 100 } 101 102 /* 103 * Reasonably sure this is a V3, disable further checks. 104 * We can't do the same in the v2 check below, because 105 * read still needs to parse/handle the v2 clientHello. 106 */ 107 formatVerified = true; 108 109 /* 110 * One of the SSLv3/TLS message types. 111 */ 112 len = ((packet.get(pos + 3) & 0xFF) << 8) + 113 (packet.get(pos + 4) & 0xFF) + headerSize; 114 115 } else { 116 /* 117 * Must be SSLv2 or something unknown. 118 * Check if it's short (2 bytes) or 119 * long (3) header. 120 * 121 * Internals can warn about unsupported SSLv2 122 */ 123 boolean isShort = ((byteZero & 0x80) != 0); 124 125 if (isShort && 126 ((packet.get(pos + 2) == 1) || packet.get(pos + 2) == 4)) { 127 128 byte majorVersion = packet.get(pos + 3); 129 byte minorVersion = packet.get(pos + 4); 130 if (!ProtocolVersion.isNegotiable( 131 majorVersion, minorVersion, false, false)) { 132 throw new SSLException("Unrecognized record version " + 133 ProtocolVersion.nameOf(majorVersion, minorVersion) + 134 " , plaintext connection?"); 135 } 136 137 /* 138 * Client or Server Hello 139 */ 140 int mask = (isShort ? 0x7F : 0x3F); 141 len = ((byteZero & mask) << 8) + 142 (packet.get(pos + 1) & 0xFF) + (isShort ? 2 : 3); 143 144 } else { 145 // Gobblygook! 146 throw new SSLException( 147 "Unrecognized SSL message, plaintext connection?"); 148 } 149 } 150 151 return len; 152 } 153 154 @Override decode(ByteBuffer[] srcs, int srcsOffset, int srcsLength)155 Plaintext[] decode(ByteBuffer[] srcs, int srcsOffset, 156 int srcsLength) throws IOException, BadPaddingException { 157 if (srcs == null || srcs.length == 0 || srcsLength == 0) { 158 return new Plaintext[0]; 159 } else if (srcsLength == 1) { 160 return decode(srcs[srcsOffset]); 161 } else { 162 ByteBuffer packet = extract(srcs, 163 srcsOffset, srcsLength, SSLRecord.headerSize); 164 165 return decode(packet); 166 } 167 } 168 decode(ByteBuffer packet)169 private Plaintext[] decode(ByteBuffer packet) 170 throws IOException, BadPaddingException { 171 172 if (isClosed) { 173 return null; 174 } 175 176 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { 177 SSLLogger.fine("Raw read", packet); 178 } 179 180 // The caller should have validated the record. 181 if (!formatVerified) { 182 formatVerified = true; 183 184 /* 185 * The first record must either be a handshake record or an 186 * alert message. If it's not, it is either invalid or an 187 * SSLv2 message. 188 */ 189 int pos = packet.position(); 190 byte byteZero = packet.get(pos); 191 if (byteZero != ContentType.HANDSHAKE.id && 192 byteZero != ContentType.ALERT.id) { 193 return handleUnknownRecord(packet); 194 } 195 } 196 197 return decodeInputRecord(packet); 198 } 199 decodeInputRecord(ByteBuffer packet)200 private Plaintext[] decodeInputRecord(ByteBuffer packet) 201 throws IOException, BadPaddingException { 202 // 203 // The packet should be a complete record, or more. 204 // 205 int srcPos = packet.position(); 206 int srcLim = packet.limit(); 207 208 byte contentType = packet.get(); // pos: 0 209 byte majorVersion = packet.get(); // pos: 1 210 byte minorVersion = packet.get(); // pos: 2 211 int contentLen = Record.getInt16(packet); // 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 // Decrypt the fragment 232 // 233 int recLim = srcPos + SSLRecord.headerSize + contentLen; 234 packet.limit(recLim); 235 packet.position(srcPos + SSLRecord.headerSize); 236 237 ByteBuffer fragment; 238 try { 239 Plaintext plaintext = 240 readCipher.decrypt(contentType, packet, null); 241 fragment = plaintext.fragment; 242 contentType = plaintext.contentType; 243 } catch (BadPaddingException bpe) { 244 throw bpe; 245 } catch (GeneralSecurityException gse) { 246 throw (SSLProtocolException)(new SSLProtocolException( 247 "Unexpected exception")).initCause(gse); 248 } finally { 249 // consume a complete record 250 packet.limit(srcLim); 251 packet.position(recLim); 252 } 253 254 // 255 // check for handshake fragment 256 // 257 if (contentType != ContentType.HANDSHAKE.id && 258 handshakeBuffer != null && handshakeBuffer.hasRemaining()) { 259 throw new SSLProtocolException( 260 "Expecting a handshake fragment, but received " + 261 ContentType.nameOf(contentType)); 262 } 263 264 // 265 // parse handshake messages 266 // 267 if (contentType == ContentType.HANDSHAKE.id) { 268 ByteBuffer handshakeFrag = fragment; 269 if ((handshakeBuffer != null) && 270 (handshakeBuffer.remaining() != 0)) { 271 ByteBuffer bb = ByteBuffer.wrap(new byte[ 272 handshakeBuffer.remaining() + fragment.remaining()]); 273 bb.put(handshakeBuffer); 274 bb.put(fragment); 275 handshakeFrag = bb.rewind(); 276 handshakeBuffer = null; 277 } 278 279 ArrayList<Plaintext> plaintexts = new ArrayList<>(5); 280 while (handshakeFrag.hasRemaining()) { 281 int remaining = handshakeFrag.remaining(); 282 if (remaining < handshakeHeaderSize) { 283 handshakeBuffer = ByteBuffer.wrap(new byte[remaining]); 284 handshakeBuffer.put(handshakeFrag); 285 handshakeBuffer.rewind(); 286 break; 287 } 288 289 handshakeFrag.mark(); 290 291 // Fail fast for unknown handshake message. 292 byte handshakeType = handshakeFrag.get(); 293 if (!SSLHandshake.isKnown(handshakeType)) { 294 throw new SSLProtocolException( 295 "Unknown handshake type size, Handshake.msg_type = " + 296 (handshakeType & 0xFF)); 297 } 298 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 } else 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 }; 345 } 346 handleUnknownRecord(ByteBuffer packet)347 private Plaintext[] handleUnknownRecord(ByteBuffer packet) 348 throws IOException, BadPaddingException { 349 // 350 // The packet should be a complete record. 351 // 352 int srcPos = packet.position(); 353 int srcLim = packet.limit(); 354 355 byte firstByte = packet.get(srcPos); 356 byte thirdByte = packet.get(srcPos + 2); 357 358 // Does it look like a Version 2 client hello (V2ClientHello)? 359 if (((firstByte & 0x80) != 0) && (thirdByte == 1)) { 360 /* 361 * If SSLv2Hello is not enabled, throw an exception. 362 */ 363 if (helloVersion != ProtocolVersion.SSL20Hello) { 364 throw new SSLHandshakeException("SSLv2Hello is not enabled"); 365 } 366 367 byte majorVersion = packet.get(srcPos + 3); 368 byte minorVersion = packet.get(srcPos + 4); 369 370 if ((majorVersion == ProtocolVersion.SSL20Hello.major) && 371 (minorVersion == ProtocolVersion.SSL20Hello.minor)) { 372 373 /* 374 * Looks like a V2 client hello, but not one saying 375 * "let's talk SSLv3". So we need to send an SSLv2 376 * error message, one that's treated as fatal by 377 * clients (Otherwise we'll hang.) 378 */ 379 if (SSLLogger.isOn && SSLLogger.isOn("record")) { 380 SSLLogger.fine( 381 "Requested to negotiate unsupported SSLv2!"); 382 } 383 384 // Note that the exception is caught in SSLEngineImpl 385 // so that SSLv2 error message can be delivered properly. 386 throw new UnsupportedOperationException( // SSLv2Hello 387 "Unsupported SSL v2.0 ClientHello"); 388 } 389 390 /* 391 * If we can map this into a V3 ClientHello, read and 392 * hash the rest of the V2 handshake, turn it into a 393 * V3 ClientHello message, and pass it up. 394 */ 395 packet.position(srcPos + 2); // exclude the header 396 handshakeHash.receive(packet); 397 packet.position(srcPos); 398 399 ByteBuffer converted = convertToClientHello(packet); 400 401 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { 402 SSLLogger.fine( 403 "[Converted] ClientHello", converted); 404 } 405 406 return new Plaintext[] { 407 new Plaintext(ContentType.HANDSHAKE.id, 408 majorVersion, minorVersion, -1, -1L, converted) 409 }; 410 } else { 411 if (((firstByte & 0x80) != 0) && (thirdByte == 4)) { 412 throw new SSLException("SSL V2.0 servers are not supported."); 413 } 414 415 throw new SSLException("Unsupported or unrecognized SSL message"); 416 } 417 } 418 } 419