8245527: LDAP Channel Binding support for Java GSS/Kerberos
Reviewed-by: dfuchs, aefimov, mullan
This commit is contained in:
parent
37b70282b5
commit
cfa3f74931
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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));
|
||||
|
@ -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);
|
||||
|
196
test/jdk/com/sun/jndi/ldap/LdapCBPropertiesTest.java
Normal file
196
test/jdk/com/sun/jndi/ldap/LdapCBPropertiesTest.java
Normal 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;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user