Showing the changes to java/pkg/sun/security/ssl/OutputRecord.java (1 of 18352 files). Show all
Unified · Split
| @@ -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 | } | ||