/* |
|
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. |
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
* |
|
* This code is free software; you can redistribute it and/or modify it |
|
* under the terms of the GNU General Public License version 2 only, as |
|
* published by the Free Software Foundation. Oracle designates this |
|
* particular file as subject to the "Classpath" exception as provided |
|
* by Oracle in the LICENSE file that accompanied this code. |
|
* |
|
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
* version 2 for more details (a copy is included in the LICENSE file that |
|
* accompanied this code). |
|
* |
|
* You should have received a copy of the GNU General Public License version |
|
* 2 along with this work; if not, write to the Free Software Foundation, |
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
* |
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
* or visit www.oracle.com if you need additional information or have any |
|
* questions. |
|
*/ |
|
package sun.security.ssl; |
|
import java.io.EOFException; |
|
import java.io.IOException; |
|
import java.nio.ByteBuffer; |
|
import javax.crypto.AEADBadTagException; |
|
import javax.crypto.BadPaddingException; |
|
import javax.net.ssl.SSLHandshakeException; |
|
/** |
|
* Interface for SSL/TLS transportation. |
|
*/ |
|
interface SSLTransport { |
|
/** |
|
* Returns the host name of the peer. |
|
* |
|
* @return the host name of the peer, or null if nothing is |
|
* available. |
|
*/ |
|
String getPeerHost(); |
|
/** |
|
* Returns the port number of the peer. |
|
* |
|
* @return the port number of the peer, or -1 if nothing is |
|
* available. |
|
*/ |
|
int getPeerPort(); |
|
/** |
|
* Shutdown the transport. |
|
*/ |
|
default void shutdown() throws IOException { |
|
// blank |
|
} |
|
/** |
|
* Return true if delegated tasks used for handshaking operations. |
|
* |
|
* @return true if delegated tasks used for handshaking operations. |
|
*/ |
|
boolean useDelegatedTask(); |
|
/** |
|
* Decodes an array of SSL/TLS network source data into the |
|
* destination application data buffers. |
|
* |
|
* For SSL/TLS connections, if no source data, the network data may be |
|
* received from the underlying underlying SSL/TLS input stream. |
|
* |
|
* @param context the transportation context |
|
* @param srcs an array of {@code ByteBuffers} containing the |
|
* inbound network data |
|
* @param srcsOffset The offset within the {@code srcs} buffer array |
|
* of the first buffer from which bytes are to be |
|
* retrieved; it must be non-negative and no larger |
|
* than {@code srcs.length}. |
|
* @param srcsLength The maximum number of {@code srcs} buffers to be |
|
* accessed; it must be non-negative and no larger than |
|
* {@code srcs.length} - {@code srcsOffset}. |
|
* @param dsts an array of {@code ByteBuffers} to hold inbound |
|
* application data |
|
* @param dstsOffset The offset within the {@code dsts} buffer array |
|
* of the first buffer from which bytes are to be |
|
* placed; it must be non-negative and no larger |
|
* than {@code dsts.length}. |
|
* @param dstsLength The maximum number of {@code dsts} buffers to be |
|
* accessed; it must be non-negative and no larger than |
|
* {@code dsts.length} - {@code dstsOffset}. |
|
* |
|
* @return a {@code Plaintext} describing the result of |
|
* the operation |
|
* @throws IOException if a problem was encountered while receiving or |
|
* decoding networking data |
|
*/ |
|
static Plaintext decode(TransportContext context, |
|
ByteBuffer[] srcs, int srcsOffset, int srcsLength, |
|
ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException { |
|
Plaintext[] plaintexts = null; |
|
try { |
|
plaintexts = |
|
context.inputRecord.decode(srcs, srcsOffset, srcsLength); |
|
} catch (UnsupportedOperationException unsoe) { // SSLv2Hello |
|
// Hack code to deliver SSLv2 error message for SSL/TLS connections. |
|
context.outputRecord.encodeV2NoCipher(); |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { |
|
SSLLogger.finest("may be talking to SSLv2"); |
|
} |
|
throw context.fatal(Alert.UNEXPECTED_MESSAGE, unsoe); |
|
} catch (AEADBadTagException bte) { |
|
throw context.fatal(Alert.BAD_RECORD_MAC, bte); |
|
} catch (BadPaddingException bpe) { |
|
/* |
|
* The basic SSLv3 record protection involves (optional) |
|
* encryption for privacy, and an integrity check ensuring |
|
* data origin authentication. We do them both here, and |
|
* throw a fatal alert if the integrity check fails. |
|
*/ |
|
Alert alert = (context.handshakeContext != null) ? |
|
Alert.HANDSHAKE_FAILURE : |
|
Alert.BAD_RECORD_MAC; |
|
throw context.fatal(alert, bpe); |
|
} catch (SSLHandshakeException she) { |
|
// may be record sequence number overflow |
|
throw context.fatal(Alert.HANDSHAKE_FAILURE, she); |
|
} catch (EOFException eofe) { |
|
// rethrow EOFException, the call will handle it if neede. |
|
throw eofe; |
|
} catch (IOException ioe) { |
|
throw context.fatal(Alert.UNEXPECTED_MESSAGE, ioe); |
|
} |
|
if (plaintexts == null || plaintexts.length == 0) { |
|
// Connection closed or record should be discarded. |
|
return Plaintext.PLAINTEXT_NULL; |
|
} |
|
Plaintext finalPlaintext = Plaintext.PLAINTEXT_NULL; |
|
for (Plaintext plainText : plaintexts) { |
|
// plainText should never be null for TLS protocols |
|
if (plainText != null && |
|
plainText != Plaintext.PLAINTEXT_NULL && |
|
plainText.contentType != ContentType.APPLICATION_DATA.id) { |
|
context.dispatch(plainText); |
|
} |
|
if (plainText == null) { |
|
plainText = Plaintext.PLAINTEXT_NULL; |
|
} else if (plainText.contentType == |
|
ContentType.APPLICATION_DATA.id) { |
|
// check handshake status |
|
// |
|
// Note that JDK does not support 0-RTT yet. Otherwise, it is |
|
// needed to check early_data. |
|
if (!context.isNegotiated) { |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,verbose")) { |
|
SSLLogger.warning("unexpected application data " + |
|
"before handshake completion"); |
|
} |
|
throw context.fatal(Alert.UNEXPECTED_MESSAGE, |
|
"Receiving application data before handshake complete"); |
|
} |
|
// Fill the destination buffers. |
|
if ((dsts != null) && (dstsLength > 0)) { |
|
ByteBuffer fragment = plainText.fragment; |
|
int remains = fragment.remaining(); |
|
// Should have enough room in the destination buffers. |
|
int limit = dstsOffset + dstsLength; |
|
for (int i = dstsOffset; |
|
((i < limit) && (remains > 0)); i++) { |
|
int amount = Math.min(dsts[i].remaining(), remains); |
|
fragment.limit(fragment.position() + amount); |
|
dsts[i].put(fragment); |
|
remains -= amount; |
|
if (!dsts[i].hasRemaining()) { |
|
dstsOffset++; |
|
} |
|
} |
|
if (remains > 0) { |
|
throw context.fatal(Alert.INTERNAL_ERROR, |
|
"no sufficient room in the destination buffers"); |
|
} |
|
} |
|
} |
|
finalPlaintext = plainText; |
|
} |
|
return finalPlaintext; |
|
} |
|
} |