BlindSSLFactory for secure LDAP Authentication bypass doesn't work on production

Viewed 622

I've a strange problem I can't get over since days, so I gave up and decided to consult to knowledgeable stackoverflow members.

Which java version I use?

Amazon coretto 1.8.0_275

What I want to achieve?

In my application, user has to authenticate himself using secure ldap connection. After authentication from LDAP directory, user will be redirected to the his homepage. However, I want that the certification check should be bypassed during the connection to the ldap server over secure port.

What I did so far and what works?

From other posts, I found that I need to use BlindSSLFactory class to bypass the certification check and inject this class to properties during ldap query, I added this to my project and If I run the project from eclipse, everything works absolutely fine and certification check is bypassed and user logs in. Note that: In my java truststore i have no signed certification whatsoever.

What doesn't work?

If I compile the project, with an installer I created, and run it as an application(not from eclipse, rather from its own installer), I get the following error. Note: I debug the following line and it prints true, as I set it before the project runs: -Dcom.sun.jndi.ldap.object.disableEndpointIdentification:

javax.naming.CommunicationException: simple bind failed: 10.148.129.11:636
at com.sun.jndi.ldap.LdapClient.authenticate(LdapClient.java:219) ~[na:1.8.0_275]
at com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2897) ~[na:1.8.0_275]
at com.sun.jndi.ldap.LdapCtx.<init>(LdapCtx.java:347) ~[na:1.8.0_275]
at com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxFromUrl(LdapCtxFactory.java:225) ~[na:1.8.0_275]
at com.sun.jndi.ldap.LdapCtxFactory.getUsingURL(LdapCtxFactory.java:189) ~[na:1.8.0_275]
at com.sun.jndi.ldap.LdapCtxFactory.getUsingURLs(LdapCtxFactory.java:243) ~[na:1.8.0_275]
at com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance(LdapCtxFactory.java:154) ~[na:1.8.0_275]
at com.sun.jndi.ldap.LdapCtxFactory.getInitialContext(LdapCtxFactory.java:84) ~[na:1.8.0_275]
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:684) ~[na:1.8.0_275]
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:313) ~[na:1.8.0_275]
at javax.naming.InitialContext.init(InitialContext.java:244) ~[na:1.8.0_275]
at javax.naming.InitialContext.<init>(InitialContext.java:216) ~[na:1.8.0_275]
at javax.naming.directory.InitialDirContext.<init>(InitialDirContext.java:101) ~[na:1.8.0_275]
at com.ricoh.sdced.festo.pab.web.login.LdapAuthentication.createLoginSession(LdapAuthentication.java:78) [classes!/:na]
at com.ricoh.sdced.festo.pab.web.login.LdapAuthentication.startLoginSession(LdapAuthentication.java:43) [classes!/:na]
at com.ricoh.sdced.festo.pab.web.views.LoginView.performLogin(LoginView.java:54) [classes!/:na]
at com.ricoh.sdced.festo.pab.web.views.LoginView.lambda$createLoginLayout$565279a2$1(LoginView.java:47) [classes!/:na]
at com.vaadin.flow.component.ComponentEventBus.fireEventForListener(ComponentEventBus.java:205) ~[flow-server-2.1.5.jar!/:2.1.5]
at com.vaadin.flow.component.ComponentEventBus.handleDomEvent(ComponentEventBus.java:373) ~[flow-server-2.1.5.jar!/:2.1.5]
at com.vaadin.flow.component.ComponentEventBus.lambda$addDomTrigger$dd1b7957$1(ComponentEventBus.java:264) ~[flow-server-2.1.5.jar!/:2.1.5]
at com.vaadin.flow.internal.nodefeature.ElementListenerMap.lambda$fireEvent$2(ElementListenerMap.java:441) ~[flow-server-2.1.5.jar!/:2.1.5]
at java.util.ArrayList.forEach(ArrayList.java:1259) ~[na:1.8.0_275]
at com.vaadin.flow.internal.nodefeature.ElementListenerMap.fireEvent(ElementListenerMap.java:441) ~[flow-server-2.1.5.jar!/:2.1.5]
at com.vaadin.flow.server.communication.rpc.EventRpcHandler.handleNode(EventRpcHandler.java:59) ~[flow-server-2.1.5.jar!/:2.1.5]
at com.vaadin.flow.server.communication.rpc.AbstractRpcInvocationHandler.handle(AbstractRpcInvocationHandler.java:64) ~[flow-server-2.1.5.jar!/:2.1.5]
at com.vaadin.flow.server.communication.ServerRpcHandler.handleInvocationData(ServerRpcHandler.java:402) ~[flow-server-2.1.5.jar!/:2.1.5]
at com.vaadin.flow.server.communication.ServerRpcHandler.lambda$handleInvocations$1(ServerRpcHandler.java:383) ~[flow-server-2.1.5.jar!/:2.1.5]
at java.util.ArrayList.forEach(ArrayList.java:1259) ~[na:1.8.0_275]
at com.vaadin.flow.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:383) ~[flow-server-2.1.5.jar!/:2.1.5]
at com.vaadin.flow.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:318) ~[flow-server-2.1.5.jar!/:2.1.5]
at com.vaadin.flow.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:89) ~[flow-server-2.1.5.jar!/:2.1.5]
at com.vaadin.flow.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:40) ~[flow-server-2.1.5.jar!/:2.1.5]
at com.vaadin.flow.server.VaadinService.handleRequest(VaadinService.java:1540) ~[flow-server-2.1.5.jar!/:2.1.5]
at com.vaadin.flow.server.VaadinServlet.service(VaadinServlet.java:247) ~[flow-server-2.1.5.jar!/:2.1.5]
at com.vaadin.flow.spring.SpringServlet.service(SpringServlet.java:95) ~[vaadin-spring-12.1.2.jar!/:na]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733) ~[tomcat-embed-core-9.0.37.jar!/:4.0.FR]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:712) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:459) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:352) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:312) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.springframework.web.servlet.mvc.ServletForwardingController.handleRequestInternal(ServletForwardingController.java:141) ~[spring-webmvc-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
at org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:177) ~[spring-webmvc-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:52) ~[spring-webmvc-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) ~[spring-webmvc-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:652) ~[tomcat-embed-core-9.0.37.jar!/:4.0.FR]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733) ~[tomcat-embed-core-9.0.37.jar!/:4.0.FR]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.37.jar!/:9.0.37]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:93) ~[spring-boot-actuator-2.3.3.RELEASE.jar!/:2.3.3.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1589) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_275]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_275]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.37.jar!/:9.0.37]
at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_275]
Caused by: java.net.SocketException: Connection or outbound has closed
at sun.security.ssl.SSLSocketImpl$AppOutputStream.write(SSLSocketImpl.java:967) ~[na:1.8.0_275]
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82) ~[na:1.8.0_275]
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140) ~[na:1.8.0_275]
at com.sun.jndi.ldap.Connection.writeRequest(Connection.java:448) ~[na:1.8.0_275]
at com.sun.jndi.ldap.Connection.writeRequest(Connection.java:421) ~[na:1.8.0_275]
at com.sun.jndi.ldap.LdapClient.ldapBind(LdapClient.java:359) ~[na:1.8.0_275]
at com.sun.jndi.ldap.LdapClient.authenticate(LdapClient.java:214) ~[na:1.8.0_275]
... 88 common frames omitted

