Skip to content

Commit

Permalink
Merge pull request #227 from cconlon/x509ExtendedTrustManagerLDAPSIde…
Browse files Browse the repository at this point in the history
…ntification

JSSE: add LDAPS endpoint identification to X509ExtendedTrustManager
  • Loading branch information
JacobBarthelmeh authored Oct 29, 2024
2 parents ad59d74 + e01db4b commit 14685a6
Show file tree
Hide file tree
Showing 12 changed files with 815 additions and 161 deletions.
2 changes: 1 addition & 1 deletion native/com_wolfssl_WolfSSLCertificate.c
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLCertificate_X509_1check_1host

hostname = (*jenv)->GetStringUTFChars(jenv, chk, 0);
if (hostname != NULL) {
/* flags and peerNamePtr not used */
/* peerNamePtr not used */
ret = wolfSSL_X509_check_host(x509, hostname,
XSTRLEN(hostname), (unsigned int)flags, NULL);
}
Expand Down
5 changes: 5 additions & 0 deletions src/java/com/wolfssl/WolfSSL.java
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,11 @@ public enum TLS_VERSION {
* level with WolfSSLContext.setDevId() and WolfSSLSession.setDevId() */
public static int devId = WolfSSL.INVALID_DEVID;

/* ------------------------- Flag Values ---------------------------- */
/** WolfSSLCertificate.checkHost() match only wildcards in left-most
* position, used for LDAPS hostname verification. */
public static int WOLFSSL_LEFT_MOST_WILDCARD_ONLY = 0x40;

/* ---------------------------- locks ------------------------------- */

/* lock for cleanup */
Expand Down
24 changes: 23 additions & 1 deletion src/java/com/wolfssl/WolfSSLCertificate.java
Original file line number Diff line number Diff line change
Expand Up @@ -1439,10 +1439,32 @@ public int getExtensionSet(String oid) throws IllegalStateException {
*/
public int checkHost(String hostname) throws IllegalStateException {

return checkHost(hostname, 0);
}

/**
* Checks that given hostname matches this certificate SubjectAltName
* or CommonName entries, behavior can be controlled via flags.
*
* @param hostname Hostname to check certificate against
* @param flags Flags to control hostname check behavior. Supported options
* include WolfSSL.WOLFSSL_LEFT_MOST_WILDCARD_ONLY to only match
* wildcards on left-most position.
*
* @return WolfSSL.SSL_SUCCESS on successful hostname match,
* WolfSSL.SSL_FAILURE on invalid match or error, or
* WolfSSL.NOT_COMPILED_IN if native wolfSSL has been compiled
* with NO_ASN defined and native API is not available.
*
* @throws IllegalStateException if WolfSSLCertificate has been freed.
*/
public int checkHost(String hostname, long flags)
throws IllegalStateException {

confirmObjectIsActive();

synchronized (x509Lock) {
return X509_check_host(this.x509Ptr, hostname, 0, 0);
return X509_check_host(this.x509Ptr, hostname, flags, 0);
}
}

Expand Down
127 changes: 90 additions & 37 deletions src/java/com/wolfssl/provider/jsse/WolfSSLTrustX509.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@

/**
* wolfSSL implementation of X509TrustManager, extends
* X509ExtendedTrustManager for additional hostname verification for HTTPS.
* X509ExtendedTrustManager for additional hostname verification for
* HTTPS (RFC 2818) and LDAPS (RFC 2830).
*
* @author wolfSSL
*/
Expand All @@ -59,6 +60,11 @@ public final class WolfSSLTrustX509 extends X509ExtendedTrustManager

private KeyStore store = null;

/** X509ExtendedTrustManager hostname type HTTPS */
private static int HOSTNAME_TYPE_HTTPS = 1;
/** X509ExtendedTrustManager hostname type LDAPS */
private static int HOSTNAME_TYPE_LDAPS = 2;

/**
* Create new WolfSSLTrustX509 object
*
Expand Down Expand Up @@ -430,18 +436,25 @@ private List<X509Certificate> certManagerVerify(
}

/**
* Verify hostname using HTTPS verification method.
* Verify hostname using HTTPS or LDAPS verification method.
*
* This method does the following operations in an attempt to verify
* the HTTPS type hostname:
* For HTTPS hostname verification (RFC 2818):
*
* 1. If SNI name has been received during TLS handshake, try to
* - If SNI name has been received during TLS handshake, try to
* first verify peer certificate against that. Skip this step when
* on server side verifying the client, since server does not set
* an SNI for the client.
* 2. Otherwise, try to verify certificate against SSLSocket
* hostname (SSLSession.getHostName()).
* 3. If both of the above fail, fail hostname verification.
* - Otherwise, try to verify certificate against SSLSocket or SSLEngine
* hostname (getHandshakeSession().getHostName()).
* - If both of the above fail, fail hostname verification.
* - Hostname matching rules for HTTPS come from RFC 2818
*
* For LDAPS hostname verification (RFC 2830):
*
* - Try to verify certificate against hostname used to create
* the SSLSocket or SSLEngine, obtained via
* getHandshakeSession().getPeerHost().
* - Hostname matching rules for LDAPS come from RFC 2830
*
* @param cert peer certificate
* @param socket SSLSocket associated with connection to peer. Only one
Expand All @@ -452,10 +465,13 @@ private List<X509Certificate> certManagerVerify(
* null.
* @param isClient true if we are calling this from client side, otherwise
* false if calling from server side.
* @param type type of hostname to verify, options are
* HOSTNAME_TYPE_HTTPS or HOSTNAME_TYPE_LDAPS
* @throws CertificateException if hostname cannot be verified
*/
private void verifyHTTPSHostname(X509Certificate cert, SSLSocket socket,
SSLEngine engine, boolean isClient) throws CertificateException {
private void verifyHostnameByType(X509Certificate cert, SSLSocket socket,
SSLEngine engine, boolean isClient, int type)
throws CertificateException {

String peerHost = null;
List<SNIServerName> sniNames = null;
Expand All @@ -464,8 +480,16 @@ private void verifyHTTPSHostname(X509Certificate cert, SSLSocket socket,
WolfSSLCertificate peerCert = null;
int ret = WolfSSL.SSL_FAILURE;

WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"verifying HTTPS hostname");
if (type == HOSTNAME_TYPE_HTTPS) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"verifying hostname type HTTPS");
} else if (type == HOSTNAME_TYPE_LDAPS) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"verifying hostname type LDAPS");
} else {
throw new CertificateException("Unsupported hostname type, " +
"HTTPS and LDAPS only supported currently: " + type);
}

