From d5a64d8994c1cf8e07218a2aa61bd4b3c9bfa10c Mon Sep 17 00:00:00 2001 From: longping_tang Date: Thu, 22 Apr 2021 11:31:14 +0800 Subject: [PATCH 1/3] Add the choice of whether to perform certificate verification when connect to ldaps:// --- .../hudson/security/LDAPSecurityRealm.java | 2 +- .../plugins/ldap/BlindSSLSocketFactory.java | 96 +++++++++++++++++++ .../plugins/ldap/LDAPConfiguration.java | 38 +++++++- .../ldap/LDAPConfiguration/config.jelly | 3 + .../LDAPConfiguration/help-sslVerify.html | 9 ++ 5 files changed, 142 insertions(+), 6 deletions(-) create mode 100644 src/main/java/jenkins/security/plugins/ldap/BlindSSLSocketFactory.java create mode 100644 src/main/resources/jenkins/security/plugins/ldap/LDAPConfiguration/help-sslVerify.html diff --git a/src/main/java/hudson/security/LDAPSecurityRealm.java b/src/main/java/hudson/security/LDAPSecurityRealm.java index e2b7e765..bfa0d193 100644 --- a/src/main/java/hudson/security/LDAPSecurityRealm.java +++ b/src/main/java/hudson/security/LDAPSecurityRealm.java @@ -1587,7 +1587,7 @@ public FormValidation validate(LDAPSecurityRealm realm, String user, String pass // we can only do deep validation if the connection is correct LDAPConfiguration.LDAPConfigurationDescriptor confDescriptor = Jenkins.getActiveInstance().getDescriptorByType(LDAPConfiguration.LDAPConfigurationDescriptor.class); for (LDAPConfiguration configuration : realm.getConfigurations()) { - FormValidation connectionCheck = confDescriptor.doCheckServer(configuration.getServerUrl(), configuration.getManagerDN(), configuration.getManagerPasswordSecret(),configuration.getRootDN()); + FormValidation connectionCheck = confDescriptor.doCheckServer(configuration.getServerUrl(), configuration.isSslVerify(),configuration.getManagerDN(), configuration.getManagerPasswordSecret(),configuration.getRootDN()); if (connectionCheck.kind != FormValidation.Kind.OK) { return connectionCheck; } diff --git a/src/main/java/jenkins/security/plugins/ldap/BlindSSLSocketFactory.java b/src/main/java/jenkins/security/plugins/ldap/BlindSSLSocketFactory.java new file mode 100644 index 00000000..d5ea37c4 --- /dev/null +++ b/src/main/java/jenkins/security/plugins/ldap/BlindSSLSocketFactory.java @@ -0,0 +1,96 @@ +package jenkins.security.plugins.ldap; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.GeneralSecurityException; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; + +public class BlindSSLSocketFactory extends SSLSocketFactory { + private static final BlindSSLSocketFactory INSTANCE; + + static { + final X509TrustManager dummyTrustManager = + new X509TrustManager() { + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) {} + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) {} + }; + try { + final SSLContext context = SSLContext.getInstance("SSL"); + final TrustManager[] trustManagers = {dummyTrustManager}; + final SecureRandom rng = new SecureRandom(); + context.init(null, trustManagers, rng); + INSTANCE = new BlindSSLSocketFactory(context.getSocketFactory()); + } catch (GeneralSecurityException e) { + throw new RuntimeException("Cannot create BlindSslSocketFactory", e); + } + } + + public static SocketFactory getDefault() { + return INSTANCE; + } + + private final SSLSocketFactory sslFactory; + + private BlindSSLSocketFactory(SSLSocketFactory sslFactory) { + this.sslFactory = sslFactory; + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) + throws IOException { + return sslFactory.createSocket(s, host, port, autoClose); + } + + @Override + public String[] getDefaultCipherSuites() { + return sslFactory.getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return sslFactory.getSupportedCipherSuites(); + } + + @Override + public Socket createSocket() throws IOException { + return sslFactory.createSocket(); + } + + @Override + public Socket createSocket(String host, int port) throws IOException, UnknownHostException { + return sslFactory.createSocket(host, port); + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + return sslFactory.createSocket(host, port); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) + throws IOException, UnknownHostException { + return sslFactory.createSocket(host, port, localHost, localPort); + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) + throws IOException { + return sslFactory.createSocket(address, port, localAddress, localPort); + } +} diff --git a/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java b/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java index 7d8ccfbd..9b4d04dd 100644 --- a/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java +++ b/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java @@ -100,6 +100,11 @@ public class LDAPConfiguration extends AbstractDescribableImpl props = new Hashtable(); if(managerDN!=null && managerDN.trim().length() > 0 && !"undefined".equals(managerDN)) { props.put(Context.SECURITY_PRINCIPAL,managerDN); @@ -412,7 +430,10 @@ public FormValidation doCheckServer(@QueryParameter String value, @QueryParamete props.put(Context.SECURITY_CREDENTIALS,managerPassword); } props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); - props.put(Context.PROVIDER_URL, LDAPSecurityRealm.toProviderUrl(server,rootDN)); + props.put(Context.PROVIDER_URL, url); + if(url.startsWith("ldaps:") && !sslVerify) { + props.put("java.naming.ldap.factory.socket", BlindSSLSocketFactory.class.getName()); + } DirContext ctx = new InitialDirContext(props); ctx.getAttributes(""); @@ -456,14 +477,18 @@ public DescriptorExtensionList props = new Hashtable(); + String url = LDAPSecurityRealm.toProviderUrl(getServerUrl(), ""); if (managerDN != null) { props.put(Context.SECURITY_PRINCIPAL, managerDN); props.put(Context.SECURITY_CREDENTIALS, getManagerPassword()); } props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); - props.put(Context.PROVIDER_URL, LDAPSecurityRealm.toProviderUrl(getServerUrl(), "")); + props.put(Context.PROVIDER_URL, url); + if(url.startsWith("ldaps:") && !sslVerify) { + props.put("java.naming.ldap.factory.socket", BlindSSLSocketFactory.class.getName()); + } DirContext ctx = new InitialDirContext(props); Attributes atts = ctx.getAttributes(""); @@ -574,6 +599,9 @@ public ApplicationContext createApplicationContext(LDAPSecurityRealm realm) { Map vars = new HashMap<>(); vars.put("com.sun.jndi.ldap.connect.timeout", "30000"); // timeout if no connection after 30 seconds vars.put("com.sun.jndi.ldap.read.timeout", "60000"); // timeout if no response after 60 seconds + if(getLDAPURL().startsWith("ldaps:") && !sslVerify) { + vars.put("java.naming.ldap.factory.socket", BlindSSLSocketFactory.class.getName()); + } vars.putAll(getExtraEnvVars()); contextSource.setBaseEnvironmentProperties(vars); contextSource.afterPropertiesSet(); diff --git a/src/main/resources/jenkins/security/plugins/ldap/LDAPConfiguration/config.jelly b/src/main/resources/jenkins/security/plugins/ldap/LDAPConfiguration/config.jelly index 3ee96c6a..b2cf0f58 100644 --- a/src/main/resources/jenkins/security/plugins/ldap/LDAPConfiguration/config.jelly +++ b/src/main/resources/jenkins/security/plugins/ldap/LDAPConfiguration/config.jelly @@ -4,6 +4,9 @@ + + + diff --git a/src/main/resources/jenkins/security/plugins/ldap/LDAPConfiguration/help-sslVerify.html b/src/main/resources/jenkins/security/plugins/ldap/LDAPConfiguration/help-sslVerify.html new file mode 100644 index 00000000..c47f1062 --- /dev/null +++ b/src/main/resources/jenkins/security/plugins/ldap/LDAPConfiguration/help-sslVerify.html @@ -0,0 +1,9 @@ +
+

