mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-22 03:54:33 +02:00
8245527: LDAP Channel Binding support for Java GSS/Kerberos
Reviewed-by: dfuchs, aefimov, mullan
This commit is contained in:
parent
37b70282b5
commit
cfa3f74931
10 changed files with 542 additions and 11 deletions
|
@ -46,9 +46,17 @@ import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
import java.security.PrivilegedAction;
|
import java.security.PrivilegedAction;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
import javax.net.ssl.SSLParameters;
|
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.
|
* A thread that creates a connection to an LDAP server.
|
||||||
|
@ -109,7 +117,7 @@ import javax.net.ssl.SSLParameters;
|
||||||
* @author Rosanna Lee
|
* @author Rosanna Lee
|
||||||
* @author Jagane Sundar
|
* @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 boolean debug = false;
|
||||||
private static final int dump = 0; // > 0 r, > 1 rw
|
private static final int dump = 0; // > 0 r, > 1 rw
|
||||||
|
@ -342,6 +350,7 @@ public final class Connection implements Runnable {
|
||||||
param.setEndpointIdentificationAlgorithm("LDAPS");
|
param.setEndpointIdentificationAlgorithm("LDAPS");
|
||||||
sslSocket.setSSLParameters(param);
|
sslSocket.setSSLParameters(param);
|
||||||
}
|
}
|
||||||
|
sslSocket.addHandshakeCompletedListener(this);
|
||||||
if (connectTimeout > 0) {
|
if (connectTimeout > 0) {
|
||||||
int socketTimeout = sslSocket.getSoTimeout();
|
int socketTimeout = sslSocket.getSoTimeout();
|
||||||
sslSocket.setSoTimeout(connectTimeout); // reuse full timeout value
|
sslSocket.setSoTimeout(connectTimeout); // reuse full timeout value
|
||||||
|
@ -637,6 +646,15 @@ public final class Connection implements Runnable {
|
||||||
ldr = ldr.next;
|
ldr = ldr.next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (isTlsConnection()) {
|
||||||
|
if (closureReason != null) {
|
||||||
|
CommunicationException ce = new CommunicationException();
|
||||||
|
ce.setRootCause(closureReason);
|
||||||
|
tlsHandshakeCompleted.completeExceptionally(ce);
|
||||||
|
} else {
|
||||||
|
tlsHandshakeCompleted.cancel(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
sock = null;
|
sock = null;
|
||||||
}
|
}
|
||||||
nparent = notifyParent;
|
nparent = notifyParent;
|
||||||
|
@ -972,4 +990,46 @@ public final class Connection implements Runnable {
|
||||||
}
|
}
|
||||||
return buf;
|
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;
|
package com.sun.jndi.ldap.sasl;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
@ -41,6 +42,7 @@ import javax.security.sasl.*;
|
||||||
import com.sun.jndi.ldap.Connection;
|
import com.sun.jndi.ldap.Connection;
|
||||||
import com.sun.jndi.ldap.LdapClient;
|
import com.sun.jndi.ldap.LdapClient;
|
||||||
import com.sun.jndi.ldap.LdapResult;
|
import com.sun.jndi.ldap.LdapResult;
|
||||||
|
import com.sun.jndi.ldap.sasl.TlsChannelBinding.TlsChannelBindingType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles SASL support.
|
* Handles SASL support.
|
||||||
|
@ -110,10 +112,38 @@ final public class LdapSasl {
|
||||||
String authzId = (env != null) ? (String)env.get(SASL_AUTHZ_ID) : null;
|
String authzId = (env != null) ? (String)env.get(SASL_AUTHZ_ID) : null;
|
||||||
String[] mechs = getSaslMechanismNames(authMech);
|
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 {
|
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
|
// Create SASL client to use using SASL package
|
||||||
saslClnt = Sasl.createSaslClient(
|
saslClnt = Sasl.createSaslClient(
|
||||||
mechs, authzId, "ldap", server, (Hashtable<String, ?>)env, cbh);
|
mechs, authzId, "ldap", server, envProps, cbh);
|
||||||
|
|
||||||
if (saslClnt == null) {
|
if (saslClnt == null) {
|
||||||
throw new AuthenticationNotSupportedException(authMech);
|
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
|
* <br>If this property is not specified, the default is to wait
|
||||||
* for the response until it is received.
|
* for the response until it is received.
|
||||||
* </li>
|
* </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>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @provides javax.naming.ldap.spi.LdapDnsProvider
|
* @provides javax.naming.ldap.spi.LdapDnsProvider
|
||||||
|
|
|
@ -41,6 +41,8 @@ module java.security.jgss {
|
||||||
jdk.security.jgss;
|
jdk.security.jgss;
|
||||||
exports sun.security.jgss.krb5 to
|
exports sun.security.jgss.krb5 to
|
||||||
jdk.security.auth;
|
jdk.security.auth;
|
||||||
|
exports sun.security.jgss.krb5.internal to
|
||||||
|
jdk.security.jgss;
|
||||||
exports sun.security.krb5 to
|
exports sun.security.krb5 to
|
||||||
jdk.security.auth;
|
jdk.security.auth;
|
||||||
exports sun.security.krb5.internal to
|
exports sun.security.krb5.internal to
|
||||||
|
|
|
@ -36,6 +36,7 @@ import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import sun.security.krb5.*;
|
import sun.security.krb5.*;
|
||||||
import sun.security.krb5.internal.Krb5;
|
import sun.security.krb5.internal.Krb5;
|
||||||
|
import sun.security.jgss.krb5.internal.TlsChannelBindingImpl;
|
||||||
|
|
||||||
abstract class InitialToken extends Krb5Token {
|
abstract class InitialToken extends Krb5Token {
|
||||||
|
|
||||||
|
@ -57,6 +58,7 @@ abstract class InitialToken extends Krb5Token {
|
||||||
private final byte[] CHECKSUM_FIRST_BYTES =
|
private final byte[] CHECKSUM_FIRST_BYTES =
|
||||||
{(byte)0x10, (byte)0x00, (byte)0x00, (byte)0x00};
|
{(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_INET = 2;
|
||||||
private static final int CHANNEL_BINDING_AF_INET6 = 24;
|
private static final int CHANNEL_BINDING_AF_INET6 = 24;
|
||||||
private static final int CHANNEL_BINDING_AF_NULL_ADDR = 255;
|
private static final int CHANNEL_BINDING_AF_NULL_ADDR = 255;
|
||||||
|
@ -333,8 +335,8 @@ abstract class InitialToken extends Krb5Token {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getAddrType(InetAddress addr) {
|
private int getAddrType(InetAddress addr, int defValue) {
|
||||||
int addressType = CHANNEL_BINDING_AF_NULL_ADDR;
|
int addressType = defValue;
|
||||||
|
|
||||||
if (addr instanceof Inet4Address)
|
if (addr instanceof Inet4Address)
|
||||||
addressType = CHANNEL_BINDING_AF_INET;
|
addressType = CHANNEL_BINDING_AF_INET;
|
||||||
|
@ -344,7 +346,7 @@ abstract class InitialToken extends Krb5Token {
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] getAddrBytes(InetAddress addr) throws GSSException {
|
private byte[] getAddrBytes(InetAddress addr) throws GSSException {
|
||||||
int addressType = getAddrType(addr);
|
int addressType = getAddrType(addr, CHANNEL_BINDING_AF_NULL_ADDR);
|
||||||
byte[] addressBytes = addr.getAddress();
|
byte[] addressBytes = addr.getAddress();
|
||||||
if (addressBytes != null) {
|
if (addressBytes != null) {
|
||||||
switch (addressType) {
|
switch (addressType) {
|
||||||
|
@ -375,8 +377,16 @@ abstract class InitialToken extends Krb5Token {
|
||||||
InetAddress acceptorAddress = channelBinding.getAcceptorAddress();
|
InetAddress acceptorAddress = channelBinding.getAcceptorAddress();
|
||||||
int size = 5*4;
|
int size = 5*4;
|
||||||
|
|
||||||
int initiatorAddressType = getAddrType(initiatorAddress);
|
// LDAP TLS Channel Binding requires CHANNEL_BINDING_AF_UNSPEC address type
|
||||||
int acceptorAddressType = getAddrType(acceptorAddress);
|
// 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;
|
byte[] initiatorAddressBytes = null;
|
||||||
if (initiatorAddress != 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_TIME = 11;
|
||||||
const int TYPE_CRED_USAGE = 12;
|
const int TYPE_CRED_USAGE = 12;
|
||||||
|
|
||||||
|
static jclass tlsCBCl = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Class: sun_security_jgss_wrapper_GSSLibStub
|
* Class: sun_security_jgss_wrapper_GSSLibStub
|
||||||
* Method: init
|
* Method: init
|
||||||
|
@ -69,6 +71,17 @@ Java_sun_security_jgss_wrapper_GSSLibStub_init(JNIEnv *env,
|
||||||
failed = loadNative(libName);
|
failed = loadNative(libName);
|
||||||
(*env)->ReleaseStringUTFChars(env, jlibName, 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) {
|
if (!failed) {
|
||||||
return JNI_TRUE;
|
return JNI_TRUE;
|
||||||
} else {
|
} else {
|
||||||
|
@ -154,11 +167,13 @@ void deleteGSSCB(gss_channel_bindings_t cb) {
|
||||||
if (cb == GSS_C_NO_CHANNEL_BINDINGS) return;
|
if (cb == GSS_C_NO_CHANNEL_BINDINGS) return;
|
||||||
|
|
||||||
/* release initiator address */
|
/* 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));
|
resetGSSBuffer(&(cb->initiator_address));
|
||||||
}
|
}
|
||||||
/* release acceptor 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));
|
resetGSSBuffer(&(cb->acceptor_address));
|
||||||
}
|
}
|
||||||
/* release application data */
|
/* release application data */
|
||||||
|
@ -189,9 +204,19 @@ gss_channel_bindings_t newGSSCB(JNIEnv *env, jobject jcb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize addrtype in CB first
|
// initialize addrtype in CB first
|
||||||
|
// 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->initiator_addrtype = GSS_C_AF_NULLADDR;
|
||||||
cb->acceptor_addrtype = GSS_C_AF_NULLADDR;
|
cb->acceptor_addrtype = GSS_C_AF_NULLADDR;
|
||||||
|
}
|
||||||
// addresses needs to be initialized to empty
|
// addresses needs to be initialized to empty
|
||||||
memset(&cb->initiator_address, 0, sizeof(cb->initiator_address));
|
memset(&cb->initiator_address, 0, sizeof(cb->initiator_address));
|
||||||
memset(&cb->acceptor_address, 0, sizeof(cb->acceptor_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;
|
import javax.security.auth.callback.CallbackHandler;
|
||||||
|
|
||||||
// JGSS
|
// JGSS
|
||||||
|
import sun.security.jgss.krb5.internal.TlsChannelBindingImpl;
|
||||||
import org.ietf.jgss.*;
|
import org.ietf.jgss.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -150,6 +151,16 @@ final class GssKrb5Client extends GssKrb5Base implements SaslClient {
|
||||||
}
|
}
|
||||||
secCtx.requestMutualAuth(mutual);
|
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
|
// Always specify potential need for integrity and confidentiality
|
||||||
// Decision will be made during final handshake
|
// Decision will be made during final handshake
|
||||||
secCtx.requestConf(true);
|
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…
Add table
Add a link
Reference in a new issue