8245527: LDAP Channel Binding support for Java GSS/Kerberos

Reviewed-by: dfuchs, aefimov, mullan
This commit is contained in:
Alexey Bakhtin 2020-09-25 07:26:54 +00:00
parent 37b70282b5
commit cfa3f74931
10 changed files with 542 additions and 11 deletions

View File

@ -46,9 +46,17 @@ import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import javax.net.SocketFactory;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.security.sasl.SaslException;
/**
* A thread that creates a connection to an LDAP server.
@ -109,7 +117,7 @@ import javax.net.ssl.SSLParameters;
* @author Rosanna Lee
* @author Jagane Sundar
*/
public final class Connection implements Runnable {
public final class Connection implements Runnable, HandshakeCompletedListener {
private static final boolean debug = false;
private static final int dump = 0; // > 0 r, > 1 rw
@ -342,6 +350,7 @@ public final class Connection implements Runnable {
param.setEndpointIdentificationAlgorithm("LDAPS");
sslSocket.setSSLParameters(param);
}
sslSocket.addHandshakeCompletedListener(this);
if (connectTimeout > 0) {
int socketTimeout = sslSocket.getSoTimeout();
sslSocket.setSoTimeout(connectTimeout); // reuse full timeout value
@ -637,6 +646,15 @@ public final class Connection implements Runnable {
ldr = ldr.next;
}
}
if (isTlsConnection()) {
if (closureReason != null) {
CommunicationException ce = new CommunicationException();
ce.setRootCause(closureReason);
tlsHandshakeCompleted.completeExceptionally(ce);
} else {
tlsHandshakeCompleted.cancel(false);
}
}
sock = null;
}
nparent = notifyParent;
@ -972,4 +990,46 @@ public final class Connection implements Runnable {
}
return buf;
}
private final CompletableFuture<X509Certificate> tlsHandshakeCompleted =
new CompletableFuture<>();
@Override
public void handshakeCompleted(HandshakeCompletedEvent event) {
try {
X509Certificate tlsServerCert = null;
Certificate[] certs;
if (event.getSocket().getUseClientMode()) {
certs = event.getPeerCertificates();
} else {
certs = event.getLocalCertificates();
}
if (certs != null && certs.length > 0 &&
certs[0] instanceof X509Certificate) {
tlsServerCert = (X509Certificate) certs[0];
}
tlsHandshakeCompleted.complete(tlsServerCert);
} catch (SSLPeerUnverifiedException ex) {
CommunicationException ce = new CommunicationException();
ce.setRootCause(closureReason);
tlsHandshakeCompleted.completeExceptionally(ex);
}
}
public boolean isTlsConnection() {
return sock instanceof SSLSocket;
}
public X509Certificate getTlsServerCertificate()
throws SaslException {
try {
if (isTlsConnection())
return tlsHandshakeCompleted.get();
} catch (InterruptedException iex) {
throw new SaslException("TLS Handshake Exception ", iex);
} catch (ExecutionException eex) {
throw new SaslException("TLS Handshake Exception ", eex.getCause());
}
return null;
}
}

View File