How is my logic in the code?

public class BlindSSLSocketFactory extends SocketFactory {

private static SocketFactory blindFactory = null;
/**
 * Builds an ALL trusting "blind" ssl socket factory.
 */
static {
// create a trust manager that will purposefully fall down on the
// job
    TrustManager[] blindTrustMan = new TrustManager[] { new X509TrustManager() {
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        public void checkClientTrusted(X509Certificate[] c, String a) {
        }

        public void checkServerTrusted(X509Certificate[] c, String a) {
        }
    } };

    // create our "blind" ssl socket factory with our lazy trust manager
    try {
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, blindTrustMan, new java.security.SecureRandom());
        blindFactory = sc.getSocketFactory();
    } catch (GeneralSecurityException e) {
        e.printStackTrace();
    }
}

/**
 * @see javax.net.SocketFactory#getDefault()
 */
public static SocketFactory getDefault() {
    return new BlindSSLSocketFactory();
}

/**
 * @see javax.net.SocketFactory#createSocket(java.lang.String, int)
 */
public Socket createSocket(String arg0, int arg1) throws IOException, UnknownHostException {
    return blindFactory.createSocket(arg0, arg1);
}

/**
 * @see javax.net.SocketFactory#createSocket(java.net.InetAddress, int)
 */
public Socket createSocket(InetAddress arg0, int arg1) throws IOException {
    return blindFactory.createSocket(arg0, arg1);
}

