1 /*
2 * Copyright (c) 1996, 2020, 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.EOFException;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.OutputStream;
32 import java.nio.ByteBuffer;
33 import java.security.GeneralSecurityException;
34 import java.util.ArrayList;
35 import javax.crypto.BadPaddingException;
36 import javax.net.ssl.SSLException;
37 import javax.net.ssl.SSLHandshakeException;
38 import javax.net.ssl.SSLProtocolException;
39
40 import sun.security.ssl.SSLCipher.SSLReadCipher;
41
42 /**
43 * {@code InputRecord} implementation for {@code SSLSocket}.
44 *
45 * @author David Brownell
46 */
47 final class SSLSocketInputRecord extends InputRecord implements SSLRecord {
48 private InputStream is = null;
49 private OutputStream os = null;
50 private final byte[] temporary = new byte[1024];
51
52 private boolean formatVerified = false; // SSLv2 ruled out?
53
54 // Cache for incomplete handshake messages.
55 private ByteBuffer handshakeBuffer = null;
56
57 private boolean hasHeader = false; // Had read the record header
58
59 SSLSocketInputRecord(HandshakeHash handshakeHash) {
60 super(handshakeHash, SSLReadCipher.nullTlsReadCipher());
61 }
62
63 @Override
64 int bytesInCompletePacket() throws IOException {
65 if (!hasHeader) {
66 // read exactly one record
67 try {
68 int really = read(is, temporary, 0, headerSize);
69 if (really < 0) {
70 // EOF: peer shut down incorrectly
71 return -1;
72 }
73 } catch (EOFException eofe) {
74 // The caller will handle EOF.
75 return -1;
76 }
77 hasHeader = true;
78 }
79
80 byte byteZero = temporary[0];
81 int len = 0;
82
83 /*
84 * If we have already verified previous packets, we can
85 * ignore the verifications steps, and jump right to the
86 * determination. Otherwise, try one last heuristic to
87 * see if it's SSL/TLS.
88 */
89 if (formatVerified ||
90 (byteZero == ContentType.HANDSHAKE.id) ||
91 (byteZero == ContentType.ALERT.id)) {
92 /*
93 * Last sanity check that it's not a wild record
94 */
95 if (!ProtocolVersion.isNegotiable(
96 temporary[1], temporary[2], false)) {
97 throw new SSLException("Unrecognized record version " +
98 ProtocolVersion.nameOf(temporary[1], temporary[2]) +
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 = ((temporary[3] & 0xFF) << 8) +
113 (temporary[4] & 0xFF) + headerSize;
114 } else {
115 /*
116 * Must be SSLv2 or something unknown.
117 * Check if it's short (2 bytes) or
118 * long (3) header.
119 *
120 * Internals can warn about unsupported SSLv2
121 */
122 boolean isShort = ((byteZero & 0x80) != 0);
123
124 if (isShort && ((temporary[2] == 1) || (temporary[2] == 4))) {
125 if (!ProtocolVersion.isNegotiable(
126 temporary[3], temporary[4], false)) {
127 throw new SSLException("Unrecognized record version " +
128 ProtocolVersion.nameOf(temporary[3], temporary[4]) +
129 " , plaintext connection?");
130 }
131
132 /*
133 * Client or Server Hello
134 */
135 //
136 // Short header is using here. We reverse the code here
137 // in case it is used in the future.
138 //
139 // int mask = (isShort ? 0x7F : 0x3F);
140 // len = ((byteZero & mask) << 8) +
141 // (temporary[1] & 0xFF) + (isShort ? 2 : 3);
142 //
143 len = ((byteZero & 0x7F) << 8) + (temporary[1] & 0xFF) + 2;
144 } else {
145 // Gobblygook!
146 throw new SSLException(
147 "Unrecognized SSL message, plaintext connection?");
148 }
149 }
150
151 return len;
152 }
153
154 // Note that the input arguments are not used actually.
155 @Override
156 Plaintext[] decode(ByteBuffer[] srcs, int srcsOffset,
157 int srcsLength) throws IOException, BadPaddingException {
158
159 if (isClosed) {
160 return null;
161 }
162
163 if (!hasHeader) {
164 // read exactly one record
165 int really = read(is, temporary, 0, headerSize);
166 if (really < 0) {
167 throw new EOFException("SSL peer shut down incorrectly");
168 }
169 hasHeader = true;
170 }
171
172 Plaintext plaintext = null;
173 if (!formatVerified) {
174 formatVerified = true;
175
176 /*
177 * The first record must either be a handshake record or an
178 * alert message. If it's not, it is either invalid or an
179 * SSLv2 message.
180 */
181 if ((temporary[0] != ContentType.HANDSHAKE.id) &&
182 (temporary[0] != ContentType.ALERT.id)) {
183 hasHeader = false;
184 return handleUnknownRecord(temporary);
185 }
186 }
187
188 // The record header should has consumed.
189 hasHeader = false;
190 return decodeInputRecord(temporary);
191 }
192
193 @Override
194 void setReceiverStream(InputStream inputStream) {
195 this.is = inputStream;
196 }
197
198 @Override
199 void setDeliverStream(OutputStream outputStream) {
200 this.os = outputStream;
201 }
202
203 // Note that destination may be null
204 private Plaintext[] decodeInputRecord(
205 byte[] header) throws IOException, BadPaddingException {
206 byte contentType = header[0]; // pos: 0
207 byte majorVersion = header[1]; // pos: 1
208 byte minorVersion = header[2]; // pos: 2
209 int contentLen = ((header[3] & 0xFF) << 8) +
210 (header[4] & 0xFF); // pos: 3, 4
211
212 if (SSLLogger.isOn && SSLLogger.isOn("record")) {
213 SSLLogger.fine(
214 "READ: " +
215 ProtocolVersion.nameOf(majorVersion, minorVersion) +
216 " " + ContentType.nameOf(contentType) + ", length = " +
217 contentLen);
218 }
219
220 //
221 // Check for upper bound.
222 //
223 // Note: May check packetSize limit in the future.
224 if (contentLen < 0 || contentLen > maxLargeRecordSize - headerSize) {
225 throw new SSLProtocolException(
226 "Bad input record size, TLSCiphertext.length = " + contentLen);
227 }
228
229 //
230 // Read a complete record.
231 //
232 ByteBuffer destination = ByteBuffer.allocate(headerSize + contentLen);
233 int dstPos = destination.position();
234 destination.put(temporary, 0, headerSize);
235 while (contentLen > 0) {
236 int howmuch = Math.min(temporary.length, contentLen);
237 int really = read(is, temporary, 0, howmuch);
238 if (really < 0) {
239 throw new EOFException("SSL peer shut down incorrectly");
240 }
241
242 destination.put(temporary, 0, howmuch);
243 contentLen -= howmuch;
244 }
245 destination.flip();
246 destination.position(dstPos + headerSize);
247
248 if (SSLLogger.isOn && SSLLogger.isOn("record")) {
249 SSLLogger.fine(
250 "READ: " +
251 ProtocolVersion.nameOf(majorVersion, minorVersion) +
252 " " + ContentType.nameOf(contentType) + ", length = " +
253 destination.remaining());
254 }
255
256 //
257 // Decrypt the fragment
258 //
259 ByteBuffer fragment;
260 try {
261 Plaintext plaintext =
262 readCipher.decrypt(contentType, destination, null);
263 fragment = plaintext.fragment;
264 contentType = plaintext.contentType;
265 } catch (BadPaddingException bpe) {
266 throw bpe;
267 } catch (GeneralSecurityException gse) {
268 throw (SSLProtocolException)(new SSLProtocolException(
269 "Unexpected exception")).initCause(gse);
270 }
271
272 if (contentType != ContentType.HANDSHAKE.id &&
273 handshakeBuffer != null && handshakeBuffer.hasRemaining()) {
274 throw new SSLProtocolException(
275 "Expecting a handshake fragment, but received " +
276 ContentType.nameOf(contentType));
277 }
278
279 //
280 // parse handshake messages
281 //
282 if (contentType == ContentType.HANDSHAKE.id) {
344
345 plaintexts.add(
346 new Plaintext(contentType, majorVersion, minorVersion,
347 -1, -1L, handshakeFrag.slice())
348 );
349
350 handshakeFrag.position(nextPos);
351 handshakeFrag.limit(fragLim);
352 }
353 }
354
355 return plaintexts.toArray(new Plaintext[0]);
356 }
357
358 return new Plaintext[] {
359 new Plaintext(contentType,
360 majorVersion, minorVersion, -1, -1L, fragment)
361 };
362 }
363
364 private Plaintext[] handleUnknownRecord(
365 byte[] header) throws IOException, BadPaddingException {
366 byte firstByte = header[0];
367 byte thirdByte = header[2];
368
369 // Does it look like a Version 2 client hello (V2ClientHello)?
370 if (((firstByte & 0x80) != 0) && (thirdByte == 1)) {
371 /*
372 * If SSLv2Hello is not enabled, throw an exception.
373 */
374 if (helloVersion != ProtocolVersion.SSL20Hello) {
375 throw new SSLHandshakeException("SSLv2Hello is not enabled");
376 }
377
378 byte majorVersion = header[3];
379 byte minorVersion = header[4];
380
381 if ((majorVersion == ProtocolVersion.SSL20Hello.major) &&
382 (minorVersion == ProtocolVersion.SSL20Hello.minor)) {
383
384 /*
385 * Looks like a V2 client hello, but not one saying
387 * error message, one that's treated as fatal by
388 * clients (Otherwise we'll hang.)
389 */
390 os.write(SSLRecord.v2NoCipher); // SSLv2Hello
391
392 if (SSLLogger.isOn) {
393 if (SSLLogger.isOn("record")) {
394 SSLLogger.fine(
395 "Requested to negotiate unsupported SSLv2!");
396 }
397
398 if (SSLLogger.isOn("packet")) {
399 SSLLogger.fine("Raw write", SSLRecord.v2NoCipher);
400 }
401 }
402
403 throw new SSLException("Unsupported SSL v2.0 ClientHello");
404 }
405
406 int msgLen = ((header[0] & 0x7F) << 8) | (header[1] & 0xFF);
407
408 ByteBuffer destination = ByteBuffer.allocate(headerSize + msgLen);
409 destination.put(temporary, 0, headerSize);
410 msgLen -= 3; // had read 3 bytes of content as header
411 while (msgLen > 0) {
412 int howmuch = Math.min(temporary.length, msgLen);
413 int really = read(is, temporary, 0, howmuch);
414 if (really < 0) {
415 throw new EOFException("SSL peer shut down incorrectly");
416 }
417
418 destination.put(temporary, 0, howmuch);
419 msgLen -= howmuch;
420 }
421 destination.flip();
422
423 /*
424 * If we can map this into a V3 ClientHello, read and
425 * hash the rest of the V2 handshake, turn it into a
426 * V3 ClientHello message, and pass it up.
427 */
428 destination.position(2); // exclude the header
429 handshakeHash.receive(destination);
430 destination.position(0);
431
432 ByteBuffer converted = convertToClientHello(destination);
433
434 if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
435 SSLLogger.fine(
436 "[Converted] ClientHello", converted);
437 }
438
439 return new Plaintext[] {
440 new Plaintext(ContentType.HANDSHAKE.id,
441 majorVersion, minorVersion, -1, -1L, converted)
442 };
443 } else {
444 if (((firstByte & 0x80) != 0) && (thirdByte == 4)) {
445 throw new SSLException("SSL V2.0 servers are not supported.");
446 }
447
448 throw new SSLException("Unsupported or unrecognized SSL message");
449 }
450 }
451
452 // Read the exact bytes of data, otherwise, return -1.
453 private static int read(InputStream is,
454 byte[] buffer, int offset, int len) throws IOException {
455 int n = 0;
456 while (n < len) {
457 int readLen = is.read(buffer, offset + n, len - n);
458 if (readLen < 0) {
459 if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
460 SSLLogger.fine("Raw read: EOF");
461 }
462 return -1;
463 }
464
465 if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
466 ByteBuffer bb = ByteBuffer.wrap(buffer, offset + n, readLen);
467 SSLLogger.fine("Raw read", bb);
468 }
469
470 n += readLen;
471 }
472
473 return n;
474 }
475
476 // Try to use up the input stream without impact the performance too much.
477 void deplete(boolean tryToRead) throws IOException {
478 int remaining = is.available();
479 if (tryToRead && (remaining == 0)) {
480 // try to wait and read one byte if no buffered input
481 is.read();
482 }
483
484 while ((remaining = is.available()) != 0) {
485 is.skip(remaining);
486 }
487 }
488 }
|
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
62 SSLSocketInputRecord(HandshakeHash handshakeHash) {
63 super(handshakeHash, SSLReadCipher.nullTlsReadCipher());
64 }
65
66 @Override
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)) {
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)) {
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
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
197 void setReceiverStream(InputStream inputStream) {
198 this.is = inputStream;
199 }
200
201 @Override
202 void setDeliverStream(OutputStream outputStream) {
203 this.os = outputStream;
204 }
205
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) {
342
343 plaintexts.add(
344 new Plaintext(contentType, majorVersion, minorVersion,
345 -1, -1L, handshakeFrag.slice())
346 );
347
348 handshakeFrag.position(nextPos);
349 handshakeFrag.limit(fragLim);
350 }
351 }
352
353 return plaintexts.toArray(new Plaintext[0]);
354 }
355
356 return new Plaintext[] {
357 new Plaintext(contentType,
358 majorVersion, minorVersion, -1, -1L, fragment)
359 };
360 }
361
362 private Plaintext[] handleUnknownRecord() throws IOException, BadPaddingException {
363 byte firstByte = header[0];
364 byte thirdByte = header[2];
365
366 // Does it look like a Version 2 client hello (V2ClientHello)?
367 if (((firstByte & 0x80) != 0) && (thirdByte == 1)) {
368 /*
369 * If SSLv2Hello is not enabled, throw an exception.
370 */
371 if (helloVersion != ProtocolVersion.SSL20Hello) {
372 throw new SSLHandshakeException("SSLv2Hello is not enabled");
373 }
374
375 byte majorVersion = header[3];
376 byte minorVersion = header[4];
377
378 if ((majorVersion == ProtocolVersion.SSL20Hello.major) &&
379 (minorVersion == ProtocolVersion.SSL20Hello.minor)) {
380
381 /*
382 * Looks like a V2 client hello, but not one saying
384 * error message, one that's treated as fatal by
385 * clients (Otherwise we'll hang.)
386 */
387 os.write(SSLRecord.v2NoCipher); // SSLv2Hello
388
389 if (SSLLogger.isOn) {
390 if (SSLLogger.isOn("record")) {
391 SSLLogger.fine(
392 "Requested to negotiate unsupported SSLv2!");
393 }
394
395 if (SSLLogger.isOn("packet")) {
396 SSLLogger.fine("Raw write", SSLRecord.v2NoCipher);
397 }
398 }
399
400 throw new SSLException("Unsupported SSL v2.0 ClientHello");
401 }
402
403 int msgLen = ((header[0] & 0x7F) << 8) | (header[1] & 0xFF);
404 if (recordBody.position() == 0) {
405 if (recordBody.capacity() < (headerSize + msgLen)) {
406 recordBody = ByteBuffer.allocate(headerSize + msgLen);
407 }
408 recordBody.limit(headerSize + msgLen);
409 recordBody.put(header, 0, headerSize);
410 } else {
411 msgLen = recordBody.remaining();
412 }
413 msgLen -= 3; // had read 3 bytes of content as header
414 readFully(msgLen);
415 recordBody.flip();
416
417 /*
418 * If we can map this into a V3 ClientHello, read and
419 * hash the rest of the V2 handshake, turn it into a
420 * V3 ClientHello message, and pass it up.
421 */
422 recordBody.position(2); // exclude the header
423 handshakeHash.receive(recordBody);
424 recordBody.position(0);
425
426 ByteBuffer converted = convertToClientHello(recordBody);
427
428 if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
429 SSLLogger.fine(
430 "[Converted] ClientHello", converted);
431 }
432
433 return new Plaintext[] {
434 new Plaintext(ContentType.HANDSHAKE.id,
435 majorVersion, minorVersion, -1, -1L, converted)
436 };
437 } else {
438 if (((firstByte & 0x80) != 0) && (thirdByte == 4)) {
439 throw new SSLException("SSL V2.0 servers are not supported.");
440 }
441
442 throw new SSLException("Unsupported or unrecognized SSL message");
443 }
444 }
445
446 // Read the exact bytes of data, otherwise, throw IOException.
447 private int readFully(int len) throws IOException {
448 int end = len + recordBody.position();
449 int off = recordBody.position();
450 try {
451 while (off < end) {
452 off += read(is, recordBody.array(), off, end - off);
453 }
454 } finally {
455 recordBody.position(off);
456 }
457 return len;
458 }
459
460 // Read SSE record header, otherwise, throw IOException.
461 private int readHeader() throws IOException {
462 while (headerOff < headerSize) {
463 headerOff += read(is, header, headerOff, headerSize - headerOff);
464 }
465 return headerSize;
466 }
467
468 private static int read(InputStream is, byte[] buf, int off, int len) throws IOException {
469 int readLen = is.read(buf, off, len);
470 if (readLen < 0) {
471 if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
472 SSLLogger.fine("Raw read: EOF");
473 }
474 throw new EOFException("SSL peer shut down incorrectly");
475 }
476
477 if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
478 ByteBuffer bb = ByteBuffer.wrap(buf, off, readLen);
479 SSLLogger.fine("Raw read", bb);
480 }
481 return readLen;
482 }
483
484 // Try to use up the input stream without impact the performance too much.
485 void deplete(boolean tryToRead) throws IOException {
486 int remaining = is.available();
487 if (tryToRead && (remaining == 0)) {
488 // try to wait and read one byte if no buffered input
489 is.read();
490 }
491
492 while ((remaining = is.available()) != 0) {
493 is.skip(remaining);
494 }
495 }
496 }
|