@ -26,6 +26,7 @@
package com.sun.jndi.ldap.sasl;
import java.io.*;
import java.security.cert.X509Certificate;
import java.util.Vector;
import java.util.Hashtable;
import java.util.StringTokenizer;
@ -41,6 +42,7 @@ import javax.security.sasl.*;
import com.sun.jndi.ldap.Connection;
import com.sun.jndi.ldap.LdapClient;
import com.sun.jndi.ldap.LdapResult;
import com.sun.jndi.ldap.sasl.TlsChannelBinding.TlsChannelBindingType;
/**
* Handles SASL support.
@ -110,10 +112,38 @@ final public class LdapSasl {
String authzId = (env != null) ? (String)env.get(SASL_AUTHZ_ID) : null;
String[] mechs = getSaslMechanismNames(authMech);
// Internal TLS Channel Binding property cannot be set explicitly
if (env.get(TlsChannelBinding.CHANNEL_BINDING) != null) {
throw new NamingException(TlsChannelBinding.CHANNEL_BINDING +
" property cannot be set explicitly");
}
Hashtable<String, Object> envProps = (Hashtable<String, Object>) env;
try {
// Prepare TLS Channel Binding data
if (conn.isTlsConnection()) {
TlsChannelBindingType cbType =
TlsChannelBinding.parseType(
(String)env.get(TlsChannelBinding.CHANNEL_BINDING_TYPE));
if (cbType == TlsChannelBindingType.TLS_SERVER_END_POINT) {
// set tls-server-end-point channel binding
X509Certificate cert = conn.getTlsServerCertificate();
if (cert != null) {
TlsChannelBinding tlsCB =
TlsChannelBinding.create(cert);
envProps = (Hashtable<String, Object>) env.clone();
envProps.put(TlsChannelBinding.CHANNEL_BINDING, tlsCB.getData());
} else {
throw new SaslException("No suitable certificate to generate " +
"TLS Channel Binding data");
}
}
}
// Create SASL client to use using SASL package
saslClnt = Sasl.createSaslClient(
mechs, authzId, "ldap", server, (Hashtable<String, ?>)env, cbh);
mechs, authzId, "ldap", server, envProps, cbh);
if (saslClnt == null) {
throw new AuthenticationNotSupportedException(authMech);

View File

@ -0,0 +1,146 @@
/*
* Copyright (c) 2020, Azul Systems, Inc. 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 com.sun.jndi.ldap.sasl;
import javax.naming.NamingException;
import javax.security.sasl.SaslException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Hashtable;
/**
* This class implements the Channel Binding for TLS as defined in
* <a href="https://www.ietf.org/rfc/rfc5929.txt">
* Channel Bindings for TLS</a>
*
* Format of the Channel Binding data is also defined in
* <a href="https://www.ietf.org/rfc/rfc5056.txt">
* On the Use of Channel Bindings to Secure Channels</a>
* section 2.1.
*
*/
public class TlsChannelBinding {
// TLS channel binding type property
public static final String CHANNEL_BINDING_TYPE =
"com.sun.jndi.ldap.tls.cbtype";
// internal TLS channel binding property
public static final String CHANNEL_BINDING =
"jdk.internal.sasl.tlschannelbinding";
public enum TlsChannelBindingType {
/**
* Channel binding on the basis of TLS Finished message.
* TLS_UNIQUE is defined by RFC 5929 but is not supported
* by the current LDAP stack.
*/
TLS_UNIQUE("tls-unique"),
/**
* Channel binding on the basis of TLS server certificate.
*/
TLS_SERVER_END_POINT("tls-server-end-point");
public String getName() {
return name;
}
final private String name;
TlsChannelBindingType(String name) {
this.name = name;
}
}
/**
* Parse value of "com.sun.jndi.ldap.tls.cbtype" property
* @param cbType
* @return TLS Channel Binding type or null if
* "com.sun.jndi.ldap.tls.cbtype" property has not been set.
* @throws NamingException
*/
public static TlsChannelBindingType parseType(String cbType) throws NamingException {
if (cbType != null) {
if (cbType.equals(TlsChannelBindingType.TLS_SERVER_END_POINT.getName())) {
return TlsChannelBindingType.TLS_SERVER_END_POINT;
} else {
throw new NamingException("Illegal value for " +
CHANNEL_BINDING_TYPE + " property.");
}
}
return null;
}
final private TlsChannelBindingType cbType;
final private byte[] cbData;
/**
* Construct tls-server-end-point Channel Binding data
* @param serverCertificate
* @throws SaslException
*/
public static TlsChannelBinding create(X509Certificate serverCertificate) throws SaslException {
try {
final byte[] prefix =
TlsChannelBindingType.TLS_SERVER_END_POINT.getName().concat(":").getBytes();
String hashAlg = serverCertificate.getSigAlgName().
replace("SHA", "SHA-").toUpperCase();
int ind = hashAlg.indexOf("WITH");
if (ind > 0) {
hashAlg = hashAlg.substring(0, ind);
if (hashAlg.equals("MD5") || hashAlg.equals("SHA-1")) {
hashAlg = "SHA-256";
}
} else {
hashAlg = "SHA-256";
}
MessageDigest md = MessageDigest.getInstance(hashAlg);
byte[] hash = md.digest(serverCertificate.getEncoded());
byte[] cbData = Arrays.copyOf(prefix, prefix.length + hash.length );
System.arraycopy(hash, 0, cbData, prefix.length, hash.length);
return new TlsChannelBinding(TlsChannelBindingType.TLS_SERVER_END_POINT, cbData);
} catch (NoSuchAlgorithmException | CertificateEncodingException e) {
throw new SaslException("Cannot create TLS channel binding data", e);
}
}
private TlsChannelBinding(TlsChannelBindingType cbType, byte[] cbData) {
this.cbType = cbType;
this.cbData = cbData;
}
public TlsChannelBindingType getType() {
return cbType;
}
public byte[] getData() {
return cbData;
}
}

View File