/**
 * @see javax.net.SocketFactory#createSocket(java.lang.String, int,
 *      java.net.InetAddress, int)
 */
public Socket createSocket(String arg0, int arg1, InetAddress arg2, int arg3)
        throws IOException, UnknownHostException {
    return blindFactory.createSocket(arg0, arg1, arg2, arg3);
}

/**
 * @see javax.net.SocketFactory#createSocket(java.net.InetAddress, int,
 *      java.net.InetAddress, int)
 */
public Socket createSocket(InetAddress arg0, int arg1, InetAddress arg2, int arg3) throws IOException {
    return blindFactory.createSocket(arg0, arg1, arg2, arg3);
}

}

And my LDAP login class where I inject this blindSSLFactory Class

@Component
public class LdapAuthentication {

private final Logger logger = LoggerFactory.getLogger(getClass());

private String username;

private String password;

private boolean isLoggedIn;

public LdapAuthentication() {
}

public void startLoginSession(String username, String password)
        throws NamingException {
    
    logger.info("preparing user login details...");
    
    this.username = username;
    this.password = password;
    
    logger.info("user login will be attempted for user: " + this.username);

    this.isLoggedIn = createLoginSession(this.username, this.password);
    
    logger.info("login attempt success result: " + this.isLoggedIn); 
}

private boolean createLoginSession(String username, String password)
        throws NamingException {

    logger.info("creating a LDAP Authentication session...");
    logger.info("System property value for            -Dcom.sun.jndi.ldap.object.disableEndpointIdentification:"
            + System.getProperty("com.sun.jndi.ldap.object.disableEndpointIdentification"));

    String ldapServerUrl = buildLdapPrefix()
            + SettingsResolver.getInstance().getSetting(
                    "ldap.server.address")
            + ":"
            + SettingsResolver.getInstance().getSetting(
                    "ldap.server.port.number");

    logger.info("LDAP authentication URL: " + ldapServerUrl);

    Properties props = new Properties();
    
    //use this line if you wanna discard ssl certificate validation
    props.put("java.naming.ldap.factory.socket",
            BlindSSLSocketFactory.class.getName());
    
    props.put(Context.INITIAL_CONTEXT_FACTORY,
            "com.sun.jndi.ldap.LdapCtxFactory");
    props.put(Context.PROVIDER_URL, ldapServerUrl);
    props.put(Context.SECURITY_PRINCIPAL, username);
    props.put(Context.SECURITY_CREDENTIALS, password);

    InitialDirContext context = null;
    try {
        context = new InitialDirContext(props);
        SearchControls controls = new SearchControls();
        controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        NamingEnumeration<SearchResult> results = context.search(
                toDC(SettingsResolver.getInstance().getSetting(
                        "ldap.server.domain.name")), String.format(
                        "(& (userPrincipalName=%s)(objectClass=user))",
                        this.username), controls);
        
        return results.hasMore();
    } catch (NamingException namingException) {
        logger.error(
                "Exception occurred while authenticating to LDAP Server: ",
                namingException);

        throw namingException;
    } finally {
        try {
            if (context != null)
                context.close();
        } catch (Exception ex) {
        }
    }
}

private static String toDC(String username) {
    String result = "";
    String[] parts = username.split("\\.");
    for (int index = 0; index < parts.length - 1; index++)
        result = result.concat("DC=").concat(parts[index]).concat(",");
    return result.concat("DC=").concat(parts[parts.length - 1]);
}

public boolean isUserLoggedIn() {
    return this.isLoggedIn;
}

private String buildLdapPrefix() {
    String securePortEnabled = SettingsResolver.getInstance().getSetting(
            "ldap.server.secure.port.enabled");

    if (securePortEnabled.contains("true")) {
        return "ldaps://";
    } else {
        return "ldap://";
    }
}

}

2 Answers

Perhaps your BlindSSLFactory is not actually being utilized in the production environment. My money is on java.naming.ldap.factory.socket being overridden elsewhere in the code, as it seems to be the means of letting your app know it should utilize this factory via an SPI like interface

A good starting point would be to remote-debug the JVM and see what the property value is set to during runtime.

Your code looks fine and all the configuration provided seems to be correct.

After digging into the problem - please, see the different comments on the main question - by applying different configurations and debugging strategies, as you indicated, the problem seems to be related with the way the setup, standalone program, is created, and not with the LDAP server integration code presented in the question.

Related