blob: 3153b07f1acced0d51b3d8470ce07dda81897fe3 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1996-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26
27package sun.security.ssl;
28
29import java.io.*;
30import java.nio.*;
31
32import javax.net.ssl.SSLException;
33import 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 */
50class 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
177
178 /*
179 * Compute the MAC and append it to this record. In case we
180 * are automatically flushing a handshake stream, make sure we
181 * have hashed the message first.
182 */
183 void addMAC(MAC signer) throws IOException {
184 //
185 // when we support compression, hashing can't go here
186 // since it'll need to be done on the uncompressed data,
187 // and the MAC applies to the compressed data.
188 //
189 if (contentType == ct_handshake) {
190 doHashes();
191 }
192 if (signer.MAClen() != 0) {
193 byte[] hash = signer.compute(contentType, buf,
194 headerSize, count - headerSize);
195 write(hash);
196 }
197 }
198
199 /*
200 * Encrypt ... length may grow due to block cipher padding
201 */
202 void encrypt(CipherBox box) {
203 int len = count - headerSize;
204 count = headerSize + box.encrypt(buf, headerSize, len);
205 }
206
207
208 /*
209 * Tell how full the buffer is ... for filling it with application or
210 * handshake data.
211 */
212 final int availableDataBytes() {
213 int dataSize = count - headerSize;
214 return maxDataSize - dataSize;
215 }
216
217 /*
218 * Return the type of SSL record that's buffered here.
219 */
220 final byte contentType() {
221 return contentType;
222 }
223
224 /*
225 * Write the record out on the stream. Note that you must have (in
226 * order) compressed the data, appended the MAC, and encrypted it in
227 * order for the record to be understood by the other end. (Some of
228 * those steps will be null early in handshaking.)
229 *
230 * Note that this does no locking for the connection, it's required
231 * that synchronization be done elsewhere. Also, this does its work
232 * in a single low level write, for efficiency.
233 */
234 void write(OutputStream s) throws IOException {
235 /*
236 * Don't emit content-free records. (Even change cipher spec
237 * messages have a byte of data!)
238 */
239 if (count == headerSize) {
240 return;
241 }
242
243 int length = count - headerSize;
244 // "should" really never write more than about 14 Kb...
245 if (length < 0) {
246 throw new SSLException("output record size too small: "
247 + length);
248 }
249
250 if (debug != null
251 && (Debug.isOn("record") || Debug.isOn("handshake"))) {
252 if ((debug != null && Debug.isOn("record"))
253 || contentType() == ct_change_cipher_spec)
254 System.out.println(Thread.currentThread().getName()
255 // v3.0/v3.1 ...
256 + ", WRITE: " + protocolVersion
257 + " " + InputRecord.contentName(contentType())
258 + ", length = " + length);
259 }
260
261 /*
262 * If this is the initial ClientHello on this connection and
263 * we're not trying to resume a (V3) session then send a V2
264 * ClientHello instead so we can detect V2 servers cleanly.
265 */
266 if (firstMessage && useV2Hello()) {
267 byte[] v3Msg = new byte[length - 4];
268 System.arraycopy(buf, headerSize + 4, v3Msg, 0, v3Msg.length);
269 V3toV2ClientHello(v3Msg);
270 handshakeHash.reset();
271 lastHashed = 2;
272 doHashes();
273 if (debug != null && Debug.isOn("record")) {
274 System.out.println(
275 Thread.currentThread().getName()
276 + ", WRITE: SSLv2 client hello message"
277 + ", length = " + (count - 2)); // 2 byte SSLv2 header
278 }
279 } else {
280 /*
281 * Fill out the header, write it and the message.
282 */
283 buf[0] = contentType;
284 buf[1] = protocolVersion.major;
285 buf[2] = protocolVersion.minor;
286 buf[3] = (byte)(length >> 8);
287 buf[4] = (byte)(length);
288 }
289 firstMessage = false;
290
291 writeBuffer(s, buf, 0, count);
292 reset();
293 }
294
295 /*
296 * Actually do the write here. For SSLEngine's HS data,
297 * we'll override this method and let it take the appropriate
298 * action.
299 */
300 void writeBuffer(OutputStream s, byte [] buf, int off, int len)
301 throws IOException {
302 s.write(buf, off, len);
303 s.flush();
304
305 if (debug != null && Debug.isOn("packet")) {
306 try {
307 HexDumpEncoder hd = new HexDumpEncoder();
308 ByteBuffer bb = ByteBuffer.wrap(buf, off, len);
309
310 System.out.println("[Raw write]: length = " +
311 bb.remaining());
312 hd.encodeBuffer(bb, System.out);
313 } catch (IOException e) { }
314 }
315 }
316
317 /*
318 * Return whether the buffer contains a ClientHello message that should
319 * be converted to V2 format.
320 */
321 private boolean useV2Hello() {
322 return firstMessage
323 && (helloVersion == ProtocolVersion.SSL20Hello)
324 && (contentType == ct_handshake)
325 && (buf[5] == HandshakeMessage.ht_client_hello)
326 && (buf[headerSize + 4+2+32] == 0); // V3 session ID is empty
327 }
328
329 /*
330 * Detect "old" servers which are capable of SSL V2.0 protocol ... for
331 * example, Netscape Commerce 1.0 servers. The V3 message is in the
332 * header and the bytes passed as parameter. This routine translates
333 * the V3 message into an equivalent V2 one.
334 */
335 private void V3toV2ClientHello(byte v3Msg []) throws SSLException {
336 int v3SessionIdLenOffset = 2 + 32; // version + nonce
337 int v3SessionIdLen = v3Msg[v3SessionIdLenOffset];
338 int v3CipherSpecLenOffset = v3SessionIdLenOffset + 1 + v3SessionIdLen;
339 int v3CipherSpecLen = ((v3Msg[v3CipherSpecLenOffset] & 0xff) << 8) +
340 (v3Msg[v3CipherSpecLenOffset + 1] & 0xff);
341 int cipherSpecs = v3CipherSpecLen / 2; // 2 bytes each in V3
342
343 /*
344 * Copy over the cipher specs. We don't care about actually translating
345 * them for use with an actual V2 server since we only talk V3.
346 * Therefore, just copy over the V3 cipher spec values with a leading
347 * 0.
348 */
349 int v3CipherSpecOffset = v3CipherSpecLenOffset + 2; // skip length
350 int v2CipherSpecLen = 0;
351 count = 11;
352 for (int i = 0; i < cipherSpecs; i++) {
353 byte byte1, byte2;
354
355 byte1 = v3Msg[v3CipherSpecOffset++];
356 byte2 = v3Msg[v3CipherSpecOffset++];
357 v2CipherSpecLen += V3toV2CipherSuite(byte1, byte2);
358 }
359
360 /*
361 * Build the first part of the V3 record header from the V2 one
362 * that's now buffered up. (Lengths are fixed up later).
363 */
364 buf[2] = HandshakeMessage.ht_client_hello;
365 buf[3] = v3Msg[0]; // major version
366 buf[4] = v3Msg[1]; // minor version
367 buf[5] = (byte)(v2CipherSpecLen >>> 8);
368 buf[6] = (byte)v2CipherSpecLen;
369 buf[7] = 0;
370 buf[8] = 0; // always no session
371 buf[9] = 0;
372 buf[10] = 32; // nonce length (always 32 in V3)
373
374 /*
375 * Copy in the nonce.
376 */
377 System.arraycopy(v3Msg, 2, buf, count, 32);
378 count += 32;
379
380 /*
381 * Set the length of the message.
382 */
383 count -= 2; // don't include length field itself
384 buf[0] = (byte)(count >>> 8);
385 buf[0] |= 0x80;
386 buf[1] = (byte)(count);
387 count += 2;
388 }
389
390 /*
391 * Mappings from V3 cipher suite encodings to their pure V2 equivalents.
392 * This is taken from the SSL V3 specification, Appendix E.
393 */
394 private static int[] V3toV2CipherMap1 =
395 {-1, -1, -1, 0x02, 0x01, -1, 0x04, 0x05, -1, 0x06, 0x07};
396 private static int[] V3toV2CipherMap3 =
397 {-1, -1, -1, 0x80, 0x80, -1, 0x80, 0x80, -1, 0x40, 0xC0};
398
399 /*
400 * See which matching pure-V2 cipher specs we need to include.
401 * We are including these not because we are actually prepared
402 * to talk V2 but because the Oracle Web Server insists on receiving
403 * at least 1 "pure V2" cipher suite that it supports and returns an
404 * illegal_parameter alert unless one is present. Rather than mindlessly
405 * claiming to implement all documented pure V2 cipher suites the code below
406 * just claims to implement the V2 cipher suite that is "equivalent"
407 * in terms of cipher algorithm & exportability with the actual V3 cipher
408 * suite that we do support.
409 */
410 private int V3toV2CipherSuite(byte byte1, byte byte2) {
411 buf[count++] = 0;
412 buf[count++] = byte1;
413 buf[count++] = byte2;
414
415 if (((byte2 & 0xff) > 0xA) ||
416 (V3toV2CipherMap1[byte2] == -1)) {
417 return 3;
418 }
419
420 buf[count++] = (byte)V3toV2CipherMap1[byte2];
421 buf[count++] = 0;
422 buf[count++] = (byte)V3toV2CipherMap3[byte2];
423
424 return 6;
425 }
426}