/* Get session associated with SSLSocket or SSLEngine */
try {
Expand All @@ -485,12 +509,15 @@ else if (engine != null) {
peerHost = session.getPeerHost();
}

/* Get SNI name if SSLSocket has received that from peer. Only check
* this when on the client side and verifying a server since SNI
* holding expected server name is available on client-side but not
* vice-versa */
if (session != null && isClient &&
(session instanceof ExtendedSSLSession)) {
/* Get SNI name if SSLSocket/SSLEngine has received that from peer.
* Only check this when on the client side and verifying a server since
* SNI holding expected server name is available on client-side but not
* vice-versa. Also only checked for HTTPS type, not LDAPS. As per
* RFC 2830, the client MUST use the server hostname it used to open
* the LDAP connection. */
if ((session != null) && isClient &&
(session instanceof ExtendedSSLSession) &&
(type == HOSTNAME_TYPE_HTTPS)) {
sniNames = ((ExtendedSSLSession)session).getRequestedServerNames();

for (SNIServerName name : sniNames) {
Expand All @@ -517,8 +544,8 @@ else if (engine != null) {
throw new CertificateException(e);
}

/* Try verifying hostname against SNI name */
if (isClient) {
/* Try verifying hostname against SNI name, if HTTPS type */
if (isClient && (type == HOSTNAME_TYPE_HTTPS)) {
if (sniHostName != null) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"trying hostname verification against SNI: " + sniHostName);
Expand All @@ -541,13 +568,18 @@ else if (engine != null) {
}
}

/* Try verifying hostname against peerHost from SSLSocket/Engine */
/* Try verifying hostname against peerHost from SSLSocket/SSLEngine */
if (peerHost != null) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"trying hostname verification against peer host: " +
peerHost);
"trying hostname verification against peer host: " + peerHost);

ret = peerCert.checkHost(peerHost);
if (type == HOSTNAME_TYPE_LDAPS) {
/* LDAPS requires wildcard left-most matching only */
ret = peerCert.checkHost(peerHost,
WolfSSL.WOLFSSL_LEFT_MOST_WILDCARD_ONLY);
} else {
ret = peerCert.checkHost(peerHost);
}
if (ret == WolfSSL.SSL_SUCCESS) {
/* Hostname successfully verified against peer host name */
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
Expand All @@ -558,10 +590,16 @@ else if (engine != null) {
}

if (isClient) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"hostname verification failed for server peer cert, " +
"tried SNI (" + sniHostName + "), peer host (" + peerHost +
")\n" + peerCert);
if (type == HOSTNAME_TYPE_HTTPS) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"hostname verification failed for server peer cert, " +
"tried SNI (" + sniHostName + "), peer host (" + peerHost +
")\n" + peerCert);
} else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"hostname verification failed for server peer cert, " +
"peer host (" + peerHost + ")\n" + peerCert);
}
} else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"hostname verification failed for client peer cert, " +
Expand Down Expand Up @@ -593,7 +631,7 @@ protected void verifyHostname(X509Certificate cert,
SSLParameters sslParams = null;
SSLSession session = null;

/* Hostname verification only done if Socket is of SSLSocket,
/* Hostname verification on Socket done only if Socket is of SSLSocket,
* not null, and connected */
if ((socket != null) && (socket instanceof SSLSocket) &&
(socket.isConnected())) {
Expand All @@ -614,8 +652,15 @@ protected void verifyHostname(X509Certificate cert,
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"verifying hostname, endpoint identification " +
"algorithm = HTTPS");
verifyHTTPSHostname(cert, (SSLSocket)socket,
null, isClient);
verifyHostnameByType(cert, (SSLSocket)socket,
null, isClient, HOSTNAME_TYPE_HTTPS);
}
else if (endpointIdAlgo.equals("LDAPS")) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"verifying hostname, endpoint identification " +
"algorithm = LDAPS");
verifyHostnameByType(cert, (SSLSocket)socket,
null, isClient, HOSTNAME_TYPE_LDAPS);
}
else {
throw new CertificateException(
Expand Down Expand Up @@ -647,7 +692,15 @@ else if (engine != null) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"verifying hostname, endpoint identification " +
"algorithm = HTTPS");
verifyHTTPSHostname(cert, null, engine, isClient);
verifyHostnameByType(cert, null, engine, isClient,
HOSTNAME_TYPE_HTTPS);
}
else if (endpointIdAlgo.equals("LDAPS")) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"verifying hostname, endpoint identification " +
"algorithm = LDAPS");
verifyHostnameByType(cert, null, engine, isClient,
HOSTNAME_TYPE_LDAPS);
}
else {
throw new CertificateException(
Expand Down Expand Up @@ -708,13 +761,13 @@ public void checkClientTrusted(X509Certificate[] certs, String type)
* Try to build and validate the client certificate chain based on the
* provided certificates and authentication type.
*
* Also does hostname verification internally if Endpoint Identification
* Does hostname verification internally if Endpoint Identification
* Algorithm has been set by application in SSLParameters, and that
* Algorithm matches "HTTPS". If that is set, hostname verification is
* done using SNI first then peer host value.
* Algorithm matches "HTTPS" or "LDAPS". If "HTTPS" is set, hostname
* verification is done using SNI first then peer host value.
*
* Other Endpoint Identification Algorithms besides "HTTPS" are not
* currently supported.
* Other Endpoint Identification Algorithms besides "HTTPS" and "LDAPS"
* are not currently supported.
*
* @param certs peer certificate chain
* @param type authentication type based on the client certificate
Expand Down
Loading

0 comments on commit 14685a6

Please sign in to comment.