1 /* 2 * Copyright (c) 1996, 2018, 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 package sun.security.ssl; 27 28 import java.io.ByteArrayInputStream; 29 import java.io.IOException; 30 import java.io.OutputStream; 31 import java.net.SocketException; 32 import java.nio.ByteBuffer; 33 import javax.net.ssl.SSLHandshakeException; 34 35 /** 36 * {@code OutputRecord} implementation for {@code SSLSocket}. 37 */ 38 final class SSLSocketOutputRecord extends OutputRecord implements SSLRecord { 39 private OutputStream deliverStream = null; 40 SSLSocketOutputRecord(HandshakeHash handshakeHash)41 SSLSocketOutputRecord(HandshakeHash handshakeHash) { 42 this(handshakeHash, null); 43 } 44 SSLSocketOutputRecord(HandshakeHash handshakeHash, TransportContext tc)45 SSLSocketOutputRecord(HandshakeHash handshakeHash, 46 TransportContext tc) { 47 super(handshakeHash, SSLCipher.SSLWriteCipher.nullTlsWriteCipher()); 48 this.tc = tc; 49 this.packetSize = SSLRecord.maxRecordSize; 50 this.protocolVersion = ProtocolVersion.NONE; 51 } 52 53 @Override encodeAlert( byte level, byte description)54 synchronized void encodeAlert( 55 byte level, byte description) throws IOException { 56 if (isClosed()) { 57 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { 58 SSLLogger.warning("outbound has closed, ignore outbound " + 59 "alert message: " + Alert.nameOf(description)); 60 } 61 return; 62 } 63 64 // use the buf of ByteArrayOutputStream 65 int position = headerSize + writeCipher.getExplicitNonceSize(); 66 count = position; 67 68 write(level); 69 write(description); 70 if (SSLLogger.isOn && SSLLogger.isOn("record")) { 71 SSLLogger.fine("WRITE: " + protocolVersion + 72 " " + ContentType.ALERT.name + 73 "(" + Alert.nameOf(description) + ")" + 74 ", length = " + (count - headerSize)); 75 } 76 77 // Encrypt the fragment and wrap up a record. 78 encrypt(writeCipher, ContentType.ALERT.id, headerSize); 79 80 // deliver this message 81 deliverStream.write(buf, 0, count); // may throw IOException 82 deliverStream.flush(); // may throw IOException 83 84 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { 85 SSLLogger.fine("Raw write", 86 (new ByteArrayInputStream(buf, 0, count))); 87 } 88 89 // reset the internal buffer 90 count = 0; 91 } 92 93 @Override encodeHandshake(byte[] source, int offset, int length)94 synchronized void encodeHandshake(byte[] source, 95 int offset, int length) throws IOException { 96 if (isClosed()) { 97 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { 98 SSLLogger.warning("outbound has closed, ignore outbound " + 99 "handshake message", 100 ByteBuffer.wrap(source, offset, length)); 101 } 102 return; 103 } 104 105 if (firstMessage) { 106 firstMessage = false; 107 108 if ((helloVersion == ProtocolVersion.SSL20Hello) && 109 (source[offset] == SSLHandshake.CLIENT_HELLO.id) && 110 // 5: recode header size 111 (source[offset + 4 + 2 + 32] == 0)) { 112 // V3 session ID is empty 113 // 4: handshake header size 114 // 2: client_version in ClientHello 115 // 32: random in ClientHello 116 117 ByteBuffer v2ClientHello = encodeV2ClientHello( 118 source, (offset + 4), (length - 4)); 119 120 byte[] record = v2ClientHello.array(); // array offset is zero 121 int limit = v2ClientHello.limit(); 122 handshakeHash.deliver(record, 2, (limit - 2)); 123 124 if (SSLLogger.isOn && SSLLogger.isOn("record")) { 125 SSLLogger.fine( 126 "WRITE: SSLv2 ClientHello message" + 127 ", length = " + limit); 128 } 129 130 // deliver this message 131 // 132 // Version 2 ClientHello message should be plaintext. 133 // 134 // No max fragment length negotiation. 135 deliverStream.write(record, 0, limit); 136 deliverStream.flush(); 137 138 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { 139 SSLLogger.fine("Raw write", 140 (new ByteArrayInputStream(record, 0, limit))); 141 } 142 143 return; 144 } 145 } 146 147 byte handshakeType = source[0]; 148 if (handshakeHash.isHashable(handshakeType)) { 149 handshakeHash.deliver(source, offset, length); 150 } 151 152 int fragLimit = getFragLimit(); 153 int position = headerSize + writeCipher.getExplicitNonceSize(); 154 if (count == 0) { 155 count = position; 156 } 157 158 if ((count - position) < (fragLimit - length)) { 159 write(source, offset, length); 160 return; 161 } 162 163 for (int limit = (offset + length); offset < limit;) { 164 165 int remains = (limit - offset) + (count - position); 166 int fragLen = Math.min(fragLimit, remains); 167 168 // use the buf of ByteArrayOutputStream 169 write(source, offset, fragLen); 170 if (remains < fragLimit) { 171 return; 172 } 173 174 if (SSLLogger.isOn && SSLLogger.isOn("record")) { 175 SSLLogger.fine( 176 "WRITE: " + protocolVersion + 177 " " + ContentType.HANDSHAKE.name + 178 ", length = " + (count - headerSize)); 179 } 180 181 // Encrypt the fragment and wrap up a record. 182 encrypt(writeCipher, ContentType.HANDSHAKE.id, headerSize); 183 184 // deliver this message 185 deliverStream.write(buf, 0, count); // may throw IOException 186 deliverStream.flush(); // may throw IOException 187 188 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { 189 SSLLogger.fine("Raw write", 190 (new ByteArrayInputStream(buf, 0, count))); 191 } 192 193 // reset the offset 194 offset += fragLen; 195 196 // reset the internal buffer 197 count = position; 198 } 199 } 200 201 @Override encodeChangeCipherSpec()202 synchronized void encodeChangeCipherSpec() throws IOException { 203 if (isClosed()) { 204 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { 205 SSLLogger.warning("outbound has closed, ignore outbound " + 206 "change_cipher_spec message"); 207 } 208 return; 209 } 210 211 // use the buf of ByteArrayOutputStream 212 int position = headerSize + writeCipher.getExplicitNonceSize(); 213 count = position; 214 215 write((byte)1); // byte 1: change_cipher_spec( 216 217 // Encrypt the fragment and wrap up a record. 218 encrypt(writeCipher, ContentType.CHANGE_CIPHER_SPEC.id, headerSize); 219 220 // deliver this message 221 deliverStream.write(buf, 0, count); // may throw IOException 222 // deliverStream.flush(); // flush in Finished 223 224 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { 225 SSLLogger.fine("Raw write", 226 (new ByteArrayInputStream(buf, 0, count))); 227 } 228 229 // reset the internal buffer 230 count = 0; 231 } 232 233 @Override flush()234 public synchronized void flush() throws IOException { 235 int position = headerSize + writeCipher.getExplicitNonceSize(); 236 if (count <= position) { 237 return; 238 } 239 240 if (SSLLogger.isOn && SSLLogger.isOn("record")) { 241 SSLLogger.fine( 242 "WRITE: " + protocolVersion + 243 " " + ContentType.HANDSHAKE.name + 244 ", length = " + (count - headerSize)); 245 } 246 247 // Encrypt the fragment and wrap up a record. 248 encrypt(writeCipher, ContentType.HANDSHAKE.id, headerSize); 249 250 // deliver this message 251 deliverStream.write(buf, 0, count); // may throw IOException 252 deliverStream.flush(); // may throw IOException 253 254 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { 255 SSLLogger.fine("Raw write", 256 (new ByteArrayInputStream(buf, 0, count))); 257 } 258 259 // reset the internal buffer 260 count = 0; // DON'T use position 261 } 262 263 @Override deliver( byte[] source, int offset, int length)264 synchronized void deliver( 265 byte[] source, int offset, int length) throws IOException { 266 if (isClosed()) { 267 throw new SocketException("Connection or outbound has been closed"); 268 } 269 270 if (writeCipher.authenticator.seqNumOverflow()) { 271 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { 272 SSLLogger.fine( 273 "sequence number extremely close to overflow " + 274 "(2^64-1 packets). Closing connection."); 275 } 276 277 throw new SSLHandshakeException("sequence number overflow"); 278 } 279 280 boolean isFirstRecordOfThePayload = true; 281 for (int limit = (offset + length); offset < limit;) { 282 int fragLen; 283 if (packetSize > 0) { 284 fragLen = Math.min(maxRecordSize, packetSize); 285 fragLen = 286 writeCipher.calculateFragmentSize(fragLen, headerSize); 287 288 fragLen = Math.min(fragLen, Record.maxDataSize); 289 } else { 290 fragLen = Record.maxDataSize; 291 } 292 293 // Calculate more impact, for example TLS 1.3 padding. 294 fragLen = calculateFragmentSize(fragLen); 295 296 if (isFirstRecordOfThePayload && needToSplitPayload()) { 297 fragLen = 1; 298 isFirstRecordOfThePayload = false; 299 } else { 300 fragLen = Math.min(fragLen, (limit - offset)); 301 } 302 303 // use the buf of ByteArrayOutputStream 304 int position = headerSize + writeCipher.getExplicitNonceSize(); 305 count = position; 306 write(source, offset, fragLen); 307 308 if (SSLLogger.isOn && SSLLogger.isOn("record")) { 309 SSLLogger.fine( 310 "WRITE: " + protocolVersion + 311 " " + ContentType.APPLICATION_DATA.name + 312 ", length = " + (count - position)); 313 } 314 315 // Encrypt the fragment and wrap up a record. 316 encrypt(writeCipher, ContentType.APPLICATION_DATA.id, headerSize); 317 318 // deliver this message 319 deliverStream.write(buf, 0, count); // may throw IOException 320 deliverStream.flush(); // may throw IOException 321 322 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { 323 SSLLogger.fine("Raw write", 324 (new ByteArrayInputStream(buf, 0, count))); 325 } 326 327 // reset the internal buffer 328 count = 0; 329 330 if (isFirstAppOutputRecord) { 331 isFirstAppOutputRecord = false; 332 } 333 334 offset += fragLen; 335 } 336 } 337 338 @Override setDeliverStream(OutputStream outputStream)339 synchronized void setDeliverStream(OutputStream outputStream) { 340 this.deliverStream = outputStream; 341 } 342 343 /* 344 * Need to split the payload except the following cases: 345 * 346 * 1. protocol version is TLS 1.1 or later; 347 * 2. bulk cipher does not use CBC mode, including null bulk cipher suites. 348 * 3. the payload is the first application record of a freshly 349 * negotiated TLS session. 350 * 4. the CBC protection is disabled; 351 * 352 * By default, we counter chosen plaintext issues on CBC mode 353 * ciphersuites in SSLv3/TLS1.0 by sending one byte of application 354 * data in the first record of every payload, and the rest in 355 * subsequent record(s). Note that the issues have been solved in 356 * TLS 1.1 or later. 357 * 358 * It is not necessary to split the very first application record of 359 * a freshly negotiated TLS session, as there is no previous 360 * application data to guess. To improve compatibility, we will not 361 * split such records. 362 * 363 * This avoids issues in the outbound direction. For a full fix, 364 * the peer must have similar protections. 365 */ needToSplitPayload()366 private boolean needToSplitPayload() { 367 return (!protocolVersion.useTLS11PlusSpec()) && 368 writeCipher.isCBCMode() && !isFirstAppOutputRecord && 369 Record.enableCBCProtection; 370 } 371 getFragLimit()372 private int getFragLimit() { 373 int fragLimit; 374 if (packetSize > 0) { 375 fragLimit = Math.min(maxRecordSize, packetSize); 376 fragLimit = 377 writeCipher.calculateFragmentSize(fragLimit, headerSize); 378 379 fragLimit = Math.min(fragLimit, Record.maxDataSize); 380 } else { 381 fragLimit = Record.maxDataSize; 382 } 383 384 // Calculate more impact, for example TLS 1.3 padding. 385 fragLimit = calculateFragmentSize(fragLimit); 386 387 return fragLimit; 388 } 389 } 390