>From aff612d055519b9a6acfa5c84f8e24ab45a70f2a Mon Sep 17 00:00:00 2001
From: Christina Fu <cfu@redhat.com>
Date: Wed, 5 Aug 2015 16:21:51 -0700
Subject: [PATCH] Ticket 1531 Directory auth plugin requires LDAP anonymous
 binds - This patch adds a feature to allow a directory based authentication
 plugin to use bound ldap conneciton instead of anonymous. Two files need to
 be edited 1. <instance>/conf/password.conf   add a "tag" and the password of
 the binding user dn to the file   e.g. externalLDAP=password123 2.
 <instance>/ca/CS.cfg   add the tag to cms.passwordlist:   e.g.
 cms.passwordlist=internaldb,replicationdb,externalLDAP   add the authsPrefix
 of the auths entry for the authentication instance   e.g.
 externalLDAP.authsPrefix=auths.instance.UserDirEnrollment   add relevant
 entries to the authentication instance   e.g.
 auths.instance.UserDirEnrollment.ldap.ldapBoundConn=true       
 auths.instance.UserDirEnrollment.ldap.ldapauth.authtype=BasicAuth       
 auths.instance.UserDirEnrollment.ldap.ldapauth.bindDN=uid=rhcs,ou=serviceaccounts,dc=EXAMPLE,dc=com
       
 auths.instance.UserDirEnrollment.ldap.ldapauth.bindPWPrompt=externalLDAP

---
 .../cms/authentication/DirBasedAuthentication.java | 54 ++++++++++++++++++----
 .../authentication/UdnPwdDirAuthentication.java    | 12 +++++
 .../authentication/UidPwdDirAuthentication.java    | 12 +++++
 .../authentication/UidPwdPinDirAuthentication.java | 12 +++++
 .../authentication/UserPwdDirAuthentication.java   | 12 +++++
 .../src/com/netscape/cmscore/apps/CMSEngine.java   | 41 +++++++++++++++-
 6 files changed, 131 insertions(+), 12 deletions(-)

diff --git a/base/server/cms/src/com/netscape/cms/authentication/DirBasedAuthentication.java b/base/server/cms/src/com/netscape/cms/authentication/DirBasedAuthentication.java
index a8a95284df14bd287f7a9a0f5ccba43bf174c4a8..d723a5756c2c83761120cf627c27154b06310595 100644
--- a/base/server/cms/src/com/netscape/cms/authentication/DirBasedAuthentication.java
+++ b/base/server/cms/src/com/netscape/cms/authentication/DirBasedAuthentication.java
@@ -84,6 +84,8 @@ public abstract class DirBasedAuthentication
     protected static final String PROP_DNPATTERN = "dnpattern";
     protected static final String PROP_LDAPSTRINGATTRS = "ldapStringAttributes";
     protected static final String PROP_LDAPBYTEATTRS = "ldapByteAttributes";
+    protected static final String PROP_LDAP_BOUND_CONN = "ldapBoundConn";
+    protected static final String PROP_LDAP_BOUND_TAG = "ldapauth.bindPWPrompt";
 
     // members
 
@@ -110,6 +112,7 @@ public abstract class DirBasedAuthentication
     /* whether to search for member=<userDN> or member=<mGroupUserIdName>=<uid> */
     protected boolean mSearchGroupUserByUserdn = true;
 
+    protected boolean mBoundConnEnable = false;
     /* factory of anonymous ldap connections */
     protected ILdapConnFactory mConnFactory = null;
 
@@ -130,6 +133,9 @@ public abstract class DirBasedAuthentication
     /* the combined list of LDAP attriubutes to retrieve*/
     protected String[] mLdapAttrs = null;
 
+    /* the password prompt (tag) for the userdn of a bound connection */
+    protected String mTag;
+
     /* default dn pattern if left blank or not set in the config */
     protected static String DEFAULT_DNPATTERN =
             "E=$attr.mail, CN=$attr.cn, O=$dn.o, C=$dn.c";
