From 9dd3ac2da23ea053f4784356823213d6354c35f8 Mon Sep 17 00:00:00 2001
From: Jack Magne <jmagne@dhcp-16-206.sjc.redhat.com>
Date: Tue, 16 Aug 2016 16:58:49 -0700
Subject: [PATCH] Authentication Instance Id PinDirEnrollment with authType
 value as SslclientAuth is not working.

Ticket #1578

The fixing of this problem required the following:

1. Hook up a java callback that is designed to allow the selection of a candidate
client auth cert to be sent to Ldap in the LdapSSLSocket factory object.

Previously we simply manually set the desired client auth cert nickname, which is provided
by the console interface when cofiguring the "removePin" portion of the UidPinDir Authentication method.

Doing it this way has the benefit of giving us some logging to show when the actual client auth cert is being
requested by the server. We get to see the list of candidate certs and when we match one of those with the requested
cert name, established by the console.

This client auth problem applies ONLY to the connection pool that is used to remove the pin attribute from
an external authentication directory.

2. Previously the code, when setting up client auth for "removePin", would make one single call to create the SSL socket
to connect to ldap over client auth. Now, based on some code I saw in the JSS test suite, the socket is constructed in two
steps. Doing this causes things to work. Further investigation down the line could figure out what is going on at the lower level.

3. Was able to test this to work with the reported problem directory server provided by QE. Note: for pin removal to work, we must also
make sure that the user we authenticating to (through client auth) has the power to actually remove the pin attribute from various users.
---
 .../cmscore/ldapconn/LdapJssSSLSocketFactory.java  | 69 ++++++++++++++++++++--
 1 file changed, 64 insertions(+), 5 deletions(-)

diff --git a/base/server/cmscore/src/com/netscape/cmscore/ldapconn/LdapJssSSLSocketFactory.java b/base/server/cmscore/src/com/netscape/cmscore/ldapconn/LdapJssSSLSocketFactory.java
index 182812c..0396ca9 100644
--- a/base/server/cmscore/src/com/netscape/cmscore/ldapconn/LdapJssSSLSocketFactory.java
+++ b/base/server/cmscore/src/com/netscape/cmscore/ldapconn/LdapJssSSLSocketFactory.java
@@ -18,9 +18,16 @@
 package com.netscape.cmscore.ldapconn;
 
 import java.io.IOException;
+import java.net.InetAddress;
 import java.net.Socket;
 import java.net.UnknownHostException;
+import java.util.Iterator;
+import java.util.Vector;
 
+import netscape.ldap.LDAPException;
+import netscape.ldap.LDAPSSLSocketFactoryExt;
+
+import org.mozilla.jss.ssl.SSLClientCertificateSelectionCallback;
 import org.mozilla.jss.ssl.SSLHandshakeCompletedEvent;
 import org.mozilla.jss.ssl.SSLHandshakeCompletedListener;
 import org.mozilla.jss.ssl.SSLSocket;
@@ -28,9 +35,6 @@ import org.mozilla.jss.ssl.SSLSocket;
 import com.netscape.certsrv.apps.CMS;
 import com.netscape.certsrv.logging.ILogger;
 
-import netscape.ldap.LDAPException;
-import netscape.ldap.LDAPSSLSocketFactoryExt;
-
 /**
  * Uses HCL ssl socket.
  *
@@ -54,7 +58,22 @@ public class LdapJssSSLSocketFactory implements LDAPSSLSocketFactoryExt {
             /*
              * let inherit TLS range and cipher settings
              */
-            s = new SSLSocket(host, port);
+
+            if (mClientAuthCertNickname == null) {
+                s = new SSLSocket(host, port);
+            }
+            else {
+                //Let's create a selection callback in the case the client auth
+                //No longer manually set the cert name.
+                //This two step process, used in the JSS client auth test suite,
+                //appears to be needed to get this working.
+
+                Socket js = new Socket(InetAddress.getByName(host), port);
+                s = new SSLSocket(js, host,
+                        null,
+                        new SSLClientCertificateSelectionCB(mClientAuthCertNickname));
+            }
+
             s.setUseClientMode(true);
             s.enableV2CompatibleHello(false);
 
@@ -67,7 +86,9 @@ public class LdapJssSSLSocketFactory implements LDAPSSLSocketFactoryExt {
                 mClientAuth = true;
                 CMS.debug("LdapJssSSLSocket: set client auth cert nickname " +
                         mClientAuthCertNickname);
-                s.setClientCertNickname(mClientAuthCertNickname);
+
+                //We have already established the manual cert selection callback
+                //Doing it this way will provide some debugging info on the candidate certs
             }
             s.forceHandshake();
 
@@ -114,4 +135,42 @@ public class LdapJssSSLSocketFactory implements LDAPSSLSocketFactoryExt {
             CMS.debug("SSL handshake happened");
         }
     }
+
+    static class SSLClientCertificateSelectionCB implements SSLClientCertificateSelectionCallback {
+        String desiredCertName = null;
+
+        public SSLClientCertificateSelectionCB(String clientAuthCertNickname) {
+            CMS.debug("SSLClientCertificateSelectionCB: Setting desired cert nickname to: " + clientAuthCertNickname);
+            desiredCertName = clientAuthCertNickname;
+        }
+
+        @Override
+        public String select(Vector certs) {
+
+            CMS.debug("SSLClientCertificatSelectionCB: Entering!");
+
+            if(desiredCertName == null) {
+                return null;
+            }
+
+            @SuppressWarnings("unchecked")
+            Iterator<String> itr = certs.iterator();
+            String selection = null;
+
+            while(itr.hasNext()){
+                String candidate = itr.next();
+                CMS.debug("Candidate cert: " + candidate);
+                if(desiredCertName.equalsIgnoreCase(candidate)) {
+                    selection = candidate;
+                    CMS.debug("SSLClientCertificateSelectionCB: desired cert found in list: " + desiredCertName);
+                }
+            }
+
+            CMS.debug("SSLClientCertificateSelectionCB: returning: " + selection);
+            return selection;
+
+        }
+
+    }
+
 }
-- 
2.5.0