@ -59,6 +59,20 @@
* <br>If this property is not specified, the default is to wait
* for the response until it is received.
* </li>
* <li>{@code com.sun.jndi.ldap.tls.cbtype}:
* <br>The value of this property is the string representing the TLS
* Channel Binding type required for an LDAP connection over SSL/TLS.
* Possible value is :
* <ul>
* <li>"tls-server-end-point" - Channel Binding data is created on
* the basis of the TLS server certificate.
* </li>
* </ul>
* <br>"tls-unique" TLS Channel Binding type is specified in RFC-5929
* but not supported.
* <br>If this property is not specified, the client does not send
* channel binding information to the server.
* </li>
* </ul>
*
* @provides javax.naming.ldap.spi.LdapDnsProvider

View File

@ -41,6 +41,8 @@ module java.security.jgss {
jdk.security.jgss;
exports sun.security.jgss.krb5 to
jdk.security.auth;
exports sun.security.jgss.krb5.internal to
jdk.security.jgss;
exports sun.security.krb5 to
jdk.security.auth;
exports sun.security.krb5.internal to

View File

@ -36,6 +36,7 @@ import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import sun.security.krb5.*;
import sun.security.krb5.internal.Krb5;
import sun.security.jgss.krb5.internal.TlsChannelBindingImpl;
abstract class InitialToken extends Krb5Token {
@ -57,6 +58,7 @@ abstract class InitialToken extends Krb5Token {
private final byte[] CHECKSUM_FIRST_BYTES =
{(byte)0x10, (byte)0x00, (byte)0x00, (byte)0x00};
private static final int CHANNEL_BINDING_AF_UNSPEC = 0;
private static final int CHANNEL_BINDING_AF_INET = 2;
private static final int CHANNEL_BINDING_AF_INET6 = 24;
private static final int CHANNEL_BINDING_AF_NULL_ADDR = 255;
@ -333,8 +335,8 @@ abstract class InitialToken extends Krb5Token {
}
}
private int getAddrType(InetAddress addr) {
int addressType = CHANNEL_BINDING_AF_NULL_ADDR;
private int getAddrType(InetAddress addr, int defValue) {
int addressType = defValue;
if (addr instanceof Inet4Address)
addressType = CHANNEL_BINDING_AF_INET;
@ -344,7 +346,7 @@ abstract class InitialToken extends Krb5Token {
}
private byte[] getAddrBytes(InetAddress addr) throws GSSException {
int addressType = getAddrType(addr);
int addressType = getAddrType(addr, CHANNEL_BINDING_AF_NULL_ADDR);
byte[] addressBytes = addr.getAddress();
if (addressBytes != null) {
switch (addressType) {
@ -375,8 +377,16 @@ abstract class InitialToken extends Krb5Token {
InetAddress acceptorAddress = channelBinding.getAcceptorAddress();
int size = 5*4;
int initiatorAddressType = getAddrType(initiatorAddress);
int acceptorAddressType = getAddrType(acceptorAddress);
// LDAP TLS Channel Binding requires CHANNEL_BINDING_AF_UNSPEC address type
// for unspecified initiator and acceptor addresses.
// CHANNEL_BINDING_AF_NULL_ADDR value should be used for unspecified address
// in all other cases.
int initiatorAddressType = getAddrType(initiatorAddress,
(channelBinding instanceof TlsChannelBindingImpl) ?
CHANNEL_BINDING_AF_UNSPEC : CHANNEL_BINDING_AF_NULL_ADDR);
int acceptorAddressType = getAddrType(acceptorAddress,
(channelBinding instanceof TlsChannelBindingImpl) ?
CHANNEL_BINDING_AF_UNSPEC : CHANNEL_BINDING_AF_NULL_ADDR);
byte[] initiatorAddressBytes = null;
if (initiatorAddress != null) {

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2020, Azul Systems, Inc. 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.jgss.krb5.internal;
import org.ietf.jgss.ChannelBinding;
/**
* TLS Channel Binding wrapper class to determine internal
* tls channel binding implementation.
*/
public class TlsChannelBindingImpl extends ChannelBinding {
public TlsChannelBindingImpl(byte[] appData) {
super(appData);
}
}

View File

@ -34,6 +34,8 @@ const int TYPE_CRED_NAME = 10;
const int TYPE_CRED_TIME = 11;
const int TYPE_CRED_USAGE = 12;
static jclass tlsCBCl = NULL;
/*
* Class: sun_security_jgss_wrapper_GSSLibStub
* Method: init
@ -69,6 +71,17 @@ Java_sun_security_jgss_wrapper_GSSLibStub_init(JNIEnv *env,
failed = loadNative(libName);
(*env)->ReleaseStringUTFChars(env, jlibName, libName);
if (tlsCBCl == NULL) {
/* initialize TLS Channel Binding class wrapper */
jclass cl = (*env)->FindClass(env,
"sun/security/jgss/krb5/internal/TlsChannelBindingImpl");
if (cl == NULL) { /* exception thrown */
return JNI_FALSE;
}
tlsCBCl = (*env)->NewGlobalRef(env, cl);
}
if (!failed) {
return JNI_TRUE;
} else {
@ -154,11 +167,13 @@ void deleteGSSCB(gss_channel_bindings_t cb) {
if (cb == GSS_C_NO_CHANNEL_BINDINGS) return;
/* release initiator address */
if (cb->initiator_addrtype != GSS_C_AF_NULLADDR) {
if (cb->initiator_addrtype != GSS_C_AF_NULLADDR &&
cb->initiator_addrtype != GSS_C_AF_UNSPEC) {
resetGSSBuffer(&(cb->initiator_address));
}
/* release acceptor address */
if (cb->acceptor_addrtype != GSS_C_AF_NULLADDR) {
if (cb->acceptor_addrtype != GSS_C_AF_NULLADDR &&
cb->acceptor_addrtype != GSS_C_AF_UNSPEC) {
resetGSSBuffer(&(cb->acceptor_address));
}
/* release application data */
@ -189,9 +204,19 @@ gss_channel_bindings_t newGSSCB(JNIEnv *env, jobject jcb) {
}
// initialize addrtype in CB first
cb->initiator_addrtype = GSS_C_AF_NULLADDR;
cb->acceptor_addrtype = GSS_C_AF_NULLADDR;
// LDAP TLS Channel Binding requires GSS_C_AF_UNSPEC address type
// for unspecified initiator and acceptor addresses.
// GSS_C_AF_NULLADDR value should be used for unspecified address
// in all other cases.
if ((*env)->IsInstanceOf(env, jcb, tlsCBCl)) {
// TLS Channel Binding requires unspecified addrtype=0
cb->initiator_addrtype = GSS_C_AF_UNSPEC;
cb->acceptor_addrtype = GSS_C_AF_UNSPEC;
} else {
cb->initiator_addrtype = GSS_C_AF_NULLADDR;
cb->acceptor_addrtype = GSS_C_AF_NULLADDR;
}
// addresses needs to be initialized to empty
memset(&cb->initiator_address, 0, sizeof(cb->initiator_address));
memset(&cb->acceptor_address, 0, sizeof(cb->acceptor_address));

View File

@ -35,6 +35,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import javax.security.auth.callback.CallbackHandler;
// JGSS
import sun.security.jgss.krb5.internal.TlsChannelBindingImpl;
import org.ietf.jgss.*;
/**
@ -150,6 +151,16 @@ final class GssKrb5Client extends GssKrb5Base implements SaslClient {
}
secCtx.requestMutualAuth(mutual);
if (props != null) {
// TLS Channel Binding
// Property name is defined in the TLSChannelBinding class of
// the java.naming module
byte[] tlsCB = (byte[])props.get("jdk.internal.sasl.tlschannelbinding");
if (tlsCB != null) {
secCtx.setChannelBinding(new TlsChannelBindingImpl(tlsCB));
}
}
// Always specify potential need for integrity and confidentiality
// Decision will be made during final handshake
secCtx.requestConf(true);

View File

@ -0,0 +1,196 @@
/*
* Copyright (c) 2020, Azul Systems, Inc. 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 8245527
* @library lib/ /test/lib
* @run main/othervm LdapCBPropertiesTest true true com.sun.jndi.ldap.tls.cbtype tls-server-end-point
* @run main/othervm LdapCBPropertiesTest false false com.sun.jndi.ldap.tls.cbtype tls-server-end-point
* @run main/othervm LdapCBPropertiesTest true true com.sun.jndi.ldap.tls.cbtype tls-server-end-point com.sun.jndi.ldap.connect.timeout 2000
* @run main/othervm LdapCBPropertiesTest false false com.sun.jndi.ldap.tls.cbtype tls-server-end-point com.sun.jndi.ldap.connect.timeout 2000
* @run main/othervm LdapCBPropertiesTest false true com.sun.jndi.ldap.tls.cbtype tls-unknown
* @run main/othervm LdapCBPropertiesTest false true jdk.internal.sasl.tlschannelbinding value
* @summary test new JNDI property to control the Channel Binding data
*/
import javax.naming.AuthenticationException;
import javax.naming.CommunicationException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import java.net.InetAddress;
import java.net.URI;
import java.util.Hashtable;
import org.ietf.jgss.GSSException;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.security.sasl.SaslException;
import jdk.test.lib.net.URIBuilder;
public class LdapCBPropertiesTest {
/*
* Where do we find the keystores?
*/
static String pathToStores = "../../../../javax/net/ssl/etc";
static String keyStoreFile = "keystore";
static String trustStoreFile = "truststore";
static String passwd = "passphrase";
static boolean debug = false;
public static void main(String[] args) throws Exception {
String keyFilename =
System.getProperty("test.src", "./") + "/" + pathToStores +
"/" + keyStoreFile;
String trustFilename =
System.getProperty("test.src", "./") + "/" + pathToStores +
"/" + trustStoreFile;
System.setProperty("javax.net.ssl.keyStore", keyFilename);
System.setProperty("javax.net.ssl.keyStorePassword", passwd);
System.setProperty("javax.net.ssl.trustStore", trustFilename);
System.setProperty("javax.net.ssl.trustStorePassword", passwd);
if (debug)
System.setProperty("javax.net.debug", "all");
/*
* Start the tests.
*/
new LdapCBPropertiesTest(args);
}
/*
* Primary constructor, used to drive remainder of the test.
*/
LdapCBPropertiesTest(String[] args) throws Exception {
InetAddress loopback = InetAddress.getLoopbackAddress();
SSLServerSocketFactory sslssf =
(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
SSLServerSocket sslServerSocket =
(SSLServerSocket) sslssf.createServerSocket(0, 0, loopback);
int serverPort = sslServerSocket.getLocalPort();
try (var ignore = new BaseLdapServer(sslServerSocket).start()) {
doClientSide(serverPort, args);
}
}
/*
* Define the client side of the test.
*
* The server should start at this time already
*/
void doClientSide(int serverPort, String[] args) throws Exception {
boolean passed = false;
boolean shouldPass = Boolean.parseBoolean(args[0]);
boolean shouldConnect = Boolean.parseBoolean(args[1]);
// set disableEndpointIdentification to disable hostname verification
if (shouldConnect) {
System.setProperty(
"com.sun.jndi.ldap.object.disableEndpointIdentification", "true");
}
// Set up the environment for creating the initial context
Hashtable env = new Hashtable();
URI uri = URIBuilder.newBuilder()
.scheme("ldaps")
.loopback()
.port(serverPort)
.build();
env.put(Context.PROVIDER_URL, uri.toString());
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");
// read properties
for (int i = 2; i < args.length; i += 2) {
env.put(args[i], args[i + 1]);
if (debug)
System.out.println("Env=" + args[i] + "=" + args[i + 1]);
}
try {
DirContext ctx = new InitialDirContext(env);
passed = shouldPass;
ctx.close();
} catch (NamingException ne) {
// only NamingException is allowed
if (debug)
System.out.println("Exception=" + ne + " cause=" + ne.getRootCause());
passed = handleNamingException(ne, shouldPass, shouldConnect);
} catch(Exception e) {
System.err.println("Failed: caught an unexpected Exception - " + e);
throw e;
} finally {
// test if internal property accessible to application
if(shouldPass &&
env.get("jdk.internal.sasl.tlschannelbinding") != null) {
throw new Exception(
"Test FAILED: jdk.internal.sasl.tlschannelbinding should not be accessible");
}
}
if (!passed) {
throw new Exception(
"Test FAILED: NamingException exception should be thrown");
}
System.out.println("Test PASSED");
}
private static boolean handleNamingException(NamingException ne, boolean shouldPass, boolean shouldConnect)
throws NamingException {
if (ne instanceof AuthenticationException &&
ne.getRootCause() instanceof SaslException) {
SaslException saslEx = (SaslException) ne.getRootCause();
if (shouldConnect && saslEx.getCause() instanceof GSSException) {
// SSL connection successful, expected exception from SaslClient
if (shouldPass)
return true;
}
}
if (!shouldConnect) {
// SSL handshake fails
Exception ex = ne;
while(ex != null && !(ex instanceof CommunicationException)) {
ex = (Exception)ex.getCause();
}
if (ex != null) {
if (ex.getCause() instanceof SSLException) {
if (!shouldPass)
return true;
}
}
}
if (!shouldPass && ne.getRootCause() == null) {
// Expected exception caused by Channel Binding parameter inconsistency
return true;
}
throw ne;
}
}