author | xuelei |
Fri, 08 Apr 2011 02:00:09 -0700 | |
changeset 9246 | c459f79af46b |
parent 6856 | 533f4ad71f88 |
child 12428 | e9feb65d37fa |
permissions | -rw-r--r-- |
2 | 1 |
/* |
6856 | 2 |
* Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. |
2 | 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 |
|
5506 | 7 |
* published by the Free Software Foundation. Oracle designates this |
2 | 8 |
* particular file as subject to the "Classpath" exception as provided |
5506 | 9 |
* by Oracle in the LICENSE file that accompanied this code. |
2 | 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 |
* |
|
5506 | 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. |
|
2 | 24 |
*/ |
25 |
||
26 |
||
27 |
package sun.security.ssl; |
|
28 |
||
29 |
import java.io.*; |
|
30 |
import java.nio.*; |
|
31 |
||
32 |
import javax.net.ssl.SSLException; |
|
33 |
import sun.misc.HexDumpEncoder; |
|
34 |
||
35 |
||
36 |
/** |
|
37 |
* SSL 3.0 records, as written to a TCP stream. |
|
38 |
* |
|
39 |
* Each record has a message area that starts out with data supplied by the |
|
40 |
* application. It may grow/shrink due to compression and will be modified |
|
41 |
* in place for mac-ing and encryption. |
|
42 |
* |
|
43 |
* Handshake records have additional needs, notably accumulation of a set |
|
44 |
* of hashes which are used to establish that handshaking was done right. |
|
45 |
* Handshake records usually have several handshake messages each, and we |
|
46 |
* need message-level control over what's hashed. |
|
47 |
* |
|
48 |
* @author David Brownell |
|
49 |
*/ |
|
50 |
class OutputRecord extends ByteArrayOutputStream implements Record { |
|
51 |
||
52 |
private HandshakeHash handshakeHash; |
|
53 |
private int lastHashed; |
|
54 |
private boolean firstMessage; |
|
55 |
final private byte contentType; |
|
56 |
||
57 |
// current protocol version, sent as record version |
|
58 |
ProtocolVersion protocolVersion; |
|
59 |
||
60 |
// version for the ClientHello message. Only relevant if this is a |
|
61 |
// client handshake record. If set to ProtocolVersion.SSL20Hello, |
|
62 |
// the V3 client hello is converted to V2 format. |
|
63 |
private ProtocolVersion helloVersion; |
|
64 |
||
65 |
/* Class and subclass dynamic debugging support */ |
|
66 |
static final Debug debug = Debug.getInstance("ssl"); |
|
67 |
||
68 |
/* |
|
69 |
* Default constructor makes a record supporting the maximum |
|
70 |
* SSL record size. It allocates the header bytes directly. |
|
71 |
* |
|
72 |
* @param type the content type for the record |
|
73 |
*/ |
|
74 |
OutputRecord(byte type, int size) { |
|
75 |
super(size); |
|
76 |
this.protocolVersion = ProtocolVersion.DEFAULT; |
|
77 |
this.helloVersion = ProtocolVersion.DEFAULT_HELLO; |
|
78 |
firstMessage = true; |
|
79 |
count = headerSize; |
|
80 |
contentType = type; |
|
81 |
lastHashed = count; |
|
82 |
} |
|
83 |
||
84 |
OutputRecord(byte type) { |
|
85 |
this(type, recordSize(type)); |
|
86 |
} |
|
87 |
||
88 |
/** |
|
89 |
* Get the size of the buffer we need for records of the specified |
|
90 |
* type. |
|
91 |
*/ |
|
92 |
private static int recordSize(byte type) { |
|
93 |
if ((type == ct_change_cipher_spec) || (type == ct_alert)) { |
|
94 |
return maxAlertRecordSize; |
|
95 |
} else { |
|
96 |
return maxRecordSize; |
|
97 |
} |
|
98 |
} |
|
99 |
||
100 |
/* |
|
101 |
* Updates the SSL version of this record. |
|
102 |
*/ |
|
103 |
synchronized void setVersion(ProtocolVersion protocolVersion) { |
|
104 |
this.protocolVersion = protocolVersion; |
|
105 |
} |
|
106 |
||
107 |
/* |
|
108 |
* Updates helloVersion of this record. |
|
109 |
*/ |
|
110 |
synchronized void setHelloVersion(ProtocolVersion helloVersion) { |
|
111 |
this.helloVersion = helloVersion; |
|
112 |
} |
|
113 |
||
114 |
/* |
|
115 |
* Reset the record so that it can be refilled, starting |
|
116 |
* immediately after the header. |
|
117 |
*/ |
|
118 |
public synchronized void reset() { |
|
119 |
super.reset(); |
|
120 |
count = headerSize; |
|
121 |
lastHashed = count; |
|
122 |
} |
|
123 |
||
124 |
/* |
|
125 |
* For handshaking, we need to be able to hash every byte above the |
|
126 |
* record marking layer. This is where we're guaranteed to see those |
|
127 |
* bytes, so this is where we can hash them. |
|
128 |
*/ |
|
129 |
void setHandshakeHash(HandshakeHash handshakeHash) { |
|
130 |
assert(contentType == ct_handshake); |
|
131 |
this.handshakeHash = handshakeHash; |
|
132 |
} |
|
133 |
||
134 |
/* |
|
135 |
* We hash (the plaintext) on demand. There is one place where |
|
136 |
* we want to access the hash in the middle of a record: client |
|
137 |
* cert message gets hashed, and part of the same record is the |
|
138 |
* client cert verify message which uses that hash. So we track |
|
139 |
* how much of each record we've hashed so far. |
|
140 |
*/ |
|
141 |
void doHashes() { |
|
142 |
int len = count - lastHashed; |
|
143 |
||
144 |
if (len > 0) { |
|
145 |
hashInternal(buf, lastHashed, len); |
|
146 |
lastHashed = count; |
|
147 |
} |
|
148 |
} |
|
149 |
||
150 |
/* |
|
151 |
* Need a helper function so we can hash the V2 hello correctly |
|
152 |
*/ |
|
153 |
private void hashInternal(byte buf [], int offset, int len) { |
|
154 |
if (debug != null && Debug.isOn("data")) { |
|
155 |
try { |
|
156 |
HexDumpEncoder hd = new HexDumpEncoder(); |
|
157 |
||
158 |
System.out.println("[write] MD5 and SHA1 hashes: len = " |
|
159 |
+ len); |
|
160 |
hd.encodeBuffer(new ByteArrayInputStream(buf, |
|
161 |
lastHashed, len), System.out); |
|
162 |
} catch (IOException e) { } |
|
163 |
} |
|
164 |
||
165 |
handshakeHash.update(buf, lastHashed, len); |
|
166 |
lastHashed = count; |
|
167 |
} |
|
168 |
||
169 |
/* |
|
170 |
* Return true iff the record is empty -- to avoid doing the work |
|
171 |
* of sending empty records over the network. |
|
172 |
*/ |
|
173 |
boolean isEmpty() { |
|
174 |
return count == headerSize; |
|
175 |
} |
|
176 |
||
100
01ef29ca378f
6447412: Issue with socket.close() for ssl sockets when poweroff on other system
xuelei
parents:
2
diff
changeset
|
177 |
/* |
01ef29ca378f
6447412: Issue with socket.close() for ssl sockets when poweroff on other system
xuelei
parents:
2
diff
changeset
|
178 |
* Return true if the record is of a given alert. |
01ef29ca378f
6447412: Issue with socket.close() for ssl sockets when poweroff on other system
xuelei
parents:
2
diff
changeset
|
179 |
*/ |
01ef29ca378f
6447412: Issue with socket.close() for ssl sockets when poweroff on other system
xuelei
parents:
2
diff
changeset
|
180 |
boolean isAlert(byte description) { |
01ef29ca378f
6447412: Issue with socket.close() for ssl sockets when poweroff on other system
xuelei
parents:
2
diff
changeset
|
181 |
// An alert is defined with a two bytes struct, |
01ef29ca378f
6447412: Issue with socket.close() for ssl sockets when poweroff on other system
xuelei
parents:
2
diff
changeset
|
182 |
// {byte level, byte description}, following after the header bytes. |
01ef29ca378f
6447412: Issue with socket.close() for ssl sockets when poweroff on other system
xuelei
parents:
2
diff
changeset
|
183 |
if (count > (headerSize + 1) && contentType == ct_alert) { |
01ef29ca378f
6447412: Issue with socket.close() for ssl sockets when poweroff on other system
xuelei
parents:
2
diff
changeset
|
184 |
return buf[headerSize + 1] == description; |
01ef29ca378f
6447412: Issue with socket.close() for ssl sockets when poweroff on other system
xuelei
parents:
2
diff
changeset
|
185 |
} |
01ef29ca378f
6447412: Issue with socket.close() for ssl sockets when poweroff on other system
xuelei
parents:
2
diff
changeset
|
186 |
|
01ef29ca378f
6447412: Issue with socket.close() for ssl sockets when poweroff on other system
xuelei
parents:
2
diff
changeset
|
187 |
return false; |
01ef29ca378f
6447412: Issue with socket.close() for ssl sockets when poweroff on other system
xuelei
parents:
2
diff
changeset
|
188 |
} |
2 | 189 |
|
190 |
/* |
|
191 |
* Compute the MAC and append it to this record. In case we |
|
192 |
* are automatically flushing a handshake stream, make sure we |
|
193 |
* have hashed the message first. |
|
194 |
*/ |
|
195 |
void addMAC(MAC signer) throws IOException { |
|
196 |
// |
|
197 |
// when we support compression, hashing can't go here |
|
198 |
// since it'll need to be done on the uncompressed data, |
|
199 |
// and the MAC applies to the compressed data. |
|
200 |
// |
|
201 |
if (contentType == ct_handshake) { |
|
202 |
doHashes(); |
|
203 |
} |
|
204 |
if (signer.MAClen() != 0) { |
|
205 |
byte[] hash = signer.compute(contentType, buf, |
|
206 |
headerSize, count - headerSize); |
|
207 |
write(hash); |
|
208 |
} |
|
209 |
} |
|
210 |
||
211 |
/* |
|
212 |
* Encrypt ... length may grow due to block cipher padding |
|
213 |
*/ |
|
214 |
void encrypt(CipherBox box) { |
|
215 |
int len = count - headerSize; |
|
216 |
count = headerSize + box.encrypt(buf, headerSize, len); |
|
217 |
} |
|
218 |
||
219 |
||
220 |
/* |
|
221 |
* Tell how full the buffer is ... for filling it with application or |
|
222 |
* handshake data. |
|
223 |
*/ |
|
224 |
final int availableDataBytes() { |
|
225 |
int dataSize = count - headerSize; |
|
226 |
return maxDataSize - dataSize; |
|
227 |
} |
|
228 |
||
229 |
/* |
|
230 |
* Return the type of SSL record that's buffered here. |
|
231 |
*/ |
|
232 |
final byte contentType() { |
|
233 |
return contentType; |
|
234 |
} |
|
235 |
||
236 |
/* |
|
237 |
* Write the record out on the stream. Note that you must have (in |
|
238 |
* order) compressed the data, appended the MAC, and encrypted it in |
|
239 |
* order for the record to be understood by the other end. (Some of |
|
240 |
* those steps will be null early in handshaking.) |
|
241 |
* |
|
242 |
* Note that this does no locking for the connection, it's required |
|
243 |
* that synchronization be done elsewhere. Also, this does its work |
|
244 |
* in a single low level write, for efficiency. |
|
245 |
*/ |
|
246 |
void write(OutputStream s) throws IOException { |
|
247 |
/* |
|
248 |
* Don't emit content-free records. (Even change cipher spec |
|
249 |
* messages have a byte of data!) |
|
250 |
*/ |
|
251 |
if (count == headerSize) { |
|
252 |
return; |
|
253 |
} |
|
254 |
||
255 |
int length = count - headerSize; |
|
256 |
// "should" really never write more than about 14 Kb... |
|
257 |
if (length < 0) { |
|
258 |
throw new SSLException("output record size too small: " |
|
259 |
+ length); |
|
260 |
} |
|
261 |
||
262 |
if (debug != null |
|
263 |
&& (Debug.isOn("record") || Debug.isOn("handshake"))) { |
|
264 |
if ((debug != null && Debug.isOn("record")) |
|
265 |
|| contentType() == ct_change_cipher_spec) |
|
266 |
System.out.println(Thread.currentThread().getName() |
|
267 |
// v3.0/v3.1 ... |
|
268 |
+ ", WRITE: " + protocolVersion |
|
269 |
+ " " + InputRecord.contentName(contentType()) |
|
270 |
+ ", length = " + length); |
|
271 |
} |
|
272 |
||
273 |
/* |
|
274 |
* If this is the initial ClientHello on this connection and |
|
275 |
* we're not trying to resume a (V3) session then send a V2 |
|
276 |
* ClientHello instead so we can detect V2 servers cleanly. |
|
277 |
*/ |
|
278 |
if (firstMessage && useV2Hello()) { |
|
279 |
byte[] v3Msg = new byte[length - 4]; |
|
280 |
System.arraycopy(buf, headerSize + 4, v3Msg, 0, v3Msg.length); |
|
281 |
V3toV2ClientHello(v3Msg); |
|
282 |
handshakeHash.reset(); |
|
283 |
lastHashed = 2; |
|
284 |
doHashes(); |
|
285 |
if (debug != null && Debug.isOn("record")) { |
|
286 |
System.out.println( |
|
287 |
Thread.currentThread().getName() |
|
288 |
+ ", WRITE: SSLv2 client hello message" |
|
289 |
+ ", length = " + (count - 2)); // 2 byte SSLv2 header |
|
290 |
} |
|
291 |
} else { |
|
292 |
/* |
|
293 |
* Fill out the header, write it and the message. |
|
294 |
*/ |
|
295 |
buf[0] = contentType; |
|
296 |
buf[1] = protocolVersion.major; |
|
297 |
buf[2] = protocolVersion.minor; |
|
298 |
buf[3] = (byte)(length >> 8); |
|
299 |
buf[4] = (byte)(length); |
|
300 |
} |
|
301 |
firstMessage = false; |
|
302 |
||
303 |
writeBuffer(s, buf, 0, count); |
|
304 |
reset(); |
|
305 |
} |
|
306 |
||
307 |
/* |
|
308 |
* Actually do the write here. For SSLEngine's HS data, |
|
309 |
* we'll override this method and let it take the appropriate |
|
310 |
* action. |
|
311 |
*/ |
|
312 |
void writeBuffer(OutputStream s, byte [] buf, int off, int len) |
|
313 |
throws IOException { |
|
314 |
s.write(buf, off, len); |
|
315 |
s.flush(); |
|
316 |
||
317 |
if (debug != null && Debug.isOn("packet")) { |
|
318 |
try { |
|
319 |
HexDumpEncoder hd = new HexDumpEncoder(); |
|
320 |
ByteBuffer bb = ByteBuffer.wrap(buf, off, len); |
|
321 |
||
322 |
System.out.println("[Raw write]: length = " + |
|
323 |
bb.remaining()); |
|
324 |
hd.encodeBuffer(bb, System.out); |
|
325 |
} catch (IOException e) { } |
|
326 |
} |
|
327 |
} |
|
328 |
||
329 |
/* |
|
330 |
* Return whether the buffer contains a ClientHello message that should |
|
331 |
* be converted to V2 format. |
|
332 |
*/ |
|
333 |
private boolean useV2Hello() { |
|
334 |
return firstMessage |
|
335 |
&& (helloVersion == ProtocolVersion.SSL20Hello) |
|
336 |
&& (contentType == ct_handshake) |
|
337 |
&& (buf[5] == HandshakeMessage.ht_client_hello) |
|
338 |
&& (buf[headerSize + 4+2+32] == 0); // V3 session ID is empty |
|
339 |
} |
|
340 |
||
341 |
/* |
|
342 |
* Detect "old" servers which are capable of SSL V2.0 protocol ... for |
|
343 |
* example, Netscape Commerce 1.0 servers. The V3 message is in the |
|
344 |
* header and the bytes passed as parameter. This routine translates |
|
345 |
* the V3 message into an equivalent V2 one. |
|
6856 | 346 |
* |
347 |
* Note that the translation will strip off all hello extensions as |
|
348 |
* SSL V2.0 does not support hello extension. |
|
2 | 349 |
*/ |
350 |
private void V3toV2ClientHello(byte v3Msg []) throws SSLException { |
|
351 |
int v3SessionIdLenOffset = 2 + 32; // version + nonce |
|
352 |
int v3SessionIdLen = v3Msg[v3SessionIdLenOffset]; |
|
353 |
int v3CipherSpecLenOffset = v3SessionIdLenOffset + 1 + v3SessionIdLen; |
|
354 |
int v3CipherSpecLen = ((v3Msg[v3CipherSpecLenOffset] & 0xff) << 8) + |
|
355 |
(v3Msg[v3CipherSpecLenOffset + 1] & 0xff); |
|
356 |
int cipherSpecs = v3CipherSpecLen / 2; // 2 bytes each in V3 |
|
357 |
||
358 |
/* |
|
359 |
* Copy over the cipher specs. We don't care about actually translating |
|
360 |
* them for use with an actual V2 server since we only talk V3. |
|
361 |
* Therefore, just copy over the V3 cipher spec values with a leading |
|
362 |
* 0. |
|
363 |
*/ |
|
364 |
int v3CipherSpecOffset = v3CipherSpecLenOffset + 2; // skip length |
|
365 |
int v2CipherSpecLen = 0; |
|
366 |
count = 11; |
|
6856 | 367 |
boolean containsRenegoInfoSCSV = false; |
2 | 368 |
for (int i = 0; i < cipherSpecs; i++) { |
369 |
byte byte1, byte2; |
|
370 |
||
371 |
byte1 = v3Msg[v3CipherSpecOffset++]; |
|
372 |
byte2 = v3Msg[v3CipherSpecOffset++]; |
|
373 |
v2CipherSpecLen += V3toV2CipherSuite(byte1, byte2); |
|
6856 | 374 |
if (!containsRenegoInfoSCSV && |
375 |
byte1 == (byte)0x00 && byte2 == (byte)0xFF) { |
|
376 |
containsRenegoInfoSCSV = true; |
|
377 |
} |
|
378 |
} |
|
379 |
||
380 |
if (!containsRenegoInfoSCSV) { |
|
381 |
v2CipherSpecLen += V3toV2CipherSuite((byte)0x00, (byte)0xFF); |
|
2 | 382 |
} |
383 |
||
384 |
/* |
|
385 |
* Build the first part of the V3 record header from the V2 one |
|
386 |
* that's now buffered up. (Lengths are fixed up later). |
|
387 |
*/ |
|
388 |
buf[2] = HandshakeMessage.ht_client_hello; |
|
389 |
buf[3] = v3Msg[0]; // major version |
|
390 |
buf[4] = v3Msg[1]; // minor version |
|
391 |
buf[5] = (byte)(v2CipherSpecLen >>> 8); |
|
392 |
buf[6] = (byte)v2CipherSpecLen; |
|
393 |
buf[7] = 0; |
|
394 |
buf[8] = 0; // always no session |
|
395 |
buf[9] = 0; |
|
396 |
buf[10] = 32; // nonce length (always 32 in V3) |
|
397 |
||
398 |
/* |
|
399 |
* Copy in the nonce. |
|
400 |
*/ |
|
401 |
System.arraycopy(v3Msg, 2, buf, count, 32); |
|
402 |
count += 32; |
|
403 |
||
404 |
/* |
|
405 |
* Set the length of the message. |
|
406 |
*/ |
|
407 |
count -= 2; // don't include length field itself |
|
408 |
buf[0] = (byte)(count >>> 8); |
|
409 |
buf[0] |= 0x80; |
|
410 |
buf[1] = (byte)(count); |
|
411 |
count += 2; |
|
412 |
} |
|
413 |
||
414 |
/* |
|
415 |
* Mappings from V3 cipher suite encodings to their pure V2 equivalents. |
|
416 |
* This is taken from the SSL V3 specification, Appendix E. |
|
417 |
*/ |
|
418 |
private static int[] V3toV2CipherMap1 = |
|
419 |
{-1, -1, -1, 0x02, 0x01, -1, 0x04, 0x05, -1, 0x06, 0x07}; |
|
420 |
private static int[] V3toV2CipherMap3 = |
|
421 |
{-1, -1, -1, 0x80, 0x80, -1, 0x80, 0x80, -1, 0x40, 0xC0}; |
|
422 |
||
423 |
/* |
|
424 |
* See which matching pure-V2 cipher specs we need to include. |
|
425 |
* We are including these not because we are actually prepared |
|
426 |
* to talk V2 but because the Oracle Web Server insists on receiving |
|
427 |
* at least 1 "pure V2" cipher suite that it supports and returns an |
|
428 |
* illegal_parameter alert unless one is present. Rather than mindlessly |
|
429 |
* claiming to implement all documented pure V2 cipher suites the code below |
|
430 |
* just claims to implement the V2 cipher suite that is "equivalent" |
|
431 |
* in terms of cipher algorithm & exportability with the actual V3 cipher |
|
432 |
* suite that we do support. |
|
433 |
*/ |
|
434 |
private int V3toV2CipherSuite(byte byte1, byte byte2) { |
|
435 |
buf[count++] = 0; |
|
436 |
buf[count++] = byte1; |
|
437 |
buf[count++] = byte2; |
|
438 |
||
439 |
if (((byte2 & 0xff) > 0xA) || |
|
440 |
(V3toV2CipherMap1[byte2] == -1)) { |
|
441 |
return 3; |
|
442 |
} |
|
443 |
||
444 |
buf[count++] = (byte)V3toV2CipherMap1[byte2]; |
|
445 |
buf[count++] = 0; |
|
446 |
buf[count++] = (byte)V3toV2CipherMap3[byte2]; |
|
447 |
||
448 |
return 6; |
|
449 |
} |
|
450 |
} |