@@ -255,6 +261,7 @@ public abstract class DirBasedAuthentication
         mName = name;
         mImplName = implName;
         mConfig = config;
+        String method = "DirBasedAuthentication: init: ";
 
         /* initialize ldap server configuration */
         mLdapConfig = mConfig.getSubStore(PROP_LDAP);
@@ -263,22 +270,31 @@ public abstract class DirBasedAuthentication
             if (mBaseDN == null || mBaseDN.trim().equals(""))
                 throw new EPropertyNotFound(CMS.getUserMessage("CMS_BASE_GET_PROPERTY_FAILED", "basedn"));
             mGroupsEnable = mLdapConfig.getBoolean(PROP_GROUPS_ENABLE, false);
-            CMS.debug("DirBasedAuthentication: mGroupsEnable=" + (mGroupsEnable ? "true" : "false"));
+            CMS.debug(method + " mGroupsEnable=" + (mGroupsEnable ? "true" : "false"));
             mGroupsBaseDN = mLdapConfig.getString(PROP_GROUPS_BASEDN, mBaseDN);
-            CMS.debug("DirBasedAuthentication: mGroupsBaseDN="+ mGroupsBaseDN);
+            CMS.debug(method + " mGroupsBaseDN="+ mGroupsBaseDN);
             mGroups= mLdapConfig.getString(PROP_GROUPS, "ou=groups");
-            CMS.debug("DirBasedAuthentication: mGroups="+ mGroups);
+            CMS.debug(method + " mGroups="+ mGroups);
             mGroupObjectClass = mLdapConfig.getString(PROP_GROUP_OBJECT_CLASS, "groupofuniquenames");
-            CMS.debug("DirBasedAuthentication: mGroupObjectClass="+ mGroupObjectClass);
+            CMS.debug(method + " mGroupObjectClass="+ mGroupObjectClass);
             mUserIDName = mLdapConfig.getString(PROP_USERID_NAME, "uid");
-            CMS.debug("DirBasedAuthentication: mUserIDName="+ mUserIDName);
+            CMS.debug(method + " mUserIDName="+ mUserIDName);
             mSearchGroupUserByUserdn = mLdapConfig.getBoolean(PROP_SEARCH_GROUP_USER_BY_USERDN, true);
-            CMS.debug("DirBasedAuthentication: mSearchGroupUserByUserdn="+ mSearchGroupUserByUserdn);
+            CMS.debug(method + " mSearchGroupUserByUserdn="+ mSearchGroupUserByUserdn);
             mGroupUserIDName = mLdapConfig.getString(PROP_GROUP_USERID_NAME, "cn");
-            CMS.debug("DirBasedAuthentication: mGroupUserIDName="+ mGroupUserIDName);
+            CMS.debug(method + " mGroupUserIDName="+ mGroupUserIDName);
         }
-        mConnFactory = CMS.getLdapAnonConnFactory("DirBasedAuthentication");
-        mConnFactory.init(mLdapConfig);
+        mBoundConnEnable = mLdapConfig.getBoolean(PROP_LDAP_BOUND_CONN, false);
+        CMS.debug(method +" mBoundConnEnable =" + (mBoundConnEnable ? "true" : "false"));
+        if (mBoundConnEnable) {
+            mTag = mLdapConfig.getString(PROP_LDAP_BOUND_TAG);
+            CMS.debug(method + " getting ldap bound conn factory using id= " + mTag);
+            mConnFactory = CMS.getLdapBoundConnFactory(mTag);
+        } else {
+            mConnFactory = CMS.getLdapAnonConnFactory("DirBasedAuthentication");
+        }
+        if (mConnFactory != null) // else can try again later when needed
+            mConnFactory.init(mLdapConfig);
 
         /* initialize dn pattern */
         String pattern = mConfig.getString(PROP_DNPATTERN, null);
