blob: 1d93d3a356d0b782dc60f3178bc3725b5906df5d [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-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.*;
31import javax.net.ssl.*;
32import javax.crypto.BadPaddingException;
33import sun.misc.HexDumpEncoder;
34
35
36/**
37 * Wrapper class around InputRecord.
38 *
39 * Application data is kept external to the InputRecord,
40 * but handshake data (alert/change_cipher_spec/handshake) will
41 * be kept internally in the ByteArrayInputStream.
42 *
43 * @author Brad Wetmore
44 */
45final class EngineInputRecord extends InputRecord {
46
47 private SSLEngineImpl engine;
48
49 /*
50 * A dummy ByteBuffer we'll pass back even when the data
51 * is stored internally. It'll never actually be used.
52 */
53 static private ByteBuffer tmpBB = ByteBuffer.allocate(0);
54
55 /*
56 * Flag to tell whether the last read/parsed data resides
57 * internal in the ByteArrayInputStream, or in the external
58 * buffers.
59 */
60 private boolean internalData;
61
62 EngineInputRecord(SSLEngineImpl engine) {
63 super();
64 this.engine = engine;
65 }
66
67 byte contentType() {
68 if (internalData) {
69 return super.contentType();
70 } else {
71 return ct_application_data;
72 }
73 }
74
75 /*
76 * Check if there is enough inbound data in the ByteBuffer
77 * to make a inbound packet. Look for both SSLv2 and SSLv3.
78 *
79 * @return -1 if there are not enough bytes to tell (small header),
80 */
81 int bytesInCompletePacket(ByteBuffer buf) throws SSLException {
82
83 /*
84 * SSLv2 length field is in bytes 0/1
85 * SSLv3/TLS length field is in bytes 3/4
86 */
87 if (buf.remaining() < 5) {
88 return -1;
89 }
90
91 int pos = buf.position();
92 byte byteZero = buf.get(pos);
93
94 int len = 0;
95
96 /*
97 * If we have already verified previous packets, we can
98 * ignore the verifications steps, and jump right to the
99 * determination. Otherwise, try one last hueristic to
100 * see if it's SSL/TLS.
101 */
102 if (formatVerified ||
103 (byteZero == ct_handshake) ||
104 (byteZero == ct_alert)) {
105 /*
106 * Last sanity check that it's not a wild record
107 */
108 ProtocolVersion recordVersion =
109 ProtocolVersion.valueOf(buf.get(pos + 1), buf.get(pos + 2));
110
111 // Check if too old (currently not possible)
112 // or if the major version does not match.
113 // The actual version negotiation is in the handshaker classes
114 if ((recordVersion.v < ProtocolVersion.MIN.v)
115 || (recordVersion.major > ProtocolVersion.MAX.major)) {
116 throw new SSLException(
117 "Unsupported record version " + recordVersion);
118 }
119
120 /*
121 * Reasonably sure this is a V3, disable further checks.
122 * We can't do the same in the v2 check below, because
123 * read still needs to parse/handle the v2 clientHello.
124 */
125 formatVerified = true;
126
127 /*
128 * One of the SSLv3/TLS message types.
129 */
130 len = ((buf.get(pos + 3) & 0xff) << 8) +
131 (buf.get(pos + 4) & 0xff) + headerSize;
132
133 } else {
134 /*
135 * Must be SSLv2 or something unknown.
136 * Check if it's short (2 bytes) or
137 * long (3) header.
138 *
139 * Internals can warn about unsupported SSLv2
140 */
141 boolean isShort = ((byteZero & 0x80) != 0);
142
143 if (isShort &&
144 ((buf.get(pos + 2) == 1) || buf.get(pos + 2) == 4)) {
145
146 ProtocolVersion recordVersion =
147 ProtocolVersion.valueOf(buf.get(pos + 3), buf.get(pos + 4));
148
149 // Check if too old (currently not possible)
150 // or if the major version does not match.
151 // The actual version negotiation is in the handshaker classes
152 if ((recordVersion.v < ProtocolVersion.MIN.v)
153 || (recordVersion.major > ProtocolVersion.MAX.major)) {
154
155 // if it's not SSLv2, we're out of here.
156 if (recordVersion.v != ProtocolVersion.SSL20Hello.v) {
157 throw new SSLException(
158 "Unsupported record version " + recordVersion);
159 }
160 }
161
162 /*
163 * Client or Server Hello
164 */
165 int mask = (isShort ? 0x7f : 0x3f);
166 len = ((byteZero & mask) << 8) + (buf.get(pos + 1) & 0xff) +
167 (isShort ? 2 : 3);
168
169 } else {
170 // Gobblygook!
171 throw new SSLException(
172 "Unrecognized SSL message, plaintext connection?");
173 }
174 }
175
176 return len;
177 }
178
179 /*
180 * Verifies and removes the MAC value. Returns true if
181 * the MAC checks out OK.
182 *
183 * On entry:
184 * position = beginning of app/MAC data
185 * limit = end of MAC data.
186 *
187 * On return:
188 * position = beginning of app data
189 * limit = end of app data
190 */
191 boolean checkMAC(MAC signer, ByteBuffer bb) {
192 if (internalData) {
193 return checkMAC(signer);
194 }
195
196 int len = signer.MAClen();
197 if (len == 0) { // no mac
198 return true;
199 }
200
201 /*
202 * Grab the original limit
203 */
204 int lim = bb.limit();
205
206 /*
207 * Delineate the area to apply a MAC on.
208 */
209 int macData = lim - len;
210 bb.limit(macData);
211
212 byte[] mac = signer.compute(contentType(), bb);
213
214 if (len != mac.length) {
215 throw new RuntimeException("Internal MAC error");
216 }
217
218 /*
219 * Delineate the MAC values, position was already set
220 * by doing the compute above.
221 *
222 * We could zero the MAC area, but not much useful information
223 * there anyway.
224 */
225 bb.position(macData);
226 bb.limit(lim);
227
228 try {
229 for (int i = 0; i < len; i++) {
230 if (bb.get() != mac[i]) { // No BB.equals(byte []); !
231 return false;
232 }
233 }
234 return true;
235 } finally {
236 /*
237 * Position to the data.
238 */
239 bb.rewind();
240 bb.limit(macData);
241 }
242 }
243
244 /*
245 * Pass the data down if it's internally cached, otherwise
246 * do it here.
247 *
248 * If internal data, data is decrypted internally.
249 *
250 * If external data(app), return a new ByteBuffer with data to
251 * process.
252 */
253 ByteBuffer decrypt(CipherBox box, ByteBuffer bb)
254 throws BadPaddingException {
255
256 if (internalData) {
257 decrypt(box);
258 return tmpBB;
259 }
260
261 box.decrypt(bb);
262 bb.rewind();
263
264 return bb.slice();
265 }
266
267 /*
268 * Override the actual write below. We do things this way to be
269 * consistent with InputRecord. InputRecord may try to write out
270 * data to the peer, and *then* throw an Exception. This forces
271 * data to be generated/output before the exception is ever
272 * generated.
273 */
274 void writeBuffer(OutputStream s, byte [] buf, int off, int len)
275 throws IOException {
276 /*
277 * Copy data out of buffer, it's ready to go.
278 */
279 ByteBuffer netBB = (ByteBuffer)
280 (ByteBuffer.allocate(len).put(buf, 0, len).flip());
281 engine.writer.putOutboundDataSync(netBB);
282 }
283
284 /*
285 * Delineate or read a complete packet from src.
286 *
287 * If internal data (hs, alert, ccs), the data is read and
288 * stored internally.
289 *
290 * If external data (app), return a new ByteBuffer which points
291 * to the data to process.
292 */
293 ByteBuffer read(ByteBuffer srcBB) throws IOException {
294 /*
295 * Could have a src == null/dst == null check here,
296 * but that was already checked by SSLEngine.unwrap before
297 * ever attempting to read.
298 */
299
300 /*
301 * If we have anything besides application data,
302 * or if we haven't even done the initial v2 verification,
303 * we send this down to be processed by the underlying
304 * internal cache.
305 */
306 if (!formatVerified ||
307 (srcBB.get(srcBB.position()) != ct_application_data)) {
308 internalData = true;
309 read(new ByteBufferInputStream(srcBB), (OutputStream) null);
310 return tmpBB;
311 }
312
313 internalData = false;
314
315 int srcPos = srcBB.position();
316 int srcLim = srcBB.limit();
317
318 ProtocolVersion recordVersion = ProtocolVersion.valueOf(
319 srcBB.get(srcPos + 1), srcBB.get(srcPos + 2));
320 // Check if too old (currently not possible)
321 // or if the major version does not match.
322 // The actual version negotiation is in the handshaker classes
323 if ((recordVersion.v < ProtocolVersion.MIN.v)
324 || (recordVersion.major > ProtocolVersion.MAX.major)) {
325 throw new SSLException(
326 "Unsupported record version " + recordVersion);
327 }
328
329 /*
330 * It's really application data. How much to consume?
331 * Jump over the header.
332 */
333 int len = bytesInCompletePacket(srcBB);
334 assert(len > 0);
335
336 if (debug != null && Debug.isOn("packet")) {
337 try {
338 HexDumpEncoder hd = new HexDumpEncoder();
339 srcBB.limit(srcPos + len);
340 ByteBuffer bb = srcBB.duplicate(); // Use copy of BB
341
342 System.out.println("[Raw read (bb)]: length = " + len);
343 hd.encodeBuffer(bb, System.out);
344 } catch (IOException e) { }
345 }
346
347 // Demarcate past header to end of packet.
348 srcBB.position(srcPos + headerSize);
349 srcBB.limit(srcPos + len);
350
351 // Protect remainder of buffer, create slice to actually
352 // operate on.
353 ByteBuffer bb = srcBB.slice();
354
355 srcBB.position(srcBB.limit());
356 srcBB.limit(srcLim);
357
358 return bb;
359 }
360}