+ If checked and ldap server is an ldaps:// style URL, requiring the certificate to be verified. +

+

+ If unchecked (the default), Jenkins will not verify the server certificate when it connects to + perform a query. +

+
\ No newline at end of file From e98596f06980d742de91ba735a94d4b8f27a6913 Mon Sep 17 00:00:00 2001 From: longping_tang Date: Fri, 28 May 2021 17:28:08 +0800 Subject: [PATCH 2/3] initialize sslVerify in constructor --- .../plugins/ldap/LDAPConfiguration.java | 18 +++++++++++------- .../hudson/security/LDAPSecurityRealmTest.java | 4 ++++ .../LDAPSecurityRealmTestNoSecretExpected.yml | 1 + 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java b/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java index 9b4d04dd..989d017a 100644 --- a/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java +++ b/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java @@ -103,7 +103,7 @@ public class LDAPConfiguration extends AbstractDescribableImpl Date: Mon, 19 Sep 2022 17:33:42 +0800 Subject: [PATCH 3/3] modify sslVerify's default value to true --- .../jenkins/security/plugins/ldap/LDAPConfiguration.java | 4 ++-- .../security/plugins/ldap/LDAPConfiguration/config.jelly | 2 +- .../plugins/ldap/LDAPConfiguration/help-sslVerify.html | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java b/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java index 989d017a..8d900f75 100644 --- a/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java +++ b/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java @@ -101,7 +101,7 @@ public class LDAPConfiguration extends AbstractDescribableImpl - + diff --git a/src/main/resources/jenkins/security/plugins/ldap/LDAPConfiguration/help-sslVerify.html b/src/main/resources/jenkins/security/plugins/ldap/LDAPConfiguration/help-sslVerify.html index c47f1062..d5f6a841 100644 --- a/src/main/resources/jenkins/security/plugins/ldap/LDAPConfiguration/help-sslVerify.html +++ b/src/main/resources/jenkins/security/plugins/ldap/LDAPConfiguration/help-sslVerify.html @@ -1,9 +1,8 @@

- If checked and ldap server is an ldaps:// style URL, requiring the certificate to be verified. + If unchecked and ldap server is an ldaps:// style URL, Jenkins will not verify the server certificate when it connects to perform a query

- If unchecked (the default), Jenkins will not verify the server certificate when it connects to - perform a query. + If checked (the default), requiring the certificate to be verified.

\ No newline at end of file