History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/security/ssl/OutputRecord.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/security/ssl/OutputRecord.java+450−0
@@ -0,0 +1,450 @@
1+/*
2+ * Copyright (c) 1996, 2010, 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+
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+
177+ /*
178+ * Return true if the record is of a given alert.
179+ */
180+ boolean isAlert(byte description) {
181+ // An alert is defined with a two bytes struct,
182+ // {byte level, byte description}, following after the header bytes.
183+ if (count > (headerSize + 1) && contentType == ct_alert) {
184+ return buf[headerSize + 1] == description;
185+ }
186+
187+ return false;
188+ }
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.
346+ *
347+ * Note that the translation will strip off all hello extensions as
348+ * SSL V2.0 does not support hello extension.
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;
367+ boolean containsRenegoInfoSCSV = false;
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);
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);
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+}