@@ -372,16 +388,34 @@ public abstract class DirBasedAuthentication
         String userdn = null;
         LDAPConnection conn = null;
         AuthToken authToken = new AuthToken(this);
+        String method = "DirBasedAuthentication: authenticate:";
 
+        CMS.debug(method + " begins...mBoundConnEnable=" + mBoundConnEnable);
         try {
             if (mConnFactory == null) {
-                conn = null;
+                CMS.debug(method + " mConnFactory null, getting conn factory");
+                if (mBoundConnEnable) {
+                    mTag = mLdapConfig.getString(PROP_LDAP_BOUND_TAG);
+                    CMS.debug(method + " getting ldap bound conn factory using id= " + mTag);
+                    mConnFactory = CMS.getLdapBoundConnFactory(mTag);
+                } else {
+                    mConnFactory = CMS.getLdapAnonConnFactory("DirBasedAuthentication");
+                }
+                if (mConnFactory != null) {
+                    mConnFactory.init(mLdapConfig);
+                    CMS.debug(method + " mConnFactory gotten, calling getConn");
+                    conn = mConnFactory.getConn();
+                }
             } else {
+                CMS.debug(method + " mConnFactory class name = " + mConnFactory.getClass().getName());
+                CMS.debug(method + " mConnFactory not null, calling getConn");
                 conn = mConnFactory.getConn();
             }
 
             // authenticate the user and get a user entry.
+            CMS.debug(method + " before authenticate() call");
             userdn = authenticate(conn, authCred, authToken);
+            CMS.debug(method + " after authenticate() call");
             authToken.set(USER_DN, userdn);
 
             // formulate the cert info.
diff --git a/base/server/cms/src/com/netscape/cms/authentication/UdnPwdDirAuthentication.java b/base/server/cms/src/com/netscape/cms/authentication/UdnPwdDirAuthentication.java
index 2f9fc43bd53d6abde848a1143687570ecd935a18..e7313526a764adff64d555a8729c960a5f53f67d 100644
--- a/base/server/cms/src/com/netscape/cms/authentication/UdnPwdDirAuthentication.java
+++ b/base/server/cms/src/com/netscape/cms/authentication/UdnPwdDirAuthentication.java
@@ -134,10 +134,22 @@ public class UdnPwdDirAuthentication extends DirBasedAuthentication {
 
             return userdn;
         } catch (ELdapException e) {
+            CMS.debug("Authenticating: closing bad connection");
+            try {
+                conn.disconnect();
+            } catch (Exception f) {
+                CMS.debug("Authenticating: conn.disconnect() exception =" + f.toString());
+            }
             log(ILogger.LL_FAILURE,
                     "Couldn't get ldap connection. Error: " + e.toString());
             throw e;
         } catch (LDAPException e) {
+            CMS.debug("Authenticating: closing bad connection");
+            try {
+                conn.disconnect();
+            } catch (Exception f) {
+                CMS.debug("Authenticating: conn.disconnect() exception =" + f.toString());
+            }
             switch (e.getLDAPResultCode()) {
             case LDAPException.NO_SUCH_OBJECT:
             case LDAPException.LDAP_PARTIAL_RESULTS:
diff --git a/base/server/cms/src/com/netscape/cms/authentication/UidPwdDirAuthentication.java b/base/server/cms/src/com/netscape/cms/authentication/UidPwdDirAuthentication.java
index 21e024fc43f91624b37b49d00ef63f77750cdfdf..26bfaab81d52fa5ad2c0094a22bfd9656c0f7684 100644
--- a/base/server/cms/src/com/netscape/cms/authentication/UidPwdDirAuthentication.java
+++ b/base/server/cms/src/com/netscape/cms/authentication/UidPwdDirAuthentication.java
@@ -235,10 +235,22 @@ public class UidPwdDirAuthentication extends DirBasedAuthentication
             return userdn;
         } catch (ELdapException e) {
             CMS.debug("Authenticating: User authentication failure: "+e);
+            CMS.debug("Authenticating: closing bad connection");
+            try {
+                conn.disconnect();
+            } catch (Exception f) {
+                CMS.debug("Authenticating: conn.disconnect() exception =" + f.toString());
+            }
             log(ILogger.LL_FAILURE, CMS.getLogMessage("CANNOT_CONNECT_LDAP", e.toString()));
             throw e;
         } catch (LDAPException e) {
             CMS.debug("Authenticating: User authentication failure: "+e);
+            CMS.debug("Authenticating: closing bad connection");
+            try {
+                conn.disconnect();
+            } catch (Exception f) {
+                CMS.debug("Authenticating: conn.disconnect() exception =" + f.toString());
+            }
             switch (e.getLDAPResultCode()) {
             case LDAPException.NO_SUCH_OBJECT:
             case LDAPException.LDAP_PARTIAL_RESULTS:
diff --git a/base/server/cms/src/com/netscape/cms/authentication/UidPwdPinDirAuthentication.java b/base/server/cms/src/com/netscape/cms/authentication/UidPwdPinDirAuthentication.java
index ed20740530b303b895372390c8c9ce8a40ff633d..82331dadefeb8bc5366b08641a2280f81880964f 100644
--- a/base/server/cms/src/com/netscape/cms/authentication/UidPwdPinDirAuthentication.java
+++ b/base/server/cms/src/com/netscape/cms/authentication/UidPwdPinDirAuthentication.java
@@ -247,9 +247,21 @@ public class UidPwdPinDirAuthentication extends DirBasedAuthentication
 
             return userdn;
         } catch (ELdapException e) {
+            CMS.debug("Authenticating: closing bad connection");
+            try {
+                conn.disconnect();
+            } catch (Exception f) {
+                CMS.debug("Authenticating: conn.disconnect() exception =" + f.toString());
+            }
             log(ILogger.LL_FAILURE, CMS.getLogMessage("CANNOT_CONNECT_LDAP", e.toString()));
             throw e;
         } catch (LDAPException e) {
+            CMS.debug("Authenticating: closing bad connection");
+            try {
+                conn.disconnect();
+            } catch (Exception f) {
+                CMS.debug("Authenticating: conn.disconnect() exception =" + f.toString());
+            }
             switch (e.getLDAPResultCode()) {
             case LDAPException.NO_SUCH_OBJECT:
             case LDAPException.LDAP_PARTIAL_RESULTS:
diff --git a/base/server/cms/src/com/netscape/cms/authentication/UserPwdDirAuthentication.java b/base/server/cms/src/com/netscape/cms/authentication/UserPwdDirAuthentication.java
index 7bcab25d3e82d242615781be641ff45a68a551f9..a95dd866582443b27ce5ca51b28de278d1775b06 100644
--- a/base/server/cms/src/com/netscape/cms/authentication/UserPwdDirAuthentication.java
+++ b/base/server/cms/src/com/netscape/cms/authentication/UserPwdDirAuthentication.java
@@ -187,9 +187,21 @@ public class UserPwdDirAuthentication extends DirBasedAuthentication
 
             return userdn;
         } catch (ELdapException e) {
+            CMS.debug("Authenticating: closing bad connection");
+            try {
+                conn.disconnect();
+            } catch (Exception f) {
+                CMS.debug("Authenticating: conn.disconnect() exception =" + f.toString());
+            }
             log(ILogger.LL_FAILURE, CMS.getLogMessage("CANNOT_CONNECT_LDAP", e.toString()));
             throw e;
         } catch (LDAPException e) {
+            CMS.debug("Authenticating: closing bad connection");
+            try {
+                conn.disconnect();
+            } catch (Exception f) {
+                CMS.debug("Authenticating: conn.disconnect() exception =" + f.toString());
+            }
             switch (e.getLDAPResultCode()) {
             case LDAPException.NO_SUCH_OBJECT:
             case LDAPException.LDAP_PARTIAL_RESULTS:
diff --git a/base/server/cmscore/src/com/netscape/cmscore/apps/CMSEngine.java b/base/server/cmscore/src/com/netscape/cmscore/apps/CMSEngine.java
index fa2c8147f401d19836e52737b99be0ac1f108298..467836bfc66d67cdbfb3895611993a732ea84bc7 100644
--- a/base/server/cmscore/src/com/netscape/cmscore/apps/CMSEngine.java
+++ b/base/server/cmscore/src/com/netscape/cmscore/apps/CMSEngine.java
@@ -331,6 +331,7 @@ public class CMSEngine implements ICMSEngine {
     }
 
     public void initializePasswordStore(IConfigStore config) throws EBaseException, IOException {
+        System.out.println("CMSEngine.initializePasswordStore() begins");
         // create and initialize mPasswordStore
         getPasswordStore();
 
@@ -345,6 +346,7 @@ public class CMSEngine implements ICMSEngine {
             String binddn;
             String authType;
             LdapConnInfo connInfo = null;
+            System.out.println("CMSEngine.initializePasswordStore(): tag=" + tag);
 
             if (tag.equals("internaldb")) {
                 authType = config.getString("internaldb.ldapauth.authtype", "BasicAuth");
@@ -382,8 +384,43 @@ public class CMSEngine implements ICMSEngine {
                 binddn = config.getString("ca.publish.ldappublish.ldap.ldapauth.bindDN");
 
             } else {
-                // ignore any others for now
-                continue;
+                /*
+                 * This section assumes a generic format of
+                 * <authPrefix>.ldap.xxx
+                 * where <authPrefix> is specified under the tag substore
+                 *
+                 * e.g.  if tag = "externalLDAP"
+                 *   cms.passwordlist=...,externalLDAP
+                 *   externalLDAP.authPrefix=auths.instance.UserDirEnrollment
+                 *
+                 *   auths.instance.UserDirEnrollment.ldap.ldapauth.authtype=BasicAuth
+                 *   auths.instance.UserDirEnrollment.ldap.ldapauth.bindDN=cn=Corporate Directory Manager
+                 *   auths.instance.UserDirEnrollment.ldap.ldapauth.bindPWPrompt=externalLDAP
+                 *   auths.instance.UserDirEnrollment.ldap.ldapconn.host=host.example.com
+                 *   auths.instance.UserDirEnrollment.ldap.ldapconn.port=389
+                 *   auths.instance.UserDirEnrollment.ldap.ldapconn.secureConn=false
+                 */
+                String authPrefix = config.getString(tag + ".authPrefix", null);
+                if (authPrefix ==  null) {
+                    System.out.println("CMSEngine.initializePasswordStore(): authPrefix not found...skipping");
+                    continue;
+                }
+                System.out.println("CMSEngine.initializePasswordStore(): authPrefix=" + authPrefix);
+                authType = config.getString(authPrefix +".ldap.ldapauth.authtype", "BasicAuth");
+                System.out.println("CMSEngine.initializePasswordStore(): authType " + authType);
+                if (!authType.equals("BasicAuth"))
+                    continue;
+
+                connInfo = new LdapConnInfo(
+                        config.getString(authPrefix + ".ldap.ldapconn.host"),
+                        config.getInteger(authPrefix + ".ldap.ldapconn.port"),
+                        config.getBoolean(authPrefix + ".ldap.ldapconn.secureConn"));
+
+                binddn = config.getString(authPrefix + ".ldap.ldapauth.bindDN", null);
+                if (binddn == null) {
+                    System.out.println("CMSEngine.initializePasswordStore(): binddn not found...skipping");
+                    continue;
+                }
             }
 
             do {
-- 
1.8.4.2

