mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 03:58:21 +00:00
8369950: TLS connection to IPv6 address fails with BCJSSE due to IllegalArgumentException
Co-authored-by: Mikhail Yankelevich <myankelevich@openjdk.org> Reviewed-by: djelinski, vyazici, dfuchs, myankelevich
This commit is contained in:
parent
5f083abafc
commit
7da91533aa
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2025, 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
|
||||
@ -44,6 +44,7 @@ import java.util.Objects;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import javax.net.ssl.*;
|
||||
import sun.net.util.IPAddressUtil;
|
||||
import sun.net.www.http.HttpClient;
|
||||
import sun.net.www.protocol.http.AuthCacheImpl;
|
||||
import sun.net.www.protocol.http.HttpURLConnection;
|
||||
@ -471,7 +472,13 @@ final class HttpsClient extends HttpClient
|
||||
SSLParameters parameters = s.getSSLParameters();
|
||||
parameters.setEndpointIdentificationAlgorithm("HTTPS");
|
||||
// host has been set previously for SSLSocketImpl
|
||||
if (!(s instanceof SSLSocketImpl)) {
|
||||
if (!(s instanceof SSLSocketImpl) &&
|
||||
!IPAddressUtil.isIPv4LiteralAddress(host) &&
|
||||
!(host.charAt(0) == '[' && host.charAt(host.length() - 1) == ']' &&
|
||||
IPAddressUtil.isIPv6LiteralAddress(host.substring(1, host.length() - 1))
|
||||
)) {
|
||||
// Fully qualified DNS hostname of the server, as per section 3, RFC 6066
|
||||
// Literal IPv4 and IPv6 addresses are not permitted in "HostName".
|
||||
parameters.setServerNames(List.of(new SNIHostName(host)));
|
||||
}
|
||||
s.setSSLParameters(parameters);
|
||||
|
||||
379
test/jdk/javax/net/ssl/HttpsURLConnection/SubjectAltNameIP.java
Normal file
379
test/jdk/javax/net/ssl/HttpsURLConnection/SubjectAltNameIP.java
Normal file
@ -0,0 +1,379 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8369950
|
||||
* @summary Test that the HttpsURLConnection does not set IP address literals for
|
||||
* SNI hostname during TLS handshake
|
||||
* @library /test/lib
|
||||
* @modules java.base/sun.net.util
|
||||
* @comment Insert -Djavax.net.debug=all into the following lines to enable SSL debugging
|
||||
* @run main/othervm SubjectAltNameIP 127.0.0.1
|
||||
* @run main/othervm SubjectAltNameIP [::1]
|
||||
*/
|
||||
|
||||
import javax.net.ssl.HandshakeCompletedListener;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
import javax.net.ssl.SSLServerSocket;
|
||||
import javax.net.ssl.SSLServerSocketFactory;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import jdk.test.lib.Asserts;
|
||||
import jdk.test.lib.net.IPSupport;
|
||||
import jdk.test.lib.net.SimpleSSLContext;
|
||||
import jtreg.SkippedException;
|
||||
import sun.net.util.IPAddressUtil;
|
||||
|
||||
public class SubjectAltNameIP {
|
||||
|
||||
// Is the server ready to serve?
|
||||
private final CountDownLatch serverReady = new CountDownLatch(1);
|
||||
|
||||
// Use any free port by default.
|
||||
volatile int serverPort = 0;
|
||||
|
||||
// Stores an exception thrown by server in a separate thread.
|
||||
volatile Exception serverException = null;
|
||||
|
||||
// SSLSocket object created by HttpsClient internally.
|
||||
SSLSocket clientSSLSocket = null;
|
||||
|
||||
// The hostname the server socket is bound to.
|
||||
String hostName;
|
||||
|
||||
static final byte[] requestEnd = new byte[] {'\r', '\n', '\r', '\n' };
|
||||
|
||||
// Read until the end of the request.
|
||||
void readOneRequest(InputStream is) throws IOException {
|
||||
int requestEndCount = 0, r;
|
||||
while ((r = is.read()) != -1) {
|
||||
if (r == requestEnd[requestEndCount]) {
|
||||
requestEndCount++;
|
||||
if (requestEndCount == 4) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
requestEndCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Define the server side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doServerSide() throws Exception {
|
||||
SSLServerSocketFactory sslssf =
|
||||
new SimpleSSLContext().get().getServerSocketFactory();
|
||||
SSLServerSocket sslServerSocket =
|
||||
(SSLServerSocket) sslssf.createServerSocket(
|
||||
serverPort, 0,
|
||||
InetAddress.getByName(hostName));
|
||||
sslServerSocket.setEnabledProtocols(new String[]{"TLSv1.3"});
|
||||
serverPort = sslServerSocket.getLocalPort();
|
||||
|
||||
/*
|
||||
* Signal the client, the server is ready to accept connection.
|
||||
*/
|
||||
serverReady.countDown();
|
||||
|
||||
SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
|
||||
OutputStream sslOS = sslSocket.getOutputStream();
|
||||
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(sslOS));
|
||||
bw.write("HTTP/1.1 200 OK\r\n\r\n");
|
||||
bw.flush();
|
||||
readOneRequest(sslSocket.getInputStream());
|
||||
sslSocket.close();
|
||||
}
|
||||
|
||||
/*
|
||||
* Define the client side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doClientSide() throws Exception {
|
||||
|
||||
/*
|
||||
* Wait for server to get started.
|
||||
*/
|
||||
serverReady.await();
|
||||
if (serverException != null) {
|
||||
throw new RuntimeException("Server failed to start.", serverException);
|
||||
}
|
||||
|
||||
SSLSocketFactory sf = new SimpleSSLContext().get().getSocketFactory();
|
||||
URI uri = new URI("https://" + hostName + ":" + serverPort + "/index.html");
|
||||
HttpsURLConnection conn = (HttpsURLConnection)uri.toURL().openConnection();
|
||||
|
||||
/*
|
||||
* Simulate an external JSSE implementation and store the client SSLSocket
|
||||
* used internally.
|
||||
*/
|
||||
conn.setSSLSocketFactory(wrapSocketFactory(sf,
|
||||
sslSocket -> {
|
||||
Asserts.assertEquals(null, clientSSLSocket, "clientSSLSocket is");
|
||||
clientSSLSocket = sslSocket;
|
||||
}));
|
||||
conn.getInputStream();
|
||||
|
||||
var sniSN = clientSSLSocket.getSSLParameters().getServerNames();
|
||||
if (sniSN != null && !sniSN.isEmpty()) {
|
||||
throw new RuntimeException("SNI server name '" +
|
||||
sniSN.getFirst() + "' must not be set.");
|
||||
}
|
||||
|
||||
if (conn.getResponseCode() == -1) {
|
||||
throw new RuntimeException("getResponseCode() returns -1");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
if (IPAddressUtil.isIPv6LiteralAddress(args[0]) && !IPSupport.hasIPv6()) {
|
||||
throw new SkippedException("Skipping test - IPv6 is not supported");
|
||||
}
|
||||
/*
|
||||
* Start the tests.
|
||||
*/
|
||||
new SubjectAltNameIP(args[0]);
|
||||
}
|
||||
|
||||
Thread serverThread = null;
|
||||
|
||||
/*
|
||||
* Primary constructor, used to drive remainder of the test.
|
||||
*
|
||||
* Fork off the other side, then do your work.
|
||||
*/
|
||||
SubjectAltNameIP(String host) throws Exception {
|
||||
hostName = host;
|
||||
startServer();
|
||||
doClientSide();
|
||||
|
||||
/*
|
||||
* Wait for other side to close down.
|
||||
*/
|
||||
serverThread.join();
|
||||
|
||||
if (serverException != null)
|
||||
throw serverException;
|
||||
}
|
||||
|
||||
void startServer() {
|
||||
serverThread = new Thread(() -> {
|
||||
try {
|
||||
doServerSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our server thread just died.
|
||||
*
|
||||
* Store the exception and release the client.
|
||||
*/
|
||||
serverException = e;
|
||||
serverReady.countDown();
|
||||
}
|
||||
});
|
||||
serverThread.start();
|
||||
}
|
||||
|
||||
/*
|
||||
* Wraps SSLSocketImpl to simulate a different JSSE implementation
|
||||
*/
|
||||
private static SSLSocketFactory wrapSocketFactory(final SSLSocketFactory wrap, final Consumer<SSLSocket> store) {
|
||||
return new SSLSocketFactory() {
|
||||
@Override
|
||||
public String[] getDefaultCipherSuites() {
|
||||
return wrap.getDefaultCipherSuites();
|
||||
}
|
||||
@Override
|
||||
public String[] getSupportedCipherSuites() {
|
||||
return wrap.getSupportedCipherSuites();
|
||||
}
|
||||
@Override
|
||||
public Socket createSocket(Socket s, String host, int port, boolean autoClose)
|
||||
throws IOException {
|
||||
final SSLSocket so =
|
||||
(SSLSocket) wrap.createSocket(s, host, port, autoClose);
|
||||
|
||||
// store the underlying SSLSocket for later use
|
||||
store.accept(so);
|
||||
|
||||
return new SSLSocket() {
|
||||
@Override
|
||||
public void connect(SocketAddress endpoint,
|
||||
int timeout) throws IOException {
|
||||
so.connect(endpoint, timeout);
|
||||
}
|
||||
@Override
|
||||
public String[] getSupportedCipherSuites() {
|
||||
return so.getSupportedCipherSuites();
|
||||
}
|
||||
@Override
|
||||
public String[] getEnabledCipherSuites() {
|
||||
return so.getEnabledCipherSuites();
|
||||
}
|
||||
@Override
|
||||
public void setEnabledCipherSuites(String[] suites) {
|
||||
so.setEnabledCipherSuites(suites);
|
||||
}
|
||||
@Override
|
||||
public String[] getSupportedProtocols() {
|
||||
return so.getSupportedProtocols();
|
||||
}
|
||||
@Override
|
||||
public String[] getEnabledProtocols() {
|
||||
return so.getEnabledProtocols();
|
||||
}
|
||||
@Override
|
||||
public void setEnabledProtocols(String[] protocols) {
|
||||
so.setEnabledProtocols(protocols);
|
||||
}
|
||||
@Override
|
||||
public SSLSession getSession() {
|
||||
return so.getSession();
|
||||
}
|
||||
@Override
|
||||
public SSLSession getHandshakeSession() {
|
||||
return so.getHandshakeSession();
|
||||
}
|
||||
@Override
|
||||
public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
|
||||
so.addHandshakeCompletedListener(listener);
|
||||
}
|
||||
@Override
|
||||
public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
|
||||
so.removeHandshakeCompletedListener(listener);
|
||||
}
|
||||
@Override
|
||||
public void startHandshake() throws IOException {
|
||||
so.startHandshake();
|
||||
}
|
||||
@Override
|
||||
public void setUseClientMode(boolean mode) {
|
||||
so.setUseClientMode(mode);
|
||||
}
|
||||
@Override
|
||||
public boolean getUseClientMode() {
|
||||
return so.getUseClientMode();
|
||||
}
|
||||
@Override
|
||||
public void setNeedClientAuth(boolean need) {
|
||||
}
|
||||
@Override
|
||||
public boolean getNeedClientAuth() {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public void setWantClientAuth(boolean want) {
|
||||
}
|
||||
@Override
|
||||
public boolean getWantClientAuth() {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public void setEnableSessionCreation(boolean flag) {
|
||||
so.setEnableSessionCreation(flag);
|
||||
}
|
||||
@Override
|
||||
public boolean getEnableSessionCreation() {
|
||||
return so.getEnableSessionCreation();
|
||||
}
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
so.close();
|
||||
}
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return so.isClosed();
|
||||
}
|
||||
@Override
|
||||
public void shutdownInput() throws IOException {
|
||||
so.shutdownInput();
|
||||
}
|
||||
@Override
|
||||
public boolean isInputShutdown() {
|
||||
return so.isInputShutdown();
|
||||
}
|
||||
@Override
|
||||
public void shutdownOutput() throws IOException {
|
||||
so.shutdownOutput();
|
||||
}
|
||||
@Override
|
||||
public boolean isOutputShutdown() {
|
||||
return so.isOutputShutdown();
|
||||
}
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return so.getInputStream();
|
||||
}
|
||||
@Override
|
||||
public OutputStream getOutputStream() throws IOException {
|
||||
return so.getOutputStream();
|
||||
}
|
||||
@Override
|
||||
public SSLParameters getSSLParameters() {
|
||||
return so.getSSLParameters();
|
||||
}
|
||||
@Override
|
||||
public void setSSLParameters(SSLParameters params) {
|
||||
so.setSSLParameters(params);
|
||||
}
|
||||
};
|
||||
}
|
||||
@Override
|
||||
public Socket createSocket(String h, int p) {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public Socket createSocket(String h, int p, InetAddress ipa, int lp) {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public Socket createSocket(InetAddress h, int p) {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public Socket createSocket(InetAddress a, int p, InetAddress l, int lp) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user